Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

fixed #6

Open
wants to merge 14 commits into from

1 participant

@stuffihavemade

No description provided.

stuffihavemade added some commits
@stuffihavemade stuffihavemade Here is some changes to Demeter. All changes preserve backward compat…
…iblity.

1) A Gemfile was added to the project.
2) Some require statements are changed to fix file not found errors.
3) Demeter now supports passing arbitrary arguments to the method. This allows
setters and true methods to be called, instead of only getters.
4) Demeter methods are now automatically defined for ActiveRecord relationships.
5) Accessors are automatically defined for child objects defined by demeter.
6) README changed to reflect new changes.
7) Unit tests added to test changes.
a639987
@stuffihavemade stuffihavemade README modified to better reflect behavior of automatic ActiveRecord …
…demeter methods.
eef9176
@stuffihavemade stuffihavemade Added new fixtures and edited demeter temporily to begin writing test…
…s for options.
b5aa04f
@stuffihavemade stuffihavemade Added new unit tests to reflect changes to be made for adding an opti…
…ons hash.
5edfcd9
@stuffihavemade stuffihavemade Implemented changes to implementation so that tests pass. In particular,
the implementation of responds_to? so it that it checks super if responds first, instead of checking last after all the demeter methods have been checked.
c38b875
@stuffihavemade stuffihavemade Updated README to reflect new features. b3a349d
@stuffihavemade stuffihavemade Removed extraneous debugger statement. c57a381
@stuffihavemade stuffihavemade version bump b73d9a0
@stuffihavemade stuffihavemade fixed README formatting problem c7dc87f
@stuffihavemade stuffihavemade Fixed bug where the default and delegate options didn't work together…
…. Also fixed bug where messages would be sent to default child object, even if the object was nil.
616cb21
@stuffihavemade stuffihavemade Bumped version number. b2bf47e
@stuffihavemade stuffihavemade Changed new DSL to object based, away from hash based. It was getting…
… unwieldy.
2170f56
@stuffihavemade stuffihavemade changed error behavior when default object is nil 297a9e7
@stuffihavemade stuffihavemade removed obsolete unit test d03c012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 17, 2011
  1. @stuffihavemade

    Here is some changes to Demeter. All changes preserve backward compat…

    stuffihavemade authored
    …iblity.
    
    1) A Gemfile was added to the project.
    2) Some require statements are changed to fix file not found errors.
    3) Demeter now supports passing arbitrary arguments to the method. This allows
    setters and true methods to be called, instead of only getters.
    4) Demeter methods are now automatically defined for ActiveRecord relationships.
    5) Accessors are automatically defined for child objects defined by demeter.
    6) README changed to reflect new changes.
    7) Unit tests added to test changes.
Commits on Jul 23, 2011
  1. @stuffihavemade
  2. @stuffihavemade
  3. @stuffihavemade
  4. @stuffihavemade

    Implemented changes to implementation so that tests pass. In particular,

    stuffihavemade authored
    the implementation of responds_to? so it that it checks super if responds first, instead of checking last after all the demeter methods have been checked.
  5. @stuffihavemade
  6. @stuffihavemade
  7. @stuffihavemade

    version bump

    stuffihavemade authored
  8. @stuffihavemade
  9. @stuffihavemade

    Fixed bug where the default and delegate options didn't work together…

    stuffihavemade authored
    …. Also fixed bug where messages would be sent to default child object, even if the object was nil.
Commits on Jul 24, 2011
  1. @stuffihavemade

    Bumped version number.

    stuffihavemade authored
Commits on Jul 31, 2011
  1. @stuffihavemade
  2. @stuffihavemade
Commits on Aug 9, 2011
  1. @stuffihavemade
