halogen 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 491dfa5f8eb8f51c4973205d1fbab6e883ce40db
4
+ data.tar.gz: 79c6b3b9b20ba7e86b6684daa38440b60b34460c
5
+ SHA512:
6
+ metadata.gz: a69872769a3355f338b642ff0ae66dcbbc37a76edb9e74f7826e3abf7c53d8525ef8399a042db3d4d38b971d3bd7851984395779af628cd1df99e10983224024
7
+ data.tar.gz: d6ba44f85ce1d6b5bf1108975aa3426354f8f1b32d3cfc62bd16d91d9f894ac14e8186d8ca6121a561d0e5659cef16b7a81c9421ea386328421329c98c5b4b06
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - '2.0'
5
+ - '2.1'
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in halogen.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Heather Rivers
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.
@@ -0,0 +1,244 @@
1
+ # Halogen
2
+
3
+ [![Build Status](https://travis-ci.org/mode/halogen.svg)](https://travis-ci.org/mode/halogen)
4
+
5
+ This library provides a framework-agnostic interface for generating
6
+ [HAL+JSON](http://stateless.co/hal_specification.html)
7
+ representations of resources in Ruby.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'halogen'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install halogen
24
+
25
+ ## Usage
26
+
27
+ ### Basic usage
28
+
29
+ Create a simple representer class and include Halogen:
30
+
31
+ ```ruby
32
+ class GoatRepresenter
33
+ include Halogen
34
+
35
+ property :name do
36
+ 'Gideon'
37
+ end
38
+
39
+ link :self do
40
+ '/goats/gideon'
41
+ end
42
+ end
43
+ ```
44
+
45
+ Instantiate:
46
+
47
+ ```ruby
48
+ repr = GoatRepresenter.new
49
+ ```
50
+
51
+ Then call `repr.render`:
52
+
53
+ ```ruby
54
+ {
55
+ name: 'Gideon',
56
+ _links: {
57
+ self: { href: '/goats/gideon' }
58
+ }
59
+ }
60
+ ```
61
+
62
+ Or `repr.to_json`:
63
+
64
+ ```ruby
65
+ '{"name": "Gideon", "_links": {"self": {"href": "/goats/gideon"}}}'
66
+ ```
67
+
68
+ ### Representer types
69
+
70
+ #### 1. Simple
71
+
72
+ Not associated with any particular resource or collection. For example, an API
73
+ entry point:
74
+
75
+ ```ruby
76
+ class ApiRootRepresenter
77
+ include Halogen
78
+
79
+ link(:self) { '/api' }
80
+ end
81
+ ```
82
+
83
+ #### 2. Resource
84
+
85
+ Represents a single item:
86
+
87
+ ```ruby
88
+ class GoatRepresenter
89
+ include Halogen
90
+
91
+ resource :goat
92
+ end
93
+ ```
94
+
95
+ When a resource is declared, `#initialize` expects the resource as the first argument:
96
+
97
+ ```ruby
98
+ repr = GoatRepresenter.new(Goat.new, ...)
99
+ ```
100
+
101
+ This makes property definitions cleaner:
102
+
103
+ ```ruby
104
+ property :name # now calls Goat#name by default
105
+ ```
106
+
107
+ #### 3. Collection
108
+
109
+ Represents a collection of items. When a collection is declared, the embedded
110
+ resource with the same name will always be embedded, whether it is requested
111
+ via standard embed options or not.
112
+
113
+ ```ruby
114
+ class GoatKidsRepresenter
115
+ include Halogen
116
+
117
+ collection :kids
118
+
119
+ embed(:kids) { ... } # always embedded
120
+ end
121
+ ```
122
+
123
+ ### Defining properties, links and embeds
124
+
125
+ Properties can be defined in several ways:
126
+
127
+ ```ruby
128
+ property(:age) { "#{goat.age} years old" }
129
+ ```
130
+
131
+ ```ruby
132
+ property :age # => Goat#age, if resource is declared
133
+ ```
134
+
135
+ ```ruby
136
+ property :age do
137
+ goat.age.round
138
+ end
139
+ ```
140
+
141
+ ```ruby
142
+ property(:age) { calculate_age }
143
+
144
+ def calculate_age
145
+ ...
146
+ end
147
+ ```
148
+
149
+ #### Conditionals
150
+
151
+ The inclusion of properties can be determined by conditionals using `if` and
152
+ `unless` options. For example, with a method name:
153
+
154
+ ```ruby
155
+ property :age, if: :include_age?
156
+
157
+ def include_age?
158
+ goat.age < 10
159
+ end
160
+ ```
161
+
162
+ With a proc:
163
+ ```ruby
164
+ property :age, unless: proc { goat.age.nil? }, value: ...
165
+ ```
166
+
167
+ For links and embeds:
168
+
169
+ ```ruby
170
+ link :kids, :templated, unless: :exclude_kids_link?, value: ...
171
+ ```
172
+
173
+ ```ruby
174
+ embed :kids, if: proc { goat.kids.size > 0 } do
175
+ ...
176
+ end
177
+ ```
178
+
179
+ #### Links
180
+
181
+ Simple link:
182
+
183
+ ```ruby
184
+ link(:root) { '/' }
185
+ # => { _links: { root: { href: '/' } } ... }
186
+ ```
187
+
188
+ Templated link:
189
+
190
+ ```ruby
191
+ link(:find, :templated) { '/goats/{?id}' }
192
+ # => { _links: { find: { href: '/goats/{?id}', templated: true } } ... }
193
+ ```
194
+
195
+ ### Embedded resources
196
+
197
+ Embedded resources are not rendered by default. They will be included if both
198
+ of the following conditions are met:
199
+
200
+ 1. The proc returns either a Halogen instance or an array of Halogen instances
201
+ 2. The embed is requested via the parent representer's options, e.g.:
202
+
203
+ ```ruby
204
+ GoatRepresenter.new(embed: { kids: true, parents: false })
205
+ ```
206
+
207
+ Embedded resources can be nested to any depth, e.g.:
208
+
209
+ ```ruby
210
+ GoatRepresenter.new(embed: {
211
+ kids: {
212
+ foods: {
213
+ ingredients: true
214
+ },
215
+ pasture: true
216
+ }
217
+ })
218
+ ```
219
+
220
+ ### Using with Rails
221
+
222
+ If Halogen is loaded in a Rails application, Rails url helpers will be
223
+ available in representers:
224
+
225
+ ```ruby
226
+ link(:new) { new_goat_url }
227
+ ```
228
+
229
+ ### More examples
230
+
231
+ * [Full representer class](examples/simple.rb)
232
+ * [Extensions](examples/extensions.md)
233
+
234
+ ### What's with the goat?
235
+
236
+ It is [majestic](https://twitter.com/ModeAnalytics/status/497876416013537280).
237
+
238
+ ## Contributing
239
+
240
+ 1. Fork it ( https://github.com/mode/halogen/fork )
241
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
242
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
243
+ 4. Push to the branch (`git push origin my-new-feature`)
244
+ 5. Create a new Pull Request
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,25 @@
1
+ # Extensions
2
+
3
+ You can extend Halogen by configuring it to include your own Ruby modules.
4
+
5
+ For instance, if you wanted to cache the rendered versions of your
6
+ representers, you might use something like this to override the default
7
+ `#render` behavior:
8
+
9
+ ```ruby
10
+ module MyCachingExtension
11
+ def render
12
+ Rails.cache.fetch(cache_key) { super }
13
+ end
14
+
15
+ def cache_key
16
+ ...
17
+ end
18
+ end
19
+ ```
20
+
21
+ ```ruby
22
+ Halogen.configure do |config|
23
+ config.extensions << MyCachingExtension
24
+ end
25
+ ```
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ #
3
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
4
+
5
+ require 'halogen'
6
+ require 'pp'
7
+
8
+ # Example of a straightforward Halogen representer with no resource,
9
+ # collection, or conditional definitions
10
+ #
11
+ class GoatRepresenter
12
+ include Halogen
13
+
14
+ # Simple instance methods that will be used for properties below
15
+ #
16
+ def id; 1; end
17
+ def first_name; 'Gideon'; end
18
+ def last_name; 'Goat'; end
19
+
20
+ # == 1. Properties
21
+ #
22
+ # If you define a property without an explicit value or proc, Halogen will
23
+ # look for a public instance method with the corresponding name.
24
+ #
25
+ # This will call GoatRepresenter#id.
26
+ #
27
+ property :id # => { id: 1 }
28
+
29
+ # You can also define a property with an explicit value, e.g.:
30
+ #
31
+ property :age, value: 9.2 # => { age: 9.2 }
32
+
33
+ # Or you can use a proc to determine the property value at render time.
34
+ #
35
+ # The example below could also be written: property(:full_name) { ... }
36
+ #
37
+ property :full_name do # => { full_name: 'Gideon Goat' }
38
+ "#{first_name} #{last_name}"
39
+ end
40
+
41
+ # == 2. Links
42
+ #
43
+ # As with properties, links can be defined with a proc:
44
+ #
45
+ link :self do
46
+ "/goats/#{id}" # => { self: { href: '/goats/1' } }
47
+ end
48
+
49
+ # ...Or with an explicit value:
50
+ #
51
+ link :root, value: '/goats' # => { root: { href: '/goats' } }
52
+
53
+ # Links can also be defined as "templated", following HAL+JSON conventions:
54
+ #
55
+ link :find, :templated do # => ... { href: '/goats/{?id}', templated: true }
56
+ '/goats/{?id}'
57
+ end
58
+
59
+ # If Halogen is loaded in a Rails application, url helpers will be available
60
+ # automatically:
61
+ #
62
+ # link(:new) { new_goat_path }
63
+
64
+ # == 3. Embeds
65
+ #
66
+ # Embedded resources are not rendered by default. They will be included if
67
+ # both of the following conditions are met:
68
+ #
69
+ # 1. The proc returns either a Halogen instance or an array of Halogen instances
70
+ # 2. The embed is requested via the parent representer's options, e.g.:
71
+ #
72
+ # GoatRepresenter.new(embed: { kids: true, parents: false })
73
+ #
74
+ embed :kids do # => { kids: <GoatKidsRepresenter#render> }
75
+ GoatKidsRepresenter.new
76
+ end
77
+
78
+ embed :parents do # => will not be included according to example options above
79
+ [
80
+ self.class.new,
81
+ self.class.new
82
+ ]
83
+ end
84
+
85
+ # Embedded resources can be nested to any depth, e.g.:
86
+ #
87
+ # GoatRepresenter.new(embed: {
88
+ # kids: {
89
+ # foods: {
90
+ # ingredients: true
91
+ # },
92
+ # enclosure: true
93
+ # }
94
+ # })
95
+ end
96
+
97
+ # Another simple representer to demonstrate embedded resources above
98
+ #
99
+ class GoatKidsRepresenter
100
+ include Halogen
101
+
102
+ property :count, value: 5
103
+ end
104
+
105
+ puts 'GoatRepresenter.new(embed: { kids: true }).render:'
106
+ puts
107
+ pp GoatRepresenter.new(embed: { kids: true }).render
108
+ #
109
+ # Result:
110
+ #
111
+ # {
112
+ # id: 1,
113
+ # age: 9.2,
114
+ # full_name: "Gideon Goat",
115
+ # _embedded: {
116
+ # kids: { count: 5 }
117
+ # },
118
+ # _links: {
119
+ # self: { href: '/goats/1' },
120
+ # root: { href: '/goats"'},
121
+ # find: { href: '/goats/{?id}', templated: true }
122
+ # }
123
+ # }
124
+ #
125
+
126
+ puts
127
+ puts 'GoatRepresenter.new.render:'
128
+ puts
129
+ pp GoatRepresenter.new.render
130
+ #
131
+ # Result:
132
+ #
133
+ # {
134
+ # id: 1,
135
+ # age: 9.2,
136
+ # full_name: "Gideon Goat",
137
+ # _links: {
138
+ # self: { href: '/goats/1' },
139
+ # root: { href: '/goats"'},
140
+ # find: { href: '/goats/{?id}', templated: true }
141
+ # }
142
+ # }