compositor 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .idea
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.pairs ADDED
@@ -0,0 +1,12 @@
1
+ pairs:
2
+ ag: Atasay Gokkaya; atasay
3
+ km: Kaan Meralan; kaan
4
+ kg: Konstantin Gredeskoul; kig
5
+ ph: Paul Henry; paul
6
+ sf: Sean Flannagan; sean
7
+ es: Eric Saxby; sax
8
+ cc: Cihan Cimen; cihan
9
+ sc: Server Cimen; server
10
+ email:
11
+ prefix: pair
12
+ domain: wanelo.com
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ script: "bundle exec rspec"
5
+ notifications:
6
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in spanx.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ #^syntax detection
3
+
4
+ # A sample Guardfile
5
+ # More info at https://github.com/guard/guard#readme
6
+
7
+ guard 'rspec' do
8
+ watch(%r{^compositor\.gemspec}) { "spec"}
9
+ watch(%r{^spec/.+_spec\.rb$})
10
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
11
+ watch('spec/spec_helper.rb') { "spec" }
12
+ end
13
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Wanelo, Inc
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ Compositor
2
+ =====
3
+
4
+ [![Build status](https://secure.travis-ci.org/wanelo/compositor.png)](http://travis-ci.org/wanelo/compositor)
5
+
6
+ Composite pattern with a neat DSL for constructing trees of objects in order to render them as a Hash, and subsequently
7
+ JSON. Used by Wanelo to generate all JSON API responses by compositing multiple objects together, converting to
8
+ a hash and then JSON.
9
+
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'compositor'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install compositor
24
+
25
+ ## Usage
26
+
27
+ For each model that needs a hash/json representation you need to create a ruby class that subclasses Composite::Leaf,
28
+ adds some custom state that's important for rendering that object, and implements #to_hash method:
29
+
30
+ ```ruby
31
+ module Compositor
32
+ class Leaf::User < ::Compositor::Leaf
33
+ attr_accessor :user
34
+
35
+ def initialize(view_context, user, attrs = {})
36
+ super(view_context, {user: user}.merge!(attrs))
37
+ end
38
+
39
+ def to_hash
40
+ with_root_element do
41
+ {
42
+ id: user.id,
43
+ username: user.username,
44
+ location: user.location,
45
+ bio: user.bio,
46
+ url: user.url
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ ```
54
+
55
+ This small class automatically registers "user" DSL method, which receives a user object and any other
56
+ important attributes.
57
+
58
+ Then this class can be merged with other similar "leaf" classes, or another "composite" class, such as
59
+ Composite::Hash or Composite::List to create a complex nested Hash or JSON data structure.
60
+
61
+ Once the tree of composite objects has been setup, calling #to_hash on the top level object quickly
62
+ generates hash by walking the tree and merging everything together.
63
+
64
+ ```ruby
65
+
66
+ composite = Composite.create(view_context) do
67
+ hash do
68
+ store store, root: :store
69
+ user current_user, root: :user
70
+ list collection: products, root: :products do |p|
71
+ product p
72
+ end
73
+ end
74
+ end
75
+
76
+ puts composite.to_hash # =>
77
+
78
+ {
79
+ :store => {
80
+ id: 12354,
81
+ name: "amazon.com",
82
+ url: "http://www.amazon.com",
83
+ ..
84
+ },
85
+ :user => {
86
+ id: 1234,
87
+ username: "kigster",
88
+ location: "San Francisco",
89
+ bio: "",
90
+ url: ""
91
+ },
92
+ :products => {
93
+ [ id: 1234, :name => "Awesome Product", ... ],
94
+ [ id: 4325, :name => "Another Awesome Product", ... ]
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Contributing
100
+
101
+ 1. Fork it
102
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
103
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
104
+ 4. Push to the branch (`git push origin my-new-feature`)
105
+ 5. Create new Pull Request
106
+
107
+ ## Maintainers
108
+
109
+ Konstantin Gredeskoul (@kigster) and Paul Henry (@letuboy)
110
+
111
+ (c) 2013, All rights reserved, distributed under MIT license.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/compositor/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Konstantin Gredeskoul","Paul Henry"]
6
+ gem.email = %w(kigster@gmail.com paul@wanelo.com)
7
+ gem.description = %q{Composite design pattern with a convenient DSL for building JSON/Hashes of complex objects}
8
+ gem.summary = %q{Composite design pattern with a convenient DSL for building JSON/Hashes of complex objects}
9
+ gem.homepage = "https://github.com/wanelo/compositor"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "compositor"
15
+ gem.require_paths = %w(lib)
16
+ gem.version = Compositor::VERSION
17
+
18
+ gem.add_development_dependency 'rspec'
19
+
20
+ gem.add_development_dependency 'guard-rspec'
21
+ gem.add_development_dependency 'rb-fsevent'
22
+ end
data/lib/compositor.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'compositor/version'
2
+
3
+ module Compositor
4
+ end
5
+
6
+ class String
7
+ def constantize
8
+ camel_cased_word = self
9
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
10
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
11
+ end
12
+
13
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
14
+ end
15
+
16
+ def underscore
17
+ self.gsub(/::/, '/').
18
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
19
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
20
+ tr("-", "_").
21
+ downcase
22
+ end
23
+ end
24
+
25
+ require_relative 'compositor/base'
26
+ require_relative 'compositor/renderer/base'
@@ -0,0 +1,52 @@
1
+ module Compositor
2
+ class Base
3
+ attr_reader :attrs
4
+ attr_accessor :root, :view_context
5
+
6
+ def initialize(view_context, attrs = {})
7
+ @attrs = attrs
8
+ self.view_context = view_context
9
+ attrs.each_pair do |key, value|
10
+ self.send("#{key}=", value)
11
+ end
12
+ end
13
+
14
+ def to_hash
15
+ raise NotImplementedError.new("Abstract method, should be implemented by subclasses")
16
+ end
17
+
18
+ def with_root_element
19
+ include_root? ? {root => yield} : yield
20
+ end
21
+
22
+ def to_h
23
+ to_hash
24
+ end
25
+
26
+ def collection_to_generator(klazz, collection)
27
+ collection.map { |o| klazz.new(view_context) }
28
+ end
29
+
30
+ def to_json(options = {})
31
+ Oj.dump(to_hash)
32
+ end
33
+
34
+ def include_root?
35
+ self.root ? true : false
36
+ end
37
+
38
+ def root_class_name
39
+ self.class.root_class_name(self.class)
40
+ end
41
+
42
+ def self.root_class_name(klazz)
43
+ klazz.name.gsub(/.*::/, '').underscore
44
+ end
45
+ end
46
+ end
47
+
48
+ require_relative 'leaf'
49
+ require_relative 'composite'
50
+ require_relative 'dsl'
51
+ require_relative 'list'
52
+ require_relative 'hash'
@@ -0,0 +1,53 @@
1
+ module Compositor
2
+ class Composite < Compositor::Base
3
+ attr_accessor :collection, :renderer
4
+
5
+ def initialize(view_context, *args)
6
+ super
7
+ self.collection ||= []
8
+ end
9
+
10
+ def to_hash
11
+ with_root_element do
12
+ renderer.new(self, collection).render
13
+ end
14
+ end
15
+
16
+ def map_collection
17
+ self.collection = self.collection.map do |item|
18
+ yield item
19
+ end
20
+ end
21
+
22
+ def composite?
23
+ true
24
+ end
25
+
26
+ def self.inherited(subclass)
27
+ method_name = subclass.name.gsub(/.*::/, '').underscore
28
+ unless method_name.eql?("base")
29
+ Compositor::DSL.send(:define_method, method_name) do |args = {}, &block|
30
+ original_generator = self.generator
31
+ composite = subclass.new(self.view_context, args)
32
+
33
+ self.generator = composite
34
+
35
+ if args[:collection] && block
36
+ # reset collection, we'll be mapping it via a block
37
+ composite.collection = []
38
+ args[:collection].each do |object|
39
+ self.instance_exec(object, &block)
40
+ end
41
+ elsif block
42
+ self.instance_eval &block
43
+ end
44
+
45
+ if original_generator
46
+ self.generator = original_generator
47
+ self.generator.collection << composite
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ module Compositor
2
+ class DSL
3
+ attr_reader :view_context
4
+ attr_accessor :result, :generator
5
+
6
+ def initialize(view_context)
7
+ @view_context = view_context
8
+ end
9
+
10
+ def self.create(view_context, &block)
11
+ dsl = new(view_context)
12
+ dsl.instance_eval &block if block
13
+ dsl
14
+ end
15
+
16
+ def paginate collection, params
17
+ paginator collection: collection,
18
+ pagination_url: params[:pagination_url],
19
+ params: params[:api_params]
20
+ end
21
+
22
+ def to_json
23
+ generator.to_json
24
+ end
25
+
26
+ def to_hash
27
+ if generator
28
+ generator.to_hash
29
+ else
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,7 @@
1
+ module Compositor
2
+ class Hash < ::Compositor::Composite
3
+ def renderer
4
+ @renderer ||= Compositor::Renderer::Merged
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ module Compositor
2
+ class Leaf < Compositor::Base
3
+ attr_accessor :object
4
+
5
+ def initialize(view_context, object = {}, args = {})
6
+ if object.is_a?(::Hash)
7
+ super(view_context, object)
8
+ else
9
+ super(view_context, {object: object}.merge!(args))
10
+ end
11
+ end
12
+
13
+ def root
14
+ if @root.is_a?(Symbol)
15
+ super
16
+ elsif @root
17
+ root_class_name.to_sym
18
+ else
19
+ nil
20
+ end
21
+ end
22
+
23
+ def composite?
24
+ false
25
+ end
26
+
27
+ def self.inherited(subclass)
28
+ method_name = root_class_name(subclass)
29
+ unless method_name.eql?("base")
30
+ Compositor::DSL.send(:define_method, method_name) do |*args|
31
+ leaf = subclass.new(@view_context, *args)
32
+ if self.generator
33
+ raise "Leaves should be called within composite" unless self.generator.composite?
34
+ self.generator.collection << leaf
35
+ else
36
+ self.generator = leaf
37
+ end
38
+ leaf
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ module Compositor
2
+ class List < Compositor::Composite
3
+ def renderer
4
+ @renderer ||= Compositor::Renderer::Iterator
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module Compositor
2
+ module Renderer
3
+ class Base
4
+ attr_reader :composite, :collection
5
+
6
+ def initialize(composite, collection)
7
+ @composite = composite
8
+ @collection = collection
9
+ end
10
+
11
+ def render
12
+ raise NoMethodError.new "Define render() method in subclasses!"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ require_relative 'iterator'
19
+ require_relative 'merged'
@@ -0,0 +1,9 @@
1
+ module Compositor
2
+ module Renderer
3
+ class Iterator < Base
4
+ def render
5
+ collection.inject([]) { |memo, item| memo << item.to_hash; memo }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Compositor
2
+ module Renderer
3
+ class Merged < Base
4
+ def render
5
+ return {} if collection.nil? or collection.size == 0
6
+ return collection.first.to_hash if collection.length == 1
7
+ collection.inject({}) do |result, hash|
8
+ result.merge!(hash.to_hash)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Compositor
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,168 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Compositor::DSL do
4
+
5
+ let(:view_context) { Object.new }
6
+
7
+ describe '#create' do
8
+ it "defines #dsl_string method on the DSL class" do
9
+ dsl = Compositor::DSL.create(view_context)
10
+ dsl.should respond_to(:dsl_string)
11
+ end
12
+
13
+ it "allows calling defined methods via a block" do
14
+ block_ran = false
15
+ Compositor::DSL.create(view_context) do |dsl|
16
+ dsl.dsl_string
17
+ block_ran = true
18
+ end
19
+ block_ran.should be_true
20
+ end
21
+
22
+ it "returns last generator called via block" do
23
+ dsl = Compositor::DSL.create(view_context) do |dsl|
24
+ dsl.dsl_string
25
+ end
26
+
27
+ {a: "b"}.should == dsl.to_hash
28
+ end
29
+
30
+ it "returns an instance of subclass" do
31
+ dsl = Compositor::DSL.create(view_context).dsl_string
32
+ dsl.should be_kind_of(Compositor::Leaf::DslString)
33
+ end
34
+ end
35
+
36
+ describe '#composite' do
37
+ it 'returns the generated array with the explicit receiver' do
38
+ integers = [1, 2, 3]
39
+ expected = {
40
+ tests: [
41
+ {number: 1},
42
+ {number: 2},
43
+ {number: 3}
44
+ ]
45
+ }
46
+
47
+ dsl = Compositor::DSL.create(view_context)
48
+ dsl.list root: :tests, collection: integers do |item|
49
+ dsl.dsl_int item
50
+ end
51
+
52
+ dsl.to_hash.should == expected
53
+ end
54
+
55
+ it 'returns the generated hash' do
56
+ expected = {
57
+ tests: {
58
+ num1: {number: 1},
59
+ num2: {number: 2},
60
+ num3: {number: 3}
61
+ }
62
+ }
63
+
64
+ dsl = Compositor::DSL.create(view_context)
65
+ dsl.hash root: :tests do
66
+ dsl.dsl_int 1, root: :num1
67
+ dsl.dsl_int 2, root: :num2
68
+ dsl.dsl_int 3, root: :num3
69
+ end
70
+
71
+ expected.should == dsl.to_hash
72
+ end
73
+
74
+ it 'returns the generated deeply nested hash' do
75
+ expected = {
76
+ tests: {
77
+ num1: {number: 1},
78
+ num2: {number: 2},
79
+ num3: {number: 3},
80
+ stuff: [
81
+ {number: 10},
82
+ {number: 11}
83
+ ]
84
+ }
85
+ }
86
+
87
+ dsl = Compositor::DSL.create(view_context)
88
+ dsl.hash root: :tests do
89
+ dsl.dsl_int 1, root: :num1
90
+ dsl.dsl_int 2, root: :num2
91
+ dsl.dsl_int 3, root: :num3
92
+ dsl.list :root => :stuff do
93
+ dsl.dsl_int 10
94
+ dsl.dsl_int 11
95
+ end
96
+ end
97
+
98
+ expected.should == dsl.to_hash
99
+ end
100
+
101
+ it 'returns the generated deeply nested hash without explicit receiver' do
102
+ expected = {
103
+ tests: {
104
+ num1: {number: 1},
105
+ num2: {number: 2},
106
+ num3: {number: 3},
107
+ stuff: [
108
+ {number: 10},
109
+ {number: 11}
110
+ ]
111
+ }
112
+ }
113
+
114
+ dsl = Compositor::DSL.create(view_context)
115
+ dsl.hash root: :tests do
116
+ dsl_int 1, root: :num1
117
+ dsl_int 2, root: :num2
118
+ dsl_int 3, root: :num3
119
+ list :root => :stuff do
120
+ dsl_int 10
121
+ dsl_int 11
122
+ end
123
+ end
124
+
125
+ expected.should == dsl.to_hash
126
+ end
127
+
128
+ it 'returns the generated array with explicit receiver' do
129
+ integers = [1, 2, 3]
130
+ expected = {
131
+ tests: [
132
+ {number: 1},
133
+ {number: 2},
134
+ {number: 3}
135
+ ]
136
+ }
137
+
138
+ dsl = Compositor::DSL.create(view_context)
139
+ dsl.list root: :tests, collection: integers do |item|
140
+ dsl_int item
141
+ end
142
+
143
+ expected.should == dsl.to_hash
144
+ end
145
+ end
146
+
147
+ describe '#empty' do
148
+ it 'returns the default type' do
149
+ dsl = Compositor::DSL.create(view_context) do
150
+ end
151
+
152
+ nil.should == dsl.to_hash
153
+
154
+ dsl = Compositor::DSL.create(view_context) do
155
+ hash collection: [], root: false
156
+ end
157
+
158
+ {}.should == dsl.to_hash
159
+
160
+ dsl = Compositor::DSL.create(view_context) do
161
+ list collection: [], root: false
162
+ end
163
+
164
+ [].should == dsl.to_hash
165
+
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'Performance' do
4
+
5
+ let(:view_context) { Object.new }
6
+ let(:permitted_dsl_performance_penalty) { 60 } # 60% slower is allowed, any slower is not.)
7
+
8
+ describe 'generating DSL' do
9
+ before do
10
+ require 'benchmark'
11
+ @timing = { }
12
+ end
13
+
14
+ it "is no more than 60% slower than non-DSL" do
15
+ dsl = nil
16
+ output = Benchmark.measure do
17
+ 10000.times do
18
+ dsl = Compositor::DSL.create(view_context) do |dsl|
19
+ hash do
20
+ dsl_string "hello"
21
+ dsl_int 3
22
+ list collection: [1, 2, 3], root: :numbers do |number|
23
+ dsl_int number
24
+ end
25
+ end
26
+ end
27
+ dsl.to_hash.should == {:a => "b", :number => 3, :numbers => [{:number => 1}, {:number => 2}, {:number => 3}]}
28
+ end
29
+ end
30
+
31
+ @timing[:dsl] = output.to_s.to_f
32
+
33
+ cmp = nil
34
+ output = Benchmark.measure do
35
+ 10000.times do
36
+ string = Compositor::Leaf::DslString.new(view_context)
37
+ int = Compositor::Leaf::DslInt.new(view_context, 3)
38
+ list = Compositor::List.new(view_context,
39
+ root: :numbers,
40
+ collection: [1, 2, 3].map! { |n| Compositor::Leaf::DslInt.new(view_context, n) })
41
+ cmp = Compositor::Hash.new(view_context, collection: [string, int, list])
42
+ cmp.to_hash.should == {:a => "b", :number => 3, :numbers => [{:number => 1}, {:number => 2}, {:number => 3}]}
43
+ end
44
+ end
45
+
46
+ @timing[:nodsl] = output.to_s.to_f
47
+
48
+ difference = (100 * (@timing[:dsl] - @timing[:nodsl]) / @timing[:nodsl])
49
+ difference.should be < permitted_dsl_performance_penalty, "DSL generation was more than #{permitted_dsl_performance_penalty}% slower than non-DSL"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,20 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
9
+ require 'rubygems'
10
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
11
+ require 'compositor'
12
+
13
+ Dir['spec/support/**/*.rb'].each { |filename| require_relative "../#{filename}" }
14
+
15
+ RSpec.configure do |config|
16
+ config.treat_symbols_as_metadata_keys_with_true_values = true
17
+ config.run_all_when_everything_filtered = true
18
+ config.filter_run :focus
19
+ config.order = 'random'
20
+ end
@@ -0,0 +1,33 @@
1
+ module Compositor
2
+ class Leaf::DslString < Compositor::Leaf
3
+ def to_hash
4
+ {
5
+ a: "b"
6
+ }
7
+ end
8
+ end
9
+
10
+ class Leaf::DslInt < Compositor::Leaf
11
+ attr_accessor :number
12
+
13
+ def initialize(view_context, number, attrs = {})
14
+ super(view_context, {number: number}.merge!(attrs))
15
+ end
16
+
17
+ def to_hash
18
+ with_root_element do
19
+ {
20
+ number: @number
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ class Leaf::DslObject < Compositor::Leaf
27
+ def to_hash
28
+ {
29
+ a: object
30
+ }
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: compositor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Konstantin Gredeskoul
9
+ - Paul Henry
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-05-29 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: guard-rspec
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rb-fsevent
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ description: Composite design pattern with a convenient DSL for building JSON/Hashes
64
+ of complex objects
65
+ email:
66
+ - kigster@gmail.com
67
+ - paul@wanelo.com
68
+ executables: []
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - .gitignore
73
+ - .pairs
74
+ - .rspec
75
+ - .travis.yml
76
+ - Gemfile
77
+ - Guardfile
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - compositor.gemspec
82
+ - lib/compositor.rb
83
+ - lib/compositor/base.rb
84
+ - lib/compositor/composite.rb
85
+ - lib/compositor/dsl.rb
86
+ - lib/compositor/hash.rb
87
+ - lib/compositor/leaf.rb
88
+ - lib/compositor/list.rb
89
+ - lib/compositor/renderer/base.rb
90
+ - lib/compositor/renderer/iterator.rb
91
+ - lib/compositor/renderer/merged.rb
92
+ - lib/compositor/version.rb
93
+ - spec/compositor/compositor_spec.rb
94
+ - spec/compositor/performance_spec.rb
95
+ - spec/spec_helper.rb
96
+ - spec/support/sample_dsl.rb
97
+ homepage: https://github.com/wanelo/compositor
98
+ licenses: []
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.24
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Composite design pattern with a convenient DSL for building JSON/Hashes of
121
+ complex objects
122
+ test_files:
123
+ - spec/compositor/compositor_spec.rb
124
+ - spec/compositor/performance_spec.rb
125
+ - spec/spec_helper.rb
126
+ - spec/support/sample_dsl.rb
127
+ has_rdoc: