Can a DataMapper scope use an associated model's scope? - ruby

Suppose I have a DataMapper scope for carnivores, like this:
class Animal
#...
def self.carnivores
all(:diet => 'meat')
end
#...
end
Can I reuse that scope in an association's scope?
class Zoo
#...
def self.with_carnivores
# Use `Animal.carnivores` scope to get zoos that have some?
all(:animals => ???)
end
#...
end

You can do this by going "in reverse" from the association.
class Zoo
#...
def self.with_carnivores
# Assuming `Animal belongs_to :zoo`
Animal.carnivores.zoo
end
#...
end
class Habitat
#...
def self.with_carnivores
# Assuming `Animal has_many :habitats`
Animal.carnivores.habitats
end
#...
end

Related

Ruby dot parenthesis call syntax

I was reading the jbuilder's README and saw these code:
class Person
# ... Class Definition ... #
def to_builder
Jbuilder.new do |person|
person.(self, :name, :age)
end
end
end
I tried to replicate it myself, and it asks for a call method, so:
class Thing
attr_accessor :name, :age
def call(*args)
puts args.inspect
end
end
Thing.new.(:name, :age) # => [:name, :age]
So why is there a self in the jbuilder call?
self here is just a parameter passed to the Jbuilder's call method.
Jbuilder needs the instance of person (which is self in the code) and the attribute names (:name and :age in the code) to produce the json data.
Example:
class Thing
attr_accessor :name, :age
def call(*args)
puts args.inspect
end
end
class Bar
def to_thing
Thing.new.(self, :name, :age)
end
end
Bar.new.to_thing

Defining class methods in Ruby

Typically when writing a model in Rails you use a DSL to setup various aspects of derived objects, for example:
class Question < ActiveRecord::Base
has_one :category
validates_presence_of :category
end
In this case, "has_one" and "validates_presence_of" create associations and validation call backs on models instantiated from Question.
I want to add a new method called "parent" to be used when defining a class:
class Question
attr_accessor :category
parent :category
end
q = Question.new
q.category = 'a category'
puts q.parent
-> 'a category'
So when objects are instantiated from class, they should have the method "parent" defined.
How do I do this? My first thought was to use a module, but this isn't an instance method, or a class method.
I believe this is what you are looking for:
module QuestionParent
module ClassMethods
def inherited(descendant)
descendant.instance_variable_set(:#parent, parent.dup)
super
end
def parent(args=nil)
#parent ||= args
end
end
module InstanceMethods
def parent
self.send self.class.parent.to_sym
end
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
class Question
include QuestionParent
attr_accessor :category
parent :category
end
Which produces:
q = Question.new
q.category = 'a category'
puts q.parent
a category
What this does is add a class method parent that will define the class variable #parent, when an instance calls the parent in the InstanceMethod the #parent symbol (here is category) is called.

Rails, helper with "!"

I often write stuff that looks like this:
def AModel < ActiveRecord::Base
belongs_to :user
def SomeCodeThatDoesSomeCalculations
# some code here
end
def SomeCodeThatDoesSomeCalculations!
self.SomeCodeThatDoesSomeCalculations
self.save
end
end
Is there a better way to generate the functions with the suffix "!" ?
If you're doing it really often you can do smth like that:
class Model < ActiveRecord::Base
def self.define_with_save(method_name)
define_method "#{method_name}!" do
send method_name
save
end
end
def save # stub method for test purpose
puts 'saving...'
end
def do_stuff
puts 'doing stuff...'
end
define_with_save :do_stuff
end
m = Model.new
m.do_stuff
# => 'doing stuff...'
m.do_stuff!
# => 'doing stuff...'
# => 'saving...'
If you want that in multiple models may be you'd like to create your own base class for them containing this define_with_save class method, or you can add it to ActiveRecord::Base itself if you are sure you need it.
Btw, I hope you're not really naming you your methods in SomeCodeThatDoesSomeCalculations notation as they are usually named like some_code_that_does_some_calculations.

Setting new class variables inside a module

I have a plugin I have been working on that adds publishing to ActiveRecord classes. I extend my classes with my publisher like so:
class Note < ActiveRecord::Base
# ...
publishable :related_attributes => [:taggings]
end
My publisher is structured like:
module Publisher
def self.included(base)
base.send(:extend, ClassMethods)
##publishing_options = [] # does not seem to be available
end
module ClassMethods
def publishable options={}
include InstanceMethods
##publishing_options = options
# does not work as class_variable_set is a private method
# self.class_variable_set(:##publishing_options, options)
# results in: uninitialized class variable ##publishing_options in Publisher::ClassMethods
puts "##publishing_options: #{##publishing_options.inspect}"
# ...
end
# ...
end
module InstanceMethods
# results in: uninitialized class variable ##publishing_options in Publisher::InstanceMethods
def related_attributes
##publishing_options[:related_attributes]
end
# ...
end
end
Any ideas on how to pass options to publishable and have them available as a class variable?
I am presuming that you want one set of publishing_options per class. In that case you just want to prefix your variable with a single #. Remember the class itself is an instance of the class Class so when you are in the context of a class method you actually want to set an instance variable on your class. Something like the following:
module Publishable
module ClassMethods
def publishable(options)
#publishing_options = options
end
def publishing_options
#publishing_options
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
Then if ActiveRecord::Base is extended as follows:
ActiveRecord::Base.send :include, Publishable
You can do:
class Note < ActiveRecord::Base
publishable :related_attributes => [:taggings]
end
class Other < ActiveRecord::Base
publishable :related_attributes => [:other]
end
Note.publishing_options
=> {:related_attributes=>[:taggings]}
Other.publishing_options
=> {:related_attributes=>[:other]}

What's the purpose of singleton_class.send & singleton_class.class_eval in this metaprogramming example?

class Product < ActiveRecord::Base
set_table_name 'produce'
end
module ActiveRecord
class Base
def self.set_table_name name
define_attr_method :table_name, name
end
def self.define_attr_method(name, value)
singleton_class.send :alias_method, "original_#{name}", name
singleton_class.class_eval do
define_method(name) do
value
end
end
end
end
I'd like to understand how set_table_name becomes defined in this example.
Why is singleton_class.send needed here?
And why is class_eval called on singleton_class instead of on self?
The reason for using "singleton_class" is because you do not want to modify the ActiveRecord::Base class, but the Product class.
More info about metaptogramming and singleton class here: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html

Resources