hash_builder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d558232e333db81f54b2908babff87135a04524
4
+ data.tar.gz: 9a7ab805dfed411cd2692581801583b55e2c4589
5
+ SHA512:
6
+ metadata.gz: 855bc8140cfd594a54e0f9ac6bfcf601c5fc108e0821f2f0cb8d55285d078718e1de2235996037b8c2e04a66a156021edd7106ea93e6355020362929516a8791
7
+ data.tar.gz: 261c27848fa2040eddde40c2beec7346c56ab35aee6d2bb0a4645f31c13f2c62d43817bcd6e0a78efe78db832f54b92f6fb49072f367b67ddd5d6db9a4b19267
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ # Build artifacts
2
+ /pkg
3
+
4
+ # Bundle config
5
+ /.bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hash_builder.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hash_builder (0.1.0)
5
+ exec_env
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (4.0.1)
11
+ i18n (~> 0.6, >= 0.6.4)
12
+ minitest (~> 4.2)
13
+ multi_json (~> 1.3)
14
+ thread_safe (~> 0.1)
15
+ tzinfo (~> 0.3.37)
16
+ atomic (1.1.14)
17
+ diff-lcs (1.2.4)
18
+ exec_env (0.1.0)
19
+ i18n (0.6.5)
20
+ json (1.8.1)
21
+ json_builder (3.1.7)
22
+ activesupport (>= 2.0.0)
23
+ json
24
+ minitest (4.7.5)
25
+ multi_json (1.8.2)
26
+ rake (10.1.0)
27
+ rspec (2.14.1)
28
+ rspec-core (~> 2.14.0)
29
+ rspec-expectations (~> 2.14.0)
30
+ rspec-mocks (~> 2.14.0)
31
+ rspec-core (2.14.4)
32
+ rspec-expectations (2.14.0)
33
+ diff-lcs (>= 1.1.3, < 2.0)
34
+ rspec-mocks (2.14.1)
35
+ thread_safe (0.1.3)
36
+ atomic
37
+ tzinfo (0.3.38)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bundler (~> 1.4)
44
+ hash_builder!
45
+ json_builder
46
+ rake (~> 10.1)
47
+ rspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Marten Lienen
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,203 @@
1
+ # HashBuilder
2
+
3
+ This gem allows you to build hashes in a way, that is totally copied from
4
+ [json_builder](https://github.com/dewski/json_builder). I created this,
5
+ because json_builder does not allow extraction of partials, which I rely
6
+ heavily on, to keep my JSON generation DRY.
7
+ And while I was at it, I found it a good idea to increase the abstraction
8
+ level and build hashes instead of JSON because, this allows for easier
9
+ manipulation of the results, application in more different circumstances
10
+ and you can generate JSON and YAML with the well known `to_json` and
11
+ `to_yaml` methods from it.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'hash_builder'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install hash_builder
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ require "hash_builder"
31
+ require "json"
32
+ require "yaml"
33
+
34
+ hash = HashBuilder.build do
35
+ url "https://github.com/CQQL"
36
+ name "CQQL"
37
+ age 21
38
+
39
+ interests [:ruby, :clojure] do |n|
40
+ name n
41
+ end
42
+
43
+ loves do
44
+ example do
45
+ code :yes
46
+ end
47
+ end
48
+ end
49
+ #=> {:url=>"https://github.com/CQQL", :name=>"CQQL", :age=>21, :interests=>[{:name=>:ruby}, {:name=>:clojure}], :loves=>{:example=>{:code=>:yes}}}
50
+
51
+ hash.to_json
52
+ #=> "{\"url\":\"https://github.com/CQQL\",\"name\":\"CQQL\",\"age\":21,\"interests\":[{\"name\":\"ruby\"},{\"name\":\"clojure\"}],\"loves\":{\"example\":{\"code\":\"yes\"}}}"
53
+
54
+ hash.to_yaml
55
+ #=> "---\n:url: https://github.com/CQQL\n:name: CQQL\n:age: 21\n:interests:\n- :name: :ruby\n- :name: :clojure\n:loves:\n :example:\n :code: :yes\n"
56
+ ```
57
+
58
+ ## Usage with rails
59
+
60
+ To use HashBuilder in a rails app, add the gem to your Gemfile.
61
+
62
+ ```ruby
63
+ gem "hash_builder"
64
+ ```
65
+
66
+ From then on HashBuilder will render `.json_builder` templates as
67
+ JSON. But there is a special case in that HashBuilder actually renders
68
+ partials as hashes instead of JSON strings so that you can use them to
69
+ create nested data structures instead of strings to use in templates.
70
+
71
+ ```ruby
72
+ class HashController < ApplicationController
73
+ def index
74
+ @users = [
75
+ { name: "CQQL", quote: "Emacs > Vim" },
76
+ { name: "Joshua Bloch", quote: "The cleaner and nicer the program, the faster it's going to run. And if it doesn't, it'll be easy to make it fast." }
77
+ ]
78
+ end
79
+ end
80
+ ```
81
+
82
+ ```ruby
83
+ # hash/index.json_builder
84
+ num_users @users.size
85
+
86
+ users @users.map { |u| render partial: "user", locals: { user: u } }
87
+ ```
88
+
89
+ ```ruby
90
+ # hash/_user.json_builder
91
+ name user[:name]
92
+ name_length user[:name].size
93
+ quote user[:quote]
94
+ ```
95
+
96
+ A request to `/hash/` returns the following JSON response
97
+
98
+ ```json
99
+ {
100
+ "num_users": 2,
101
+ "users": [
102
+ {
103
+ "name": "CQQL",
104
+ "name_length": 4,
105
+ "quote": "Emacs > Vim"
106
+ },
107
+ {
108
+ "name": "Joshua Bloch",
109
+ "name_length": 12,
110
+ "quote": "The cleaner and nicer the program, the faster it's going to run. And if it doesn't, it'll be easy to make it fast."
111
+ }
112
+ ]
113
+ }
114
+ ```
115
+
116
+ ## Performance
117
+
118
+ There is a [benchmark script](./benchmark.rb), that returns the
119
+ following results on my machine
120
+
121
+ ```
122
+ $ ruby benchmark.rb
123
+ user system total real
124
+ HashBuilder 0.650000 0.070000 0.720000 ( 0.714533)
125
+ HashBuilder + JSON.generate 1.290000 0.060000 1.350000 ( 1.352620)
126
+ HashBuilder + to_json 4.650000 0.070000 4.720000 ( 4.716388)
127
+ JSONBuilder 1.780000 0.090000 1.870000 ( 1.872545)
128
+ ```
129
+
130
+ For some reason transforming hashes to JSON with `to_json` is really slow.
131
+
132
+ ## Tradeoffs
133
+
134
+ This is built with [exec_env](https://github.com/CQQL/exec_env), so
135
+ there is quite a lot of ruby magic going on under the hood. As long as
136
+ you only use lexical bingings in your block, you won't notice
137
+ anything.
138
+
139
+ ```ruby
140
+ def render_user (user)
141
+ HashBuilder.build do
142
+ name user.name
143
+ end
144
+ end
145
+ ```
146
+
147
+ But your dynamic bindings will be lost, because the block is executed
148
+ in another context.
149
+
150
+ ```ruby
151
+ class User
152
+ attr_accessor :email
153
+
154
+ def to_hash
155
+ HashBuilder.build do
156
+ # email accessor is not available here.
157
+ email_address email # => Error
158
+ end
159
+ end
160
+ end
161
+ ```
162
+
163
+ This can be fixed however by explicitly passing a scop.e
164
+
165
+ ```ruby
166
+ class User
167
+ attr_accessor :email
168
+
169
+ def to_hash
170
+ HashBuilder.build(scope: self) do
171
+ # All is fine now.
172
+ email_address email
173
+ end
174
+ end
175
+ end
176
+ ```
177
+
178
+ But there is yet another problem. If you tried to set the hash key
179
+ `email`, you would receive an error because the line `email email`
180
+ would actually expand to `user.email(user.email)` if `user` is the
181
+ user object, because `user` responds to `email`. The quite ugly
182
+ workaround looks like this
183
+
184
+ ```ruby
185
+ class User
186
+ attr_accessor :email
187
+
188
+ def to_hash
189
+ HashBuilder.build(scope: self) do
190
+ # Bypass scope and local variables
191
+ xsend :email, email
192
+ end
193
+ end
194
+ end
195
+ ```
196
+
197
+ ## Contributing
198
+
199
+ 1. Fork it
200
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
201
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
202
+ 4. Push to the branch (`git push origin my-new-feature`)
203
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/benchmark.rb ADDED
@@ -0,0 +1,57 @@
1
+ require "benchmark"
2
+ require "json"
3
+ require "hash_builder"
4
+ require "json_builder"
5
+
6
+ template = (-> {
7
+ name "Garrett Bjerkhoel"
8
+ birthday Time.local(1991, 9, 14)
9
+
10
+ street do
11
+ address "1143 1st Ave"
12
+ address2 "Apt 200"
13
+ city "New York"
14
+ state "New York"
15
+ zip 10065
16
+ end
17
+
18
+ skills do
19
+ ruby true
20
+ asp false
21
+ php true
22
+ mysql true
23
+ mongodb true
24
+ haproxy true
25
+ marathon false
26
+ end
27
+
28
+ single_skills ['ruby', 'php', 'mysql', 'mongodb', 'haproxy']
29
+ booleans [true, true, false, nil]
30
+ })
31
+
32
+ runs = 15000
33
+ Benchmark.bm(28) do |bench|
34
+ bench.report("HashBuilder") do
35
+ runs.times do
36
+ HashBuilder.build(&template)
37
+ end
38
+ end
39
+
40
+ bench.report("HashBuilder + JSON.generate") do
41
+ runs.times do
42
+ JSON.generate(HashBuilder.build(&template))
43
+ end
44
+ end
45
+
46
+ bench.report("HashBuilder + to_json") do
47
+ runs.times do
48
+ HashBuilder.build(&template).to_json
49
+ end
50
+ end
51
+
52
+ bench.report("JSONBuilder") do
53
+ runs.times do
54
+ JSONBuilder::Compiler.generate(&template)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hash_builder/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hash_builder"
8
+ spec.version = HashBuilder::VERSION
9
+ spec.authors = ["Marten Lienen"]
10
+ spec.email = ["marten.lienen@gmail.com"]
11
+ spec.description = %q{Build hashes with the full power of ruby at your fingertips}
12
+ spec.summary = %q{The readme has examples}
13
+ spec.homepage = "https://github.com/CQQL/hash_builder"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.4"
22
+ spec.add_development_dependency "rake", "~> 10.1"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "json_builder"
25
+
26
+ spec.add_dependency "exec_env"
27
+ end
@@ -0,0 +1,39 @@
1
+ module HashBuilder
2
+ # Renders templates with '.json_builder' extension.
3
+ #
4
+ # If the template is a normal view, it will render a JSON string.
5
+ # If the template however is a partial, it renders a Hash so that
6
+ # json_builder files can use partials.
7
+ class Template
8
+ def self.default_format
9
+ Mime::JSON
10
+ end
11
+
12
+ def self.call (template)
13
+ render_code = <<-RUBY
14
+ HashBuilder.build(scope: self, locals: local_assigns) do
15
+ #{template.source}
16
+ end
17
+ RUBY
18
+ if !is_partial?(template)
19
+ # ActiveModel defines #as_json in a way, that is not compatible
20
+ # with JSON.
21
+ if defined?(ActiveModel)
22
+ render_code = "ActiveSupport::JSON.encode(#{render_code})"
23
+ else
24
+ render_code = "JSON.generate(#{render_code})"
25
+ end
26
+ end
27
+
28
+ render_code
29
+ end
30
+
31
+ def self.is_partial? (template)
32
+ template.virtual_path.split("/").last.start_with?("_")
33
+ end
34
+ end
35
+ end
36
+
37
+ if defined?(ActionView)
38
+ ActionView::Template.register_template_handler :json_builder, HashBuilder::Template
39
+ end
@@ -0,0 +1,3 @@
1
+ module HashBuilder
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,51 @@
1
+ require "exec_env"
2
+
3
+ require "hash_builder/version"
4
+ require "hash_builder/template"
5
+
6
+ module HashBuilder
7
+ # Build a Hash.
8
+ #
9
+ # args - Arguments to be passed to the hash
10
+ # scope - Method calls in the block will be sent to scope object
11
+ # locals - Local variables to be injected into the block
12
+ #
13
+ # Examples
14
+ #
15
+ # HashBuilder.build(locals: {foo: 1}) do
16
+ # foo foo
17
+ # bar 2
18
+ # end
19
+ # # => { foo: 1, bar: 2}
20
+ def self.build (args: [], scope: nil, locals: {}, &block)
21
+ env = ExecEnv::Env.new
22
+ env.scope = scope
23
+ env.locals = locals
24
+
25
+ block_result = env.exec(*args, &block)
26
+
27
+ messages = env.messages
28
+
29
+ if messages.size > 0
30
+ hash = {}
31
+
32
+ messages.each do |(name, args, block)|
33
+ arg = args.first
34
+
35
+ if args.size == 1 && block && arg.is_a?(Enumerable) && !arg.is_a?(Hash)
36
+ hash[name] = arg.map do |*objects|
37
+ HashBuilder.build(args: objects, scope: scope, locals: locals, &block)
38
+ end
39
+ elsif block
40
+ hash[name] = HashBuilder.build(scope: scope, locals: locals, &block)
41
+ elsif args.size == 1
42
+ hash[name] = arg
43
+ end
44
+ end
45
+
46
+ hash
47
+ else
48
+ block_result
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ require "json"
2
+
3
+ class ViewStub
4
+ def local_assigns
5
+ {}
6
+ end
7
+ end
8
+
9
+ describe HashBuilder::Template do
10
+ let(:view) { ViewStub.new }
11
+
12
+ it "should render a template into a JSON string" do
13
+ template = double("template", source: "foo 1\nbar 'test'", virtual_path: "test/index")
14
+
15
+ code = HashBuilder::Template.call(template)
16
+ json = view.instance_eval(code)
17
+
18
+ expect(JSON.parse(json)).to eq({ "foo" => 1, "bar" => "test" })
19
+ end
20
+
21
+ it "should render a hash if the template is a partial" do
22
+ template = double("template", source: "foo 1\nbar 'test'", virtual_path: "test/_index")
23
+
24
+ code = HashBuilder::Template.call(template)
25
+ hash = view.instance_eval(code)
26
+
27
+ expect(hash).to eq({ foo: 1, bar: "test" })
28
+ end
29
+ end
@@ -0,0 +1,64 @@
1
+ describe HashBuilder do
2
+ it "should construct a simple hash" do
3
+ hash = HashBuilder.build do
4
+ key "A test key"
5
+
6
+ number 1337
7
+ end
8
+
9
+ expect(hash).to eq({ key: "A test key", number: 1337 })
10
+ end
11
+
12
+ it "should construct nested hashes" do
13
+ hash = HashBuilder.build do
14
+ level1 "Easy"
15
+
16
+ container do
17
+ level2 "Harder"
18
+ end
19
+ end
20
+
21
+ expect(hash).to eq({ level1: "Easy", container: { level2: "Harder" } })
22
+ end
23
+
24
+ it "should iterate over enumerables if arg and block is given" do
25
+ hash = HashBuilder.build do
26
+ numbers (0..3).to_a do |num|
27
+ i num
28
+ end
29
+ end
30
+
31
+ expect(hash).to eq({ numbers: [{ i: 0 }, { i: 1 }, { i: 2 }, { i: 3 }] })
32
+ end
33
+
34
+ it "should pass the environment into nested blocks" do
35
+ scope = Object.new
36
+ scope.instance_variable_set(:@id, 13)
37
+
38
+ hash = HashBuilder.build(locals: { user: "CQQL" }, scope: scope) do
39
+ user do
40
+ name user
41
+ id @id
42
+ end
43
+ end
44
+
45
+ expect(hash).to eq({ user: { id: 13, name: "CQQL" } })
46
+ end
47
+
48
+ it "should not set keys that have no explicit value" do
49
+ hash = HashBuilder.build do
50
+ read
51
+ write 1
52
+ end
53
+
54
+ expect(hash).to eq({ write: 1 })
55
+ end
56
+
57
+ it "should allow setting keys to nil" do
58
+ hash = HashBuilder.build do
59
+ foo nil
60
+ end
61
+
62
+ expect(hash).to eq({ foo: nil })
63
+ end
64
+ end
@@ -0,0 +1 @@
1
+ require "hash_builder"
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash_builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marten Lienen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json_builder
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: exec_env
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Build hashes with the full power of ruby at your fingertips
84
+ email:
85
+ - marten.lienen@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - benchmark.rb
98
+ - hash_builder.gemspec
99
+ - lib/hash_builder.rb
100
+ - lib/hash_builder/template.rb
101
+ - lib/hash_builder/version.rb
102
+ - spec/hash_builder/template_spec.rb
103
+ - spec/hash_builder_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: https://github.com/CQQL/hash_builder
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.1.5
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: The readme has examples
129
+ test_files:
130
+ - spec/hash_builder/template_spec.rb
131
+ - spec/hash_builder_spec.rb
132
+ - spec/spec_helper.rb