I have a scope method defined in my Child's model profile.rb as follows
scope :fees_to, -> (fees_to) { where("fees_to <= ?", "#{fees_to}") }
And in the parent which is tutor.rb there is
has_one :profile, dependent: :destroy
Now in rails console i can do Profile.all.fees_to(10) for example and its valid. But how can i do call the fees_to method through the parent Tutor?
Basically now i am able to filter through my profiles in the index view of it. But what i would like to do is to filter through the tutor index view based on values from the child.
All help and advice would be greatly appreciated! Thank you
This makes no sense with a has_one. You cannot "have one" and still apply a scope to it. Scopes work on many records, to apply additional conditions and narrow down the result set, not specific records.
You're effectively trying to add a where(fees_to < ?) condition to a specific record, which obviously makes no sense.
If you want to use a scope via an association, it needs to be a has_many.
In profile.rb:
You can create a function like:
def fees_to n
Profile.fees_to(n) # your scope is called here
end
Then in rails console :
tutor = Tutor.first
tutor.profile.fees_to(10)
it works like a filter. If profile's fees_to is > n, you will get [] (array empty)
UPDATED
bad => #tutor = Tutor.all
good => #tutor = Tutor.includes(:profile).all # with eager loading, avoid n+1 query.
may be you want make loop like that:
#tutor.each do |tutor|
tutor.profile.fees_to(10)
end
I defined the following in my tutor.rb
def self.fees_search(n)
#profile = Profile.fees_to(n)
if #profile.empty?
return Tutor.none
else
#profile.each do |y|
y.tutor
end
end
end
Not sure if its clunky or if there are redundancies, but it works perfectly fine in rails console. I can do Tutor.fees_search(10) and it renders all the respective Tutors accordingly.
Cheers
Related
Context:
Each Order has many Items & Logistics. Each Item & Logistic (as well as the Order itself) have many Revenues.
I am creating Order + Items & Logistics at once using an accepts_nested_attributes_for on Order. However, Revenues gets created using an after_create callback on each of the models Order, Item, and Logistics. Why? Because given the difference in interpretation in these models, the code reads cleaner this way. (But if this way of doing it is what's causing this question to be asked, I will obviously reconsider!)
One key attribute that I need to store in Revenues is pp_charge_id. But pp_charge_id is not something that either Order, Items, or Logistics needs to worry about. I've attached an attr_accessor :pp_charge_id to Order, so that one works fine, however, once I'm in the child Items or Logistics models, I no longer have access to pp_charge_id which again I need to save an associated Revenue. How should I do this?
Controller Code:
#order = Order.new(params) #params includes Order params, and nested params for child Item & Logistics
#order.pp_charge_id = "cash"
#order.save #I need this to not only save the Order, the children Item & Logistics, but then to also create the associated Revenue for each of the aforementioned 3 models
ORDER Model Code:
has_many :items
has_many :revenues
attr_accessor :pp_charge_id
after_create :create_revenue
def create_revenue
self.revenues.create(pp_charge_id: self.pp_charge_id)
end
#This WORKS as expected because of the attr_accessor
ITEM/ LOGISTIC model code:
has_many :revenues
belongs_to :order
after_create :create_revenue
def create_revenue
self.revenues.create(pp_charge_id: self.order.pp_charge_id)
end
#This DOES NOT work because self.order.pp_charge_id is nil
ORDER model code:
belongs_to :order
belongs_to :item
belongs_to :logistic
Again I understand the attr_accessor is not designed to persist across a request or even if the Order itself is reloaded. But it also doesn't make sense to save it redundantly in a table that has no use for it. If the only way to do this is to put the pp_charge_id into the params for the order and save everything all at once (including Revenues), then let me know because I know how to do that. (Again, would just rather avoid that because of how it's interpreted: params are coming from User, Revenue data is something I'm providing)
I think if you want the order's pp_charge_id to apply to all its items and logistics, I'd put all that into the order's after_create callback:
# order.rb
def create_revenue
revenues.create(pp_charge_id: pp_charge_id)
items.each {|i| i.revenues.create(pp_charge_id: pp_charge_id)}
logistics.each {|l| l.revenues.create(pp_charge_id: pp_charge_id)}
end
EDIT: Alternately, you could add inverse_of to your belongs_to declarations, and then I believe Item#create_revenue would see the same Order instance that you set in the controller. So if you also added an attr_accessor to the Item class, you could write its create_revenue like this:
# item.rb
def create_revenue
revenues.create(pp_charge_id: pp_charge_id || order.pp_charge_id)
end
This should cover the new requirement you've mentioned in your comment.
instead of using after_create and accessors you should consider having a proper method that does exactly what you need, ie:
Order.create_with_charge(:cash, params)
i find it disturbing to persist redundant information in the database just because the code reads cleaner that way!
Tables:
User
Project has_many Results
Project has_many Data through ProjectData
Results belongs_to data, project
In the Result table I have a column :position of type int.
So I would like to get all the results with a level < 50, actually the value of count.
I am thinking in adding in the Result class
def get_top_level current_user
tsum = []
Project.where(user_id: current_user).each do |project|
tsum << project.results.where("level <= ?", 50).count
end
return sum(tsum)
end
This will work, but I feel that there should be a easy and prettier way of doing this.
And is it ok to user the class name in a view and pass different values for example:
<%=Results.get_top_level(current_user)%>
Or
<%=#results.get_top_level(current_user)%>
If none of those are a good practice, can you help me with a alternative solution for this.
Thank you.
I would create a method on the project model something like this.
def get_top_level
self.results.select{ |result| result.level <= 50 }
end
On the user model. What's the relationship here, does a user have many projects? Or just one project.
def get_top_level
self.top_level_projects.inject(:+)
end
def top_level_projects
self.projects.map(&:get_top_level)
end
Now when you call current_user.get_top_level
This will find the top_level_projects, map the associated results and add them all together.
I have a couple of models that are composites of multiple objects. I basically manage them manually for saves and updates. However, when I select items out, I don't have access to the associated properties of said item. For example:
class ObjectConnection < ActiveRecord::Base
def self.get_three_by_location_id location_id
l=ObjectConnection.find_all_by_location_id(location_id).first(3)
r=[]
l.each_with_index do |value, key|
value[:engine_item]=Item.find(value.engine_id)
value[:chassis_item]=Item.find(value.chassis_id)
r << value
end
return r
end
end
and each item:
class Item < ActiveRecord::Base
has_many :assets, :as => :assetable, :dependent => :destroy
When I use the ObjectLocation.find_three_by_location_id, I don't have access to assets whereas if use Item.find(id) in most other situations, I do.
I tried using includes but that didn't seem to do it.
thx
Sounds like the simplest solution would be to add methods to your ObjectConnection model for easy access like so:
class ObjectConnection < ActiveRecord::Base
def engine
Engine.find(engine_id)
end
def chassis
Chassis.find(chassis_id)
end
# rest of class omitted...
I'm not exactly sure what you're asking... If this doesn't answer what you're asking, then can you try to be a little bit more clear with what exactly you are trying to accomplish? Are the Chassis and Engine mdoels supposed to be polymorphic associations with your Item model?
Also, the code you're using above won't work due to the fact that you are trying to dynamically set properties on a model. It's not your calls to Item.find that are failing, it's your calls to value[:engine_item]= and value[:chassis_item] that are failing. You would need to modify it to be something like this if you wanted to keep that flow:
def self.get_three_by_location_id location_id
l=ObjectConnection.find_all_by_location_id(location_id).first(3)
r=[]
l.each_with_index do |obj_conn, key|
# at this point, obj_conn is an ActiveRecord object class, you can't dynamically set attributes on it at this point
value = obj_conn.attributes # returns the attributes of the ObjectConnection as a hash where you can then add additional key/value pairs like on the next 2 lines
value[:engine_item]=Item.find(value.engine_id)
value[:chassis_item]=Item.find(value.chassis_id)
r << value
end
r
end
But I still think that this whole method seems unnecessary due to the fact that if you setup proper associations on your ObjectConnection model to begin with, then you don't need to go and try to handle the associations manually like you're attempting to do here.
I've got a nice 3-level nested-form using formtastic_cocoon (jquery), and now I want to be able to sort the 2nd set of items in the form.
I've got jQuery ui working no problem, so now to set and update the sort order in rails.
I started following the rails bits of railscasts sortable lists
http://asciicasts.com/episodes/147-sortable-lists
The form structure is User->Tasks->Location.
In my Task Model I set index to
def index
#task = Task.find(params[:id],:order=>'position')
end
def edit
#task = Task.find(params[:id],:order=>'position')
end
and I was expecting my console to see
... FROM 'tasks' WHERE ('users'.'id' = 12) ORDER BY ('position')
or something along those lines, but there is no order by output.
Is there somewhere else that I need to define this order?? Where does the nested_object get its relationship from? The model only?
My models are
class User < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :user
end
I changed the model to
class User < ActiveRecord::Base
has_many :tasks, :order=>'position'
end
You are using the wrong model, that id is of a user, so you have to do:
User.find(params[:id]).tasks(:order => 'position ASC')
Otherwise you are just getting the task with id = 12 and not the tasks whose user_id = 12
From the answer that you've given, I suspect the real issue lies not in the tasks controller. The default order you gave is great, but if you had some other order or filter requirements, the tasks model won't quite do it either.
I suspect you were actually in users#edit, or possibly a _form.html.erb, where it displays the form elements for each task. There might have been a #user.tasks.each {...} or similar loop block.
For a given user then, do: #user.tasks.order(:position). Or maybe you need open tasks: #user.tasks.where(:open=>true) etc.
Your code is slightly wrong here.
To find that user's tasks you would do this in your route:
User.find(params[:id]).tasks(:order=>'position')
I have a model called Stem. I need a 'thumbs up' feature, so I have created a second model called Thumb, which consists of stem_id and user_id.
I'm also using the restful authentication plugin for user credentials.
I have the 'thumbs up' button working, which adds a row to the thumbs table fine, but I'd like to be able to check if the currently logged in user has already given a thumbs up to this particular stem.
I tried adding this to the Stem model:
def thumbed
Thumb.count_by_sql ["SELECT COUNT(*) FROM thumbs WHERE user_id = ? AND stem_id = ?", current_user.id, self.id ]
end
The problem here is that the stem model has no access to the current_user variable the the controllers have.
Is there a way I can get access to this property, or alternatively, is there another way I could go about checking this? I was hoping to get this as a property in the model because the stems are passed over to a Flex app using RubyAMF.
Thanks!
Your controller knows the current_user, and your Stem model probably shouldn't. You can, however clean up your code and avoid hard-wiring SQL with a named_scope and pass the user into that.
#thumb.rb
named_scope :for_user_id, lambda {|id| {:conditions => {:user_id => id}}}
#stem.rb
def thumbed_by_user(user)
self.thumbs.for_user_id(user.id).count > 0
end
# some controller
stem = some_method_that_gets_a_stem
is_already_thumbed = stem.thumbed_by_user(current_user)
Can you pass the current user to thumbed? Where are you calling it from?
BTW, you could try to simplify all of this using associations. I'm not 100% sure I understand what you're trying to do, but it sounds like you have the following...
class Stem
has_many :thumbs
end
class User
has_many :thumbs
end
class Thumb
belongs_to :user
belongs_to :stem
end
Then you can use find though associations to get at your thumbs without resorting to direct SQL. Check out this RailsCast: http://railscasts.com/episodes/3-find-through-association
What ended up working for me first was something like this:
I added these methods to my stem model:
def thumbed_by? usr_id
Thumb.count(:conditions => {:user_id => usr_id, :stem_id => self.id}) > 0
end
def owned_by? usr_id
self.id == usr_id
end
I also added this:
attr_accessor :thumbed, owned
Then in my show action where these were needed, I added the following lines:
#stem.thumbed = #stem.thumbed_by? current_user.id
#stem.owned = #stem.owned_by? current_user.id
This works exactly how I would like (the Flex app is already interpreting it as properly), but is there a way I could shorten this?