How to create a Rails 4 Concern that takes an argument
If I understand your question correctly this is about how to write such a concern, and not about the actual return value of restricted_data
. I would implement the concern skeleton as such:
require "active_support/concern"
module Restrictable
extend ActiveSupport::Concern
module ClassMethods
attr_reader :restricted
private
def restrictable(except: []) # Alternatively `options = {}`
@restricted = except # Alternatively `options[:except] || []`
end
end
def restricted_data
"This is forbidden: #{self.class.restricted}"
end
end
Then you can:
class C
include Restrictable
restrictable except: [:this, :that, :the_other]
end
c = C.new
c.restricted_data #=> "This is forbidden: [:this, :that, :the_other]"
That would comply with the interface you designed, but the except
key is a bit strange because it's actually restricting those values instead of allowing them.
How to use concerns in Rails 4
So I found it out by myself. It is actually a pretty simple but powerful concept. It has to do with code reuse as in the example below. Basically, the idea is to extract common and / or context specific chunks of code in order to clean up the models and avoid them getting too fat and messy.
As an example, I'll put one well known pattern, the taggable pattern:
# app/models/product.rb
class Product
include Taggable
...
end
# app/models/concerns/taggable.rb
# notice that the file name has to match the module name
# (applying Rails conventions for autoloading)
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
class_attribute :tag_limit
end
def tags_string
tags.map(&:name).join(', ')
end
def tags_string=(tag_string)
tag_names = tag_string.to_s.split(', ')
tag_names.each do |tag_name|
tags.build(name: tag_name)
end
end
# methods defined here are going to extend the class, not the instance of it
module ClassMethods
def tag_limit(value)
self.tag_limit_value = value
end
end
end
So following the Product sample, you can add Taggable to any class you desire and share its functionality.
This is pretty well explained by DHH:
In Rails 4, we’re going to invite programmers to use concerns with the
default app/models/concerns and app/controllers/concerns directories
that are automatically part of the load path. Together with the
ActiveSupport::Concern wrapper, it’s just enough support to make this
light-weight factoring mechanism shine.
Rails 4 routing concern with url parameter
As the error describes, you need to specify the :action
to hit. The following should work
concern :votable do
get 'vote/:vote_type', action: :vote
end
resources :parking_locations, concerns: :votable
This will hit the vote
action of the ParkingLocationsController
and you'll have access to the vote_type
param.
Use of concern in rails 4
Use the Rails built in I18n functionality instead. Doing localization in the model layer is just wrong. Models should only be concerned with data and business logic - not how data (like dates) are presented.
Rails 4: around_action with parameter
There are some clues:
- around_action receives a
callback
and ablock
as params, if we send afunction
as the 1st param, thatfunction
mustn't have any parameter! - We can send a block instead (like you did) but we must pass the current given block to that block as well, your code misses the passing block, that is why the exception raised.
- In
protect_from_exception_with
, I can callblock_given?
, it returnstrue
, but I don't know how to get the block out!
This works:
module CatchException
extend ActiveSupport::Concern
module ClassMethods
def protect_from_exception_with(failure_template, params)
around_action -> { catch_exception_with(failure_template) }, params
end
end
private
def log_error(e)
# Many things happen here
end
def catch_exception_with(failure_template)
self.send(params[:action])
rescue => e
log_error(e)
render failure_template
end
end
Thankfully, we still have the params in catch_exception_with
, make it easy, call the action back to the controller!
Rails concerns: class method with block
The problem was solved with call method.
I rewrote instance_method as follows:
def instance_method
class_attrs = self.class.display_attrs.map { |n| n[:attr] }
class_blocks = self.class.display_attrs.map { |n| n[:block].call(self) if n[:block] }
(class_attrs + class_blocks).compact!
end
module ClassMethods
#...
display_attrs << { block: block} if block_given?
#...
end
Related Topics
Can You Install Documentation for Existing Gems
How to Make Like Clause Case-Insensitive
How to Summarize Array of Integers as an Array of Ranges
How to Run Ruby on Rails Applications on a Windows Box
Ruby on Rails: Confirmation Page for Activerecord Object Creation
Dynamic Active Record Store Accessors Based Off a User Form
Rails Server Cannot Start; Getaddrinfo: Nodename Nor Servname Provided, or Not Known (Socketerror)
How to Strip Leading and Trailing Quote from String, in Ruby
Ruby Inside JavaScript Block [Slim Template]
How to Reset a Factory_Girl Sequence