Rails 6/ Ruby 2.7
So I have two classes ModuleX::SubModuleA::Order and ModuleY::SubModuleB::OrderType
and I want to do something like
ModuleX::SubModuleA::Order.joins("ModuleY::SubModuleB::OrderType")...
This syntax would be pretty simple normally: ModuleX::SubModuleA::Order.joins(:order_type) but I cannot find any documentation regarding how this works for classes in modules.
The syntax of joins allows two uses: first, referencing a relation defined on the left-hand model by its (symbolic) name, or second, raw SQL. Neither method is actually impacted at all by the use of modules, because neither references the class of the model you're joining onto.
In your case, you probably want to set up a relation between orders and order types, like:
class ModuleX::SubModuleA::Order
belongs_to :order_type, class_name: 'ModuleY::SubModuleB::OrderType'
end
Then, you can just do the same syntax as you expect:
ModuleX::SubModuleA::Order.joins(:order_type).all
Related
What is the exact purpose of associations? I understand what the relationships mean and when to use each type for example:
belongs_to, has_many, has_one , has_and_belongs_to_many, ect
but i dont quite understand what purpose they serve in terms of how the connect things within rails. Any input would be appreciated. Thanks!
What you're calling "associations" I would call "macros". That is, the belongs_to, has_many etc. macros are simply class methods being called on your ActiveRecord objects which, when called, define a bunch of functionality based on the association name.
So, what you're asking is: What functionality do these macro methods define? The answer for that lies within the Rails documentation for each of these methods:
has_many
belongs_to
has_one
And, even more, you should read the overall documentation on ActiveRecord::Associations::ClassMethods.
But, in short, these macros define methods with names based on the association names you pass into them. So, for example:
belongs_to :my_object
Will define, as a greatly-simplified example:
def my_object
MyObject.find_by_id(my_object_id)
end
So it's basically like metaprogramming your objects to have the methods needed to find the other, associated objects, update their collections, and so on.
I'm working up an app that interfaces with a legacy database which has a type column used for single table inheritance. This database is still used by an existing PHP application so I have to work with what is there. I'm trying to set up some models for this and one of the key tables is set up with an STI scheme and a type column, however, all of the types in that column are entirely lowercase.
In my testing so far, rails works fine with the type column if I change the value to match the class name (for example, Claimant instead of claimant). However I don't want to go changing those values in the production database even though it would probably be ok, nor do I want to have to go in and modify the legacy app to save the names differently...
In order to fix this I have two questions...
1) Is there anyway I can configure the model to recognize that type = 'claimant' maps to class = 'Claimant'?
2) failing that, is there a way I can tell rails to not use STI on this table even though it has a type column?
I've done some googling and haven't come up with much yet...
I haven't tried this in an STI setting, but when using a legacy table I was able to use the method "set_table_name" on the ActiveRecord model.
class Claimant < ActiveRecord::Base
set_table_name 'claimant'
end
Or with a custom method:
def table_name
'claimant'
end
Apologies I haven't got an STI table handy to test this on, but thought it might help solve your problem.
In answer to the second part of your question, I believe you can disable Rails looking at the type column, by just specifying a non-existant column name.
class Claimant < ActiveRecord::Base
inheritance_column = :_type_column_disabled
end
Usually using word Group as a model name is nice and easy, but my applicatoin is just getting little bit bigger and I was thinking about using UserGroup. This solves my problem, On the other hand making it 2 words creates code that is not visually satisfying #user.user_group or task.task_type .
What would be the other disadvantages of using names like these other than that?
I think we've all been there. My 2 cents:
UserGroup. In a web application most of the times a group is a group of users, so you may safely drop the User.
TaskType. Type is too general a name, so I'd compromise: call the class TaskType but use types as the association name so you can write the arguably nicer some_task.types:
class Task
has_many :types, :class_name => :TaskType
...
end
This question relates to using Ruby on Rails 3 with MongoMapper and EmbeddedDocument. Also, the ManyAssociation.
http://mongomapper.com/documentation/embedded-document.html
The MongoMapper examples (in the link above) show two separate classes:
class Order
include MongoMapper::Document
many :line_items
timestamps!
end
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
But this pollutes the global namespace with LineItem. Out of context, LineItem for what? And what if I want another model, say WishList, to also have a LineItem set?
So it is possible to embed the LineItem class inside Order, like this:
class Order
include MongoMapper::Document
many :line_items, :class_name => "Order::LineItem"
timestamps!
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
end
While this may be technically fine (yes?), will I run into design issues later on? Does it just make the code too ugly? Too complex?
Presumably, the existence of this in the Ruby language means someone thinks it's an ok idea?
One thing I always liked about Django is how it uses "apps" to group related model classes (and separate the namespaces). So my code above is also achieving that in Rails.
I don't see any technical problems to either approach. The problem I've run into is that when a class is embedded in it's parent and is also in the same file, I'll forget it's there. So if your namespaces it as Order::LineItem you can make an "order" folder in your "models" folder and put "line_item.rb" in there.
The other trouble is if you want to have a controller for Order::LineItem, you also have to namespace it and put it in a folder, and in the router it will look like:
resource :orders do
resources :line_items, :controller => "order/line_items"
end
Unless you know your app is going to have multiple types of line_items, I'd recommend not namespacing it—you could be over-coding if you did. For example, if you were later to need two types of line_items you might even find that some of your code could be re-used between models—and if you namespaced your first line_item you may find yourself de-namespacing it.
In Python, namespaces are considered a great thing that there should be more of. But when you think about the global namespace of your Rails app, it's not that cluttered. Gem authors are pretty good about keeping all of their classes namespaced in their one gem module, so in your Rails app you're going to have one namespace for each gem you're using (Rails itself is 5 gems, not sure how many global constants though), plus and a bunch of files included by Rails (e.g. SecureRandom). It turns out to be really nice to have those files "just there" and I've found in practice that namespace collisions are rare and you can easily work around them. I've only run into a namespace issue maybe once, but several times I've accidentally defined a "send" method on a model—a much more common problem with similar repercussions.
I'm working on modifying part of an existing Rails app to use the Class-Table-Inheritance gem (https://github.com/brunofrank/class-table-inheritance). All's well, except that I have defined some instance methods in my superclass -- Person, which all subclasses need to be able to access. For instance, full_name, which returns the concatenated first and last names, or cite_name, which returns the first initial and last name. Since the CTI gem doesn't actually use Ruby inheritance (all subclasses still inherit from ActiveRecord::Base, and the gem does some funky voodoo to link the ActiveRecord fields together), I can't access these methods in the subclasses I've created. Any thoughts on working around this? I'm not interested in STI, but I'm willing to either fork and hack on this particular CTI gem, or look at other solutions.
try http://peterhamilton.github.com/citier, it's based on CITIEsForRAILS, I forked it & put it through rapid development and various other changes. It fixes most, if not all the bugs it had and works so simply.
Just off the top of my head, did you see that in the migration tables of your subclasses you need to have the line:
create_table :videos, :inherits => :product do |t|
and also in your Model.rb files
class Product < ActiveRecord::Base
acts_as_superclass # I'm guessing you might be missing this line??
end
class Book < ActiveRecord::Base
inherits_from :product
end
I think that the brand new CITIEsForRAILS gem (Class Inheritance & Table Inheritance EmbeddingS For RAILS, see https://github.com/altrabio/CITIEsForRAILS ) does exactly what you desire. This gem extends CTI, STI, & Multi Table Inheritance while preserving Ruby class Inheritance.