I want to hide certain implementations from the main Model methods, because of clean code reasons. I don't want my Model to contain a lot of huge methods, only the clearest and verbose functionalities.
For example:
class SomeModel
#included stuff
#fields & attrs
def modelMethod
variable = functionality1(functionality2)
if some condition
functionality3
else
functionality4
end
end
Should I put my functionality methods under a private or protected part at the end of the same model file, or should I put them into a helper file?
If I'm right, the codes in the helpers are only used for the View. What is the convention for this?
Having private or protected has nothing to do with the type of cleanup you're trying to do.
This is related to inheritance method visibility/access (though inheritance can obviously be used for reusability).
Methods will depends on reusability. Why not leverage concerns? Say we have SomeModel and want multiple models to implement suspensions.
# app/models/some_model.rb
class SomeModel
include Suspendable
end
Then add your model concern.
# app/models/concerns/suspendable.rb
module Suspendable
extend ActiveSupport::Concern
included do
has_one :suspension
scope :active, -> { joins('LEFT OUTER JOIN suspensions').where(suspension: {id: nil} }
end
end
Or if this only really applies to a single model, but want to keep the model strictly DB manipulations (not Business oriented), then you could have namespaced concerns.
# app/models/concerns/some_model/availability.rb
module SomeModel::Availability
extend ActiveSupport::Concern
module ClassMethods
def availabilities_by_some_logic
end
end
end
http://api.rubyonrails.org/v5.0/classes/ActiveSupport/Concern.html
If you have a method or set of methods that are used in various models:
Rails Concerns
This is different from private/protected and you can have private/protected methods in a concern. This is just how to extract out duplication.
If you have a method that is needed by the model, and only the model (not subclasses of the model, and never called outside the class:
private
If you have a method that is needed by the model and its subclasses but not from outside the model:
protected
If you need to be able to call the method from outside the class:
neither
this answer goes into better detail on those
Related
I am trying to develop a reusable module for Active Record models to be used shared in models. It works well except child classes can't find the variables.
This is easier demonstrated with code
This is the module:
module Scanner
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def scan(*fields)
#scan = fields if fields.present?
#scan
end
end
end
Usage:
class User < ActiveRecord::Base
include Scanner
scan :user_name
end
Calling User.scan returns :user_name. Perfect!
However. This is an issue:
class Admin < User
end
Calling Admin.scan returns nil
How come :user_name is not being set in the parent? I am following the source for AR for methods like primary_key and table_name, they seem to be inherting values from the parent
class_attribute takes care of the required inheritance semantics for you. As you've seen, it's not quite as simple as putting an instance variable on the class, because that leaves it nil on subclasses: they're instance variables, and that's a separate instance.
The examples you mention do other, more specialised and more complicated, things... but you'll find plenty of straightforward examples in the Rails source that do use class_attribute.
Consider an abstract Tag concept where there are different kinds of tags, say Topic and Location (amongst others), that are unrelated apart from being tags. They have the same base Tag properties in common but are otherwise different.
A Topic concept is based on a similar Tag concept. An operation like Topic::Update would usually inherit from Topic::Create, but such an operation also needs to inherit from Tag::Update. Ruby doesn't support multiple inheritance - can Trailblazer support this?
Trailblazer operations support inheritance through a builds block that allows them to instantiate a subclass based on the contents of the supplied params hash. This works where the base class (Tag) is public-facing and operations are invoked through the base class. However, in this example, the public-facing class is the Topic subclass.
Operations need to be invoked through the subclass (Topic) but have its operations be based off a common Tag base class (a reverse builder ?).
Here is one way that this can be achieved through single inheritance (but it illustrates the shortcomings of this approach)...
Each type of tag is stored in its own database table and has ActiveRecord classes like this:
class Tag < ActiveRecord::Base
self.abstract_class = true
end
class Topic < Tag; end
The Trailblazer concept would follow a similar design - a Tag operation would provide the base functionality and be subclassed by more a specific operation (Topic). The Tag operation would not be used directly - a Topic controller, for example, would use the Topic operation.
The Topic operation inherits from Tag but must specify its own Topic model which seems to be only possible within each operation, requiring each to be subclassed explicitly:
class Topic < Tag
class Create < Tag::Create
model Topic
end
class Update < Tag::Update
model Topic
end
class Delete < Tag::Delete
model Topic
end
end
A problem with this is that the contract, being defined on the base operation, thinks that it is a Tag rather than a Topic and this leads to issues where it's used as a model. An example showing where this is a problem is in a cell's view: the Topic concept has a cell that presents views to manipulate its objects. It renders forms using simple_form_for, like this:
simple_form_for operation.contract
This doesn't work as expected because the contract thinks it is a Tag and this breaks the form:
its parameters are sent as params[:tag] instead of params[:topic]
the submit button's label is Create Tag instead of Create Topic.
The cell can't use operation.model (which would otherwise work) because it won't see any form errors when rendering after a submited operation fails.
A way to solve this is to be explicit with simple_form_for:
simple_form_for operation.contract, as: :topic, url: topics_path ...
Another problem occurs when adding properties to Topic, because this requires extending the Tag contract. The usual way to do this is to add a contract do..end block to the Topic::Create operation. The problem occurs because such a block would not be seen by Topic::Update and Topic::Delete because they inherit from their Tag counterparts and not from Topic::Create.
An alternative would be for a subclassed Topic::Update operation to inherit from Topic::Create. This would remove the need to specify the model (because Topic::Create does it) but would mean that anything added by the Tag::Update operation would be lost:
class Update < Create
action :update
end
The action needs to be respecified because Tag::Update isn't inherited but, because Topic::Create is inherited, properties added in Topic::Create are available in Topic::Update.
Both of these styles work as long as changes are only in one base class. It breaks whe there are changes in both because Ruby does not support multiple inheritance. Consider the Delete operation which usually looks like this:
class Delete < Create
action :find
def process(params)
# validate params and then delete
end
end
If that is Tag::Delete then Topic::Delete could be either
class Delete < Tag::Delete
model Topic
end
or
class Delete < Create
action :find
end
In the former case Topic::Delete would be unaware of properties added by Topic::Create and, in the latter case, Topic::Delete would lacks the process method defned in Tag::Delete.
How can a Trailblazer concept inherit another and be able to extend its operations ?
The effect of multiple inheritance can be achieved by using modules.
First define the ActiveRecord objects like this:
class Topic < ActiveRecord::Base; end
class Location < ActiveRecord::Base; end
There is no longer a base Tag abstract class, allowing Tag to be defined as a module like this (app/concepts/tag/crud.rb):
module Tag
module Create
def self.included(base)
base.send :include, Trailblazer::Operation::Model
base.send :model, base.parent # e.g. Thing::Tag => Thing
base.send :contract, Form
end
class Form < Reform::Form
property ...
end
def process(params)
...
end
end
module Update
def self.included(base)
base.send :action, :update
end
end
module Delete
def self.included(base)
base.send :action, :find
end
def process(params)
...
end
end
end
Code that would normally be placed inside operation classes (such as include Model and contract) are placed inside a self.included method so that they are executed within the scope of the including class. The ruby send method needs to be used to invoke such methods on the including class from within the module's self.included method.
Using this Tag module, a Topic tag would look like this (app/concepts/tag/topic/crud.rb)
class Topic
class Create < Trailblazer::Operation
include Tag::Create
contract do
property ...
end
end
class Update < Create
include Tag::Update
end
class Delete < Create
include Tag::Delete
def process(params)
....
super
end
end
end
This allows extension of the Tag contract by Topic::Create, which adds properties to the contract, and further customisation of Tag methods like the Delete::process example that calls super to invoke Tag::Delete::process. Aditionally, the contract will know it's a Topic so things like simple_form will work properly.
Using a module to share common data is one (correct) way of inheriting.
However, you shouldn't forget that you can also use Trailblazer's compositional interface, where you could use inheritance across operation classes to inherit generic logic, and then refer to the layer objects using composition.
module Location
class Create < Tag::Create # inheritance.
contract Tag::Contract::Create # compositional API.
end
end
The compositional interface allows you to reference a separate class and is explained in the 2.0 docs. It works for policies, contracts, representers and callback objects.
Is there a proper place for helper methods for models in Rails? There are helper methods for controllers and views, but I'm not sure where the best place to put model helper methods. Aside from adding a method to ActiveRecord::Base, which I'd prefer not to.
UPDATE: It seems Concerns make a lot of sense. Here's an example of what I want. Certain models can never be deleted, so I add a callback that always throws an exception:
before_destroy :nope
def nope
raise 'Deleting not allowed'
end
With concerns, I could do something like this?
class MyModel < ActiveRecord::Base
include Undeletable
end
module Undeletable
extend ActiveSupport::Concern
included do
before_destroy :nope
end
def nope
raise 'Deleting not allowed'
end
end
Is this the Rails way of doing this?
If you want to use a helper_method my_helper_method inside a model, you can write
ApplicationController.helpers.my_helper_method
If you need a bit more flexibility, for example if you also need to override some methods, you can do this:
class HelperProxy < ActionView::Base
include ApplicationController.master_helper_module
def current_user
#let helpers act like we're a guest
nil
end
def self.instance
#instance ||= new
end
end
and then use with
HelperProxy.instance.my_helper_method
If you have strong nerves, you can also try to include the ApplicationController.master_helper_module directly into your model.
via : makandracards's post.
For your reference: http://railscasts.com/episodes/132-helpers-outside-views
If what you are asking is where to put code that is shared across multiple models in rails 4.2, then the standard answer has to be to use Concerns: How to use concerns in Rails 4
However, there are some good arguments (e.g. this) to just using standard rails module includes, and extends as marek-lipka suggests.
I would strongly recommend NOT using ApplicationController helper methods in a model, as you'll be importing a lot unnecessary baggage along with it. Doing so is usually a bad smell in my opinion, as it means you are not separating the MVC elements, and there is too much interdependency in your app.
If you need to modify a model object by adding a method that is just used within a view, then have a look at decorators. For example https://github.com/drapergem/draper
This is a great idea about concern in rails: http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns
And it's also a good idea to make very small methods that are not part of a public API. Without using concerns, those become private methods in a ruby class.
Does it makes sense to create private methods inside of a Rails ActiveSupport::Concern module? If so, does private work both for regular instance methods and class methods in the concern definition?
Does it makes sense to create private methods inside of a Rails ActiveSupport::Concern module?
Considering that concerns are smart modules that will eventually be included in other classes — yes, it does. It's just a portable code, extractable behavior and I'd like to consider it as part of my controller (or model, etc.) as I'm writing it. So basically you just declare methods private or protected as you normally would.
Maybe the post you linked have been updated since 2013, but DHH does exactly that in the one of the examples there:
module Dropboxed
extend ActiveSupport::Concern
included do
before_create :generate_dropbox_key
end
def rekey_dropbox
generate_dropbox_key
save!
end
private # <- Let's list some privates
def generate_dropbox_key
self.dropbox_key = SignalId::Token.unique(24) do |key|
self.class.find_by_dropbox_key(key)
end
end
end
As to private class methods, I agree with #Hugo and never used them myself, but here's how you can achieve this:
module Dropboxed
extend ActiveSupport::Concern
included do
private_class_method :method_name
end
module ClassMethods
def method_name
end
end
end
It's just my opinion but right now I'm scratching my head about private class method, what are they good for? Anyway, if you really need them refer to this post: How to create a private class method?
It does make sense to have private instance methods in a concern module and will work fine. Private class methods will work fine as well but following the above stated post.
I have an issue with Ruby on Rails.
I have several model classes that inherit from the same class in order to have some generic behaviour.
The parent class is called CachedElement.
One of the child is called Outcome.
I want an other model, called Flow to belong to any child of CachedElement.
Hence Flow has a polymorphic attributes called element, to which it belongs_to
When I create a new flow, that belongs to an Outcome, the element_type is set to "CachedElement" which is the parent class, instead of "Outcome".
This is confusing because since I have several type of CachedElement which are stored in different tables, the element_id refers to several different element.
In short I would like the element_type field to refer to the child class name and not the parent class name.
How can I do that ?
The field element_type is set to the parent class because ActiveRecord expects you to use single-table inheritance when deriving from other models. The field will reference the base class because it refers to the table that each instance is stored in.
If the children of CachedElement are stored in their own tables, it may be more helpful to replace the use of inheritance with the use of Ruby modules. The standard approach for sharing logic between classes is to use mix-ins instead of inheritance. For example:
module Cacheable
# methods that should be available for all cached models
# ...
end
class Outcome < ActiveRecord::Base
include Cacheable
# ...
end
You can now easily use polymorphic associations as you have been doing already, and element_type will be set to the proper class.
the file should go on you lib folder. but...
you could do the inheritance thing as well.
all you need to do is to tell you parent class to act as an abstract class.
# put this in your parent class then try to save a polymorphic item again.
# and dont forget to reload, (I prefer restart) if your gonna try this in
# your console.
def self.abstract_class?
true
end
and thats pretty much it, this was kinda unespected for me and actually really
hard to find in the documentation and anywhere else.
Kazuyoshi Tlacaelel.
Thanks, that's what I did, but it was kind of tricky to be able to inherits both instance and class methods from the module
Class methods can be done by:
module Cachable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def a_class_method
"I'm a class method!"
end
end
def an_instance_method
"I'm an instance method!"
end
end
class Outcome < ActiveRecord::Base
include Cacheable
end
if you want to add class methods and instance methods through a mixin (Module)
then I recommend you to abstract these in different modules.
module FakeInheritance
def self.included(klass)
klass.extend ClassMethods
klass.send(:include, InstanceMethods)
end
module ClassMethods
def some_static_method
# you dont need to add self's anywhere since they will be merged into the right scope
# and that is very cool because your code is more clean!
end
end
module InstanceMethods
# methods in here will be accessable only when you create an instance
end
end
# fake inheritance with static and instance methods
class CachedElement
include FakeInheritance
end