Implications of Having an object_id Column in Rails - ruby-on-rails

What are the implications of having a object_id / object_type on a model (for a polymorphic association) in regards to Object itself containing an object_id and that overriding it (http://ruby-doc.org/core-2.3.1/Object.html#method-i-object_id)?
class Event
belongs_to :object, polymorphic: true # object_id/object_type
end

When I search for object_id through the whole codebase of one of my rails projects (including all gems), I can see over 200 hits. In Rails only, this is about 50 hits.
I'd expect problems with records comparison, using them as hash keys, putting them to sets, perhaps also duplicating them with dup. In Rails, record.object_id is referenced in caching, has_many_through associations, AREL, pretty printing the record, minitest expectations, also in pry debugger,
But just from quick-looking trough the code it is very hard to guess if it will cause problems or not and I generally tend to be very defensive about such potential problems - you'll never know for sure if your next usage of the object will not break things in a way that is both very hard to debug and perhaps impossible to fix.
As I said above, I'd be very curious if you tried, but myself would rather name it belongs_to :thing, polymorphic: true or better yet something even more specific.

If you attempt to define object_id on a class you'll find that you get the following angry warning from the Ruby interpreter warning: redefining 'object_id' may cause serious problems (try it from IRB). That sounds scary - and the incidents may be tied to specific versions of Ruby (and vary based on the version used). I'd recommend fixing this.

I believe you can belong_to object, while defining the foreign_key to something else, like foreign_key: :object_identifier. That way you dont have to worry about object_id.

I think the best to follow this documentation Active Record Associations, I think defining another object_id will make you sustain the relation manually in your code and you will lose Active Record features for doing that.

Related

rails associations & object identity: maintaining object identity through a series of changes

`First of all, the fact that I am even asking this question implies that I am consciously choosing not to (strictly) obey the law of Demeter.
Since sometime (probably rails 3?) referring to model.association.first results in a new object each time, unless you use .to_a on the association:
campaign.campaign_shirts.first.to_s
=> "#<CampaignShirt:0x007fdd02c7fd58>"
campaign.campaign_shirts.first.to_s
=> "#<CampaignShirt:0x007fdd02ca6318>"
c.campaign_shirts.to_a.first.to_s
=> "#<CampaignShirt:0x007fdd02d13170>"
c.campaign_shirts.to_a.first.to_s
=> "#<CampaignShirt:0x007fdd02d13170>"
I've worked on several Rails 3/4 applications without even noticing this, probably because I do try to respect Demeter as much as is practical.
In this case I want Campaign to be in control, because it is a big state machine where many of its state changes involve transactionally coordinating changes in itself and various child objects.
Is there a way to freeze the association arrays at create and/or fetch time?
EDIT: I noticed almost immediately that they are frozen when you use Campaign.includes(...).find, which I am doing in my app. However I still have a problem in specs where the objects are factories created by FactoryGirl. Is there a way to say "freeze all the associations on this object" or do I have to call .to_a on each of them?
EDIT 2: I still have a problem when I refer to campaign through a belongs_to on user. (this seemed like a separate question, so I asked it here).
EDIT 3: the problem with the belongs_to includes extension was just syntax, so I'm removing the details of that.
So, my remaining problem is to get User.selected_campaign to act like it does in my app when it is built up by FactoryGirl. I'm going to try just doing a .reload at the start of each spec, which should trigger the includes extensions, at the cost of some spec performance.
I don't know the (gory) details of your setup, but what if you just memoize first on the Campaign object?
def Campaign
def first_campaign_shirt
#first_campaign_shirt ||= campaign_shirts.first
end
end
I think this way you obey the Law of Demeter again? But it might get annoying if you need more getters than just first_shirt. So consider this just a suggestion that won't fit in a comment box. :)
All of my in-app use cases were solved by carefully choosing where to add includes(..) to associations and scopes.
I was only able to solve the factory girl problem by calling reload on a factory object after creating it.

Rails 3.2.x associations and attr_accessible

Trying to find the definitive answer on whether active record associations should be in the list of attr_accessible attributes.
I've seen
class Foo
attr_accessible :name
attr_accessible :bars
belongs_to :bar
end
also seen
attr_accessible :bars_id
want to know the proper way to be able to do Foo.new(name: 'name' bar: barvar)
As often the definitive answer is: "It depends™"
Only the attributes you want to mass-assign should be made accessible.
So if you want or need to do…
Foo.new(name: 'name', bar: barvar)
…then you simply have to make bar accessible.
In the end assign_attributes is called which does a simple send("#{attribute_name}=", attribute_value) after checking the accessibility of the attribute.
Some coding style aspects:
Often mass assignment happens when processing the param hash. At least that's where the security problems are lurking. There you rarely have a Bar object but more often a bar_id.
However if you work with model instances, most people prefer using the association methods (as #Andrew Nesbitt wrote) because that often has some advantages (automatic saving, automatic update of the association counterpart, cleaner code, …)
So there are reasons to have one or the other or both.
My personal opinion: One should not waste a lot of time on this topic since Rails 4.0 will have a better solution for parameter sanitizing. (See strong_parameters if you want it in Rails 3, too)
You can avoid needing to make bar_id accessible by using the association builder:
# singular (has_one)
foo = bar.build_foo(name: 'name')
# plural (has_many)
foo = bar.foos.build(name: 'name')
The only time you would need to make an association accessible is if you are using accepts_nested_attributes.
While you can avoid making bars_id (shouldn't that be bar_id?) accessible in your example, the question is if parts of your application still needs access to it. Using active_admin, I had to make the whatever_id accessible to make things work with relations.

Lots of different models in tests (rspec)? Advanced

I am only looking for answers from senior/more experienced Ruby/Rails developers on this one, since I think this is a bit more advanced of a question.
I have a gem I am working on that adds some behavior to AR models. I have to test it for a lot of different associations (has_many, habtm, has_one etc), and I also have to test behavior when different options are passed for the associations (e.g. :foreign_key). Now with all these different models, I can use the same table in the database because the fields themselves do not need to change, only behavior specified through has_many, belongs_to and so on.
Keep in mind there are a lot of different options, so the number of models is quite large.
First I don't think it would be bad practice to have the definition of the models next to / in the test itself, for readability purposes (if I have multiple tests that use the same model then I would group them together and use the before method). So this is one of my goals, and you can comment on this if you don't agree.
The second thing I am not sure of is I wanted to keep the simple/same name of the model in all the tests, for example "Task", instead of TaskWithManySubtasksAndForeignKey or something ugly like that. The problem is there are so many models it's hard to come up with meaningful and simple names. I'm not quite sure about this - using the same name, since it's a constant, is a little problematic. I have a solution with a proxy class but I don't think this is the optimal solution. I was considering using variables (with the let method) like "taskModel", but it seemed a little verbose and unusual.
One other option that comes to mind, but I am not sure is possible to do easily, is to remove an existing association and then define a new one. So e.g. add a has_many and then remove it, add a habtm...
How would you go about doing this?
Defining unique models in the spec files is not necessarily a bad idea since it makes it easy to see exactly how each model is defined. The obvious problem with this approach is if you want to reuse the models in other test files. The Rails approach to this is to define all the models in separate files and then just require them in the tests that need it.
I think it really just depends on how many models you have and how much you want to reuse. In one of my gems, I took the approach of defining the models in the spec file, in another gem, I defined them in the spec helper, and in yet another I took the Rails approach and used a separate directory for them. If you asked me which one I preferred, I'd probably go with the spec that also contains the models because it's all in one place. Definitely a subjective problem though.
Another approach I've taken on occasion is to create an anonymous class that's guaranteed to only be around for the life of that test:
describe 'my test' do
let(:my_class) do
Class.new(Task) do
has_many :things
belongs_to :something_else
end
end
it 'should have many things' do
my_class.should have(100).things
end
end

Resolving a class name conflict in a Rails application

I have been building a Rails application that performs accounting functionality. As part of this, I have a model with the class name Transaction. So far so good, I have been building this functionality for a month or so, and everything is working as expected.
Until now...
I have just discovered some older reporting functionality that was developed months ago using the Ruport library has stopped working. It appears that Ruport, when generating PDFs, requires a library that also has a class/module named Transaction.
TypeError in Admin/team reportsController#generate
Transaction is not a module
...
This error occurred while loading the following files:
pdf/writer
transaction/simple
So, I'm looking for a quick fix here. One that hopefully doesn't involve renaming my Transaction model and refactoring the last few weeks worth of code.
Looking forward to some clever suggestions :)
Already answered and old, but I came here with the same problem, but solved it in a different way.
I have two Models named Pull and Query.
Trying to reference Query.some_static_method() within a method in Pull resulted in Query resolving to ActiveRecord::AttributeMethods::Query:Module.
Solved it by putting the empty namespace in front of it with ::Query.some_static_method()
I believe the issue is down to Ruport requiring the PDF::Writer gem, which in turn requires the Transaction::Simple gem which defines the module Transaction.
There is certainly a #transaction method in ActiveRecord, but I do not think there is a Transaction module or class within Rails. I'll be happy to be corrected on that one.
Namespacing is usually the best practice for avoiding naming conflicts like this. E.g.
module Account
class Transaction < ActiveRecord::Base
....
end
end
However, namespacing ActiveRecord models may throw up other issues.
As time consuming as it may be, renaming your Transaction model may be the best bet.
You can still keep your existing transactions database table if you wanted, so your migrations don't need to change, by putting self.table_name = "transactions" inside your model.
Your associations with other models can also still be named "transaction(s)" by specifying the class_name in your association call. E.g.
class User < ActiveRecord::Base
has_many :transactions, :class_name => "AccountTransaction"
end
Those two suggestions may or may not save you some time.
Your problem may come from the fact that Transaction is also a reserved word in Rails…

rails has_and_belongs_to_many or manual implementation

I'm designing a ruby on rails app for a pharmacy, and one of the features is that there are stores who have pharmacists who work there. In addition, there are pharmacists, who can work at many stores. This sounds like a job for HABTM, right? Well, being the novice I am, I manually designed a workaround (because I never heard of HABTM - I basically taught myself rails and never got to some of the more advanced relationships). Right now, when a pharmacist is saved, there's a couple of lines in the create and update action of the pharmacists controller that turns the stores that they work at into a string, with each store_id separated by a comma. Then, when a store is displayed, it does a MYSQL request by
#pharmacists = Pharmacist.find :all, :conditions => "stores REGEXP '#{#store.id}'"
Would moving this system over to a rails based HABTM system be more efficient? Of course it would require less code in the end, but would it be worth it? In other words, what benefits, other than less code, would I get from moving this association to be managed by rails?
The benefit is that you will be using the right tool for the job! The whole point of using a framework such as Rails is that it helps you solve common problems without having to re-invent the wheel, which is what you've done here. By using associations you'll also be using a relational database properly and can take advantage of benefits like foreign key indexing, which will be faster than string manipulation.
You should use a has_and_belongs_to_many relationship unless you need to store extra attributes on the join model (for example the date a pharmacist started working at a store) in which case use has_many :through.
Using Rails associations will give you all the convenient methods that Rails provides, such as these:
# Find the stores the first pharmacist works at
#stores = Pharmacist.first.stores
# Find the pharmacists who work at a store
#pharmacists = Store.find_by_name('A Store').pharmacists
A Guide to ActiveRecord Associations

Resources