I have a model with polymorphic association that I am running rspecs on.
The tests require the polymorphic field to point to a model with some fields (e.g. name).
Since I prefer not to use any of my existing (and complex)models, I was thinking of somehow creating a new simple models (that will only exist in tests) that my main models can point to.
Is there a way to do this ?
Any other way to test models when they are dependant on another models ?
As you expect, tests should be independant.
This is where stubs and mocks appear and it's the Rails way to proceed.
If you want to create a module dedicated to tests (which is overkill cause it's the role of stubs), just remind ruby classes and modules are executable.
So you could simply do:
if Rails.env.test?
module Foo
...
end
end
Problem solved - using the 'mocha' gem.
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
In tests, can just do:
class MockUser {
# Stuff
}
comment = Comment.create!.expects('commentable').returns(MockUser.new)
Now it will simply work:
comment.commentable.name
Related
I'm using the acts_as_bookable gem for some basic reservation/booking stuff in a Rails app, and I need to add an additional validation to the Booking model that the gem creates.
What I mean by that is, inside the gem, located at lib/acts_as_bookable/booking.rb is the following module/class:
module ActsAsBookable
class Booking < ::ActiveRecord::Base
self.table_name = 'acts_as_bookable_bookings'
belongs_to :bookable, polymorphic: true
belongs_to :booker, polymorphic: true
validates_presence_of :bookable
validates_presence_of :booker
validate :bookable_must_be_bookable,
:booker_must_be_booker
# A bunch of other stuff
end
end
Which is fine. However, I want to add an additional piece of logic that stops a booker from booking the same instance of a bookable. Basically, a new validator.
I thought I could just add a file in my /models directory called acts_as_bookable.rb and just modify the class like this:
module ActsAsBookable
class Booking
validates_uniqueness_of :booker, scope: [:time, :bookable]
end
end
But this doesn't work. I could modify the gem itself (I've already forked it to bring a few dependencies up to date, since it's a pretty old gem) but that doesn't feel like the right solution. This is logic specific to this app's implementation, and so my gut feeling is that it belongs in an override inside this specific project, not the base gem.
What am I doing wrong here? And is there a better/alternative approach that would be more suitable?
A clean way to create monkeypatches/augmentations to objects outside of your control is to create a seperate module:
module BookingMonkeyPatch
extend ActiveSupport::Concern
included do
validates_uniqueness_of :booker, scope: [:time, :bookable]
end
end
This lets you test the monkeypatch seperately - and you can "turn the monkeypatch on" by including the module:
ActsAsBookable::Booking.include(BookingMonkeyPatch)
This can be done in an initializer or anywhere else in the lifecycle.
Altough if bookable is a polymorpic assocation you need to use:
validates_uniqueness_of :booker_id, scope: [:time, :bookable_id, :bookable_type]
The uniqueness validation does not work correctly when just passed the name of an assocation as it creates a query based on database columns. This is an example of a leaky abstraction.
See:
Justin Weiss - 3 Ways to Monkey-Patch Without Making a Mess
I am integrating a rails app (rails 3 at the moment, moving to 5) with another application. The user model in the rails app will have associations that are related to the integration, scopes, and a bunch of methods.
I would like to separate these from the user model file to avoid cluttering it up and keep all the associations, scopes and methods related to the integration in a single place rather than watch the user model become cluttered with things only relevant to users with the integration enabled.
Is this possible, and if so, what mechanism would I use?
You can use concerns:
module AdditionalLogic
extend ActiveSupport::Concern
included do
scope :disabled, -> { where(disabled: true) }
belongs_to :user
# etc..
end
# other methods
end
class YourModel < ActiveRecord::Base
include AdditionalLogic
end
I am going through a co-workers code and am not able to find a single tutorial where this has been used. Can someone point me to some resources where this has been used. This has made code very clean but I haven't found any reference to it. This is only part of this class. It includes other some more methods.
class Manager
include ActiveModel::Model
include ActiveModel::Associations
attr_accessor :application_id, :user_id, :user_application_id,.........
belongs_to :application
belongs_to :user_application
belongs_to :user .. more belongs .......
# This method is necessary to enable this ActiveModel Class to be used in views along with Form helpers
def self._reflect_on_association(association) #:nodoc:
_reflections[association.to_sym]
end
def []=(attr, value)
self.send("#{attr}=", value)
end
def [](attr)
multi_attribute_ids = [:some_ids.to_s, :someid2.to_s]
return if multi_attribute_ids.include?(attr)
self.send(attr)
end
def applicant_name
end
-- some more methods
end
What would be the use of such a "manager". What are the two methods that are using self.send doing here. Is this a common pattern in rails.
Yes, with the introduction of ActiveModel in Rails 3, it has become an increasingly common pattern to use domain objects (called a manager in this case) that are not backed by an actual database table but which look and feel like models.
Even though ActiveModel makes it particularly convenient to pick and choose Rails model features to be incorporated into arbitrary classes, this pattern is something Rails pioneers have been encouraging since a long time.
As has been illustrated clearly in the example you posted, this pattern allows us to define virtual models and virtual associations which can easily take advantage of form helpers and other rails niceties written assuming model objects.
I have an application which defines some models. I want to extend the functionality of some models(eg. adding methods,adding associations) from application to my engine.
I tried adding a model in the engine with the same name as my application's model and Rails will automatically merge them, however it doesn't work.
eg:
(Application's model)
class Article < ActiveRecord:Base
def from_application
puts "application"
end
end
(Inside my Engine)
module MyEngine
class Article < ::Article
has_many :metrics, :class_name => 'Metric'
end
end
has_many association is not getting applied to my Articles model when I try to access #articles.metrics. Any ideas ?
You have the right idea and are close. But your implementation is a little off.
Generally, your engine should have no knowledge of your host app. That way, your engine and the host app(s) stay loosely coupled. So, classes in your engine should not inherit from classes in your host app. (BTW, your approach doesn't work, I believe, because of the way ruby does constant lookups, but that's a different discussion.)
Instead, use the included hook. In the host app, you do something like:
class Article < ActiveRecord:Base
include FooEngine::BarModule
def from_application
puts "application"
end
end
And inside the engine:
module FooEngine
module BarModule
def self.included(base)
base.class_eval do
has_many :metrics, :class_name => 'Metric'
end
end
end
end
When the ruby interpreter goes to include FooEngine::BarModule in Article, it will look for and run (if found) the self.included method in FooEngine::BarModule, passing in the Articleclass as base.
You then call class_eval on the base (Article) which re-opens the Article class so that you can add methods or whatever monkey business you're up to (define new methods in situ, include or extend other modules, etc.).
In your example, you call the has_many method, which will create the various association methods provided by has_many.
If (a) you're going to add a lot of metrics-related functionality through your engine, (b) you want to have lots of classes make use of the metrics-related functionality, and (c) you want some of the functionality to vary from class-to-class (where included), you might consider creating an acts_as_having_metrics (or similar). Once you head down this path, it's a whole new world of wondrous metaprogramming.
Best of luck.
Do you have your metrics model have a belongs_to association with Articles.
You might want to give the other side of the association, Metrics a belongs_to Articles to have this work properly. Also, make sure to have a migration to hold articles_id on the metrics table. Everything should work fine.
Usually there are a lot of models in a Ruby on Rails project, so:
Is it a good practice to namespace them (in modules/folders)? What are the downsides?
EG:
Shop
category.rb
details.rb
Products
category.rb
base.rb
etc
(instead of ShopCategory, to have Shop::Category?)
Should also the controllers be namespaced in the same manner?
I've recently found this post but back from 2007 by Pratik Naik. Says there namespace in models doesn't really resemble databases. Uses something like below. Even there's a quote from DHH too.
Rails::Initializer.run do |config|
# Your existing stuff
config.load_paths << "#{RAILS_ROOT}/app/models/pets"
end
http://m.onkey.org/2007/12/9/namespaced-models
p/s: I don't know whether the post is still relevant or not, just something I found recently when I wanted namespaces in my models.
I'm doing that a lot.
So yes I think that's something you should do.
It'll be be a lot easier for you to view models if you have them subdivided in subdirectories instead of having them all in the same one.
The same recommendation is also valid for your controllers and your views.
I recommend using single table inheritance for your category model. For example:
Category < ActiveRecord::Base end
ShopCategory < Category end
ProductCategory < Category end
Shop < ActiveRecord::Base
belongs_to :shop_category
end
Product < ActiveRecord::Base
belongs_to :product_category
end
This will encapsulate commonly used category behaviour and attributes into a single model and could allow you to reuse a lot of code and have a single controller. Using namespacing only makes sense to me when the underlying classes have some sort of data/functionality in common. (example: acts_as_versioned creates a Version class namespaced under the model)