I have a rails app that is using Redis as the DB, and Ohm as an ORM.
I have a model Activity that has a set of Users. In the rails console I am able to call Activity[id].users.to_a and get the expected list. When trying to do the same thing in a method in my app, I always get an empty string. I've tried this several different ways, including using self.users.to_a in the method and nothing has worked.
I haven't come across a problem like this before where everything is working fine in console but not in the app. BTW the rails.env is set to development.
Any help would be really appreciated, this has stumped me for a bit now. Thanks.
Activity.rb
class Activity < Ohm::Model
include Ohm::Timestamps
include Ohm::DataTypes
include Ohm::Callbacks
include ModuleLibrary
attribute :type
index :type
attachment :attached
attribute :time, Type::Integer
attribute :description
attribute :to_calendar, Type::Integer
set :contacts, :Contact
set :users, :User
reference :company, :Company
reference :account, :Account
def user_names #empty string in app, works fine in console
users.map(&:name).to_sentence
end
end
EDIT: So apparently it turns out where I was calling the method from, the users hadn't been added yet. (duh!) I moved where the method is called to a different spot, and it's all working now.
I have a piece of code like:
class Foo < ActiveRecord::Base
#Some Code
has_many :bars, finder_sql: proc{ "Valid SQL" }
#Some more code
end
Now when I call foo.bar (where foo is an instance of Foo), how can I paginate the results, as bar is being fetched from the finder_sql? Is there any quick and easy way of doing it?
Give kaminari a try. It is a gem that helps with pagination and works really well. Not sure if it works with find_sql and associations, but it's worth a try. I know it works with scopes.
I have a Game model which has_many :texts. The problem is that I have to order the texts differently depending on which game they belong to (yes, ugly, but it's legacy data). I created a Text.in_game_order_query(game) method, which returns the appropriate ordering.
My favourite solution would have been to place a default scope in the Text model, but that would require knowing which game they're part of. I also don't want to create separate classes for the texts for each game - there are many games, with more coming up, and all the newer ones will use the same ordering. So I had another idea: ordering texts in the has_many, when I do know which game they're part of:
has_many :texts, :order => Text.in_game_order_query(self)
However, self is the class here, so that doesn't work.
Is there really no other solution except calling #game.texts.in_game_order(#game) every single time??
I had a very similar problem recently and I was convinced that it wasn't possible in Rails but that I learned something very interesting.
You can declare a parameter for a scope and then not pass it in and it will pass in the parent object by default!
So, you can just do:
class Game < ActiveRecord
has_many :texts, -> (game) { Text.in_game_order_query(game) }
Believe or not, you don't have to pass in the game. Rails will do it magically for you. You can simply do:
game.texts
There is one caveat, though. This will not work presently in Rails if you have preloading enabled. If you do, you may get this warning:
DEPRECATION WARNING: The association scope 'texts' is instance dependent (the scope block takes an argument). Preloading happens before the individual instances are created. This means that there is no instance being passed to the association scope. This will most likely result in broken or incorrect behavior. Joining, Preloading and eager loading of these associations is deprecated and will be removed in the future.
Following up using PradeepKumar's idea, I found the following solution to work
Assuming a class Block which has an attribute block_type, and a container class (say Page), you could have something like this:
class Page
...
has_many :blocks do
def ordered_by_type
# self is the array of blocks
self.sort_by(&:block_type)
end
end
...
end
Then when you call
page.blocks.ordered_by_type
you get what you want - defined by a Proc.
Obviously, the Proc could be much more complex and is not working in the SQL call but after there result set has been compiled.
UPDATE:
I re-read this post and my answer after a bunch of time, and I wonder if you could do something as simple as another method which you basically suggested yourself in the post.
What if you added a method to Game called ordered_texts
def ordered_texts
texts.in_game_order(self)
end
Does that solve the issue? Or does this method need to be chainable with other Game relation methods?
Would an Association extension be a possibility?
It seems that you could make this work:
module Legacy
def legacy_game_order
order(proxy_association.owner.custom_texts_order)
end
end
class Game << ActiveRecord::Base
includes Legacy
has_many :texts, :extend => Legacy
def custom_texts_order
# your custom query logic goes here
end
end
That way, given a game instance, you should be able to access instance's custom query without having to pass in self:
g = Game.find(123)
g.texts.legacy_game_order
Here is a way where you can do it,
has_many :texts, :order => lambda { Text.in_game_order_query(self) }
This is another way which I usually wont recommend(but will work),
has_many :texts do
def game_order(game)
find(:all, :order => Text.in_game_order_query(game))
end
end
and you can call them by,
game.texts.game_order(game)
Im not sure what your order/query looks like in the in_game_order_query class method but i believe you can do this
has_many :texts, :finder_sql => proc{Text.in_game_order_query(self)}
Just letting you know that I have never used this before but I would appreciate it if you let me know if this works for you or not.
Check out http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many for more documentation on :finder_sql
I think if you want runtime information processed you should get this done with:
has_many :texts, :order => proc{ {Text.in_game_order_query(self)} }
I have a model Foo with several belongs_to associations; I'll refer to them here as Bar and Baz. So the model would look like this:
class Foo
belongs_to :bar
belongs_to :baz
def do_stuff_with_bar_and_baz
bar.do_stuff(baz)
end
end
We noticed that do_stuff_with_bar_and_baz was unusually slow (~4 seconds), even though the underlying MySQL statements were very fast (~0.5ms). I benchmarked the bar and baz calls, and discovered that they took ~2.3s and ~221ms respectively... just to go through the Rails association code.
I then put in the following methods:
class Foo
belongs_to :bar
belongs_to :baz
def bar
Bar.find(self.bar_id)
end
def baz
Baz.find(self.baz_id)
end
def do_stuff_with_bar_and_baz
bar.do_stuff(baz)
end
end
This bypasses the ActiveRecord association code and loads the associated records directly. With this code, the time to load the Bar and Baz in do_stuff_with_bar_and_baz dropped to 754ms and 5ms respectively.
This is disheartening. The standard Rails associations appear to be horrendously inefficient, but I really don't want to have to replace all of them (that defeats a the purpose of a significant amount of ActiveRecord).
So, I'm looking for alternatives:
Is there something that I'm potentially doing wrong that's slowing things down? (The real code is obviously more complicated that this. However, the belongs_to is accurate; there's no additional options on the real code).
Have other people encountered this?
How have they dealt with it?
Seems like you have different queries and problem is not in rails, but in DB.
Maybe you have other conditions and you haven't right indexes for they.
Updated
Appears to be a precedence error and nothing to do with the question I originally asked. See discussion below.
Original question
Is it possible to use active record associations in callbacks? I've tested this code in the console and it works fine as long as it isn't in a callback. I'm trying to create callbacks that pull attributes from other associated models and I keep getting errors of nil.attribute.
If callbacks are not the correct approach to take, how would one do a similar action in rails? If the associations are simple, you could use create_association(attributes => ), but as associations get more complex this starts to get messy.
For example...
class User < ActiveRecord::Base
belongs_to :b
before_validation_on_create {|user| user.create_b} #note, other logic prevents creating multiple b
end
class B < ActiveRecord::Base
has_many :users, :dependent => destroy
after_create{ |b| b.create_c }
has_one :c
end
class C < ActiveRecord::Base
belongs_to :b
after_create :create_alert_email
private
def create_alert_email
self.alert_email = User.find_by_b_id(self.b_id).email #error, looks for nil.email
end
end
Off course associations are available in your callbacks. After all, the create_after_email is simply a method. You can even call it alone, without using a callback. ActiveRecord doesn't apply any special flag to callback methods to prevent them from working as any other method.
Also notice you are running a User#find query directly without taking advantage of any association method. An other reason why ActiveRecord association feature should not be the guilty in this case.
The reason why you are getting the error should probably searched somewhere else.
Be sure self.b_id is set and references a valid record. Perhaps it is nil or actually there's no User record with that value. In fact, you don't test whether the query returns a record or nil: you are assuming a record with that value always exists. Are you sure this assumption is always statisfied?