Rails ActiveRecord scope with multiple conditions - ruby-on-rails-3

I have a Rails app where I have a Unit model and Status model. Status has_many units and Unit belongs_to Status.
I wrote a scope on Unit to find all of the Units with a specific Status, "In Service" like so:
scope :in_service, lambda { where(status_id: Status.find_by_unit_status("In Service").id)}
This allows me to call Unit.in_service.count to get a count of all Units that are In Service.
But what I really want to do is write a scope that will allow me to scope out all Units with a Status of In Service, At Post, and At Station to get an accurate view of Units since these other two Statuses are considering the Unit to be available.
I'm not sure how I would write something like this. Where the scope contains multiple conditions or data fields.
Can anyone lend a hand?
Update
I tried writing a class method called available like this:
def self.available
Unit.where(status_id: Status.find_by_unit_status("In Service").id)
.where(status_id: Status.find_by_unit_status("At Post").id)
.where(status_id: Status.find_by_unit_status("At Station").id)
end
I'm not sure if this method even is what I'm looking for since I want to find Units with each of these statuses. I think what I just wrote might be constraining the method to where the Unit must have all of these statuses.

You've got a couple things going on here. First if you want to find all units where the status is one of many, you'll need to pass that as an array like so:
scope :in_service, lambda { where(status_id: Status.where(unit_status: ["In Service", "At Post", "At Station"]).map(&:id))}
Second, in your updated method example, you're chaining a bunch of where clauses together, which will result in each one joined with an AND.

Related

Best way to order data by association value in rails

