bldr 0.3.0 → 0.5.2

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/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm 1.9.2
1
+ rvm 1.9.3
data/HISTORY.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 0.5.0 (2012-02-08)
2
+ * Add support "partials" (@ihunter)
3
+
1
4
  ## 0.2.0 (2011-09-09)
2
5
  * Add new `attribute` inferred object syntax (@ihunter)
3
6
 
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Alex Sharp
1
+ Copyright (c) 2011-2012 Alex Sharp
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -9,11 +9,19 @@ the box -- Rails 3 support is planned for the near future.
9
9
 
10
10
  If you would like to contribute, pull requests with specs are warmly accepted :)
11
11
 
12
+ ## Why
13
+
14
+ If you're building an API, `Model#to_json` just doesn't cut it. Besides the JSON
15
+ representation of your models arguably being a presentation concern, trying
16
+ to cram all of this logic into an `#as_json` method quickly turns into pure chaos.
17
+
18
+ There are other json templating libraries available -- [rabl](http://github.com/nesquena/rabl) being the most popular -- but I wasn't satisfied with any of the DSL's, so I created Bldr.
19
+
12
20
  ## Features
13
21
 
14
22
  * Simple json templating DSL
15
- * Uses Tilt's built-in rendering and template caching for better
16
- performance
23
+ * Uses Tilt's built-in rendering and template caching for better performance
24
+ * Partials
17
25
 
18
26
  ## Installation
19
27
 
@@ -42,271 +50,7 @@ end
42
50
 
43
51
  ## Usage
44
52
 
45
- In your sinatra endpoints/actions, use the `bldr` helper method to
46
- render templates.
47
-
48
- ```ruby
49
- get "/posts" do
50
- # ...
51
- posts = Post.all
52
- bldr :'template.json', :locals => {:posts => posts}
53
- end
54
-
55
- # views/template.json.bldr
56
- collection :posts => posts do
57
- attributes :title
58
- attribute :comment_count { |post| post.comments.count }
59
-
60
- collection :comments => current_object.comments do
61
- attributes :body, :author, :email
62
- end
63
- end
64
- ```
65
-
66
- ## Examples
67
-
68
- ### Rendering a basic object
69
-
70
- ```ruby
71
- object do
72
- attribute :title, "my title"
73
- end
74
- ```
75
-
76
- Output:
77
-
78
- ```javascript
79
- {
80
- "title": "my title"
81
- }
82
- ```
83
-
84
- ### Rendering a simple list of attributes
85
-
86
- ```ruby
87
- object :post do
88
- attribute :title, "my title"
89
- end
90
- ```
91
-
92
- Output:
93
-
94
- ```javascript
95
- {
96
- "post": {
97
- "title": "my title"
98
- }
99
- }
100
- ```
101
-
102
- ### Rendering a simple list of attributes from an object
103
-
104
- ```ruby
105
- object :post => post do
106
- attributes :title, :body
107
- end
108
- ```
109
-
110
- Output:
111
-
112
- ```javascript
113
- {
114
- "post": {
115
- "title": "my title",
116
- "body": "..."
117
- }
118
- }
119
- ```
120
-
121
- ### Dynamic attributes
122
-
123
- ```ruby
124
- object :post => post do
125
- attribute :comment_count do |post|
126
- post.comments.count
127
- end
128
- end
129
- ```
130
-
131
- Output:
132
-
133
- ```javascript
134
- {
135
- "post": {
136
- "comment_count": 1
137
- }
138
- }
139
-
140
- ```
141
-
142
- ### Attribute aliases
143
-
144
- ```ruby
145
- object :post => post do
146
- attributes :title, :body
147
-
148
- object :author => post.author do
149
- attribute :surname => :last_name
150
- end
151
- end
152
- ```
153
-
154
- Output:
155
-
156
- ```javascript
157
- {
158
- "post": {
159
- "title": "my title",
160
- "body": "...",
161
- "author": {
162
- "surname": "Doe"
163
- }
164
- }
165
- }
166
- ```
167
-
168
- ### Nested objects
169
-
170
- ```ruby
171
- object :post => post do
172
- attributes :title, :body
173
-
174
- object :author => post.author do
175
- attributes :first_name, :last_name, :email
176
-
177
- attribute(:full_name) { |author| "#{author.first_name} #{author.last_name}" }
178
- end
179
- end
180
- ```
181
-
182
- Output:
183
-
184
- ```javascript
185
- {
186
- "post": {
187
- "title": "my title",
188
- "body": "...",
189
- "author": {
190
- "first_name": "John",
191
- "last_name": "Doe",
192
- "email": "john@doe.com",
193
- "full_name": "John Doe"
194
- }
195
- }
196
- }
197
- ```
198
-
199
- ### Root-level attributes
200
-
201
- ```ruby
202
- get '/redirector' do
203
- url = params['redirect_url']
204
- bldr :'redirect.json', :locals => {:url => url}
205
- end
206
-
207
- # views/redirect.json.bldr
208
- object do
209
- attribute(:redirect_to) { url }
210
- end
211
- ```
212
-
213
- Output:
214
-
215
- ```javascript
216
- {"redirect_to": "http://example.org"}
217
- ```
218
-
219
- ### Collections
220
-
221
- All the examples above can be used inside a collection block. Here we
222
- assume a Post model which has many Comments. You might use the below
223
- code to render an action which returns a collection of posts, where
224
- each post has a collection of comments.
225
-
226
- ```ruby
227
- collection :posts => posts do
228
- attributes :title
229
- attribute :comment_count { |post| post.comments.count }
230
-
231
- # current_object
232
- collection :comments => current_object.comments do
233
- attributes :body, :author_name, :author_email
234
- end
235
- end
236
- ```
237
-
238
- Output:
239
-
240
- ```javascript
241
- {
242
- "posts": [
243
- {
244
- "title": "my title",
245
- "comment_count": 2,
246
- "comments": [
247
- {
248
- "body": "...",
249
- "author_name": "Comment Troll",
250
- "email": "troll@trolling.edu"
251
- },
252
- {
253
- "body": "...",
254
- "author_name": "Uber Troll",
255
- "email": "uber.troll@earthlink.net"
256
- }
257
- ]
258
- }
259
- ]
260
- }
261
- ```
262
-
263
- When inside of a collection block, you can use the `current_object`
264
- method to access the member of the collection currently being iterated
265
- over. This allows you to do nested collections, as in the example above.
266
-
267
- ### Templates
268
-
269
- It is recommended to name your templates with the content type extension before
270
- the .bldr extension. For example: `my_template.json.bldr`.
271
-
272
- The templates themselves are just plain ruby code. They are evaluated in the context of a
273
- `Bldr::Node` instance, which provides the bldr DSL. The DSL is comprised
274
- primarily of 3 simple methods:
275
-
276
- + `object` - Creates an object
277
- + `collection` - Iterates over a collection of objects
278
- + `attributes` - Add attributes to the current object.
279
-
280
- ### Local Variables
281
-
282
- You may pass local variables from your sinatra actions to bldr templates
283
- by passing the `bldr` method a `:locals` hash, like so:
284
-
285
- ```ruby
286
- get '/posts' do
287
- posts = Post.all.recent
288
-
289
- bldr :'posts/index.json', :locals => {:posts => posts}
290
- end
291
- ```
292
-
293
- ### Custom Handlers
294
-
295
- Bldr supports the definition of custom handlers, based on the class of specific
296
- values in the result. You would make use of this to define how specific classes
297
- are rendered by Bldr at a global level.
298
-
299
- For example, the `BSON::ObjectId` class defines `to_json` and `as_json` methods
300
- that produce a result like the following: `{"$oid": "4e77a682364141ecf5000002"}`.
301
- If you wanted to over-ride this default format, you could do so by defining a
302
- custom handler:
303
-
304
- ```ruby
305
- # place in a file that get's loaded before your application code
306
- Bldr.handler BSON::ObjectId do |val|
307
- val.to_s # => "4e77a682364141ecf5000002"
308
- end
309
- ```
53
+ See the [Documentation & Examples](https://github.com/ajsharp/bldr/wiki/Documentation-&-Examples) page on the wiki.
310
54
 
311
55
  ## Editor Syntax Support
312
56
 
@@ -318,20 +62,21 @@ au BufRead,BufNewFile *.bldr set filetype=ruby
318
62
 
319
63
  ## TODO
320
64
 
321
- * Rails 3 support
65
+ * Rails 3 support. An attempt for this was made for this but was reverted in e1cfd7fcbe130b316d95773d8c73ece4e247200e. Feel free to take a shot.
322
66
  * Replace current_object with a block param for collection methods
323
67
  * XML support
324
68
 
325
69
  ## Acknowledgements
326
70
 
327
71
  * [RABL](http://github.com/nesquena/rabl) - Inspiration
328
- * [Tilt](https://github.com/rtomayko/tilt) - Mega awesome goodness
72
+ * [Tilt](https://github.com/rtomayko/tilt) - Mega awesome templating goodness
329
73
 
330
74
  ## Contributors
331
75
 
332
76
  * Ian Hunter (@ihunter)
77
+ * Justin Smestad (@jsmestad)
333
78
 
334
79
  ## Copyright
335
80
 
336
- Copyright (c) 2011 Alex Sharp. See the MIT-LICENSE file for full
81
+ Copyright (c) 2011-2012 Alex Sharp. See the MIT-LICENSE file for full
337
82
  copyright information.
@@ -24,5 +24,6 @@ Gem::Specification.new do |s|
24
24
  s.add_development_dependency 'json_pure'
25
25
  s.add_development_dependency 'sinatra', '~>1.2.6'
26
26
  s.add_development_dependency 'tilt', '~>1.3.2'
27
- s.add_development_dependency 'yajl-ruby'
27
+ s.add_development_dependency 'yajl-ruby', '>= 1.0'
28
+ s.add_development_dependency 'actionpack', '~> 3.0.7'
28
29
  end
@@ -1,11 +1,17 @@
1
-
2
1
  $:.unshift(File.dirname(File.expand_path(__FILE__)))
3
2
 
3
+ begin
4
+ require 'tilt'
5
+ rescue LoadError
6
+ end
7
+
4
8
  require 'multi_json'
5
- require 'bldr/engine'
6
- require 'bldr/template'
7
9
  require 'bldr/node'
8
10
 
11
+ if defined?(Tilt)
12
+ require 'bldr/template'
13
+ end
14
+
9
15
  module Bldr
10
16
  class << self
11
17
 
@@ -3,7 +3,7 @@ module Bldr
3
3
 
4
4
  class Node
5
5
 
6
- attr_reader :current_object, :result, :parent
6
+ attr_reader :current_object, :result, :parent, :opts, :views
7
7
 
8
8
  # Initialize a new Node instance.
9
9
  #
@@ -19,28 +19,15 @@ module Bldr
19
19
  # @param [Object] value an object to serialize.
20
20
  def initialize(value = nil, opts = {}, &block)
21
21
  @current_object = value
22
+ @opts = opts
22
23
  @parent = opts[:parent]
23
-
24
+ @views = opts[:views]
24
25
  # Storage hash for all descendant nodes
25
26
  @result = {}
26
27
 
27
28
  instance_eval(&block) if block_given?
28
29
  end
29
-
30
- # Merge the local results into the ancestor result hash.
31
- #
32
- # @return [Hash]
33
- def render!
34
- result
35
- end
36
-
37
- # Return the json-encoded result hash.
38
- #
39
- # @return [String] the json-encoded result hash
40
- def to_json
41
- MultiJson.encode(result)
42
- end
43
-
30
+
44
31
  # Create and render a node.
45
32
  #
46
33
  # @example A keyed object
@@ -84,23 +71,36 @@ module Bldr
84
71
  value = nil
85
72
  end
86
73
 
87
- node = Node.new(value, :parent => self, &block)
88
- merge_result!(key, node.render!)
89
- self.to_json
74
+ return nil if value.nil? and base.kind_of? Hash
75
+ node = Node.new(value, opts.merge(:parent => self), &block)
76
+ merge_result!(key, node.result)
77
+
78
+ self
90
79
  end
91
80
 
92
81
  def collection(items, &block)
93
- key = items.keys.first
94
- values = items.values.to_a.first
95
82
 
96
- vals = if values
97
- values.map{|item| Node.new(item, :parent => self, &block).render!}
83
+ if items.respond_to?('keys')
84
+ key = items.keys.first
85
+ values = items.values.to_a.first
98
86
  else
99
- []
87
+ key = nil
88
+ values = items
100
89
  end
101
- merge_result! key, vals
90
+
91
+ vals = if values
92
+ values.map{|item| Node.new(item, opts.merge(:parent => self), &block).result}
93
+ else
94
+ []
95
+ end
102
96
 
103
- self.to_json
97
+ if items.respond_to?('keys')
98
+ merge_result! key, vals
99
+ else
100
+ @result = massage_value(vals)
101
+ end
102
+
103
+ self
104
104
  end
105
105
 
106
106
  # Add attributes to the result hash in a variety of ways
@@ -138,7 +138,10 @@ module Bldr
138
138
  #
139
139
  # @return [Nil]
140
140
  def attributes(*args, &block)
141
- raise(ArgumentError, "You cannot use #attributes when inferred object is not present.") if current_object.nil?
141
+ if current_object.nil?
142
+ raise(ArgumentError, "No current_object to apply #attributes to.")
143
+ end
144
+
142
145
  args.each do |arg|
143
146
  if arg.is_a?(Hash)
144
147
  merge_result!(arg.keys.first, current_object.send(arg.values.first))
@@ -146,18 +149,17 @@ module Bldr
146
149
  merge_result!(arg, current_object.send(arg))
147
150
  end
148
151
  end
149
- nil
150
152
  end
151
153
 
152
154
  def attribute(*args,&block)
153
155
  if block_given?
154
156
  raise(ArgumentError, "You may only pass one argument to #attribute when using the block syntax.") if args.size > 1
155
- raise(ArgumentError, "You cannot use a block of arity > 0 if inferred object is not present.") if block.arity > 0 and current_object.nil?
157
+ raise(ArgumentError, "You cannot use a block of arity > 0 if current_object is not present.") if block.arity > 0 and current_object.nil?
156
158
  merge_result!(args.first, (block.arity == 1) ? block.call(current_object) : current_object.instance_eval(&block))
157
159
  else
158
160
  case args.size
159
161
  when 1 # inferred object
160
- raise(ArgumentError, "You cannot pass one argument to #attribute when inferred object is not present.") if current_object.nil?
162
+ raise(ArgumentError, "#attribute can't be used when there is no current_object.") if current_object.nil?
161
163
  if args[0].is_a?(Hash)
162
164
  merge_result!(args[0].keys.first, current_object.send(args[0].values.first))
163
165
  else
@@ -169,10 +171,33 @@ module Bldr
169
171
  raise(ArgumentError, "You cannot pass more than two arguments to #attribute.")
170
172
  end
171
173
  end
172
- nil
174
+ end
175
+
176
+ # Render a template inline within a view
177
+ #
178
+ # @example Simple render
179
+ # object :person => dude do
180
+ # template "path/to/template"
181
+ # end
182
+ #
183
+ # @example Using locals
184
+ # object :person => dude do
185
+ # template "path/to/template", :locals => {:foo => 'bar'}
186
+ # end
187
+ def template(template,options={})
188
+ locals = options[:locals] || options['locals']
189
+ merge_result! nil, Bldr::Template.new(find_template(template)).render(self, locals).result
173
190
  end
174
191
 
175
192
  private
193
+
194
+ def find_template(template)
195
+ path = []
196
+ path << views if views
197
+ template += ".json.bldr" unless template =~ /\.json\.bldr$/
198
+ path << template
199
+ File.join(*path)
200
+ end
176
201
 
177
202
  # Merges values into the "local" result hash.
178
203
  def merge_result!(key, val)
@@ -183,10 +208,6 @@ module Bldr
183
208
  end
184
209
  end
185
210
 
186
- def append_result!(key, val)
187
- result[key] << massage_value(val)
188
- end
189
-
190
211
  # put any specializations in here
191
212
  # @todo: add config handlers to specify your own overridable Class->lambda methods of serialization
192
213
  def massage_value(val)
@@ -196,6 +217,6 @@ module Bldr
196
217
  val
197
218
  end
198
219
  end
199
-
220
+
200
221
  end
201
222
  end
@@ -3,8 +3,8 @@ require 'tilt'
3
3
  module Bldr
4
4
 
5
5
  class Template < Tilt::Template
6
- # attr_reader :engine
7
- # self.default_mime_type = 'application/json'
6
+
7
+ self.default_mime_type = 'application/json'
8
8
 
9
9
  def initialize_engine
10
10
  require_template_library 'bldr'
@@ -15,13 +15,12 @@ module Bldr
15
15
  end
16
16
 
17
17
  def prepare
18
- @engine = Bldr::Engine.new(data, options)
18
+ # We get NotImplementedError by Tilt when we don't have this method
19
19
  end
20
20
 
21
21
  def precompiled_template(locals)
22
22
  data.to_s
23
23
  end
24
-
25
24
  end
26
25
 
27
26
  Tilt.register 'bldr', Bldr::Template
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Bldr
3
- VERSION = '0.3.0'
3
+ VERSION = '0.5.2'
4
4
  end
@@ -5,18 +5,30 @@ module Sinatra
5
5
  module Bldr
6
6
  module Helpers
7
7
 
8
+ # Wrapper for Tilt's `render` method
9
+ #
10
+ # We use this to properly set the scope the template gets rendered
11
+ # within to a `Bldr::Node` object and pass in local variables.
12
+ #
8
13
  # @param [String, Symbol] template the template to render
9
14
  # Can be a relative file location or a string template.
10
15
  # The template may also be passed in as the block argument
11
16
  # to this method, in which case, template argument is nil.
12
17
  #
18
+ # @example Render a template in a file
19
+ # get '/users/:id' do
20
+ # user = User.find(params['id'])
21
+ # bldr :'users/public.bldr', :locals => {:user => user}
22
+ # end
23
+ #
13
24
  # @param [Hash] opts a hash of options
14
25
  # @option opts [Hash] :locals a hash of local variables to be used in the template
15
26
  # @option
16
27
  def bldr(template, opts = {}, &block)
17
- opts[:scope] = ::Bldr::Node.new
28
+ opts[:scope] = ::Bldr::Node.new(nil,opts.merge(:views => (settings.views || "./views")))
18
29
  locals = opts.delete(:locals)
19
- render(:bldr, template, opts, locals, &block)
30
+ MultiJson.encode render(:bldr, template, opts, locals, &block).result
31
+ # @todo add support for alternate formats, like plist
20
32
  end
21
33
  end
22
34
 
@@ -0,0 +1,3 @@
1
+ object do
2
+ attribute(:foo) { "bar" }
3
+ end
@@ -0,0 +1,3 @@
1
+ object :name => obj do
2
+ attribute :foo
3
+ end
@@ -0,0 +1,3 @@
1
+ object do
2
+ attribute(:foo) { "bar" }
3
+ end
@@ -8,12 +8,12 @@ describe "Defining different types of handlers" do
8
8
  "bar"
9
9
  end
10
10
 
11
- output = "{\"foo\":\"bar\"}"
11
+ output = {:foo => 'bar'}
12
12
  node = Bldr::Node.new do
13
13
  object { attribute(:foo) { Time.now } }
14
14
  end
15
15
 
16
- node.to_json.should == output
16
+ node.result.should == output
17
17
  end
18
18
  end
19
19
 
@@ -10,7 +10,7 @@ describe "evaluating a tilt template" do
10
10
  alex.name = 'alex'
11
11
 
12
12
  tpl = Bldr::Template.new { "object(:person => alex) { attribute(:name) }" }
13
- tpl.render(Bldr::Node.new, :alex => alex).should == jsonify({:person => {:name => 'alex'}})
13
+ tpl.render(Bldr::Node.new, :alex => alex).result.should == {:person => {:name => 'alex'}}
14
14
  end
15
15
 
16
16
  it "works when render two top-level objects" do
@@ -24,11 +24,11 @@ describe "evaluating a tilt template" do
24
24
  RUBY
25
25
  }
26
26
 
27
- result = tpl.render(Bldr::Node.new, :alex => alex, :john => john)
28
- result.should == jsonify({
27
+ result = tpl.render(Bldr::Node.new, :alex => alex, :john => john).result
28
+ result.should == {
29
29
  :person_1 => {:name => 'alex'},
30
30
  :person_2 => {:name => 'john'}
31
- })
31
+ }
32
32
  end
33
33
 
34
34
  it "renders nil -> null correctly" do
@@ -38,8 +38,8 @@ describe "evaluating a tilt template" do
38
38
  object(:person_1 => alex) { attributes(:age) }
39
39
  RUBY
40
40
  }
41
- result = tpl.render(Bldr::Node.new, :alex => alex)
42
- result.should == %%{"person_1":{"age":null}}%
41
+ result = tpl.render(Bldr::Node.new, :alex => alex).result
42
+ result.should == {:person_1 => {:age => nil}}
43
43
  end
44
44
 
45
45
  describe "root Object nodes" do
@@ -55,8 +55,8 @@ describe "evaluating a tilt template" do
55
55
  end
56
56
  RUBY
57
57
  }
58
- result = tpl.render(Bldr::Node.new, :alex => alex, :ian => ian)
59
- parse_json(result).should == {'person' => {'name' => 'alex', 'age' => 25}}
58
+ result = tpl.render(Bldr::Node.new, :alex => alex, :ian => ian).result
59
+ result.should == {:person => {:name => 'alex', :age => 25}}
60
60
  end
61
61
 
62
62
  it "returns json for root object templates with nested collections" do
@@ -71,9 +71,9 @@ describe "evaluating a tilt template" do
71
71
  end
72
72
  RUBY
73
73
  }
74
- result = tpl.render(Bldr::Node.new, :alex => alex, :friends => [ian])
75
- parse_json(result).should == {
76
- 'person'=> {'name' => 'alex', 'age' => 25, 'friends' => [{'name' => 'ian', 'age' => 32}]}
74
+ result = tpl.render(Bldr::Node.new, :alex => alex, :friends => [ian]).result
75
+ result.should == {
76
+ :person=> {:name => 'alex', :age => 25, :friends => [{:name => 'ian', :age => 32}]}
77
77
  }
78
78
  end
79
79
 
@@ -86,8 +86,8 @@ describe "evaluating a tilt template" do
86
86
  end
87
87
  RUBY
88
88
  }
89
- result = tpl.render(Bldr::Node.new, :alex => alex)
90
- result.should == %%{"person_1":{"age":null}}%
89
+ result = tpl.render(Bldr::Node.new, :alex => alex).result
90
+ result.should == {:person_1 => {:age => nil}}
91
91
  end
92
92
 
93
93
  end
@@ -105,9 +105,9 @@ describe "evaluating a tilt template" do
105
105
  end
106
106
  RUBY
107
107
  }
