From bef0c6e74d3b035941c60d817f7618b4c6bb2a71 Mon Sep 17 00:00:00 2001 From: matt camuto Date: Wed, 25 Sep 2013 21:09:07 -0700 Subject: [PATCH 01/10] test funky way to override class name --- lib/compositor/base.rb | 10 ++++++--- lib/compositor/leaf.rb | 11 +++++++++ spec/compositor/base_spec.rb | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/compositor/base.rb b/lib/compositor/base.rb index c30e573..52de825 100644 --- a/lib/compositor/base.rb +++ b/lib/compositor/base.rb @@ -1,5 +1,6 @@ module Compositor - class MethodAlreadyDefinedError < RuntimeError; end + class MethodAlreadyDefinedError < RuntimeError; + end class Base attr_reader :attrs @@ -38,11 +39,14 @@ def root_class_name end def self.root_class_name(klazz) + return klazz.dsl_override if klazz.respond_to?(:dsl_override) + return nil if klazz.name.nil? klazz.name.gsub(/(.*::)|(Compositor$)/, '').underscore end def self.inherited(subclass) method_name = root_class_name(subclass) + return if method_name.nil? unless method_name.eql?("base") || method_name.start_with?("abstract") # check if it's already defined if Compositor::DSL.instance_methods.include?(method_name.to_sym) @@ -50,8 +54,8 @@ def self.inherited(subclass) end Compositor::DSL.send(:define_method, method_name) do |*args, &block| subclass. - new(@context, *args). - dsl(self, &block) + new(@context, *args). + dsl(self, &block) end end end diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index 54a111b..e250df2 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -24,4 +24,15 @@ def dsl(dsl) end end end + + # Mimic method so you can overide leave name in a class. Since base uses + # inherited callback this option does not currently exited in the class + # itself. + def self.AbstractLeaf hsh + raise "Abstract leaf hsh must contain :as element" unless hsh[:as] + clazz = Class.new(Compositor::Base) + clazz.class_eval "def self.dsl_override; '#{hsh[:as]}'; end;" + clazz + end + end diff --git a/spec/compositor/base_spec.rb b/spec/compositor/base_spec.rb index 0a09aff..03ce3c0 100644 --- a/spec/compositor/base_spec.rb +++ b/spec/compositor/base_spec.rb @@ -40,5 +40,48 @@ class AbstractUserCompositor < Compositor::Leaf Compositor::DSL.instance_methods.should_not include(:abstract_user) end + describe "#override_root_class_name" do + + it "override does work" do + lambda { + + class Crew < Compositor::AbstractLeaf :as => "Crew" + end + + module Motley + class Crew < Compositor::AbstractLeaf :as => "MotleyCrew" + end + end + + module TwoLive + class Crew < Compositor::AbstractLeaf :as => "TwoLiveCrew" + end + end + + module WorldClassWrecking + class Crew < Compositor::AbstractLeaf :as => "WorldClassWreckingCrew" + end + end + }.call + [:Crew, :MotleyCrew, :WorldClassWreckingCrew].each do |dsl_name| + Compositor::DSL.instance_methods.should include(dsl_name) + end + end + + it "Fails with same override" do + expect do + lambda { + class Gang < Compositor::AbstractLeaf :as => "Gang" + end + + module KoolAndThe + class Gang < Compositor::AbstractLeaf :as => "Gang" + end + end + + }.call + end.to raise_error + end + end end end From 25df720fc6b2b1e9d740f5be31a08872977f584b Mon Sep 17 00:00:00 2001 From: matt camuto Date: Thu, 26 Sep 2013 13:15:27 -0700 Subject: [PATCH 02/10] use define_singleton_method.. to the rescue for anon class-level override --- lib/compositor/base.rb | 1 + lib/compositor/leaf.rb | 10 ++--- spec/compositor/base_spec.rb | 16 ++++---- tmp.rb | 75 ++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 tmp.rb diff --git a/lib/compositor/base.rb b/lib/compositor/base.rb index 52de825..57e1127 100644 --- a/lib/compositor/base.rb +++ b/lib/compositor/base.rb @@ -38,6 +38,7 @@ def root_class_name self.class.root_class_name(self.class) end + def self.root_class_name(klazz) return klazz.dsl_override if klazz.respond_to?(:dsl_override) return nil if klazz.name.nil? diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index e250df2..d905438 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -28,11 +28,11 @@ def dsl(dsl) # Mimic method so you can overide leave name in a class. Since base uses # inherited callback this option does not currently exited in the class # itself. - def self.AbstractLeaf hsh - raise "Abstract leaf hsh must contain :as element" unless hsh[:as] - clazz = Class.new(Compositor::Base) - clazz.class_eval "def self.dsl_override; '#{hsh[:as]}'; end;" - clazz + def self.AbstractLeaf options + raise "Abstract leaf hsh must contain :as element" unless options[:as] + Class.new(Compositor::Leaf) do + define_singleton_method(:dsl_override) { options[:as] } + end end end diff --git a/spec/compositor/base_spec.rb b/spec/compositor/base_spec.rb index 03ce3c0..8cabf24 100644 --- a/spec/compositor/base_spec.rb +++ b/spec/compositor/base_spec.rb @@ -45,25 +45,26 @@ class AbstractUserCompositor < Compositor::Leaf it "override does work" do lambda { - class Crew < Compositor::AbstractLeaf :as => "Crew" + class Crew < Compositor::AbstractLeaf :as => "crew" end module Motley - class Crew < Compositor::AbstractLeaf :as => "MotleyCrew" + class Crew < Compositor::AbstractLeaf :as => "motley_crew" end end module TwoLive - class Crew < Compositor::AbstractLeaf :as => "TwoLiveCrew" + class Crew < Compositor::AbstractLeaf :as => "two_live_crew" end end module WorldClassWrecking - class Crew < Compositor::AbstractLeaf :as => "WorldClassWreckingCrew" + class Crew < Compositor::AbstractLeaf :as => "world_class_wrecking_crew" end end }.call - [:Crew, :MotleyCrew, :WorldClassWreckingCrew].each do |dsl_name| + + [:crew, :motley_crew, :world_class_wrecking_crew].each do |dsl_name| Compositor::DSL.instance_methods.should include(dsl_name) end end @@ -71,14 +72,13 @@ class Crew < Compositor::AbstractLeaf :as => "WorldClassWreckingCrew" it "Fails with same override" do expect do lambda { - class Gang < Compositor::AbstractLeaf :as => "Gang" + class Gang < Compositor::AbstractLeaf :as => "gang" end module KoolAndThe - class Gang < Compositor::AbstractLeaf :as => "Gang" + class Gang < Compositor::AbstractLeaf :as => "gang" end end - }.call end.to raise_error end diff --git a/tmp.rb b/tmp.rb new file mode 100644 index 0000000..f2db0be --- /dev/null +++ b/tmp.rb @@ -0,0 +1,75 @@ +class Tmp + def self.iii + self.instance_variable_get(:@iii) + end +end + +class Aaa < Tmp +end + +class Bbb < Aaa +end + + +c = Class.new(Aaa) do + def self.make nm,val + self.class.send :define_method, nm do + val + end + end +end + + + +c.make :foo, "bar" +c.make :foo1, "bar1" +puts c.foo +puts c.foo1 + +val = 10 +c.define_singleton_method(:bla) { val } + +puts c.bla + +class F < c + +end + + +puts F.foo +puts F.bla +exit(0); + + +#Aaa.class.send(:define_method, :foo) do +# 18 +#end + + +#puts Aaa.foo +#puts Tmp.foo + +val=10 +c = Class.new(Aaa) +c.instance_eval do + self.send(:define_method,:foo) do + v + end +end + +puts c.methods.sort +puts c.new.methods.sort + +Aaa.instance_eval do + def foo + 18 + end + #send(:define_method, :foo) do + # 18 + #end +end + +puts Aaa.foo + + + From cb7a79f1870f4b69948441f2e83e0dde526ea08c Mon Sep 17 00:00:00 2001 From: matt camuto Date: Thu, 26 Sep 2013 13:24:13 -0700 Subject: [PATCH 03/10] comment --- lib/compositor/leaf.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index d905438..e6cbb95 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -25,9 +25,7 @@ def dsl(dsl) end end - # Mimic method so you can overide leave name in a class. Since base uses - # inherited callback this option does not currently exited in the class - # itself. + #Mimic Method. Should this be lowercase? def self.AbstractLeaf options raise "Abstract leaf hsh must contain :as element" unless options[:as] Class.new(Compositor::Leaf) do From 081cb2fb781228366fb04226c6f02597ff2cf122 Mon Sep 17 00:00:00 2001 From: matt camuto Date: Thu, 26 Sep 2013 13:31:49 -0700 Subject: [PATCH 04/10] remove tmp file --- tmp.rb | 75 ---------------------------------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 tmp.rb diff --git a/tmp.rb b/tmp.rb deleted file mode 100644 index f2db0be..0000000 --- a/tmp.rb +++ /dev/null @@ -1,75 +0,0 @@ -class Tmp - def self.iii - self.instance_variable_get(:@iii) - end -end - -class Aaa < Tmp -end - -class Bbb < Aaa -end - - -c = Class.new(Aaa) do - def self.make nm,val - self.class.send :define_method, nm do - val - end - end -end - - - -c.make :foo, "bar" -c.make :foo1, "bar1" -puts c.foo -puts c.foo1 - -val = 10 -c.define_singleton_method(:bla) { val } - -puts c.bla - -class F < c - -end - - -puts F.foo -puts F.bla -exit(0); - - -#Aaa.class.send(:define_method, :foo) do -# 18 -#end - - -#puts Aaa.foo -#puts Tmp.foo - -val=10 -c = Class.new(Aaa) -c.instance_eval do - self.send(:define_method,:foo) do - v - end -end - -puts c.methods.sort -puts c.new.methods.sort - -Aaa.instance_eval do - def foo - 18 - end - #send(:define_method, :foo) do - # 18 - #end -end - -puts Aaa.foo - - - From 9af40aa8b62b1b417943b6ae7fea8cee75d9d98e Mon Sep 17 00:00:00 2001 From: Matt Camuto Date: Thu, 26 Sep 2013 22:11:15 -0700 Subject: [PATCH 05/10] Update leaf.rb --- lib/compositor/leaf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index e6cbb95..c392dd3 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -27,7 +27,7 @@ def dsl(dsl) #Mimic Method. Should this be lowercase? def self.AbstractLeaf options - raise "Abstract leaf hsh must contain :as element" unless options[:as] + raise "Abstract leaf options must contain :as element" unless options[:as] Class.new(Compositor::Leaf) do define_singleton_method(:dsl_override) { options[:as] } end From b98924e1e32478462d55a317ac352097f64f4e4e Mon Sep 17 00:00:00 2001 From: matt camuto Date: Fri, 27 Sep 2013 14:02:58 -0700 Subject: [PATCH 06/10] leaf change and more tests --- lib/compositor/leaf.rb | 7 ++-- spec/compositor/base_spec.rb | 12 +++---- spec/compositor/hash_spec.rb | 63 ++++++++++++++++++++++++++++-------- spec/support/sample_dsl.rb | 34 +++++++++++++++++++ 4 files changed, 92 insertions(+), 24 deletions(-) diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index e6cbb95..19ee198 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -25,11 +25,10 @@ def dsl(dsl) end end - #Mimic Method. Should this be lowercase? - def self.AbstractLeaf options - raise "Abstract leaf hsh must contain :as element" unless options[:as] + # Create a named leaf, Primarily to override the dsl name at the class level + def self.NamedLeaf name Class.new(Compositor::Leaf) do - define_singleton_method(:dsl_override) { options[:as] } + define_singleton_method(:dsl_override) { name } end end diff --git a/spec/compositor/base_spec.rb b/spec/compositor/base_spec.rb index 8cabf24..0e34d6d 100644 --- a/spec/compositor/base_spec.rb +++ b/spec/compositor/base_spec.rb @@ -45,21 +45,21 @@ class AbstractUserCompositor < Compositor::Leaf it "override does work" do lambda { - class Crew < Compositor::AbstractLeaf :as => "crew" + class Crew < Compositor::NamedLeaf "crew" end module Motley - class Crew < Compositor::AbstractLeaf :as => "motley_crew" + class Crew < Compositor::NamedLeaf "motley_crew" end end module TwoLive - class Crew < Compositor::AbstractLeaf :as => "two_live_crew" + class Crew < Compositor::NamedLeaf "two_live_crew" end end module WorldClassWrecking - class Crew < Compositor::AbstractLeaf :as => "world_class_wrecking_crew" + class Crew < Compositor::NamedLeaf "world_class_wrecking_crew" end end }.call @@ -72,11 +72,11 @@ class Crew < Compositor::AbstractLeaf :as => "world_class_wrecking_crew" it "Fails with same override" do expect do lambda { - class Gang < Compositor::AbstractLeaf :as => "gang" + class Gang < Compositor::NamedLeaf "gang" end module KoolAndThe - class Gang < Compositor::AbstractLeaf :as => "gang" + class Gang < Compositor::NamedLeaf "gang" end end }.call diff --git a/spec/compositor/hash_spec.rb b/spec/compositor/hash_spec.rb index 89d507c..90d9a5b 100644 --- a/spec/compositor/hash_spec.rb +++ b/spec/compositor/hash_spec.rb @@ -5,11 +5,11 @@ it 'returns the generated map' do expected = { - tests: { - num1: {number: 1}, - num2: {number: 2}, - num3: {number: 3} - } + tests: { + num1: {number: 1}, + num2: {number: 2}, + num3: {number: 3} + } } dsl = Compositor::DSL.create(context) @@ -24,15 +24,15 @@ it 'returns the generated deeply nested map without explicit receiver' do expected = { - tests: { - num1: {number: 1}, - num2: {number: 2}, - num3: {number: 3}, - stuff: [ - {number: 10}, - {number: 11} - ] - } + tests: { + num1: {number: 1}, + num2: {number: 2}, + num3: {number: 3}, + stuff: [ + {number: 10}, + {number: 11} + ] + } } dsl = Compositor::DSL.create(context) @@ -58,4 +58,39 @@ {}.should == dsl.to_hash end end + + describe "Same top level class names" do + it "supports both classes" do + expected = { + :tests => + { + :employee => { + :name => "squiggy", + :salary => 1000, + :status => "baller" + }, + :peasent => { + :name => "bob", + :age => 12 + }, + :internal_ballers => [ + {:name => "squiggy", :salary => 1000000, :status => "baller"}, + {:name => "squiggy", :salary => 2000000, :status => "baller"} + ] + } + } + + dsl_hash = Compositor::DSL.create(context) do + map root: :tests do + internal_person 1000, root: :employee + api_person root: :peasent + list :root => :internal_ballers do + internal_person 1000000 + internal_person 2000000 + end + end + end.to_hash + expected.should == dsl_hash + end + end end diff --git a/spec/support/sample_dsl.rb b/spec/support/sample_dsl.rb index ce68f2a..cc93dfa 100644 --- a/spec/support/sample_dsl.rb +++ b/spec/support/sample_dsl.rb @@ -33,3 +33,37 @@ def to_hash } end end + +module PublicApi + class Person < Compositor::NamedLeaf("api_person") + def to_hash + with_root_element do + { + name: "bob", + age: 12 + } + end + end + end +end + +module PrivateApi + class Person < Compositor::NamedLeaf("internal_person") + attr_accessor :salary + + def initialize(view_context, salary, attrs = {}) + super(view_context, {salary: salary}.merge!(attrs)) + end + + def to_hash + with_root_element do + { + name: "squiggy", + salary: salary, + status: "baller" + } + end + end + end +end + From 9fbe8a1e77722c527d18b0acc7e73cd4df82bf96 Mon Sep 17 00:00:00 2001 From: matt camuto Date: Fri, 27 Sep 2013 15:01:00 -0700 Subject: [PATCH 07/10] readme --- README.md | 33 ++++++++++++++++++++++++++++++++- lib/compositor/leaf.rb | 2 +- spec/support/sample_dsl.rb | 23 +++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3acf86d..fe746f8 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ was defined on ```view_context``` (by Rails, which copies them from the Controll so became automatically available inside DSL. Note that all instance variables must be defined *before* the DSL instance is created. -### Method Name Collisions in the DSL +### Method Name Collisions and Override in the DSL Because DSL uses only the last word of the class name (eg, ```user``` for a class named ```MyModule::UserCompositor```), there is a possibility of name collisions. In order to prevent that, Compositor will detect if a DSL method is already @@ -182,6 +182,37 @@ defined and throw exception if another class tries to redefine it. If you prefer to have your own ```Compositor``` class hierarchy, or just compositors that should not be added to the DSL, you can name the classes starting with ```Abstract```, such as ```MyModule::AbstractCompositor```. +The option does exist to Override the DSL method name for named leafs. This will allow you to for example have two or more +User classes defined for composites in a hierarchy, each with a different logical name. For example: + +```ruby +# Will get the 'user' dsl method name +class User < Compositor::Leaf + # methods omitted +end + +# Will get v0_user dsl method name +module Api + module V0 + class User < Compositor::NamedLeaf("v0_user") + # methods omitted + end + end +end + +# Will get v1_user method name +module Api + module V1 + class User < Compositor::NamedLeaf("v1_user") + # methods omitted + end + end +end +``` + +Please note, if you try to use the same named leaf logical name an error will occur. + + ### Performance Note of caution: despite the fact that typical DSL generation can take mere 50-100 microseconds, defining complex responses diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index 19ee198..c767e20 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -25,7 +25,7 @@ def dsl(dsl) end end - # Create a named leaf, Primarily to override the dsl name at the class level + # Create a named leaf, To override the dsl name at the class level def self.NamedLeaf name Class.new(Compositor::Leaf) do define_singleton_method(:dsl_override) { name } diff --git a/spec/support/sample_dsl.rb b/spec/support/sample_dsl.rb index cc93dfa..917e05e 100644 --- a/spec/support/sample_dsl.rb +++ b/spec/support/sample_dsl.rb @@ -67,3 +67,26 @@ def to_hash end end + +module Api + module V0 + class User < Compositor::NamedLeaf("v0_user") + def to_hash + # hash of stuff for v0 rep of user + end + end + end +end + +module Api + module V1 + class User < Compositor::NamedLeaf("v1_user") + def to_hash + # hash of stuff v1 rep of user + end + end + end +end + + + From 4f156fd91edba8dddd6db93686af0c0c9e305359 Mon Sep 17 00:00:00 2001 From: matt camuto Date: Fri, 27 Sep 2013 15:19:18 -0700 Subject: [PATCH 08/10] readme --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe746f8..22b1520 100644 --- a/README.md +++ b/README.md @@ -210,8 +210,17 @@ module Api end ``` -Please note, if you try to use the same named leaf logical name an error will occur. +Please note, if you try to use the same named leaf logical name an error will occur. For example: +```ruby + class Foo < Compositor::NamedLeaf("baz") + # methods omitted + end + + class Bar < Compositor::NamedLeaf("baz") + # methods omitted + end +``` ### Performance From fd79b8155b31867f07d6c7194ecb05db83473dac Mon Sep 17 00:00:00 2001 From: matt camuto Date: Wed, 20 Nov 2013 20:52:20 -0800 Subject: [PATCH 09/10] used correct eigenclass style for anonymous class level methods, some comments --- lib/compositor/base.rb | 5 ++--- lib/compositor/leaf.rb | 10 +++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/compositor/base.rb b/lib/compositor/base.rb index 57e1127..c2b004b 100644 --- a/lib/compositor/base.rb +++ b/lib/compositor/base.rb @@ -41,14 +41,13 @@ def root_class_name def self.root_class_name(klazz) return klazz.dsl_override if klazz.respond_to?(:dsl_override) - return nil if klazz.name.nil? + return nil if klazz.name.nil? # Anonymous classes will have nil name, we do not want to have them included klazz.name.gsub(/(.*::)|(Compositor$)/, '').underscore end def self.inherited(subclass) method_name = root_class_name(subclass) - return if method_name.nil? - unless method_name.eql?("base") || method_name.start_with?("abstract") + unless (method_name.nil? || method_name.eql?("base") || method_name.start_with?("abstract")) # check if it's already defined if Compositor::DSL.instance_methods.include?(method_name.to_sym) raise MethodAlreadyDefinedError.new("Method #{method_name} is already defined on the DSL class.") diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index c767e20..e5526f9 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -26,9 +26,17 @@ def dsl(dsl) end # Create a named leaf, To override the dsl name at the class level + # Note we use an interstitial anonymous class since the DSL builder relies + # on self.inherited. Could get more in terms of flexibility if this used + # module inheritance instead. But this works fine with snytax borrows from + # Camping and Sequal. + # + # class Worm < Compositor::NamedLeaf("WiggleWiggleWorm") + # end def self.NamedLeaf name Class.new(Compositor::Leaf) do - define_singleton_method(:dsl_override) { name } + eigenclass = class << self; self; end; + eigenclass.send(:define_method, :dsl_override) { name } end end From 151d83c924f5e8ceb02dd9148d19cb2b2b07d4a8 Mon Sep 17 00:00:00 2001 From: matt camuto Date: Wed, 20 Nov 2013 20:56:36 -0800 Subject: [PATCH 10/10] some comments --- lib/compositor/leaf.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/compositor/leaf.rb b/lib/compositor/leaf.rb index e5526f9..71558d9 100644 --- a/lib/compositor/leaf.rb +++ b/lib/compositor/leaf.rb @@ -31,8 +31,19 @@ def dsl(dsl) # module inheritance instead. But this works fine with snytax borrows from # Camping and Sequal. # - # class Worm < Compositor::NamedLeaf("WiggleWiggleWorm") - # end + # Example: + # + #module Motley + # class Crew < Compositor::NamedLeaf("motley_crew") + # end + #end + # + #module TwoLive + # class Crew < Compositor::NamedLeaf("two_live_crew") + # end + #end + + def self.NamedLeaf name Class.new(Compositor::Leaf) do eigenclass = class << self; self; end;