This page is out of date. Refresh to see the latest.
View
18 Gemfile
@@ -0,0 +1,18 @@
+source "http://rubygems.org"
+# Add dependencies required to use your gem here.
+# Example:
+# gem "activesupport", ">= 2.3.5"
+
+# Add dependencies to develop your gem here.
+# Include everything needed to run rake, tests, features, etc.
+gem "activerecord", "~> 3.0.3"
+
+group :development, :test do
+ gem "rspec", "1.3.2"
+ gem "bundler", "~> 1.0.0"
+ gem "jeweler", "~> 1.5.2"
+ gem "sqlite3", "1.3.3"
+ gem 'ruby-debug19', :require => 'ruby-debug'
+ gem "factory_girl", "~> 2.0.0.rc1"
+end
+
View
135 README.markdown
@@ -53,19 +53,143 @@ You can easily override a method that has been "demeterized"; just declare it be
class User < ActiveRecord::Base
has_one :address
- demeter_address
+ demeter :address
def address_country
@address_country ||= address.country.upcase
end
end
+Demeter will automatically defined accessor methods from relationship defined. So, for
+
+ class User < ActiveRecord::Base
+ demeter :address
+ end
+
+ class Address < ActiveRecord::Base
+ demeter :coordinate
+ end
+
+ class Coordinate < ActiveRecord::Base
+ demeter :coordinate
+ end
+
+the methods
+
+ user.address_coordinate
+
+and
+
+ user.address_coordinate=
+
+will be defined automatically. Note that attempting to assign to coordinate
+while having a nil address will throw an error.
+
+Demeter will also automatically be defined for ActiveRecord relationships. So, for
+
+ class User < ActiveRecord::Base
+ has_one :address
+ end
+
+the demeter method
+
+ user.address_country
+
+will automatically be defined. IMPORTANT NOTE: to retain backwards compatiblity,
+if the demeter macro method is used in a class, then the implicit ActiveRecord
+demeter methods will cease to work, and will need to be defined explicitly. So, for
+
+ class User < ActiveRecord::Base
+ has_one :address
+ demeter :other
+ end
+
+the demeter method
+
+ user.address_country
+
+will no longer be defined. Instead, use
+
+ class User < ActiveRecord::Base
+ has_one :address
+ demeter :other, :address
+ end
+.
+
+The 'Demeter' module also currently two options: default and delegate. Default
+allows for any message passed to a parent object that is not understood to be first send to a designated child object, before being sent to the superclass. So, for
+
+ class User
+ extend Demeter
+ demeter do |d|
+ d.has :address |a|
+ a.is_default
+ end
+ end
+ end
+
+the demeter method
+
+ user.country
+
+is valid. Defining more than one default at a time e.g.
+
+ class User
+ extend Demeter
+ demeter do |d|
+ d.has :address |a|
+ a.is_default
+ end
+ d.has :animal |a|
+ a.is_default
+ end
+ end
+ end
+
+will cause an error to be raised. An error will also be raised if
+a message is attempted to be passed to a default object that is nil.
+
+Delegate allows redirecting an incoming message to another message. For example,
+
+ class User
+ extend Demeter
+ demeter do |d|
+ d.has :address do |a|
+ a.delegates(:zip).to :zip_code
+ end
+ end
+ end
+
+will make the demeter method
+
+ user.address_zip
+
+exhibit the same behavior as
+
+ user.address_zip_code
+
+Delegate and default can be used together, and with multiple demeters at once.
+For example,
+
+ class User
+ extend Demeter
+ demeter do |d|
+ d.has :animal
+ d.has :address do |a|
+ a.is_default
+ a.delegates(:zip).to :zip_code
+ end
+ end
+
+will allow for the demeter method
+
+ user.zip
+
+.
To-Do
-----
-* Allow more than one level
-* Automatically create accessors
-* Detect all `has_one` and `belongs_to` relations and automatically "demeterized" them on ActiveRecord
+* Allow demeter methods to be defined automatically recursively
* RDoc
Maintainer
@@ -78,6 +202,7 @@ Contributor
* Nando Vieira (<http://simplesideias.com.br/>)
* Tino Gomes (<http://blog.tinogomes.com/>)
+* stuffihavemade (<http://github.com/stuffihavemade/>)
License:
--------
@@ -101,4 +226,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
2  Rakefile
@@ -1,6 +1,6 @@
require 'rake'
require 'spec/rake/spectask'
-require 'lib/demeter/version'
+require File.expand_path("lib/demeter/version", File.dirname(__FILE__))
require 'jeweler'
desc 'Default: run specs.'
View
71 lib/demeter.rb
@@ -1,3 +1,5 @@
+require File.expand_path("demeter/demeter_options/demeter_options_dispatcher", File.dirname(__FILE__))
+
module Demeter
def self.extended(base)
base.class_eval do
@@ -5,7 +7,7 @@ def self.extended(base)
extend ClassMethods
class << self
- attr_accessor :demeter_names
+ attr_accessor :demeter_names, :default_name, :nil_block
end
base.demeter_names = []
@@ -13,38 +15,65 @@ class << self
end
module InstanceMethods
- def method_missing(method_name, *attrs, &block)
- object_method_name = method_name.to_s
- object_name = self.class.demeter_names.find {|name| object_method_name =~ /^#{name}_/ }
- return super unless object_name
+ def method_missing_proc method_name, *attrs, &block
+ object_method_name = method_name.to_s
- object_method_name.gsub!(/^#{object_name}_/, "")
+ object_name = self.class.demeter_names.find do |name|
+ object_method_name =~ /^#{name}_/
+ end
- instance_eval <<-TXT
- def #{method_name} # def address_street
- #{object_name}.#{object_method_name} if #{object_name} # address.street
- end # end
- TXT
+ child = self.class.default_name
- send(method_name)
+ if object_name.nil? and child.nil?
+ false
+ elsif object_name.nil?
+ child_object = self.send child
+ if child_object.nil?
+ false
+ elsif child_object.respond_to? method_name
+ lambda {child_object.send method_name, *attrs, &block}
+ else
+ false
+ end
+ else
+ object_method_name.gsub!(/^#{object_name}_/, "")
+ target = self.send object_name
+ if target.nil?
+ lambda {nil}
+ else
+ lambda do
+ target.send object_method_name, *attrs, &block
+ end
+ end
+ end
end
-
- def respond_to?(method_name, include_private = false)
- object_method_name = method_name.to_s
- object_name = self.class.demeter_names.find {|name| object_method_name =~ /^#{name}_/ }
-
- if object_name && (object = send(object_name))
- object.respond_to?(object_method_name.gsub(/^#{Regexp.escape(object_name.to_s)}_/, ""))
+ def method_missing(method_name, *attrs, &block)
+ result = method_missing_proc(method_name, *attrs, &block)
+ if result
+ result.call
else
super
end
end
+
+ def respond_to?(method_name, include_private = false)
+ super || !! method_missing_proc(method_name)
+ end
end
module ClassMethods
- def demeter(*attr_names)
- self.demeter_names = attr_names
+ def demeter *attrs, &block
+ dispatcher = DemeterOptionsDispatcher.new self
+ dispatcher.dispatch! *attrs, &block
+
+ self.demeter_names = dispatcher.to_be_demetered
+ self.default_name = dispatcher.default_name
+ self.class_eval do
+ demeter_names.each do |name|
+ attr_accessor name
+ end
+ end
end
end
end
View
26 lib/demeter/active_record.rb
@@ -3,4 +3,30 @@ def self.inherited(base)
super
base.send :extend, ::Demeter
end
+ class << self
+ alias :belongs_to_fefda791 :belongs_to
+ alias :has_one_b66cd805 :has_one
+ alias :has_many_0ca27733 :has_many
+ alias :has_and_belongs_to_many_7fe5eedb :has_and_belongs_to_many
+ def association_to_demeter *args, &block
+ first = args.first
+ if not self.demeter_names.include? first
+ self.demeter_names << first
+ end
+ block.call *args
+ end
+
+ def belongs_to *args
+ association_to_demeter(*args) {|*x| belongs_to_fefda791 *x}
+ end
+ def has_one *args
+ association_to_demeter(*args) {|*x| has_one_b66cd805 *x}
+ end
+ def has_many *args
+ association_to_demeter(*args){|*x| has_many_0ca27733 *x}
+ end
+ def has_and_belongs_to_many *args
+ association_to_demeter(*args) {|*x| has_and_belongs_to_many_7fe5eedb *x}
+ end
+ end
end if defined? ActiveRecord::Base
View
27 lib/demeter/demeter_options/delegate_adder.rb
@@ -0,0 +1,27 @@
+class DelegateAdder
+ def initialize klass
+ @klass = klass
+ end
+ def add_methods! options
+ options.sub_options.each do |s_o|
+ self.add_method! s_o
+ end
+ end
+ def add_method! sub_option
+ sub_option.delegate_options.each do |d_o|
+ @klass.class_eval do
+ object_name_prefix = sub_option.name.to_s + '_'
+
+ receiving_name = d_o.receiving.to_s
+ incoming_name = d_o.incoming.to_s
+
+ incoming_method = object_name_prefix + incoming_name
+ receiving_method = object_name_prefix + receiving_name
+
+ define_method(incoming_method) do |*args|
+ self.send *([receiving_method] + args)
+ end
+ end
+ end
+ end
+end
View
42 lib/demeter/demeter_options/demeter_options_dispatcher.rb
@@ -0,0 +1,42 @@
+require File.expand_path("options", File.dirname(__FILE__))
+require File.expand_path("delegate_adder", File.dirname(__FILE__))
+
+class DemeterOptionsDispatcher
+ attr_accessor :to_be_demetered, :default_name
+ def initialize klass
+ @klass = klass
+ self.to_be_demetered = []
+ end
+ def simple_list_of_demeter_names? *args
+ not args.empty? and
+ args.inject(true) {|x,y| x and (y.is_a? String or y.is_a? Symbol)}
+ end
+ def dispatch! *args
+ if simple_list_of_demeter_names? *args
+ args.each do |a|
+ to_be_demetered << a
+ end
+ else
+ options = DemeterOptions.new
+ yield options
+ options.sub_options.each do |s_o|
+ to_be_demetered << s_o.name
+ end
+ default_s_o = options.sub_option_with_default
+
+ if not default_s_o.nil?
+ default_s_o.delegate_options.each do |d_o|
+ @klass.class_eval do
+ define_method(d_o.incoming) do |*args, &block|
+ full_name = default_s_o.name.to_s + '_' + d_o.receiving.to_s
+ self.send full_name, *args, &block
+ end
+ end
+ end
+ end
+
+ self.default_name = default_s_o.name unless default_s_o.nil?
+ DelegateAdder.new(@klass).add_methods! options
+ end
+ end
+end
View
54 lib/demeter/demeter_options/options.rb
@@ -0,0 +1,54 @@
+class DemeterOptions
+ attr_accessor :sub_options
+ def initialize
+ self.sub_options = []
+ end
+ def has name
+ sub = DemeterSubOption.new
+ sub.name = name
+ self.sub_options << sub
+ yield sub if block_given?
+ if more_than_one_default?
+ message = 'At most one default object may be defined per class.'
+ raise MoreThanOneDefaultDefinedError.new message
+ end
+ end
+ def more_than_one_default?
+ sub_options.find_all do |s|
+ s.is_default?
+ end.size > 1
+ end
+ def sub_option_with_default
+ self.sub_options.find {|o| o.is_default?}
+ end
+end
+
+class MoreThanOneDefaultDefinedError < RuntimeError
+end
+
+class DemeterSubOption
+ attr_accessor :name, :delegate_options
+ def initialize
+ self.delegate_options = []
+ @is_default = false
+ end
+ def is_default?
+ @is_default
+ end
+ def is_default
+ @is_default = true
+ end
+ def delegates incoming_message_name
+ delegate_option = DelegateOption.new
+ delegate_option.incoming = incoming_message_name
+ delegate_options << delegate_option
+ delegate_option
+ end
+end
+
+class DelegateOption
+ attr_accessor :incoming, :receiving
+ def to receiving_message_name
+ self.receiving = receiving_message_name
+ end
+end
View
6 lib/demeter/version.rb
@@ -1,8 +1,8 @@
module Demeter
module Version
- MAJOR = "2"
- MINOR = "0"
- PATCH = "3"
+ MAJOR = "3"
+ MINOR = "3"
+ PATCH = "1"
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
end
end
View
21 spec/demeter/active_record_spec.rb
@@ -26,4 +26,25 @@
Task.demeter_names.should == [:project]
Owner.demeter_names.should == [:task]
end
+
+ it "creates demeter from a has_one definition" do
+ implicit = Implicit.new
+ implicit.build_sub
+ implicit.sub.define_singleton_method(:test){true}
+ implicit.sub_test.should == true
+ end
+
+ it "creates demeter from a belongs_to definition" do
+ sub = Sub.new
+ sub.build_implicit
+ sub.implicit.define_singleton_method(:test){true}
+ sub.implicit_test.should == true
+ end
+
+ it "creates demeter from a has_many definition" do
+ superObj = Super.new
+ superObj.implicits.build
+ superObj.implicits.define_singleton_method(:test){true}
+ superObj.implicits_test.should == true
+ end
end
View
81 spec/demeter/demeter_options/demeter_options_dispatcher_spec.rb
@@ -0,0 +1,81 @@
+require File.expand_path("../../spec_helper", File.dirname(__FILE__))
+
+describe DemeterOptionsDispatcher do
+ it "works with the old style declaration methods" do
+ klass = Class.new
+ d = DemeterOptionsDispatcher.new klass
+ d.dispatch! :hello, :goodbye
+ d.to_be_demetered.should == [:hello, :goodbye]
+ d.default_name.should == nil
+ end
+
+ it "works with the DSL style declaration methods" do
+ klass = Class.new
+ d = DemeterOptionsDispatcher.new klass
+ d.dispatch! do |d|
+ d.has :hello
+ d.has :goodbye
+ end
+ d.to_be_demetered.should == [:hello, :goodbye]
+ d.default_name.should == nil
+ end
+
+ it "allows the defintion of a default child object" do
+ klass = Class.new
+ default_class = Class.new
+ d = DemeterOptionsDispatcher.new klass
+ d.dispatch! do |d|
+ d.has :hello do |h|
+ h.is_default
+ end
+ end
+ d.default_name.should == :hello
+ end
+
+ it "allows the definition of exactly one child object" do
+ klass = Class.new
+ default_class = Class.new
+ d = DemeterOptionsDispatcher.new klass
+ doing do
+ d.dispatch! do |d|
+ d.has :hello do |h|
+ h.is_default
+ end
+ d.has :goodbye do |g|
+ g.is_default
+ end
+ end
+ end.should raise_error MoreThanOneDefaultDefinedError
+ end
+
+ it "allows delegation from one message name to another" do
+ klass = Class.new
+ d = DemeterOptionsDispatcher.new klass
+ d.dispatch! do |d|
+ d.has :hello do |h|
+ h.delegates(:greet).to :greeting
+ end
+ end
+ klass.new.should respond_to :hello_greet
+ end
+
+ it "allows for combination of default and delegation" do
+ klass = Class.new
+
+ class Hello
+ def greeting
+ 'hi'
+ end
+ end
+
+ d = DemeterOptionsDispatcher.new klass
+ d.dispatch! do |d|
+ d.has :hello do |h|
+ h.is_default
+ h.delegates(:greet).to :greeting
+ end
+ end
+ klass.new.should respond_to :greet
+ end
+
+end
View
111 spec/demeter/demeter_spec.rb
@@ -1,14 +1,17 @@
-require File.dirname(__FILE__) + "/../spec_helper"
+require File.expand_path("../spec_helper", File.dirname(__FILE__))
describe "Demeter" do
subject { User.new }
before do
User.demeter :address, :video_game
+ Address.demeter :coordinate
subject.name = "John"
subject.address.street = "Some street"
subject.address.zip_code = "98005"
+ subject.address.coordinate.lat = 75.0
+ subject.address.coordinate.lon = 85.0
subject.video_game.title = "God of War 3"
subject.video_game.production_year = 1999
subject.profile.interests = %w(games programming music movies)
@@ -25,6 +28,15 @@
user.should respond_to(:address_zip_code)
end
+ it "should respond to nested demeterized methods" do
+ User.demeter :address
+ Address.demeter :coordinate
+ user = User.new
+
+ user.should respond_to(:address_coordinate_lat)
+ user.should respond_to(:address_coordinate_lon)
+ end
+
it "should keep responding to instance methods" do
User.demeter :address
user = User.new
@@ -68,11 +80,39 @@
subject.video_game_production_year.should == 1999
end
+ it "should delegate nested methods from coordinate object" do
+ subject.address_coordinate_lat.should == 75.0
+ subject.address_coordinate_lon.should == 85.0
+ end
+
+ it "should delegate setters to nested methods from coordinate object" do
+ subject.address_coordinate_lat = -75.0
+ subject.address_coordinate_lat.should == -75.0
+ subject.address_coordinate_lon = -85.0
+ subject.address_coordinate_lon.should == -85.0
+ end
+
+ it "should be able to pass in an arbitary number of arguments" do
+ result = subject.address_coordinate_last_of_arbitrary_number_of_args(1,2,3)
+ result.should == 3
+ end
+
+ it "should have accessors defined automatically" do
+
+ NoAccessorsParent.new.should respond_to(:no_accessors_child)
+ NoAccessorsParent.new.should respond_to(:no_accessors_child=)
+ end
+
it "should return nil when demeter object is not set" do
subject.address = nil
subject.address_title.should be_nil
end
+ it "should return nil when nested demeter object is not set" do
+ subject.address = nil
+ subject.address_coordinate_lat.should be_nil
+ end
+
it "should raise exception when method is not defined on the demeter class" do
doing { subject.address_foo }.should raise_error(NoMethodError)
end
@@ -98,6 +138,75 @@ def address_street
doing { subject.address_title }.should raise_error(NoMethodError)
end
+ it "should respond to a message directly to a child marked default" do
+ single = SingleWithOptions.new
+ single.should respond_to(:city)
+ end
+
+ it "should send a message directly to a child marked default" do
+ single = SingleWithOptions.new
+ single.address.city = "hello"
+ single.city.should == "hello"
+ end
+
+ it "should still be able to send normal messages to a child marked default" do
+ single = SingleWithOptions.new
+ single.address.city = "hello"
+ single.address_city.should == "hello"
+ end
+
+
+ it "should still be able to respond to normal messages to a child marked default" do
+ single = SingleWithOptions.new
+ single.should respond_to(:address_city)
+ end
+
+ it "should not send a message to a default child that it understands" do
+ single = SingleWithOptions.new
+ single.define_singleton_method(:city) {:passed}
+ single.city.should == :passed
+ end
+
+ it "should raise an error if more than one default is defined" do
+ doing do
+ WithTooManyDefaults.demeter do |d|
+ d.add :address do |a|
+ a.is_default_with_class Address
+ end
+ d.add :animal do |a|
+ a.is_default_with_class Animal
+ end
+ end
+ end.should raise_error
+ end
+
+ it "should be able to work with and without options simultaneously" do
+ options = WithAndWithoutOptions.new
+ options.should respond_to(:animal_name)
+ options.should respond_to(:address_city)
+ end
+
+ it "should be able to delegate an incoming message to a different message" do
+ single = SingleWithOptions.new
+ single.address.zip_code = 12345
+ single.address_zip.should == 12345
+ end
+
+ it "should allow default and delegate to work together" do
+ single = SingleWithOptions.new
+ single.address.zip_code = 12345
+ single.zip.should == 12345
+ end
+
+ it "should be able to respond correctly for a delegated method" do
+ single = SingleWithOptions.new
+ single.should respond_to(:address_zip)
+ end
+
+ it "should be able to delegate an incoming message with an arbitrary number of arguments" do
+ single = SingleWithOptions.new
+ single.address_l(1,2,3).should == 3
+ end
describe Demeter::ClassMethods do
it "should return an array of demeter_names" do
User.demeter_names.should be_kind_of(Array)
View
97 spec/resources/classes.rb
@@ -19,6 +19,25 @@ class Address
attr_accessor :country
attr_accessor :state
attr_accessor :zip_code
+ attr_accessor :coordinate
+
+ extend Demeter
+
+ def initialize
+ @coordinate = Coordinate.new
+ end
+ def last_of_arbitrary_number_of_args *args
+ args[-1]
+ end
+end
+
+class Coordinate
+ attr_accessor :lat
+ attr_accessor :lon
+
+ def last_of_arbitrary_number_of_args *args
+ args[-1]
+ end
end
class VideoGame
@@ -42,3 +61,81 @@ def initialize
class Animal
attr_accessor :name
end
+
+class NoAccessorsParent
+ extend Demeter
+ demeter :no_accessors_child
+end
+
+class NoAccessorsChild
+end
+
+class SingleWithOptions
+ extend Demeter
+ demeter do |d|
+ d.has :address do |a|
+ a.is_default
+ a.delegates(:zip).to :zip_code
+ a.delegates(:l).to :last_of_arbitrary_number_of_args
+ end
+ end
+
+ def initialize
+ @address = Address.new
+ end
+end
+
+class WithTooManyDefaults
+ extend Demeter
+end
+
+class TwoWithOneOption
+ extend Demeter
+ demeter do |d|
+ d.has :address do |a|
+ a.delegates(:zip).to :zip_code
+ end
+ d.has :animal do |a|
+ a.is_default
+ end
+ end
+end
+
+class ParentClassDefault
+ def method_missing name, *args
+ :passed
+ end
+end
+
+class NilClass
+ def passed
+ :failed
+ end
+end
+
+class ChildClassDefault < ParentClassDefault
+ extend Demeter
+ demeter do |d|
+ d.has :address do |a|
+ a.is_default
+ a.delegates(:zip).to :zip_code
+ a.delegates(:l).to :last_of_arbitrary_number_of_args
+ end
+ end
+end
+
+class WithAndWithoutOptions
+ extend Demeter
+ demeter do |d|
+ d.has :animal
+ d.has :address do |a|
+ a.is_default
+ a.delegates(:zip).to :zip_code
+ end
+ end
+
+ def initialize
+ @animal = Animal.new
+ @address = Address.new
+ end
+end
View
13 spec/resources/models.rb
@@ -12,3 +12,16 @@ class Task < ActiveRecord::Base
class Owner < ActiveRecord::Base
belongs_to :task
end
+
+class Implicit < ActiveRecord::Base
+ has_one :sub
+ belongs_to :super
+end
+
+class Sub < ActiveRecord::Base
+ belongs_to :implicit
+end
+
+class Super < ActiveRecord::Base
+ has_many :implicits
+end
View
11 spec/schema.rb
@@ -12,4 +12,15 @@
t.string :name
t.references :owner
end
+
+ create_table :implicits do |t|
+ t.references :super
+ end
+
+ create_table :subs do |t|
+ t.references :implicit
+ end
+
+ create_table :supers do |t|
+ end
end
Something went wrong with that request. Please try again.