Concerns limited by static table names - ruby-on-rails

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.

Related

How do you create and use functions in Arel that are not bound to a column?

Lets say I want to create:
SELECT * FROM memberships WHERE memberships.expires < NOW()
I attempted:
fn = Arel::Nodes::Function.new("NOW")
memberships = Arel::Table.new("memberships")
memberships.project(Arel.star).where(
memberships[:expires].lt(fn)
)
Which give TypeError (Cannot visit Arel::Nodes::Function).
All the sources I have found when just cover using Arel::Nodes::NamedFunction to create functions that are bound to a specific attribute. I do know there are other ways to solve this like sql literals but I'm curious as to how to actually use Arel::Nodes::Function.
You can use Arel::Nodes::NamedFunction.
This class initializes with the name of the function and an arguments array. The Vistor for a NamedFunction will assemble the SQL as FUNCTION_NAME(comma, separated, arguments).
Since NOW has no arguments all we need to do is pass an empty arguments array.
fn = Arel::Nodes::NamedFunction.new("NOW",[])
memberships = Arel::Table.new("memberships")
memberships.project(Arel.star).where(
memberships[:expires].lt(fn)
).to_sql
#=> "SELECT * FROM [memberships] WHERE [memberships].[expires] < NOW()"
Function appears to be a super class for inheritance purposes only, children include Sum,Exists,Min,Max,Avg, and NamedFunction (which allows you to call any other function by name as shown above)

Extra attribute in many to many join table

I have a many to many association between User and Todo through a join model called UserTodo
Of the many users a todo has, there's one owner. So, I created a column in the user_todos join table called is_owner.
Question is, how do I populate this attribute while creating a Todo?
Currently, I'm creating the todo, then separately updating this attribute in TodoController#create action.
#todo = current_user.todos.create(todo_params)
#todo.user_todos.first.update_attribute(:is_owner, true)
This seems wrong. Is there a single call I can make to populate this attribute while creating the todo?
Second, is there a way to query if a user is an owner of a todo, this way?
current_user.todos.first.is_owner?
I would make a user_todo.rb file with a UserTodo class and do stuff like:
ut=UserTodo.new
ut.todo = Todo.create(todo_params)
ut.user = current_user
ut.is_owner = true
ut.save
current_user.todos_as_usertodos.first.is_owner?
You can make on user.rb
def todos_as_usertodos
UserTodo.where(user_id: id).to_a
end
See where I'm going with this? You want to return and use UserTodo objects vs. Todo objects because they have more info in them. The info you need. That extra is_owner boolean. When you goto just a plain todo object you lose that info.
That seems a bad way since you use .first to get your instance. I'll do something like
UserTodo.create(user: current_user, is_owner: true, todo: Todo.create(todo_params))
Second
I'm not sure if that is possible
You can use this one-liner to create the join model with extra attributes:
current_user.user_todos.create(todo: Todo.create(todo_params), is_owner: true)
Since is_owner is an attribute of the join model, you have to access it through that model too:
current_user.user_todos.first.is_owner?

How to delimit a rails model with a constraint

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

How to update table row from values determined within controller

ruby_on_rails rails 4 assignment non-screen data to insert record
Rather than using screen values (e.g. simple_form_for #user_evaluation_result) to populate the columns to insert a row I need to calculate some of the values in controller.
For example if I have these statements in the controller
….
# which if I had simple_form_for user_evaluation_result would get populated by the screen
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
….
# standard stuff I use for screen derived updates
def user_evaluation_result_params
params.require(:user_evaluation_result).
permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars )
end
How do I assign values to :user_assumption_id etc so that insert works. I have tried all sorts of statements. Alternatively do I use another format instead of calling "def user_evaluation_result_params".
Thanks in advance - Pierre
I'm hoping I've interpreted the question properly...
First, to make sure we're on the same page... The code inside of your user_evaluation_result_params method is using Strong Parameters to create an ActionController::Parameters object for the purpose of protecting your model from unpermitted mass-assignments. So, in general, when you're creating or updating an ActiveRecord object from a form in a view template, you want to use Strong Parameters so that users can't manipulate your form to set attributes that you're not expecting.
That said, if you want to set attributes on an object you don't have to use mass assignment. Here is an example of using one-at-a-time assignment (the opposite of mass-assignment):
obj = MyObject.new
obj.attr_one = "One"
obj.attr_two = "Two"
obj.save
There is nothing wrong with this approach other than that it's kind of a lot of work for the general case. So mass-assignment just saves us from having to do this all the time. But it sounds like this one-at-a-time assignment is what you're wanting in this case. So try something like this:
def create
#user_evaluation_result = UserEvaluationResult.new
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = "some value"
#user_evaluation_result.save
end
Note, instead of setting #user_evaluation_result.user_assumption_id directly, as you asked about, it is preferred to set the actual object association as I did above. Try to keep associations outside of mass-assignment and use object relationships to build up your object graphs.
Or, if you have some attributes coming from a form you can mix and match the two approaches:
def create
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = params[:user_evaluation_result][:some_other_attr]
#user_evaluation_result.save
end
private
def user_evaluation_result_params
params.require(:user_evaluation_result)
.permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars)
end

Active record where query for value inside of an array

Question: Is it possible to build a class method scope that can query objects based on values inside an array in a table? If yes, how can I do this?
In my example, I have a “wells” table that has an array field called “well_tags”. I want to build a query that returns all objects that have a specified value (such as “ceramic”) in the wells_tags array. The basic query would be something like this:
#well = Well.all
#query = #well.where(“well_tags contains ceramic”)
And then the class method scope would look something like this, with the “well_tag_search” param passed in from the controller:
class Well < ActiveRecord::Base
def self.well_tag_filter(well_tag_search)
if well_tag_search.present?
where(“well_tags contains ceramic")
else
Well.all
end
end
I found another post that asks a similar question (see link below), but I cannot get the answer to work for me...the result is always 'nil' when I know there should be at least 1 object. I am a beginner using sqlite (for now) as my database and rails 4.0.
Active Record Query where value in array field
Thanks!
UPDATE: some progress
I figured out how to create an array of all the objects I want using the ‘select’ method. But I still need to return the results as an Active Record object so I create a class method scope.
#well = Well.select
{ |well| if well.well_tags.present?
then well.well_tags.include? ‘ceramic' end }
#well.class #=> array
Not sure where Show is coming from.
Can you try doing Well.all instead of Show.all?

Resources