Class: Jbuilder

Inherits:
ActiveSupport::BasicObject
Defined in:
lib/jbuilder.rb

Direct Known Subclasses

JbuilderTemplate

Defined Under Namespace

Classes: KeyFormatter

Constant Summary collapse

@@key_formatter =
KeyFormatter.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key_formatter = @@key_formatter.clone) ⇒ Jbuilder

Returns a new instance of Jbuilder.



49
50
51
52
# File 'lib/jbuilder.rb', line 49

def initialize(key_formatter = @@key_formatter.clone)
  @attributes = ::ActiveSupport::OrderedHash.new
  @key_formatter = key_formatter
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, value = nil, *args) ⇒ Object (private)



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/jbuilder.rb', line 223

def method_missing(method, value = nil, *args)
  result = if ::Kernel.block_given?
    if value
      # json.comments @post.comments { |comment| ... }
      # { "comments": [ { ... }, { ... } ] }
      _map_collection(value) { |element| if ::Proc.new.arity == 2 then yield self, element else yield element end }
    else
      # json.comments { ... }
      # { "comments": ... }
      _scope { yield self }
    end
  else
    if args.empty?
      if ::Jbuilder === value
        # json.age 32
        # json.person another_jbuilder
        # { "age": 32, "person": { ...  }
        value.attributes!
      else
        # json.age 32
        # { "age": 32 }
        value
      end
    else
      if value.respond_to?(:each)
        # json.comments(@post.comments, :content, :created_at)
        # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
        _map_collection(value) do |element|
          args.each do |attribute|
            _set_value attribute, element.send(attribute)
          end
        end
      else
        # json.author @post.creator, :name, :email_address
        # { "author": { "name": "David", "email_address": "[email protected]" } }
        _scope { extract! value, *args }
      end
    end
  end
  _set_value method, result
end

Class Method Details

.encode(*args) {|jbuilder| ... } ⇒ Object

Yields a builder and automatically turns the result into a JSON string

Yields:

  • (jbuilder)


41
42
43
44
45
# File 'lib/jbuilder.rb', line 41

def self.encode(*args)
  jbuilder = new(*args)
  yield jbuilder
  jbuilder.target!
end

.key_format(*args) ⇒ Object

Same as the instance method key_format! except sets the default.



111
112
113
# File 'lib/jbuilder.rb', line 111

def self.key_format(*args)
  @@key_formatter = KeyFormatter.new(*args)
end

Instance Method Details

#array!(collection) ⇒ Object

Turns the current element into an array and iterates over the passed collection, adding each iteration as an element of the resulting array.

Example:

json.array!(@people) do |person|
  json.name person.name
  json.age calculate_age(person.birthday)
end

[ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ]

If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:

json.(@people) { |person| ... }

It’s generally only needed to use this method for top-level arrays. If you have named arrays, you can do:

json.people(@people) do |person|
  json.name person.name
  json.age calculate_age(person.birthday)
end

{ "people": [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] }

If you omit the block then you can set the top level array directly:

json.array! [1, 2, 3]

[1,2,3]


166
167
168
169
170
171
172
# File 'lib/jbuilder.rb', line 166

def array!(collection)
  @attributes = if ::Kernel::block_given?
    _map_collection(collection) { |element| if ::Proc.new.arity == 2 then yield self, element else yield element end }
  else
    collection
  end
end

#attributes!Object

Returns the attributes of the current builder.



208
209
210
# File 'lib/jbuilder.rb', line 208

def attributes!
  @attributes
end

#call(object = nil, *attributes) ⇒ Object



199
200
201
202
203
204
205
# File 'lib/jbuilder.rb', line 199

def call(object = nil, *attributes)
  if attributes.empty?
    array!(object, &::Proc.new)
  else
    extract!(object, *attributes)
  end
end

#child!Object

Turns the current element into an array and yields a builder to add a hash.

Example:

json.comments do
  json.child! { json.content "hello" }
  json.child! { json.content "world" }
end

{ "comments": [ { "content": "hello" }, { "content": "world" } ]}

More commonly, you’d use the combined iterator, though:

json.comments(@post.comments) do |comment|
  json.content comment.formatted_content
end


131
132
133
134
# File 'lib/jbuilder.rb', line 131

def child!
  @attributes = [] unless @attributes.is_a? ::Array
  @attributes << _scope { yield self }
end

#extract!(object, *attributes) ⇒ Object

Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.

Example:

@person = Struct.new(:name, :age).new("David", 32)

or you can utilize a Hash

@person = {:name => "David", :age => 32}

json.extract! @person, :name, :age

{ "name": David", "age": 32 }, { "name": Jamie", "age": 31 }

If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:

json.(@person, :name, :age)


191
192
193
194
195
196
197
# File 'lib/jbuilder.rb', line 191

def extract!(object, *attributes)
  if object.is_a?(::Hash)
    attributes.each {|attribute| _set_value attribute, object.send(:fetch, attribute)}
  else
    attributes.each {|attribute| _set_value attribute, object.send(attribute)}
  end
end

#key_format!(*args) ⇒ Object

Specifies formatting to be applied to the key. Passing in a name of a function will cause that function to be called on the key. So :upcase will upper case the key. You can also pass in lambdas for more complex transformations.

Example:

json.key_format! :upcase
json.author do
  json.name "David"
  json.age 32
end

{ "AUTHOR": { "NAME": "David", "AGE": 32 } }

You can pass parameters to the method using a hash pair.

json.key_format! :camelize => :lower
json.first_name "David"

{ "firstName": "David" }

Lambdas can also be used.

json.key_format! ->(key){ "_" + key }
json.first_name "David"

{ "_first_name": "David" }


106
107
108
# File 'lib/jbuilder.rb', line 106

def key_format!(*args)
  @key_formatter = KeyFormatter.new(*args)
end

#set!(key, value = nil) ⇒ Object

Dynamically set a key value pair.

Example:

json.set!(:each, "stuff")

{ "each": "stuff" }

You can also pass a block for nested attributes

json.set!(:author) do
  json.name "David"
  json.age 32
end

{ "author": { "name": "David", "age": 32 } }


70
71
72
73
74
75
76
# File 'lib/jbuilder.rb', line 70

def set!(key, value = nil)
  if ::Kernel::block_given?
    _set_value(key, _scope { yield self })
  else
    _set_value(key, value)
  end
end

#target!Object

Encodes the current builder as JSON.



213
214
215
# File 'lib/jbuilder.rb', line 213

def target!
  ::MultiJson.encode @attributes
end