boxer 1.0.0
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 +5 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +106 -0
- data/Rakefile +1 -0
- data/boxer.gemspec +27 -0
- data/lib/boxer.rb +101 -0
- data/lib/boxer/version.rb +3 -0
- data/spec/boxer_spec.rb +206 -0
- data/spec/spec_helper.rb +5 -0
- metadata +82 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Brad Fults, Gowalla Incorporated
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Boxer
|
2
|
+
|
3
|
+
Boxer is a template engine for creating nested and multi-view JSON objects
|
4
|
+
from Ruby hashes.
|
5
|
+
|
6
|
+
## The Problem
|
7
|
+
|
8
|
+
Say you have a couple ActiveRecord models in your Rails app and you want to
|
9
|
+
render an API response in JSON, but the view of each of those model objects
|
10
|
+
may change based on the API action that's being requested.
|
11
|
+
|
12
|
+
* User
|
13
|
+
* Place
|
14
|
+
|
15
|
+
For instance, the API for `GET /users/:id` should render a full representation
|
16
|
+
of the User object in question, including all relevant attributes.
|
17
|
+
|
18
|
+
But in your `GET /places/:id/users` API call, you only need short-form
|
19
|
+
representations of the users at that place, without every single attribute
|
20
|
+
being included in the response.
|
21
|
+
|
22
|
+
## The Solution
|
23
|
+
|
24
|
+
Boxer allows you to define a box for each type of object you'd like to display
|
25
|
+
(or for each amalgamation of objects you want to display—it's up to you).
|
26
|
+
|
27
|
+
Boxer.box(:user) do |box, user|
|
28
|
+
{
|
29
|
+
:name => user.name,
|
30
|
+
:age => user.age,
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
To display different views on the same object, you can use Boxer's views:
|
35
|
+
|
36
|
+
Boxer.box(:user) do |box, user|
|
37
|
+
box.view(:base) do
|
38
|
+
{
|
39
|
+
:name => user.name,
|
40
|
+
:age => user.age,
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
box.view(:full, :extends => :base) do
|
45
|
+
{
|
46
|
+
:email => user.email,
|
47
|
+
:is_private => user.private?,
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
As you might guess, the `:full` view includes all attributes in the `:base`
|
53
|
+
view by virtue of the `:extends` option.
|
54
|
+
|
55
|
+
Now, in order to render a User with the `:base` view, simple call `Boxer.ship`:
|
56
|
+
|
57
|
+
Boxer.ship(:user, User.first)
|
58
|
+
|
59
|
+
Boxer assumes that you want the `:base` view if no view is specified to
|
60
|
+
`ship`—it's the only specially-named view.
|
61
|
+
|
62
|
+
To render the full view for the same user:
|
63
|
+
|
64
|
+
Boxer.ship(:user, User.first, :view => :full)
|
65
|
+
|
66
|
+
Which will give you back a Ruby hash on which you can call `#to_json`, to render
|
67
|
+
your JSON response<sup>1</sup>:
|
68
|
+
|
69
|
+
>> Boxer.ship(:user, User.first, :view => :full).to_json
|
70
|
+
=> "{"name": "Bo Jim", "age": 17, "email": "b@a.com", "is_private": false}"
|
71
|
+
|
72
|
+
Composing different boxes together is as simple as calling `Boxer.ship` from
|
73
|
+
within a box—it's just Ruby:
|
74
|
+
|
75
|
+
Boxer.box(:place) do |box, place|
|
76
|
+
{
|
77
|
+
:name => place.name,
|
78
|
+
:address => place.address,
|
79
|
+
:top_user => Boxer.ship(:user, place.users.order(:visits).first),
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
1. `Hash#to_json` requires the [`json` library](http://rubygems.org/gems/json)
|
84
|
+
|
85
|
+
## More Features
|
86
|
+
|
87
|
+
See [the wiki](/h3h/boxer/wiki) for more features of Boxer, including:
|
88
|
+
|
89
|
+
* [Extra Arguments](/h3h/boxer/wiki/Extra-Arguments)
|
90
|
+
* [Preconditions](/h3h/boxer/wiki/Preconditions)
|
91
|
+
* [Helper Methods in Boxes](/h3h/boxer/wiki/Helper-Methods-in-Boxes)
|
92
|
+
* [Box Includes](/h3h/boxer/wiki/Box-Includes)
|
93
|
+
* [Multiple Inheritance](/h3h/boxer/wiki/Multiple-Inheritance)
|
94
|
+
|
95
|
+
## Installation
|
96
|
+
|
97
|
+
Install the [boxer gem](http://rubygems.org/gems/boxer).
|
98
|
+
|
99
|
+
## Original Author
|
100
|
+
|
101
|
+
* [Brad Fults](http://h3h.net/), Gowalla Incorporated
|
102
|
+
|
103
|
+
## Inspiration
|
104
|
+
|
105
|
+
Boxer was inspired by [rabl](https://github.com/nesquena/rabl),
|
106
|
+
by Nathan Esquenazi.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/boxer.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "boxer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'boxer'
|
7
|
+
s.version = Boxer::VERSION
|
8
|
+
s.authors = ['Brad Fults']
|
9
|
+
s.email = ['bfults@gmail.com']
|
10
|
+
s.homepage = 'http://github.com/h3h/boxer'
|
11
|
+
s.license = 'MIT'
|
12
|
+
s.summary = %q{
|
13
|
+
Easy custom-defined templates for JSON generation of objects in Ruby.
|
14
|
+
}
|
15
|
+
s.description = %q{
|
16
|
+
A composable templating system for generating JSON via Ruby hashes, with
|
17
|
+
different possible views on each object and runtime data passing.
|
18
|
+
}
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ['lib']
|
24
|
+
|
25
|
+
s.add_development_dependency 'rspec', '>= 2.0.0'
|
26
|
+
s.add_runtime_dependency 'activesupport', '>= 3.0.0'
|
27
|
+
end
|
data/lib/boxer.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'boxer/version'
|
2
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
+
require 'active_support/core_ext/array/extract_options'
|
4
|
+
require 'active_support/core_ext/hash/deep_merge'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
class Boxer
|
8
|
+
cattr_accessor :config
|
9
|
+
|
10
|
+
self.config = OpenStruct.new({
|
11
|
+
:box_includes => [],
|
12
|
+
})
|
13
|
+
|
14
|
+
class ViewMissingError < StandardError; end
|
15
|
+
|
16
|
+
def initialize(name, options={}, &block)
|
17
|
+
@name = name
|
18
|
+
@block = block
|
19
|
+
@fallback = []
|
20
|
+
@views = {}
|
21
|
+
@views_chain = {}
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
## class methods
|
26
|
+
|
27
|
+
def self.box(name, options={}, &block)
|
28
|
+
(@boxes ||= {})[name] = self.new(name, options, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.boxes
|
32
|
+
@boxes
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.clear!
|
36
|
+
@boxes = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.configure
|
40
|
+
yield config
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.ship(name, *args)
|
44
|
+
fail "Unknown box: #{name.inspect}" unless @boxes.has_key?(name)
|
45
|
+
@boxes[name].ship(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
## instance methods
|
49
|
+
|
50
|
+
def emit(val)
|
51
|
+
@fallback = [val]
|
52
|
+
end
|
53
|
+
|
54
|
+
def ship(*args)
|
55
|
+
args = args.dup
|
56
|
+
if args.last.is_a?(Hash)
|
57
|
+
args[-1] = args.last.dup
|
58
|
+
view = args.last.delete(:view)
|
59
|
+
args.slice!(-1) if args.last.empty?
|
60
|
+
end
|
61
|
+
view ||= :base
|
62
|
+
|
63
|
+
modules = self.class.config.box_includes
|
64
|
+
black_box = Class.new do
|
65
|
+
modules.each do |mod|
|
66
|
+
include mod
|
67
|
+
end
|
68
|
+
end
|
69
|
+
block_result = black_box.new.instance_exec(self, *args, &@block)
|
70
|
+
|
71
|
+
if @fallback.length > 0
|
72
|
+
return @fallback.pop
|
73
|
+
elsif @views_chain.any?
|
74
|
+
unless @views_chain.has_key?(view)
|
75
|
+
fail ViewMissingError.new([@name, view].map(&:inspect).join('/'))
|
76
|
+
end
|
77
|
+
return @views_chain[view].inject({}) do |res, view_name|
|
78
|
+
res.deep_merge(@views[view_name].call(*args))
|
79
|
+
end
|
80
|
+
else
|
81
|
+
return block_result
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def precondition
|
86
|
+
yield self
|
87
|
+
end
|
88
|
+
|
89
|
+
def view(name, opts={}, &block)
|
90
|
+
@views_chain[name] = []
|
91
|
+
if opts.has_key?(:extends)
|
92
|
+
ancestors = Array(opts[:extends]).map do |parent|
|
93
|
+
(@views_chain[parent] || []) + [parent]
|
94
|
+
end.flatten.uniq
|
95
|
+
@views_chain[name] += ancestors
|
96
|
+
end
|
97
|
+
@views_chain[name] << name
|
98
|
+
|
99
|
+
@views[name] = block
|
100
|
+
end
|
101
|
+
end
|
data/spec/boxer_spec.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'boxer'
|
3
|
+
|
4
|
+
module MyTestModule
|
5
|
+
def my_test_method; 42 end
|
6
|
+
end
|
7
|
+
|
8
|
+
module MySecondTestModule
|
9
|
+
def my_second_test_method; 43 end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Boxer do
|
13
|
+
|
14
|
+
describe ".box" do
|
15
|
+
it "can create a box based on a simple hash" do
|
16
|
+
Boxer.box(:foo) do
|
17
|
+
{:working => true}
|
18
|
+
end
|
19
|
+
|
20
|
+
Boxer.ship(:foo).should eq({:working => true})
|
21
|
+
end
|
22
|
+
|
23
|
+
it "defaults to shipping the base view, when it exists" do
|
24
|
+
Boxer.box(:foo) do |box|
|
25
|
+
box.view(:base) { {:working => true} }
|
26
|
+
end
|
27
|
+
|
28
|
+
Boxer.ship(:foo).should eq({:working => true})
|
29
|
+
end
|
30
|
+
|
31
|
+
it "fails if views are specified, but :base is missing" do
|
32
|
+
Boxer.box(:foo) do |box|
|
33
|
+
box.view(:face) { {:working => true} }
|
34
|
+
end
|
35
|
+
|
36
|
+
expect {
|
37
|
+
Boxer.ship(:foo).should eq({:working => true})
|
38
|
+
}.to raise_exception(Boxer::ViewMissingError)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "executes its block in a sandbox context, not a global one" do
|
42
|
+
Boxer.box(:foo) do |box|
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
context_obj = Boxer.ship(:foo)
|
47
|
+
context_obj.inspect.should_not include('RSpec')
|
48
|
+
context_obj.inspect.should include('Class')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises a ViewMissing error if given a non-existent view" do
|
52
|
+
Boxer.box(:foo) do |box|
|
53
|
+
box.view(:face) { {:working => true} }
|
54
|
+
end
|
55
|
+
|
56
|
+
expect {
|
57
|
+
Boxer.ship(:foo).should eq({:working => true})
|
58
|
+
}.to raise_exception(Boxer::ViewMissingError)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe ".clear!" do
|
63
|
+
it "clears all boxes" do
|
64
|
+
Boxer.box(:foo) { {:working => true} }
|
65
|
+
Boxer.clear!
|
66
|
+
Boxer.boxes.should be_empty
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".configure" do
|
71
|
+
it "sets config.box_includes via its supplied block" do
|
72
|
+
Boxer.config.box_includes = []
|
73
|
+
Boxer.configure {|config| config.box_includes = [MyTestModule] }
|
74
|
+
Boxer.config.box_includes.should include(MyTestModule)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe ".ship" do
|
79
|
+
it "accepts arguments and passes them to a box for shipping" do
|
80
|
+
Boxer.box(:bar) do |box, x, y, z|
|
81
|
+
box.view(:base) { {:working => true, :stuff => [x, y, z]} }
|
82
|
+
end
|
83
|
+
|
84
|
+
Boxer.ship(:bar, 1, 2, 3).should eq(
|
85
|
+
{:working => true, :stuff => [1, 2, 3]}
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "allows a hash as the final argument" do
|
90
|
+
Boxer.box(:bar) do |box, x, y, z|
|
91
|
+
box.view(:base) { {:working => true, :stuff => [x, y, z]} }
|
92
|
+
end
|
93
|
+
|
94
|
+
Boxer.ship(:bar, 1, 2, :banana => true).should eq(
|
95
|
+
{:working => true, :stuff => [1, 2, {:banana => true}]}
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "includes modules from config.box_includes in shipping boxes" do
|
100
|
+
Boxer.config.box_includes = [MyTestModule, MySecondTestModule]
|
101
|
+
Boxer.box(:bar) do |box|
|
102
|
+
box.view(:base) { {:a => my_test_method, :b => my_second_test_method} }
|
103
|
+
end
|
104
|
+
|
105
|
+
Boxer.ship(:bar).should eq({:a => 42, :b => 43})
|
106
|
+
end
|
107
|
+
|
108
|
+
it "does not mutate passed in arguments" do
|
109
|
+
Boxer.box(:bar) do |box, num, opts|
|
110
|
+
box.view(:base) { {} }
|
111
|
+
end
|
112
|
+
|
113
|
+
hash = {:view => :base}.freeze
|
114
|
+
orig_hash = hash.dup
|
115
|
+
Boxer.ship(:bar, 1, hash)
|
116
|
+
hash.should eq(orig_hash)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#precondition" do
|
121
|
+
it "ships the value emitted in the precondition" do
|
122
|
+
Boxer.box(:foo) do |box, obj|
|
123
|
+
box.precondition {|resp| resp.emit({}) if obj.nil? }
|
124
|
+
box.view(:base) { {:working => true} }
|
125
|
+
end
|
126
|
+
|
127
|
+
Boxer.ship(:foo, nil).should eq({})
|
128
|
+
end
|
129
|
+
|
130
|
+
it "disregards the precondition if no value is emitted" do
|
131
|
+
Boxer.box(:foo) do |box, obj|
|
132
|
+
box.precondition {|resp| resp.emit({}) if obj.nil? }
|
133
|
+
box.view(:base) { {:working => true} }
|
134
|
+
end
|
135
|
+
|
136
|
+
Boxer.ship(:foo, Object.new).should eq({:working => true})
|
137
|
+
end
|
138
|
+
|
139
|
+
it "can handle nil as an emitted precondition value" do
|
140
|
+
Boxer.box(:foo) do |box, obj|
|
141
|
+
box.precondition {|resp| resp.emit(nil) if obj.nil? }
|
142
|
+
box.view(:base) { {:working => true} }
|
143
|
+
end
|
144
|
+
|
145
|
+
Boxer.ship(:foo, nil).should eq(nil)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "doesn't remember a fallback value from a previous shipping" do
|
149
|
+
Boxer.box(:foo) do |box, obj|
|
150
|
+
box.precondition {|resp| resp.emit({}) if obj.nil? }
|
151
|
+
box.view(:base) { {:working => true} }
|
152
|
+
end
|
153
|
+
|
154
|
+
Boxer.ship(:foo, nil).should eq({})
|
155
|
+
Boxer.ship(:foo, Object.new).should eq({:working => true})
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "#view" do
|
160
|
+
it "allow extending of other views" do
|
161
|
+
Boxer.box(:foo) do |box|
|
162
|
+
box.view(:base) { {:working => true} }
|
163
|
+
box.view(:face, :extends => :base) { {:awesome => true} }
|
164
|
+
end
|
165
|
+
|
166
|
+
Boxer.ship(:foo, :view => :face).should eq(
|
167
|
+
{:working => true, :awesome => true}
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "extends by smashing lesser (extended) views" do
|
172
|
+
Boxer.box(:foo) do |box|
|
173
|
+
box.view(:base) { {:working => true} }
|
174
|
+
box.view(:face, :extends => :base) { {:working => :awesome} }
|
175
|
+
end
|
176
|
+
|
177
|
+
Boxer.ship(:foo, :view => :face).should eq(
|
178
|
+
{:working => :awesome}
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "extends by merging nested keys without overriding" do
|
183
|
+
Boxer.box(:foo) do |box|
|
184
|
+
box.view(:base) { {:working => {:a => 1}} }
|
185
|
+
box.view(:face, :extends => :base) { {:working => {:b => 2}} }
|
186
|
+
end
|
187
|
+
|
188
|
+
Boxer.ship(:foo, :view => :face).should eq(
|
189
|
+
{:working => {:a => 1, :b => 2}}
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "allows extending in a chain of more than two views" do
|
194
|
+
Boxer.box(:foo) do |box|
|
195
|
+
box.view(:base) { {:working => {:a => 1}} }
|
196
|
+
box.view(:face, :extends => :base) { {:working => {:b => 2}} }
|
197
|
+
box.view(:race, :extends => :face) { {:working => {:c => 3}} }
|
198
|
+
end
|
199
|
+
|
200
|
+
Boxer.ship(:foo, :view => :race).should eq(
|
201
|
+
{:working => {:a => 1, :b => 2, :c => 3}}
|
202
|
+
)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: boxer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brad Fults
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-18 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70290064287800 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.0.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70290064287800
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
requirement: &70290064287300 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70290064287300
|
36
|
+
description: ! "\n A composable templating system for generating JSON via Ruby
|
37
|
+
hashes, with\n different possible views on each object and runtime data passing.\n
|
38
|
+
\ "
|
39
|
+
email:
|
40
|
+
- bfults@gmail.com
|
41
|
+
executables: []
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files: []
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- Gemfile
|
47
|
+
- LICENSE
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- boxer.gemspec
|
51
|
+
- lib/boxer.rb
|
52
|
+
- lib/boxer/version.rb
|
53
|
+
- spec/boxer_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
homepage: http://github.com/h3h/boxer
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.10
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Easy custom-defined templates for JSON generation of objects in Ruby.
|
80
|
+
test_files:
|
81
|
+
- spec/boxer_spec.rb
|
82
|
+
- spec/spec_helper.rb
|