How to delimit a rails model with a constraint - ruby-on-rails

Is it possible (and how) to delimit an ActiveRecord model with "a where"? I.E. When I call OrderCommunication.all then the query would do something like select * from ordcomm where type = 'order'. I know it's kind of nasty but our database just can't be modified at all and we can't refactor. Basically I need to declare my model with a where ordcomm = 'order' so I don't do it in all my subsequent queries.

You can use a default scope to get this done.
default_scope { where(type: 'order') }
Now, if you try OrderCommunication.all, you would only get the records with the type of 'order'
If you instead want to fetch all the records, use unscoped
OrderCommunication.unscoped.all

Related

Is it possible to specify order of a chained scope query

I'm currently doing a chained scope query like this
Enrollment.active.occurs_today
Is it possible to order the query so that .active is queried before .occurs_today and prevent lazy loading? Something similar to below hopefully
Enrollment.active.eval.occurs_today
What's not always appreciated is that the order of chained scope doesn't really matter. The chained scopes are building a single query that includes all the conditions, and then when you action the query (by requesting records or a count, for example) at that point the records are retrieved using the combined conditions.
The exception is the rails console, where each scope is executed as it's entered.
if you have pseudo-scope (something that does a query that returns id and then queries on that) you could implement it as a class method and arrange to pass into the class method other scopes as arguments to execute them first. A possible implementation might be...
def self.occurs_today(*scopes)
result = scopes.inject(all) {|relation, scope| relation.send(scope)}
where(id: result.select { |s| s.occurs_on?(Date.current) }.map(&:id))
end
You could then do
Enrollment.occurs_today(:active)
or several scopes
Enrollment.occurs_today(:active, :priority)
In both cases, the select will be performed on the already scoped relation.
alternatively, I think you could see an improvement in performance (and automatic recognition of scope order) just by changing your existing occurs_today scope.
Instead of selecting on all records...
scope :occurs_today, -> { where(id: all.select { |s| s.occurs_on?(Date.current) }.pluck(:id) }
...select on only the currently scoped records...
scope :occurs_today, -> { where(id: select { |s| s.occurs_on?(Date.current) }.pluck(:id) }

Rails - finding an object in an active record association by an attribute without issuing more queries

Very short question, I feel like the answer must be already on StackOverflow but I couldn't find it.
I have some incoming parameters. They each have a unique ID.
I query the database and get an active record association using something like:
existing_things = current_user.things.where(uuid: [param_uuids])
This gives me an Active Record Association of those objects.
However, what I later do is something like:
existing_things.where(uuid: some_specific_uuid)
Of course, when I run the above query, it issues an SQL statement.
What I would love to do is find an object in a pre-loaded Active Record array of objects, and return that object without issuing another query.
How should I do that?
The same way you would if it was an ordinary array: Enumerable#find.
existing_things.find { |t| t.uuid == some_specific_uuid }
(or select if you're expecting more than one match)
If you're doing a lot of lookups like that, you might want to get a Hash involved:
things_by_uuid = existing_things.index_by(&:uuid)
things_by_uuid[some_specific_uuid]
(again, if you're expecting more than one match per value, there's group_by)

Return ActiveRecord relation from WHERE NOT EXISTS query

Okay so here's a fun one. The relations are something like:
A Client has_many state_changes, and a StateChange belongs_to one Client.
I have this query:
Client.find_by_sql('SELECT * FROM clients cs WHERE NOT EXISTS (SELECT * FROM state_changes WHERE state_changes.client_id = cs.id AND current = true)')
The problem is that this returns an Array object and not an ActiveRecord Relation. I need to run an update on the state_changes that belong to the returned clients from that query.
So there's two issues essentially, getting the results as an ActiveRecord relation, and then getting all of their state_changes, also as an ActiveRecord relation, so that I can run the update.
I also understand that this might be a convoluted way to go about it...
I also understand that this might be a convoluted way to go about it..
Having easy AR interface - indeed it is convoluted :)
I would probably go with some scopes:
class StateChange
scope :active, -> { where.not(current: false) }
# I believe with your setup it is not equal to `where(current: true)`
end
class Client
scope :some_smart_name, -> { includes(:state_changes).merge(StateChange.active) }
end
This should return you clients who who don't have associated state_changes with current set to false.

Group all records by custom method in Rails

I'd like to retrieve all records for a particular model and "index" each one by their name attribute. The end result should be a Hash, where the keys are the name of the record, and the value is the record. I can do this easy enough with a method like
def self.all_by_name
hash = {}
all.each { |model| hash[model.name] = model }
hash
end
Is there a way to do this with an active record query?
all.group(:name) is what you need!
Some documentation: http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-group
No, active record will either return an array or an active record relation. You would have to massage it after into the data type of your choice. ActiveRecor dcan return anything that SQL can normally return. However, you could do use the group method which is essentially the group by in SQL.
I'm not sure in your case what your method should do because it seems like you are going to overwrite the value of your key if there are 2 objects with the same class. Perhaps if you better defined what you need in an example I could help you better.

Concerns limited by static table names

I have a method that is common to two models which I would like to share via a concern.
The method includes an activerecord join statement that specifies the name of the table "Regions".
Is there any way the table name can be dynamic to reflect the current class?
def children
children = Region.joins("LEFT JOIN Regions AS ancestor ON ancestor.lft BETWEEN #{lft}+1 AND #{rgt}-1 AND Regions.lft BETWEEN ancestor.lft+1 AND ancestor.rgt-1")
children = children.where("ancestor.id IS NULL")
children = children.where(lft: (lft+1..rgt-1))
return children
end
If the children method is defined in a concern as a class method, then you have access to all the class methods from the model using the concern. This means that you can call joins directly without specifying the model class.
You can use table_name or quoted_table_name to get the table name of the current model for use in the query conditions.
def children
result = joins("LEFT JOIN #{table_name} AS ancestor ON ancestor.lft BETWEEN #{lft}+1 AND #{rgt}-1 AND #{table_name}.lft BETWEEN ancestor.lft+1 AND ancestor.rgt-1")
result = result.where("ancestor.id IS NULL")
result.where(lft: (lft+1..rgt-1))
end
Also, the last return statement is unnecessary since Ruby always returns the last executed line in a method. I have also changed the variable name holding the result since it was the same as the method name (children), which can lead to nasty errors.

Resources