In a rails 4 app I have two models with the same schema but different tables. They are FeedEntry and HistoricalFeedEntry. I would like HistoricalFeedEntry to only inherit functionality I add to FeedEntry. The models look like so:
class FeedEntry < ActiveRecord::Base
def self.published_at_cutoff
# date cutoff before which entries are old
Time.now - 1*7*24*60*60
end
end
class HistoricalFeedEntry < FeedEntry
end
When I enter the rails console and do HistoricalFeedEntry.all I get all the results from the FeedEntry table. What I would like is to only inherit published_at_cutoff (and other methods defined by me). Thanks.
You can use a mix-in for such a thing. Create a module which contains the business logic, class methods and instance methods. And "include" it in each of the models. Something like below:
class FeedEntry < ActiveRecord::Base
include FeedEntryBusinessLogic
end
module FeedEntryBusinessLogic
def self.published_at_cutoff
# date cutoff before which entries are old
Time.now - 1*7*24*60*60
end
end
class HistoricalFeedEntry < ActiveRecord::Base
include FeedEntryBusinessLogic
end
Because you are using Rails 4, you can use Concerns ( which are similar ). Read:
https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns
Related
So I've got a gem which contains an ActiveRecord Model
# one of 30 or so models used by a suite of applications
class MyModel < ActiveRecord::Base
# some library code
end
# in Rails app, I want to add some behavior to this model using good old ruby class reopening
class MyModel < ActiveRecord::Base
scope :active, { where active: true }
end
The problem is that my app-native version of this model is only autoloaded thanks to Rails. Since it was loaded by the library and the constant is registered, it will never go to the local model to add the locally defined behavior.
Short of having an initializer with a hard-coded list of requires to the local version of the model, how might I get rails to marry the two definitions of the class to end up with one class that has all the behavior?
The simplest solution would be to:
In your gem, change the model name to something more generic (MyModelTemplate) and make it abstract:
class MyModelTemplate < ActiveRecord::Base
self.abstract_class = true
# some library code
end
Make your real model inherit MyModelTemplate:
class MyModel < MyModelTemplate
end
I'm trying to create a mixin that allows an ActiveRecord model to act as a delegate for another model. So, doing it the typical way:
class Apple < ActiveRecord::Base
def foo_species
"Red delicious"
end
end
class AppleWrapper < ActiveRecord::Base
belongs_to :apple
# some meta delegation code here so that AppleWrapper
# has all the same interface as Apple
end
a = Apple.create
w = AppleWrapper.create
w.apple = a
w.foo_species
# => 'Red delicious'
What I want is to abstract this behavior into a Mixin, so that given a bunch of data models, I can create "Wrapper" classes that are also ActiveRecords, but that each wrapper corresponds to a specific class. Why? Each of the data models have calculations, aggregations with other models, and I want the "Wrapper" classes to contain fields (in the schema) that correspond to these calculations...so in effect. the Wrapper acts as a cached version of the original data model, with the same interface.
I will have to write out each Wrapper...so for Apple, Orange, Pear, there is a different Wrapper model for each of them. However, I just want to abstract out the wrapper behavior...so that there's a class level method that sets what the Wrapper points to, a la:
module WrapperMixin
extend ActiveSupport::Concern
module ClassMethods
def set_wrapped_class(klass)
# this sets the relation to another data model and does the meta delegation
end
end
end
class AppleWrapper < ActiveRecord::Base
include WrapperMixin
set_wrapped_class Apple
end
class OrangeWrapper < ActiveRecord::Base
include WrapperMixin
set_wrapped_class Orange
end
How would I set this up? And would this have to be a STI type relation? That is, does the Wrapper class have to have a wrapped_record_id and a wrapped_record_type?
You can use belongs_to in your set_wrapped_class method.
def set_wrapped_class(klass)
belongs_to klass.to_s.downcase.to_sym
end
I have a standard ActiveRecord model with the following:
class MyModel < ActiveRecord::Base
custom_method :first_field, :second_field
end
At the moment, that custom_method is picked up by a module sent to ActiveRecord::Base. The functionality basically works, but of course, it attaches itself to every model class, not just MyModel. So if I have MyModel and MyOtherModel in the same action, it'll assume MyOtherModel has custom_method :first_field, :second_field as well.
So, my question is: How do I attach a method (eg: def custom_method(*args)) to every class that inherits from ActiveRecord::Base, but not by attaching it to ActiveRecord::Base itself?
Any ideas appreciated.
===
Edit
The custom_method is currently attached to ActiveRecord::Base by the following:
module MyCustomModule
def self.included(base)
base.extend(self)
end
def custom_method(*args)
# Zippity doo dah - code goes here
end
end
ActiveRecord::Base.send(:include, MyCustomModule)
Do you know about descendants?
ActiveRecord::Base.descendants
You have to be sure to touch the models before calling it.
See excellent discussion here:
Is there a way to get a collection of all the Models in your Rails app?
I concur with the commentors above that you may want to consider adding your methods to the meta class, or an intermediary class, or a Module mixin.
I have this method in my book model but now I realize I need this in a category model as well:
def proper_user?(logged_in_user)
return false unless logged_in_user.is_a? User
user == logged_in_user
end
I now have this method duplicated in the books model and the category model. Both category and books has belongs_to :user and both have the user_id:integer in the table as well. I simply want to extract this somewhere where so I can its DRY.
i tried to put the method in application_controller.rb but it says undefined method `proper_user?' for #
Thanks
Jeff
I think you'd want to be able to call this method like this:
book.proper_user?(current_user)
So it would work best to define it in each model rather then in User. This is best done by mixing in a module with the method:
module UserMethods
def proper_user?(logged_in_user)
# ... etc ...
end
end
and including it in each model:
class Book < AR::Base
include UserMethods
class Category < AR::Base
include UserMethods
The module can go in a source file in config/initializers, or you can put it elsewhere and change config.autoload_paths in config/environment.rb to point to the location.
Since it is related to the User model, why not put it there?
I have a rails app with several models.
I have a function that I want to access from several models.
What's the best place to put this code and how can I make it accessible from the models that need to get at it?
My understanding is that helpers are just for views. Is this correct?
It seems wrong to create a plug-in and put it in the vendor folder - this is my code and integral to my app. Is this correct?
Thanks.
The simplest solution would be to create a module under lib and mix this into the models that need it, for instance, in lib/fooable.rb:
module Fooable
def do_foo
end
end
And then in your various models:
class MyModel < ActiveRecord::Base
include Fooable
end
No need to require fooable.rb, the Rails autoloading mechanism will find it for you as long as it's named using correct conventions.
In order to lessen the repetition of code, you could also create a main class which would include that module and the simply inherit from it from every model you'd like to share the behaviour.
Something like:
module Fooable
def do_foo
end
end
class ParentModel < ActiveRecord::Base
include Fooable
end
class Product < ParentModel end
class User < ParentModel end
class Item < ActiveRecord::Base end
Thus, in that example, both Product and User would share the do_foo functionality and Item would not.