marble 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.autotest +9 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +6 -0
  4. data/.yardopts +3 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +93 -0
  7. data/LICENSE +20 -0
  8. data/README.md +82 -0
  9. data/Rakefile +20 -0
  10. data/lib/marble.rb +274 -0
  11. data/lib/marble/rails_template_handler.rb +43 -0
  12. data/lib/marble/version.rb +4 -0
  13. data/marble.gemspec +29 -0
  14. data/test/rails/.autotest +5 -0
  15. data/test/rails/.gitignore +17 -0
  16. data/test/rails/Gemfile +11 -0
  17. data/test/rails/Gemfile.lock +87 -0
  18. data/test/rails/README.md +1 -0
  19. data/test/rails/Rakefile +4 -0
  20. data/test/rails/app/controllers/application_controller.rb +2 -0
  21. data/test/rails/app/controllers/test_controller.rb +6 -0
  22. data/test/rails/app/views/test/index.json.marble +4 -0
  23. data/test/rails/app/views/test/index.yaml.marble +4 -0
  24. data/test/rails/config.ru +4 -0
  25. data/test/rails/config/application.rb +15 -0
  26. data/test/rails/config/boot.rb +6 -0
  27. data/test/rails/config/environment.rb +5 -0
  28. data/test/rails/config/environments/development.rb +9 -0
  29. data/test/rails/config/environments/production.rb +9 -0
  30. data/test/rails/config/environments/test.rb +9 -0
  31. data/test/rails/config/initializers/secret_token.rb +1 -0
  32. data/test/rails/config/initializers/session_store.rb +1 -0
  33. data/test/rails/config/routes.rb +3 -0
  34. data/test/rails/public/404.html +26 -0
  35. data/test/rails/public/422.html +26 -0
  36. data/test/rails/public/500.html +26 -0
  37. data/test/rails/public/favicon.ico +0 -0
  38. data/test/rails/public/robots.txt +5 -0
  39. data/test/rails/script/rails +6 -0
  40. data/test/rails/test/integration/marble_test.rb +13 -0
  41. data/test/rails/test/test_helper.rb +3 -0
  42. data/test/test_helper.rb +7 -0
  43. data/test/test_marble.rb +127 -0
  44. metadata +204 -0
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+
3
+ Autotest.add_hook :initialize do |at|
4
+ at.testlib = 'minitest/unit'
5
+
6
+ %w{test/rails}.each do |exception|
7
+ at.add_exception(exception)
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ coverage
2
+ rdoc
3
+ pkg
4
+ test/tmp
5
+ test/version_tmp
6
+ tmp
7
+ pkg
8
+ *.gem
9
+ *.rbc
10
+ lib/bundler/man
11
+ spec/reports
12
+ .config
13
+ InstalledFiles
14
+ .bundle
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
@@ -0,0 +1,6 @@
1
+ script: 'rake test'
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - ree
6
+ - jruby
@@ -0,0 +1,3 @@
1
+ --readme README.md
2
+ --markup markdown
3
+ --markup-provider maruku
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,93 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ marble (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ ZenTest (4.5.0)
10
+ abstract (1.0.0)
11
+ actionmailer (3.0.5)
12
+ actionpack (= 3.0.5)
13
+ mail (~> 2.2.15)
14
+ actionpack (3.0.5)
15
+ activemodel (= 3.0.5)
16
+ activesupport (= 3.0.5)
17
+ builder (~> 2.1.2)
18
+ erubis (~> 2.6.6)
19
+ i18n (~> 0.4)
20
+ rack (~> 1.2.1)
21
+ rack-mount (~> 0.6.13)
22
+ rack-test (~> 0.5.7)
23
+ tzinfo (~> 0.3.23)
24
+ activemodel (3.0.5)
25
+ activesupport (= 3.0.5)
26
+ builder (~> 2.1.2)
27
+ i18n (~> 0.4)
28
+ activerecord (3.0.5)
29
+ activemodel (= 3.0.5)
30
+ activesupport (= 3.0.5)
31
+ arel (~> 2.0.2)
32
+ tzinfo (~> 0.3.23)
33
+ activeresource (3.0.5)
34
+ activemodel (= 3.0.5)
35
+ activesupport (= 3.0.5)
36
+ activesupport (3.0.5)
37
+ arel (2.0.9)
38
+ autotest (4.4.6)
39
+ ZenTest (>= 4.4.1)
40
+ builder (2.1.2)
41
+ erubis (2.6.6)
42
+ abstract (>= 1.0.0)
43
+ i18n (0.5.0)
44
+ json (1.5.1)
45
+ mail (2.2.15)
46
+ activesupport (>= 2.3.6)
47
+ i18n (>= 0.4.0)
48
+ mime-types (~> 1.16)
49
+ treetop (~> 1.4.8)
50
+ maruku (0.6.0)
51
+ syntax (>= 1.0.0)
52
+ mime-types (1.16)
53
+ minitest (2.1.0)
54
+ mocha (0.9.12)
55
+ polyglot (0.3.1)
56
+ rack (1.2.2)
57
+ rack-mount (0.6.13)
58
+ rack (>= 1.0.0)
59
+ rack-test (0.5.7)
60
+ rack (>= 1.0)
61
+ rails (3.0.5)
62
+ actionmailer (= 3.0.5)
63
+ actionpack (= 3.0.5)
64
+ activerecord (= 3.0.5)
65
+ activeresource (= 3.0.5)
66
+ activesupport (= 3.0.5)
67
+ bundler (~> 1.0)
68
+ railties (= 3.0.5)
69
+ railties (3.0.5)
70
+ actionpack (= 3.0.5)
71
+ activesupport (= 3.0.5)
72
+ rake (>= 0.8.7)
73
+ thor (~> 0.14.4)
74
+ rake (0.8.7)
75
+ syntax (1.0.0)
76
+ thor (0.14.6)
77
+ treetop (1.4.9)
78
+ polyglot (>= 0.3.1)
79
+ tzinfo (0.3.25)
80
+ yard (0.6.8)
81
+
82
+ PLATFORMS
83
+ ruby
84
+
85
+ DEPENDENCIES
86
+ autotest (~> 4.4)
87
+ json (~> 1.5)
88
+ marble!
89
+ maruku (~> 0.6)
90
+ minitest (~> 2.0)
91
+ mocha (~> 0.9)
92
+ rails (= 3.0.5)
93
+ yard (~> 0.6)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Alexander Kern
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,82 @@
1
+ # marble [![StillMaintained Status](http://stillmaintained.com/CapnKernul/marble.png)](http://stillmaintained.com/CapnKernul/marble) [![Build Status](http://travis-ci.org/CapnKernul/marble.png)](http://travis-ci.org/CapnKernul/marble) #
2
+
3
+ Marble is a Ruby object builder. It makes generating complex Ruby objects, and
4
+ by extension JSON and YAML, much more readable. It's especially useful for Rails
5
+ JSON API templates.
6
+
7
+ ## Installation ##
8
+
9
+ Marble works both with and without Rails. Installation is as simple as adding
10
+ this line to your `Gemfile`:
11
+
12
+ gem 'marble'
13
+
14
+ If you'd like to use marble without bundler, you can also directly install the
15
+ gem:
16
+
17
+ gem install marble
18
+
19
+ ## Usage ##
20
+
21
+ First, require marble and create a builder.
22
+
23
+ require 'marble'
24
+ builder = Marble.new
25
+
26
+ This builder can build any Ruby object using the `#build` method. The yielded
27
+ value is the current builder:
28
+
29
+ builder.build do |m|
30
+ ['foo']
31
+ end # => ['foo']
32
+
33
+ However, this isn't very useful. The `#array` and `#hash` methods are far more
34
+ interesting. They allow you to build complex arrays and hashes in a minimal
35
+ amount of lines:
36
+
37
+ builder.array do |m|
38
+ m.item 'foo'
39
+ end # => ['foo']
40
+
41
+ builder.hash do |m|
42
+ m.foo 'bar'
43
+ end # => { 'foo' => 'bar' }
44
+
45
+ The returned value can be converted to JSON and YAML, giving you a really
46
+ concise way of creating JSON and YAML templates for API's.
47
+
48
+ See the `Marble` documentation for more details.
49
+
50
+ ## Usage with Rails ##
51
+
52
+ Marble provides a template handler for Rails. It supports object literal, JSON,
53
+ and YAML output formats. Name your view template with the extension `marble`.
54
+ The template creates a builder with the name `marble` for you to use. You can
55
+ rename it using the block parameter. For example, in order to generate a JSON
56
+ view:
57
+
58
+ # view_name.json.marble
59
+ marble.hash do |m|
60
+ m.foo 'bar'
61
+ end
62
+
63
+ # Renders: {"foo": "bar"}
64
+
65
+ See the `Marble::RailsTemplateHandler` documentation for more details.
66
+
67
+ ## Note on Patches/Pull Requests ##
68
+
69
+ * Fork the project.
70
+ * Make your feature addition or bug fix.
71
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
72
+ * Commit, but do not mess with the `Rakefile`. If you want to have your own version, that is fine but bump the version in a commit by itself in another branch so I can ignore it when I pull.
73
+ * Send me a pull request. Bonus points for git flow feature branches.
74
+
75
+ ## Resources ##
76
+
77
+ * [GitHub Repository](https://github.com/CapnKernul/marble)
78
+ * [Documentation](http://rubydoc.info/github/CapnKernul/marble)
79
+
80
+ ## License ##
81
+
82
+ Marble is licensed under the MIT License. See `LICENSE` for details.
@@ -0,0 +1,20 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ task :default => 'test:unit'
5
+
6
+ task :test => ['test:unit', 'test:rails']
7
+
8
+ require 'rake/testtask'
9
+ Rake::TestTask.new('test:unit') do |t|
10
+ t.ruby_opts += ['-rubygems']
11
+ t.libs << 'test'
12
+ t.pattern = 'test/test_*.rb'
13
+ end
14
+
15
+ task 'test:rails' do
16
+ sh <<-CMD
17
+ cd test/rails
18
+ rake
19
+ CMD
20
+ end
@@ -0,0 +1,274 @@
1
+ # Builder for Ruby objects. Provides a convenient interface for generating
2
+ # complex arrays and hashes.
3
+ #
4
+ # First instantiate a builder:
5
+ #
6
+ # builder = Marble.new
7
+ #
8
+ # Then, call either `#build`, `#array`, or `#hash` on the builder. You can use
9
+ # the yielded block parameter to give the builder a shorter name. I suggest `m`.
10
+ #
11
+ # builder.hash do |m|
12
+ # m.zombies 'oh my!'
13
+ # end
14
+ #
15
+ # The returned value is the built object.
16
+ class Marble
17
+ # Builds an arbitrary value.
18
+ #
19
+ # @example Build a simple value
20
+ # builder = Marble.new
21
+ # builder.build do |m|
22
+ # true
23
+ # end # => true
24
+ #
25
+ # @example Build a more complex value
26
+ # builder = Marble.new
27
+ # builder.build do |m|
28
+ # m.array do
29
+ # m.item 'foo'
30
+ # end
31
+ # end # => ['foo']
32
+ #
33
+ # @yield [builder] block to evaluate for the value
34
+ # @yieldparam builder [Marble] the current builder
35
+ # @return [Object] the built value
36
+ def build(&block)
37
+ value_structure(&block)
38
+ end
39
+
40
+ # Builds a hash.
41
+ #
42
+ # @example Build a simple hash
43
+ # builder = Marble.new
44
+ # builder.hash do |m|
45
+ # m.foo 'bar'
46
+ # m.baz 'quz'
47
+ # end # => { 'foo' => 'bar', 'baz' => 'quz' }
48
+ #
49
+ # @example Build nested hashes
50
+ # builder = Marble.new
51
+ # builder.hash do |m|
52
+ # m.foo :hash do
53
+ # m.bar 'baz'
54
+ # end
55
+ # end # => { 'foo' => { 'bar' => 'baz' } }
56
+ #
57
+ # @yield [builder] block to evaluate within the hash's context
58
+ # @yieldparam builder [Marble] the current builder
59
+ # @return [Hash] the built hash
60
+ def hash(&block)
61
+ insert_structure({}, &block)
62
+ end
63
+
64
+ # Builds an array.
65
+ #
66
+ # @example Build a simple array
67
+ # builder = Marble.new
68
+ # builder.array do |m|
69
+ # m.item 'foo'
70
+ # m.item 'bar'
71
+ # end # => ['foo', 'bar']
72
+ #
73
+ # @example Build nested arrays
74
+ # builder = Marble.new
75
+ # builder.array do |m|
76
+ # m.item :array do
77
+ # m.item 'foo'
78
+ # end
79
+ # end # => [['foo']]
80
+ #
81
+ # @yield [builder] block to evaluate within the array's context
82
+ # @yieldparam builder [Marble] the current builder
83
+ # @return [Array] the built array
84
+ def array(&block)
85
+ insert_structure([], &block)
86
+ end
87
+
88
+ # Calls `#pair` or `#key` depending on the context.
89
+ #
90
+ # When the current structure responds to `#push` (for example, in an array
91
+ # context), it calls `#item` ignoring the method name. This allows you to be
92
+ # more semantic when defining the items in an array. However, in most cases
93
+ # calling `#item` will suffice.
94
+ #
95
+ # When the current structure responds to `#[]=` (for example, in a hash
96
+ # context), it calls `#pair` with the stringified method name as the key.
97
+ # This, in general, is a more concise and preferred way than explicitly
98
+ # calling `#pair`.
99
+ #
100
+ # @example Build an array
101
+ # builder = Marble.new
102
+ # builder.array do |m|
103
+ # m.milk 'toast'
104
+ # end # => ['foo', 'bar']
105
+ #
106
+ # @example Build a hash
107
+ # builder = Marble.new
108
+ # builder.hash do |m|
109
+ # m.milk 'toast'
110
+ # end # => { 'milk' => 'toast' }
111
+ def method_missing(method, *args, &block)
112
+ if @current.respond_to?(:push)
113
+ item(*args, &block)
114
+ elsif @current.respond_to?(:[]=)
115
+ pair(method.to_s, *args, &block)
116
+ else
117
+ super
118
+ end
119
+ end
120
+
121
+ # Inserts an item into the current structure.
122
+ #
123
+ # Arrays are the most common structure into which you insert an item (any
124
+ # value). Items are inserted using `#push`, so if you'd like to duck-type an
125
+ # array for whatever reason, go ahead.
126
+ #
127
+ # You can provide values using either the second parameter or a block. If you
128
+ # provide a block, the block will be evaluated immediately.
129
+ #
130
+ # Blocks by default insert the value of the block into the item. However, you
131
+ # can optionally specify the structure type of the block as either `:array` or
132
+ # `:hash` to insert an array or hash structure. That means that these are all
133
+ # equivalent:
134
+ #
135
+ # m.array do
136
+ # m.item ['value']
137
+ # end
138
+ #
139
+ # m.array do
140
+ # m.item do
141
+ # ['value']
142
+ # end
143
+ # end
144
+ #
145
+ # m.array do
146
+ # m.item :hash do
147
+ # m.pair 'key', 'value'
148
+ # end
149
+ # end
150
+ #
151
+ # Choose the format that makes the most sense in a given context.
152
+ #
153
+ # @overload item(value)
154
+ # @param value [Object] the value to insert into the item
155
+ # @overload item()
156
+ # @yield [builder] block to evaluate for the item's value
157
+ # @yieldparam builder [Marble] the current builder
158
+ # @yieldreturn [Object] the value to insert into the item
159
+ # @overload item(structure_type)
160
+ # @param structure_type [:array, :hash] the block's structure type
161
+ # @yield [builder] block to evaluate for the item's value
162
+ # @yieldparam builder [Marble] the current builder
163
+ # @yieldreturn [Object] the value to insert into the item
164
+ def item(value_or_structure_type = nil, &block)
165
+ if block_given?
166
+ @current.push evaluate_structure(value_or_structure_type, &block)
167
+ else
168
+ @current.push value_or_structure_type
169
+ end
170
+ end
171
+
172
+ # Inserts a pair into the current structure.
173
+ #
174
+ # Hashes are the most common structure into which you insert a pair (key and
175
+ # value). Pairs are inserted using `#[]=`, so if you'd like to duck-type a
176
+ # hash for whatever reason, go ahead.
177
+ #
178
+ # You can provide values using either the second parameter or a block. If you
179
+ # provide a block, the block will be evaluated immediately.
180
+ #
181
+ # Blocks by default insert the value of the block into the pair. However, you
182
+ # can optionally specify the structure type of the block as either `:array` or
183
+ # `:hash` to insert an array or hash structure. That means that these are all
184
+ # equivalent:
185
+ #
186
+ # m.hash do
187
+ # m.pair 'key', ['value']
188
+ # end
189
+ #
190
+ # m.hash do
191
+ # m.pair 'key' do
192
+ # ['value']
193
+ # end
194
+ # end
195
+ #
196
+ # m.hash do
197
+ # m.pair 'key', :array do
198
+ # m.item 'value'
199
+ # end
200
+ # end
201
+ #
202
+ # Choose the format that makes the most sense in a given context.
203
+ #
204
+ # @overload pair(key, value)
205
+ # @param key [Object] the key to use for the pair
206
+ # @param value [Object] the value to insert into the pair
207
+ # @overload pair(key)
208
+ # @param key [Object] the key to use for the pair
209
+ # @yield [builder] block to evaluate for the pair's value
210
+ # @yieldparam builder [Marble] the current builder
211
+ # @yieldreturn [Object] the value to insert into the pair
212
+ # @overload pair(key, structure_type)
213
+ # @param key [Object] the key to use for the pair
214
+ # @param structure_type [:array, :hash] the block's structure type
215
+ # @yield [builder] block to evaluate for the pair's value
216
+ # @yieldparam builder [Marble] the current builder
217
+ # @yieldreturn [Object] the value to insert into the pair
218
+ def pair(key, value_or_structure_type = nil, &block)
219
+ if block_given?
220
+ @current[key] = evaluate_structure(value_or_structure_type, &block)
221
+ else
222
+ @current[key] = value_or_structure_type
223
+ end
224
+ end
225
+
226
+ private
227
+
228
+ # Inserts a value structure.
229
+ #
230
+ # This is really only used as an easy way to insert into the current structure
231
+ # to whatever the block evaluates.
232
+ #
233
+ # @yield [builder] block to evaluate for the value
234
+ # @yieldparam builder [Marble] the current builder
235
+ def value_structure(&block)
236
+ insert_structure(nil, &block)
237
+ end
238
+
239
+ # Inserts a structure into the current structure.
240
+ #
241
+ # @param structure [Object] the new structure to insert
242
+ # @yield [builder] block to evaluate for the value
243
+ # @yieldparam builder [Marble] the current builder
244
+ def insert_structure(structure)
245
+ if block_given?
246
+ parent = @current
247
+ @current = structure
248
+ value = yield(self)
249
+ @current = parent
250
+ end
251
+
252
+ structure || value
253
+ end
254
+
255
+ # Convenience method for inserting the correct structure type based on a
256
+ # symbol.
257
+ #
258
+ # @param type [Symbol] the structure type
259
+ # @yield [builder] block to evaluate for the value
260
+ # @yieldparam builder [Marble] the current builder
261
+ def evaluate_structure(type, &block)
262
+ case type
263
+ when :hash then hash(&block)
264
+ when :array then array(&block)
265
+ else value_structure(&block)
266
+ end
267
+ end
268
+ end
269
+
270
+ require 'marble/version'
271
+
272
+ if defined? ActionView::Template
273
+ require 'marble/rails_template_handler'
274
+ end