Trying to order all rows of a model for an ordering filter with react on the front, I have encountered this problem.
For example if I have "rooms" and each reservation having many products, having each product different prices, I came up with this way of ordering the rooms by the their respective lowest valued product or the highest:
scope :high_price, lambda { joins(:products).group('rooms.id').order('max(products.week_price) DESC') }
scope :low_price, lambda { joins(:products).group('rooms.id').order('min(products.week_price) ASC') }
The problem comes when, if I save this into an instance variable:
#ordered_rooms = Room.low_price
And then I try to manipulate given instance, I will run into this issue:
ActiveRecord::StatementInvalid (PG::GroupingError: ERROR: column "products.id" must appear in the GROUP BY clause or be used in an aggregate function
I found some explanation of the problem here, but then my doubt would be how to do this queries so this error does not come up?
I found that, if the metric I was looking for, would be the number of products by room, it would be easier to look up with:
scope :most_products, lambda { order('products_count DESC') }
adding this relation into product:
belongs_to :room, required: false, counter_cache: true
would it be possible to define this kind of cache for other metrics, or how should I go about these queries?

Rails Active Records using maximum and minimum simultaneously

Below both the query work fines
Event.joins(:visit).where(:page_id => 3).group(:visit_id).minimum('events.time')
Event.joins(:visit).where(:page_id => 3).group(:visit_id).maximum('events.time')
I want to do find the diff maximum('events.time') - minimum('events.time') and group by visit id
For this I am writing below query
Event.joins(:visit).where(:page_id => 3).group(:visit_id).(maximum('events.time') - minimum('events.time'))
I am getting error, (undefined method maximum for main:Object)
can anyone help me out for this active records query
That's because you're trying to call maximum and minimum as though they were helper or instance methods. You have to call them on an instance of an ActiveRecord object. In your case I'm guessing that you have class Event < ActiveRecord::Base at the top of your model file.
Even if you change your code to use the ActiveRecord methods, you still have malformed Ruby code at (:visit_id).(maximum. So if the calculation performs and you get a date your code would look like this:
Event.joins(:visit).where(:page_id => 3).group(:visit_id).(SomeTimeStamp), which will throw a different error.
You'll have to rework your query either way, but the important thing to note is that you can't call minimum or maximum as helper methods.

How to get deeply nested errors to get to my REST API?

First, some background:
I have a Company model, a Project model and a Task model. A Project belongs to a company and a Task belongs_to a Project.
The Project model holds several attributes: company_id, date. These attributes uniquely identify a project
I am letting the users create a task by API by POSTing to a URL that contains the details necessary to identify the Project. For example:
POST /projects/<comnpany_name>/<date>/tasks/
In order to make life easier for the users, in case there is no project with the given details, I'd like to create the project on the fly by the given details, and then to create the task and assign it to the project.
...And my problem is:
When there is a problem to create the project, let's say that the company name is not valid, what is the right way to return the error message and communicate to the user?
I'll explain what I mean: I added a create_by_name_and_company_name method to the Project:
def self.create_by_name_and_company_name(name, company_name)
if company = Company.find_by_name(company_name)
project = Project.create(company_id: company.id,
name: name)
else # cannot create this project, trying to communicate the error
project = Project.new(name: name)
project.errors.add(:company, 'must have a valid name')
end
company
end
I was hoping that by returning an unsaved Company object, with errors set, will be a good way communicate the error (This is similar to how rails work when there's a validation error).
The problem is that when calling valid? on the company object, it removed the error I wrote there and adds the regular validation errors (in this case, company can't be blank).
And a bonus question...
And there is a conceptual problem as well: since I'm creating a model by providing parameters that are being used to create the actual attributes, they doesn't always map nicely to the errors[:attr] hash. In this case it is not so bad and I'm using the company field for the company name parameter, but I guess this can get messier when the parameters provided to the create method are less similar to the model attributes.
So what is the preferred approach to tackle that problem? Is there something basically wrong with that approach? if so, what is the preferred approach?
About overriding the default rails validation error message, you need to write your validation constraint like this:
validates_presence_of :name, :message => "must be a valid name"
I figure that it is best to avoid such nesting and stick to a shallower API.

Can anyone explain how CDbCriteria->scopes works?

I've just checked the man page of CDbCriteria, but there is not enough info about it.
This property is available since v1.1.7 and I couldn't find any help for it.
Is it for dynamically changing Model->scopes "on-the-fly"?
Scopes are an easy way to create simple filters by default. With a scope you can sort your results by specific columns automatically, limit the results, apply conditions, etc. In the links provided by #ldg there's a big example of how cool they are:
$posts=Post::model()->published()->recently()->findAll();
Somebody is retrieving all the recently published posts in one single line. They are easier to maintain than inline conditions (for example Post::model()->findAll('status=1')) and are encapsulated inside each model, which means big transparency and ease of use.
Plus, you can create your own parameter based scopes like this:
public function last($amount)
{
$this->getDbCriteria()->mergeWith(array(
'order' => 't.create_time DESC',
'limit' => $amount,
));
return $this;
}
Adding something like this into a Model will let you choose the amount of objects you want to retrieve from the database (sorted by its create time).
By returning the object itself you allow method chaining.
Here's an example:
$last3posts=Post::model()->last(3)->findAll();
Gets the last 3 items. Of course you can expand the example to almost any property in the database. Cheers
Yes, scopes can be used to change the attributes of CDbCriteria with pre-built conditions and can also be passed parameters. Before 1.1.7 you could use them in a model() query and can be chained together. See:
http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes
Since 1.1.7, you can also use scopes as a CDbCriteria property.
See: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-named-scopes

Find all records of a certain type in Polymorphic table using ActiveRecord in Rails 3

I have a table Category that is a polymorphic model for a bunch of other models. For instance
model Address has shipping, billing,
home, work category
model Phone has home, mobile, work,
fax category
model Product has medical, it
equipment, automotive, aerospace, etc
categories.
What I want to be able to do is something like
Product.all_categories and get and array of all categories that are specific to this model.
Of course I can do something like this for each model in question:
Category.select("name").where("categorizable_type = ?","address")
Also pace_car - which is rails 3 ready, allows me to do something like this:
Category.for_category_type(Address)
But I was wondering if there is a more straightforward / elegant solution to this problem using Active Record iteself - without relying on a gem?
Thank you
I'm not aware of anything built-in to ActiveRecord to give you this, but you could set up that for_category_type method in one line of code in your Category controller:
scope :for_category_type, lambda { |class_name| where("categorizable_type = ?", class_name) }