108
- result = tpl.render(Bldr::Node.new, :people => [alex,ian])
109
- parse_json(result).should == {
110
- 'people'=> [{'name' => 'alex', 'age' => 25},{'name' => 'ian', 'age' => 32}]
108
+ result = tpl.render(Bldr::Node.new, :people => [alex,ian]).result
109
+ result.should == {
110
+ :people => [{:name => 'alex', :age => 25}, {:name => 'ian', :age => 32}]
111
111
  }
112
112
  end
113
113
 
@@ -122,16 +122,16 @@ describe "evaluating a tilt template" do
122
122
  end
123
123
  RUBY
124
124
  }
125
- result = tpl.render(Bldr::Node.new, :people => [alex,ian])
126
- parse_json(result).should == {
127
- 'people'=> [{
128
- 'name' => 'alex',
129
- 'age' => 25,
130
- "friends" => [{"name" => 'bo', "age" => 33}]
125
+ result = tpl.render(Bldr::Node.new, :people => [alex,ian]).result
126
+ result.should == {
127
+ :people=> [{
128
+ :name => 'alex',
129
+ :age => 25,
130
+ :friends => [{:name => 'bo', :age => 33}]
131
131
  },{
132
- 'name' => 'ian',
133
- 'age' => 32,
134
- "friends" => [{"name" => 'eric', "age" => 34}]
132
+ :name => 'ian',
133
+ :age => 32,
134
+ :friends => [{:name => 'eric', :age => 34}]
135
135
  }]
136
136
  }
137
137
  end
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  ERROR_MESSAGES = { :attribute_lambda_one_argument => "You may only pass one argument to #attribute when using the block syntax.",
4
- :attribute_inferred_missing_one_argument => "You cannot pass one argument to #attribute when inferred object is not present.",
4
+ :attribute_inferred_missing_one_argument => "#attribute can't be used when there is no current_object.",
5
5
  :attribute_more_than_two_arg => "You cannot pass more than two arguments to #attribute.",
6
- :attribute_inferred_missing_arity_too_large => "You cannot use a block of arity > 0 if inferred object is not present.",
7
- :attributes_inferred_missing => "You cannot use #attributes when inferred object is not present." }
6
+ :attribute_inferred_missing_arity_too_large => "You cannot use a block of arity > 0 if current_object is not present.",
7
+ :attributes_inferred_missing => "No current_object to apply #attributes to." }
8
8
 
9
9
  describe "Node#object" do
10
10
 
@@ -52,7 +52,7 @@ describe "Node#object" do
52
52
  end
53
53
  it "renders 2 arguments statically" do
54
54
  node = wrap { attribute(:name, "alex") }
55
- node.render!.should == {:name => 'alex'}
55
+ node.result.should == {:name => 'alex'}
56
56
  end
57
57
  it "renders 1 argument and one lambda with zero arity" do
58
58
  node = wrap {
@@ -60,7 +60,7 @@ describe "Node#object" do
60
60
  "alex"
61
61
  end
62
62
  }
63
- node.render!.should == {:name => 'alex'}
63
+ node.result.should == {:name => 'alex'}
64
64
  end
65
65
  it "errors on 1 argument and one lambda with arity 1" do
66
66
  expect {
@@ -73,7 +73,7 @@ describe "Node#object" do
73
73
  end
74
74
  it "should render null attributes to null, not 'null'" do
75
75
  node = wrap { attribute(:name, nil) }
76
- node.render!.should == {:name => nil}
76
+ node.result.should == {:name => nil}
77
77
  end
78
78
 
79
79
  end
@@ -136,7 +136,7 @@ describe "Node#object" do
136
136
  end
137
137
  it "renders 2 arguments statically" do
138
138
  node = wrap { attribute(:name, "alex") }
139
- node.render!.should == {:person => {:name => 'alex'}}
139
+ node.result.should == {:person => {:name => 'alex'}}
140
140
  end
141
141
  it "renders 1 argument and one lambda with zero arity" do
142
142
  node = wrap {
@@ -144,7 +144,7 @@ describe "Node#object" do
144
144
  "alex"
145
145
  end
146
146
  }
147
- node.render!.should == {:person => {:name => 'alex'}}
147
+ node.result.should == {:person => {:name => 'alex'}}
148
148
  end
149
149
  it "errors on 1 argument and one lambda with arity 1" do
150
150
  expect {
@@ -200,23 +200,23 @@ describe "Node#object" do
200
200
  end
201
201
  it "renders 1 argument to the inferred object" do
202
202
  node = wrap { attribute(:name) }
203
- node.render!.should == {:person => {:name => 'alex'}}
203
+ node.result.should == {:person => {:name => 'alex'}}
204
204
  end
205
205
  it "renders 1 argument hash to the inferred object as the different key" do
206
206
  node = wrap { attribute(:fake => :name) }
207
- node.render!.should == {:person => {:fake => 'alex'}}
207
+ node.result.should == {:person => {:fake => 'alex'}}
208
208
  end
209
209
  it "renders 2 arguments statically" do
210
210
  node = wrap { attribute(:name, "ian") }
211
- node.render!.should == {:person => {:name => 'ian'}}
211
+ node.result.should == {:person => {:name => 'ian'}}
212
212
  end
213
213
  it "renders 1 argument and one lambda with zero arity" do
214
214
  node = wrap { attribute(:name){"ian"} }
215
- node.render!.should == {:person => {:name => 'ian'}}
215
+ node.result.should == {:person => {:name => 'ian'}}
216
216
  end
217
217
  it "renders 1 argument and one lambda with arity 1" do
218
218
  node = wrap { attribute(:name){|person| person.name} }
219
- node.render!.should == {:person => {:name => 'alex'}}
219
+ node.result.should == {:person => {:name => 'alex'}}
220
220
  end
221
221
  it "renders nil attributes" do
222
222
  node = node_wrap do
@@ -225,15 +225,14 @@ describe "Node#object" do
225
225
  end
226
226
  end
227
227
 
228
- node.render!.should == {:person => {:age => nil}}
228
+ node.result.should == {:person => {:age => nil}}
229
229
  end
230
230
 
231
231
  end
232
232
 
233
233
  describe "#attributes" do
234
-
235
- it "errors if the current_object is nil" do
236
- expect {
234
+ describe "when an object key is passed a null value" do
235
+ subject {
237
236
  node = node_wrap do
238
237
  object(:person => nil) do
239
238
  attributes(:one, :two) do |person|
@@ -241,11 +240,20 @@ describe "Node#object" do
241
240
  end
242
241
  end
243
242
  end
244
- }.to raise_error(ArgumentError, ERROR_MESSAGES[:attributes_inferred_missing])
243
+ }
244
+
245
+ it "does not raise an inferred object error" do
246
+ expect {
247
+ subject
248
+ }.not_to raise_error(ArgumentError, ERROR_MESSAGES[:attributes_inferred_missing])
249
+ end
250
+
251
+ its(:result) { should == {} }
245
252
  end
253
+
246
254
  it "renders each argument against the inferred object" do
247
255
  node = wrap { attributes(:name, :age) }
248
- node.render!.should == {:person => {:name => 'alex', :age => 25}}
256
+ node.result.should == {:person => {:name => 'alex', :age => 25}}
249
257
  end
250
258
  it "renders nil attributes" do
251
259
  node = node_wrap do
@@ -254,7 +262,7 @@ describe "Node#object" do
254
262
  end
255
263
  end
256
264
 
257
- node.render!.should == {:person => {:name => 'alex', :age => nil}}
265
+ node.result.should == {:person => {:name => 'alex', :age => nil}}
258
266
  end
259
267
 
260
268
  end
@@ -263,26 +271,25 @@ describe "Node#object" do
263
271
 
264
272
  describe "embedded objects" do
265
273
  it "evaluates the block and returns json" do
266
- node = Bldr::Node.new
267
- result = node.object(:dude => Person.new("alex")) do
268
- attributes :name
269
-
270
- object(:bro => Person.new("john")) do
274
+ node = node_wrap do
275
+ object(:dude => Person.new("alex")) do
271
276
  attributes :name
277
+
278
+ object(:bro => Person.new("john")) do
279
+ attributes :name
280
+ end
272
281
  end
273
282
  end
274
283
 
275
- result.should == jsonify({
276
- :dude => {:name => 'alex', :bro => {:name => 'john'}}
277
- })
284
+ node.result.should == {:dude => {:name => 'alex', :bro => {:name => 'john'}}}
278
285
  end
279
286
  end
280
287
 
281
288
  end
282
289
 
283
- describe "Node#render!" do
290
+ describe "Node#result" do
284
291
  it "returns an empty hash when not passed an object" do
285
- Bldr::Node.new.render!.should == {}
292
+ Bldr::Node.new.result.should == {}
286
293
  end
287
294
 
288
295
  it "a document with a single node with no nesting" do
@@ -292,7 +299,7 @@ describe "Node#render!" do
292
299
  end
293
300
  end
294
301
 
295
- node.render!.should == {:person => {:name => 'alex'}}
302
+ node.result.should == {:person => {:name => 'alex'}}
296
303
  end
297
304
 
298
305
  it "works for multiple top-level objects" do
@@ -308,7 +315,7 @@ describe "Node#render!" do
308
315
  end
309
316
  end
310
317
 
311
- node.render!.should == {:alex => {:name => 'alex'}, :john => {:name => 'john'}}
318
+ node.result.should == {:alex => {:name => 'alex'}, :john => {:name => 'john'}}
312
319
  end
313
320
 
314
321
  it "recursively renders nested objects" do
@@ -322,7 +329,7 @@ describe "Node#render!" do
322
329
  end
323
330
  end
324
331
 
325
- node.render!.should == {
332
+ node.result.should == {
326
333
  :alex => {
327
334
  :name => 'alex',
328
335
  :friend => {:name => 'john'}
@@ -343,7 +350,7 @@ describe "Node#render!" do
343
350
  end
344
351
  end
345
352
 
346
- node.render!.should == {:person => {:surname => 'alex', :age => 25}}
353
+ node.result.should == {:person => {:surname => 'alex', :age => 25}}
347
354
  end
348
355
  end
349
356
  end
@@ -360,12 +367,12 @@ describe "Node#to_json" do
360
367
  end
361
368
  end
362
369
 
363
- node.to_json.should == jsonify({
370
+ node.result.should == {
364
371
  :person => {
365
372
  :name => 'alex',
366
373
  :friend => {:name => 'pete', :age => 30}
367
374
  }
368
- })
375
+ }
369
376
  end
370
377
 
371
378
  it "returns null values for nil attributes" do
@@ -375,8 +382,8 @@ describe "Node#to_json" do
375
382
  end
376
383
  end
377
384
 
378
- parse_json(node.to_json)['person'].should have_key('age')
379
- parse_json(node.to_json)['person']['age'].should be_nil
385
+ node.result[:person].should have_key(:age)
386
+ node.result[:person][:age].should be_nil
380
387
  end
381
388
  end
382
389
 
@@ -392,7 +399,7 @@ describe "Node#collection" do
392
399
  end
393
400
  end
394
401
 
395
- node.render!.should == {
402
+ node.result.should == {
396
403
  :person => {
397
404
  :name => 'alex', :age => 26,
398
405
  :friends => [{:name => 'john', :age => 24}, {:name => 'jeff', :age => 25}]
@@ -401,14 +408,24 @@ describe "Node#collection" do
401
408
  end
402
409
 
403
410
  # @todo fix this
404
- it "renders properly when a collection is the root node" do
411
+ it "renders properly when a collection is the named root node" do
405
412
  nodes = node_wrap do
406
413
  collection :people => [Person.new('bert'), Person.new('ernie')] do
407
414
  attributes :name
408
415
  end
409
416
  end
410
417
 
411
- nodes.render!.should == {:people => [{:name => 'bert'}, {:name => 'ernie'}]}
418
+ nodes.result.should == {:people => [{:name => 'bert'}, {:name => 'ernie'}]}
419
+ end
420
+
421
+ it "renders properly when a collection is the root node" do
422
+ nodes = node_wrap do
423
+ collection [Person.new('bert'), Person.new('ernie')] do
424
+ attributes :name
425
+ end
426
+ end
427
+
428
+ nodes.result.should == [{:name => 'bert'}, {:name => 'ernie'}]
412
429
  end
413
430
 
414
431
  it "gracefully handles empty collections" do
@@ -418,7 +435,7 @@ describe "Node#collection" do
418
435
  end
419
436
  end
420
437
 
421
- nodes.render!.should == {:people => []}
438
+ nodes.result.should == {:people => []}
422
439
  end
423
440
 
424
441
  it "gracefully handles nil collections" do
@@ -428,7 +445,7 @@ describe "Node#collection" do
428
445
  end
429
446
  end
430
447
 
431
- nodes.render!.should == {:people => []}
448
+ nodes.result.should == {:people => []}
432
449
  end
433
450
 
434
451
  it "renders nested collections properly" do
@@ -446,7 +463,7 @@ describe "Node#collection" do
446
463
  end
447
464
  end
448
465
 
449
- nodes.render!.should == {
466
+ nodes.result.should == {
450
467
  :posts => [
451
468
  {:title => 'my post', :comment_count => 1, :comments => [{:body => 'my comment'}]}
452
469
  ]
@@ -471,7 +488,7 @@ describe "Node#collection" do
471
488
  end
472
489
  end
473
490
 
474
- nodes.render!.should == {
491
+ nodes.result.should == {
475
492
  :posts => [
476
493
  {
477
494
  :title => 'post 1',
@@ -498,7 +515,104 @@ describe "Node#collection" do
498
515
  end
499
516
  end
500
517
 
501
- node.render!.should == {:name => 'john doe', :age => 25}
518
+ node.result.should == {:name => 'john doe', :age => 25}
502
519
  end
503
520
 
504
521
  end
522
+
523
+ describe "Node#partial" do
524
+ it "includes the partial as a top level" do
525
+ nodes = node_wrap do
526
+ template "spec/fixtures/partial.json.bldr"
527
+ end
528
+
529
+ nodes.result.should == {:foo => "bar"}
530
+ end
531
+
532
+ it "includes the partial on a top level object" do
533
+ nodes = node_wrap do
534
+ object :container do
535
+ attribute(:blah) { "baz" }
536
+ template "spec/fixtures/partial.json.bldr"
537
+ end
538
+ end
539
+
540
+ nodes.result.should == {:container => {:blah => "baz", :foo => "bar"}}
541
+ end
542
+
543
+ it "includes the partial on a top level collection" do
544
+ nodes = node_wrap do
545
+ collection :people => [Person.new('bert'), Person.new('ernie')] do
546
+ attribute(:blah) { "baz" }
547
+ template "spec/fixtures/partial.json.bldr"
548
+ end
549
+ end
550
+
551
+ nodes.result.should == {:people => [{:blah => "baz", :foo => 'bar'}, {:blah => "baz", :foo => 'bar'}]}
552
+ end
553
+
554
+ it "includes the partial on a sub object" do
555
+ nodes = node_wrap do
556
+ object :container do
557
+ object :sub do
558
+ attribute(:blah) { "baz" }
559
+ template "spec/fixtures/partial.json.bldr"
560
+ end
561
+ end
562
+ end
563
+
564
+ nodes.result.should == {:container => {:sub => {:blah => "baz", :foo => "bar"}}}
565
+ end
566
+
567
+ it "includes the partial on a sub collection" do
568
+ nodes = node_wrap do
569
+ object :container do
570
+ collection :people => [Person.new('bert'), Person.new('ernie')] do
571
+ attribute(:blah) { "baz" }
572
+ template "spec/fixtures/partial.json.bldr"
573
+ end
574
+ end
575
+ end
576
+
577
+ nodes.result.should == {:container => {:people => [{:blah => "baz", :foo => 'bar'}, {:blah => "baz", :foo => 'bar'}]}}
578
+ end
579
+
580
+ it "includes both the partials" do
581
+ nodes = node_wrap do
582
+ object :container do
583
+ template "spec/fixtures/partial.json.bldr"
584
+ object :sub do
585
+ attribute(:blah) { "baz" }
586
+ template "spec/fixtures/partial.json.bldr"
587
+ end
588
+ end
589
+ end
590
+
591
+ nodes.result.should == {:container => {:foo => "bar", :sub => {:blah => "baz", :foo => "bar"}}}
592
+ end
593
+
594
+ it "includes the partial with the locals" do
595
+ Obj = Struct.new(:foo)
596
+ nodes = node_wrap do
597
+ template "spec/fixtures/partial_with_locals.json.bldr", :locals => {:obj => Obj.new('test')}
598
+ end
599
+
600
+ nodes.result.should == {:name => {:foo => 'test'}}
601
+ end
602
+
603
+ it "raises an error when the partial isn't found" do
604
+ expect {
605
+ nodes = node_wrap do
606
+ template "unknown/path"
607
+ end
608
+ }.to raise_error(Errno::ENOENT)
609
+ end
610
+
611
+ it "doesn't raise an error when with a base path option specified and the right file" do
612
+ nodes = node_wrap nil, :views => 'spec/fixtures/some' do
613
+ object :foo do
614
+ template "include"
615
+ end
616
+ end
617
+ end
618
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bldr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-19 00:00:00.000000000Z
12
+ date: 2012-02-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
16
- requirement: &2153229880 !ruby/object:Gem::Requirement
16
+ requirement: &70126171949220 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.0.3
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153229880
24
+ version_requirements: *70126171949220
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json_pure
27
- requirement: &2153229460 !ruby/object:Gem::Requirement
27
+ requirement: &70126171948780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2153229460
35
+ version_requirements: *70126171948780
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sinatra
38
- requirement: &2153228920 !ruby/object:Gem::Requirement
38
+ requirement: &70126171948200 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.2.6
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2153228920
46
+ version_requirements: *70126171948200
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: tilt
49
- requirement: &2153228420 !ruby/object:Gem::Requirement
49
+ requirement: &70126171947660 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,18 +54,29 @@ dependencies:
54
54
  version: 1.3.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2153228420
57
+ version_requirements: *70126171947660
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: yajl-ruby
60
- requirement: &2153228040 !ruby/object:Gem::Requirement
60
+ requirement: &70126171947160 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
64
64
  - !ruby/object:Gem::Version
65
- version: '0'
65
+ version: '1.0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70126171947160
69
+ - !ruby/object:Gem::Dependency
70
+ name: actionpack
71
+ requirement: &70126171946680 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 3.0.7
66
77
  type: :development
67
78
  prerelease: false
68
- version_requirements: *2153228040
79
+ version_requirements: *70126171946680
69
80
  description: Provides a simple and intuitive templating DSL for serializing objects
70
81
  to JSON.
71
82
  email:
@@ -84,7 +95,6 @@ files:
84
95
  - Rakefile
85
96
  - bldr.gemspec
86
97
  - lib/bldr.rb
87
- - lib/bldr/engine.rb
88
98
  - lib/bldr/node.rb
89
99
  - lib/bldr/template.rb
90
100
  - lib/bldr/version.rb
@@ -93,7 +103,10 @@ files:
93
103
  - perf/benchmark.rb
94
104
  - perf/results.txt
95
105
  - spec/fixtures/nested_objects.json.bldr
106
+ - spec/fixtures/partial.json.bldr
107
+ - spec/fixtures/partial_with_locals.json.bldr
96
108
  - spec/fixtures/root_template.json.bldr
109
+ - spec/fixtures/some/include.json.bldr
97
110
  - spec/functional/handlers_spec.rb
98
111
  - spec/functional/tilt_template_spec.rb
99
112
  - spec/integration/sinatra_spec.rb
@@ -123,13 +136,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
136
  version: '0'
124
137
  requirements: []
125
138
  rubyforge_project: bldr
126
- rubygems_version: 1.8.6
139
+ rubygems_version: 1.8.15
127
140
  signing_key:
128
141
  specification_version: 3
129
142
  summary: Templating library with a simple, minimalist DSL.
130
143
  test_files:
131
144
  - spec/fixtures/nested_objects.json.bldr
145
+ - spec/fixtures/partial.json.bldr
146
+ - spec/fixtures/partial_with_locals.json.bldr
132
147
  - spec/fixtures/root_template.json.bldr
148
+ - spec/fixtures/some/include.json.bldr
133
149
  - spec/functional/handlers_spec.rb
134
150
  - spec/functional/tilt_template_spec.rb
135
151
  - spec/integration/sinatra_spec.rb
@@ -1,15 +0,0 @@
1
-
2
- module Bldr
3
-
4
- class Engine
5
- attr_reader :template, :options, :result
6
-
7
- def initialize(template, options = {})
8
- @template, @options = template, options
9
- @result = {}
10
- @handlers = {}
11
- end
12
-
13
- end
14
-
15
- end