I have a self-rolled authorization system base on RC#386. I noticed a lot of duplication between the GuestPermission class and the MemberPermision class.
class GuestPremission < BasePermission
allow_action :static, [:about, :careers, :contact, :help, :home]
end
and
class MemberPermission < BasePermission
allow_action :static, [:about, :careers, :contact, :help, :home]
end
Both inherit from BasePermission. I want to refactor the MemberPermission class to inherit from GuestPermission instead, like this:
class GuestPermission < BasePermission
allow_action :static, [:about, :careers, :contact, :help, :home]
end
class MemberPermission < GuestPermission
...
end
Ideally, this should reduce the duplication in MemberPermission that already is granted to and exists in GuestPermission. However, I get a recursive error: filter chain haled as :authorize rendered or redirected.
This usually tells me that the requested controller#action, (in this case static#home) hasn't been implicitly authorized. But in this case, it should be inheriting from GuestPermission.
Any ideas on fixing this so that the duplication of permissions isn't necessary?
you can do it like this, use rails's concern:
module Xxx
extend ActiveSupport::Concern
included do
class_eval do
allow_action :static, [:about, :careers, :contact, :help, :home]
end
end
end
class GuestPremission < BasePermission
include Xxx
end
class MemberPermission < BasePermission
include Xxx
end
Related
I'm working in Rails 3 and have a table with multiple child tables, e.g.
class Foo < ActiveRecord::Base
has_many :things
has_many :items
has_many :widgets
end
class Thing < ActiveRecord::Base
belongs_to :foo
end
class Item < ActiveRecord::Base
belongs_to :foo
end
class Widget < ActiveRecord::Base
belongs_to :foo
end
Is there a simple way for me to check to if a given Foo has a child record in one or more of the tables? Basically, is there a better way to do this:
if !foo.things.empty? or !foo.items.empty? or !foo.widgets.empty?
puts "This foo is in use!"
emd
Well, I think you're on the right track, but maybe just put that as a method in your Foo model
class Foo < ActiveRecord::Base
def children?
things.any? || items.any? || widgets.any?
end
end
Then just say, Foo.first.children? and get true if the Foo instance has any children.
This is what any? is for.
class Foo < ActiveRecord::Base
def children?
things.any? || items.any? || widgets.any?
end
end
Since this has become a topic of contention, I present to you:
> foo = Foo.last
Foo Load (0.6ms) SELECT "foos"......
> foo.items
Item Load (0.9ms) SELECT "items".*.......
> foo.items.any?
=> true #OH, WHAT's that? NO SQL CALLS? GEE WILLICKERS
> foo.items.exists?
Item Exists (0.5ms) #Hmmmmmm....
=> true
The point here is that under any circumstances, exists makes a DB call, where as any? will not, if spaces is always loaded into memory. Now as I said, many times, the importance is not the efficiency of the DB call (AND YES, the SQL call exists? makes is more efficient), but the fact that any? won't necessarily make a call to the DB, which is a HUGE advantage. Look for yourself:
[20] pry(main)> Benchmark.measure { foo.item.exists? }
Item Exists (0.5ms) SELECT 1 AS one FROM "items" ...
=> #<Benchmark::Tms:0x007fc1f28a8638
#cstime=0.0,
#cutime=0.0,
#label="",
#real=0.002927,
#stime=0.0,
#total=0.00999999999999801,
#utime=0.00999999999999801>
[21] pry(main)> Benchmark.measure { foo.items.any? }
=> #<Benchmark::Tms:0x007fc1f29d1aa0
#cstime=0.0,
#cutime=0.0,
#label="",
#real=7.6e-05,
#stime=0.0,
#total=0.0,
#utime=0.0>
For a more concise timing, look at this:
> Benchmark.measure { 1000.times {foo.items.exists?} }.total
=> 2.5299999999999994
> Benchmark.measure { 1000.times {foo.items.any?} }.total
=> 0.0
Now as I said, many times, it depends on circumstance -- you could have many circumstances where these items aren't loaded into memory, but many times, they are. Choose which one works best for you depending on how you're calling it.
This should work for any given model.
class Foo < ActiveRecord::Base
def children?
has_associated_records = self.class.reflect_on_all_associations.map { |a| self.send(a.name).any? }
has_associated_records.include?(true)
end
end
You could subclass Thing Item and Widget. Or add a polymorphic join table to keep track of it. Not ideal, I know.
You could at least do this, so it would read a little better.
if foo.things.exists? || foo.items.exists? || foo.widgets.exists?
puts "This foo is in use!"
end
'empty?' uses 'exists?' behind the scenes, I believe.
Suppose all the associations are loaded into memory:
class Foo < ActiveRecord::Base
has_many :things
has_many :items
has_many :widgets
def in_use?
[things, items, widgets].flatten.any?
end
end
Edit
I just realized that this is wrong: each association (even if still loaded into memory) will be loaded which isn't good.
things.any? || items.any? || widgets.any?
is more correct and has been proposed before me.
The answer by #Marcelo De Polli is the most generalized one posted so far.
This answer is an updated version of it for Rails 5.
The parent class for a model is ApplicationRecord in Rails 5 and later, which used to be ActiveRecord::Base up to Rails 4 (n.b., the original question is tagged as Rails 3).
For simplicity of the code, use:
class Foo < ApplicationRecord
def children?
self.class.reflect_on_all_associations.map{ |a| self.send(a.name).any? }.any?
end
end
To pursue more run-time efficiency when a model may have many classes of children, use:
class Foo < ApplicationRecord
def children?
self.class.reflect_on_all_associations.each{ |a| return true if self.send(a.name).any? }
false
end
end
I have the following Concern:
module Eventable
extend ActiveSupport::Concern
# ...
included do
has_many :subscriptions, as: :entity, dependent: :destroy
end
end
My Models are:
class Experiment < ActiveRecord::Base
include Eventable
end
class Subscription < ActiveRecord::Base
belongs_to :entity, polymorphic: true
end
In my controller I try to create a subscription for an experiment, as following:
class SubscriptionsController < ApplicationController
before_filter :find_entity
def create
subscription = Subscriptions.new(params[:subscription])
#entity.subscriptions << subscription # Why is it false?
# ...
end
end
But it doesn't work.
While debugging, I noticed that
#entity.subscriptions.count create incorrect SQL query:
SELECT COUNT(*) FROM [subscriptions] WHERE [subscriptions].[experiment_id] = 123
while I expect:
SELECT COUNT(*) FROM [subscriptions] WHERE [subscriptions].[entity_id] = 123 AND [subscriptions].[entity_type] = 'Experiment'
Note: If I do the following, it works correctly:
subscription.entity = #entity
subscription.save
Thanks for help!
The reason for this error: class Experiment (not my class) already had has_many :subscriptions
Advise: if you have strange behavior, and you use others people code, stop and review the code!
I have method and spec.
class Event
def self.renew_subscription(user)
subscription = user.subscription
result = subscription.renew
user.pay(subscription.plan.price_in_cents) if result
result
end
end
let!(:user) { create :user }
describe ".renew_subscription" do
before do
user.subscription.stub!(:renew).and_return(true)
user.subscription.stub!(:plan).
and_return(Struct.new("SP", :price_in_cents).new(699))
end
context "when have to pay" do
it "pays" do
user.should_receive(:pay)
Event.renew_subscription user
end
end
end
There user belongs_to :subscription and subsription belongs_to :plan
Is there the way to stub subscription.renew and subscription.plan (or subscription.plan.price_in_cents)?
I think it's probably safe for you to do something like this:
Subscription.any_instance.stub(:renew).and_return(true)
plan = mock_model(Plan)
Subscription.any_instance.stub(:plan).and_return(plan)
plan.stub(:price_in_cents).and_return(699)
There are probably other ways of doing it too, but I hope that helps.
In rails3, I make same scopes in model. for example
class Common < ActiveRecord::Base
scope :recent , order('created_at DESC')
scope :before_at , lambda{|at| where("created_at < ?" , at) }
scope :after_at , lambda{|at| where("created_at > ?" , at) }
end
I want to split common scopes to module in lib. So I try like this one.
module ScopeExtension
module Timestamps
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
scope :recent , lambda{order('created_at DESC')}
scope :before_at , lambda{|at| where("created_at < ?" , at) }
scope :after_at , lambda{|at| where("created_at > ?" , at) }
end
end
and I write this one
class Common < ActiveRecord::Base
include ScopeExtension::Timestamps
end
But Rails show this error.
undefined method `scope' for ScopeExtension::Timestamps::ClassMethods:Module
(I didn't forget auto loading library)
How can I easily reuse common scope feature in active record?
I guess this problem to relate loading sequence. But I don't have any idea to solve.
Please hint me.
I solved this calling the scope on self.included(class):
module Timestamps
def self.included(k)
k.scope :created_yesterday, k.where("created_at" => Date.yesterday.beginning_of_day..Date.yesterday.end_of_day)
k.scope :updated_yesterday, k.where("created_at" => Date.today.beginning_of_day..Date.today.end_of_day)
k.scope :created_today, k.where("created_at" => Date.today.beginning_of_day..Date.today.end_of_day)
k.scope :updated_today, k.where("created_at" => Date.today.beginning_of_day..Date.today.end_of_day)
end
end
In Rails 3 there's no difference between a declared scope and a class method that returns an ActiveRecord::Relation, so it can be more elegant to use a mixin module:
class MyClass < ActiveRecord::Base
extend ScopeExtension::Timestamps
end
module ScopeExtension
module Timestamps
def recent
order('created_at DESC')
end
def before_at(at)
where('created_at < ?' , at)
end
def after_at(at)
where('created_at > ?' , at)
end
end
end
MyClass.after_at(2.days.ago).before_at(1.hour.ago).recent
I'm working on a fairly simple site that allows users to choose recipe ingredients, their quantities and then shows them nutritional info based on their recipe and a large database.
Right now, I feel like I'm repeating myself a bit. I want to be able to make this "DRY" by having one method each in the Recipe and Recipe_Ingredient model that will do the same thing only accept the right parameter, which will be the type of nutrient.
Here is the relevant code in my view that currently calls two different methods (and will call more when extended to the other nutrients):
<ul>Calories <%= #recipe.total_calories %></ul>
<ul>Fat (grams) <%= #recipe.total_fat %></ul>
In my recipe model, I have methods that iterate over each of the ingredients in the recipe:
def total_calories
recipe_ingredients.to_a.sum { |i| i.total_calories }
end
def total_fat
recipe_ingredients.to_a.sum { |i| i.total_fat }
end
In the block, we call two separate methods that actually calculate the nutrients for each individual recipe ingredient:
def total_calories
ingredient.calories*ingredient.weight1*quantity/100
end
def total_fat
ingredient.fat*ingredient.weight1*quantity/100
end
This last piece is where we reference the database of ingredients. For context, here are the relationships:
class RecipeIngredient < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
Thanks in advance for any help.
Lev
The send method with a symbol parameter works well for that kind of DRY.
<ul>Calories <%= #recipe.total :calories %></ul>
<ul>Fat (grams) <%= #recipe.total :fat %></ul>
Recipe
def total(type)
recipe_ingredients.to_a.sum { |i| i.total type }
end
RecipeIngredient
def total(type)
ingredient.send(type) * ingredient.weight1 * quantity / 100
end
You could use meta programming to dynamically add the methods. Here is a start, you can get even more DRY than this.
class DynamicTotalMatch
attr_accessor :attribute
def initialize(method_sym)
if method_sym.to_s =~ /^total_of_(.*)$/
#attribute = $1.to_sym
end
end
def match?
#attribute != nil
end
end
Recipe
class Recipe
def self.method_missing(method_sym, *arguments, &block)
match = DynamicTotalMatch.new(method_sym)
if match.match?
define_dynamic_total(method_sym, match.attribute)
send(method_sym, arguments.first)
else
super
end
end
def self.respond_to?(method_sym, include_private = false)
if DynamicTotalMatch.new(method_sym).match?
true
else
super
end
end
protected
def self.define_dynamic_total(method, attribute)
class_eval <<-RUBY
def self.#{method}(#{attribute})
recipe_ingredients.to_a.sum { |i| i.send(attribute)
end
RUBY
end
end
RecipeIngredient
class RecipeIngredient
def self.method_missing(method_sym, *arguments, &block)
match = DynamicTotalMatch.new(method_sym)
if match.match?
define_dynamic_total(method_sym, match.attribute)
send(method_sym, arguments.first)
else
super
end
end
def self.respond_to?(method_sym, include_private = false)
if DynamicTotalMatch.new(method_sym).match?
true
else
super
end
end
protected
def self.define_dynamic_total(method, attribute)
class_eval <<-RUBY
def self.#{method}(#{attribute})
ingredient.send(attribute) * ingredient.weight1 * quantity / 100
end
RUBY
end
end
Example was copied from ActiveRecord and this page: http://technicalpickles.com/posts/using-method_missing-and-respond_to-to-create-dynamic-methods/