conglomerate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9008dac9ea7c50dfb488faa1eb897070f62499f4
4
+ data.tar.gz: f4260d896b2b80ca6e5581fb79eb08d212914f27
5
+ SHA512:
6
+ metadata.gz: e8a62475f4429d00aa29ab15dcc08affc5bf18444f57e50e2efa37c892111de6b173008f4504bf3ddd76931e3999a7450cbdce972b7f9bba2185dbacf5c24c82
7
+ data.tar.gz: e4dc81daac27fed5f17bdc1b3796a1f638c164bffe02a30f9cc13bdb66bb4e785c22aa8dc5e797793d4d4fff48d4f7004855a51cd656f0ff640cfa71ce63db25
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 TeamSnap
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.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Conglomerate
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/conglomerate.png)](http://badge.fury.io/rb/conglomerate)
4
+ [![Code Climate](https://codeclimate.com/github/teamsnap/conglomerate.png)](https://codeclimate.com/github/teamsnap/conglomerate)
5
+ [![Coverage Status](https://coveralls.io/repos/teamsnap/conglomerate/badge.png?branch=master)](https://coveralls.io/r/teamsnap/conglomerate?branch=master)
6
+ [![Dependency Status](https://gemnasium.com/teamsnap/conglomerate.png)](https://gemnasium.com/teamsnap/conglomerate)
7
+ [![License](http://img.shields.io/license/MIT.png?color=green)](http://opensource.org/licenses/MIT)
8
+
9
+ A library to serialize Ruby objects into collection+json.
10
+
11
+ ![conglomerate](http://i.imgur.com/QkKZ0ru.jpg)
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'conglomerate'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install conglomerate
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ # TODO
31
+ ```
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it ( http://github.com/teamsnap/conglomerate/fork )
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "conglomerate/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "conglomerate"
8
+ spec.version = Conglomerate::VERSION
9
+ spec.authors = ["Shane Emmons"]
10
+ spec.email = ["oss@teamsnap.com"]
11
+ spec.summary = "A library to serialize Ruby objects into collection+json"
12
+ spec.description = "A library to serialize Ruby objects into collection+json"
13
+ spec.homepage = "https://github.com/teamsnap/conglomerate"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec", "~> 3.0.0.beta1"
24
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "conglomerate/serializer"
2
+
3
+ module Conglomerate
4
+ def self.serializer
5
+ Module.new do
6
+ def self.included(descendant)
7
+ descendant.send(:include, ::Conglomerate::Serializer)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,200 @@
1
+ module Conglomerate
2
+ module Serializer
3
+ def self.included(descendant)
4
+ descendant.extend(ClassMethods)
5
+ end
6
+
7
+ def initialize(objects, context: nil)
8
+ self.objects = [*objects].compact
9
+ self.context = context
10
+ end
11
+
12
+ def serialize
13
+ {
14
+ "collection" => actions.inject({}) do |collection, action|
15
+ send("apply_#{action}", collection)
16
+ end
17
+ }
18
+ end
19
+
20
+ private
21
+
22
+ attr_accessor :objects, :context
23
+
24
+ def actions
25
+ [:version, :href, :queries, :items, :template, :links]
26
+ end
27
+
28
+ def apply_version(collection)
29
+ collection.merge({"version" => "1.0"})
30
+ end
31
+
32
+ def apply_href(collection, proc: self.class._href, object: nil)
33
+ if object
34
+ collection.merge({"href" => context.instance_exec(object, &proc)})
35
+ else
36
+ collection.merge({"href" => context.instance_eval(&proc)})
37
+ end
38
+ end
39
+
40
+ def apply_data(collection, data: [], object: nil)
41
+ data = data.map do |name|
42
+ {"name" => name.to_s, "value" => object.nil? ? "" : object.send(name)}
43
+ end
44
+
45
+ if data.empty?
46
+ collection
47
+ else
48
+ collection.merge({"data" => data})
49
+ end
50
+ end
51
+
52
+ def apply_queries(collection)
53
+ queries = self.class._queries.map do |query|
54
+ build_query(query[:rel], query[:data], query[:block])
55
+ end
56
+
57
+ if queries.empty?
58
+ collection
59
+ else
60
+ collection.merge({"queries" => queries})
61
+ end
62
+ end
63
+
64
+ def apply_items(collection)
65
+ items = objects.map do |object|
66
+ item = {}
67
+
68
+ if self.class._item_href
69
+ item = apply_href(
70
+ item, :proc => self.class._item_href, :object => object
71
+ )
72
+ end
73
+
74
+ names = self.class._attributes.map { |attr| attr[:name] }
75
+ item = apply_data(item, :data => names, :object => object)
76
+
77
+ links = self.class._attributes
78
+ .select { |attr| attr[:block] }
79
+
80
+ if links.empty?
81
+ item.empty? ? nil : item
82
+ else
83
+ apply_links(item, :links => links, :object => object)
84
+ end
85
+ end
86
+
87
+ if items.compact.empty?
88
+ collection
89
+ else
90
+ collection.merge({"items" => items})
91
+ end
92
+ end
93
+
94
+ def apply_template(collection)
95
+ attrs = self.class._attributes
96
+ .select { |attr| attr[:template] }
97
+ .map { |attr| attr[:name] }
98
+
99
+ if attrs.empty?
100
+ collection
101
+ else
102
+ collection.merge({"template" => apply_data({}, :data => attrs)})
103
+ end
104
+ end
105
+
106
+ def apply_links(collection, links: self.class._links, object: nil)
107
+ if object && !links.empty?
108
+ links = links.map do |link|
109
+ if object.send(link[:name])
110
+ build_item_link(
111
+ link[:rel], :proc => link[:block], :object => object
112
+ )
113
+ else
114
+ nil
115
+ end
116
+ end.compact.reject { |link| link["href"].nil? }
117
+
118
+ if links.empty?
119
+ collection
120
+ else
121
+ collection.merge({"links" => links})
122
+ end
123
+ elsif !links.empty?
124
+ collection.merge(
125
+ {
126
+ "links" => links.map do |link|
127
+ {
128
+ "rel" => link[:rel].to_s,
129
+ "href" => context.instance_eval(&link[:block])
130
+ }
131
+ end.reject { |link| link["href"].nil? }
132
+ }
133
+ )
134
+ else
135
+ collection
136
+ end
137
+ end
138
+
139
+ def build_query(rel, data, block)
140
+ query = {"rel" => rel.to_s}
141
+ query = apply_href(query, :proc => block)
142
+ apply_data(query, :data => data)
143
+ end
144
+
145
+ def build_item_link(rel, proc: nil, object: nil)
146
+ link = {"rel" => rel.to_s}
147
+ apply_href(link, :proc => proc, :object => object)
148
+ end
149
+
150
+ module ClassMethods
151
+ def href(&block)
152
+ self._href = block
153
+ end
154
+
155
+ def item_href(&block)
156
+ self._item_href = block
157
+ end
158
+
159
+ def query(rel, data: [], &block)
160
+ self._queries = self._queries << {
161
+ :rel => rel, :data => [*data], :block => block
162
+ }
163
+ end
164
+
165
+ def attribute(name, template: false, rel: nil, &block)
166
+ self._attributes = self._attributes << {
167
+ :name => name, :template => template, :rel => rel, :block => block
168
+ }
169
+ end
170
+
171
+ def link(rel, &block)
172
+ self._links = self._links << {
173
+ :rel => rel, :block => block
174
+ }
175
+ end
176
+
177
+ attr_writer :_href, :_item_href, :_queries, :_attributes, :_links
178
+
179
+ def _href
180
+ @_href || Proc.new { request.original_url }
181
+ end
182
+
183
+ def _item_href
184
+ @_item_href || nil
185
+ end
186
+
187
+ def _queries
188
+ @_queries || []
189
+ end
190
+
191
+ def _attributes
192
+ @_attributes || []
193
+ end
194
+
195
+ def _links
196
+ @_links || []
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,3 @@
1
+ module Conglomerate
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,215 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ class ConglomerateTestSerializer
5
+ include Conglomerate.serializer
6
+
7
+ href { test_url }
8
+ item_href { |item| item_url(item.id) }
9
+
10
+ attribute :description, :template => true
11
+ attribute :id
12
+ attribute :event_id, :rel => :event do |item|
13
+ event_url(item.event_id)
14
+ end
15
+ attribute :roster_id, :rel => :roster do |item|
16
+ roster_url(item.roster_id)
17
+ end
18
+ attribute :team_ids, :rel => :teams do |item|
19
+ team_url(item.team_ids.join(","))
20
+ end
21
+
22
+ link :events do
23
+ events_url
24
+ end
25
+
26
+ query :search, :data => :id do
27
+ search_items_url
28
+ end
29
+ end
30
+
31
+ class ConglomerateExtraTestSerializer
32
+ include Conglomerate.serializer
33
+
34
+ attribute :id
35
+ end
36
+
37
+ class ConglomerateNullSerializer
38
+ include Conglomerate.serializer
39
+ end
40
+
41
+ describe Conglomerate do
42
+ let(:object) do
43
+ double(
44
+ "Object",
45
+ :id => 1,
46
+ :description => "Tasty Burgers",
47
+ :event_id => 2,
48
+ :roster_id => nil,
49
+ :team_ids => [1,2]
50
+ )
51
+ end
52
+
53
+ let(:context) do
54
+ request = double("Request", :original_url => "https://example.com/items")
55
+ double(
56
+ "Context",
57
+ :request => request,
58
+ :search_items_url => "https://example.com/items/search",
59
+ ).tap do |context|
60
+ allow(context).to receive(:item_url).with(1) {
61
+ "https://example.com/items/1"
62
+ }
63
+ allow(context).to receive(:event_url).with(2) {
64
+ "https://example.com/events/2"
65
+ }
66
+ allow(context).to receive(:events_url) {
67
+ "https://example.com/events"
68
+ }
69
+ allow(context).to receive(:team_url).with("1,2") {
70
+ "https://example.com/teams/1,2"
71
+ }
72
+ allow(context).to receive(:test_url) { "abc" }
73
+ end
74
+ end
75
+
76
+ let(:test_serializer) do
77
+ ConglomerateTestSerializer.new(object, :context => context).serialize
78
+ end
79
+
80
+ let(:extra_test_serializer) do
81
+ ConglomerateExtraTestSerializer.new(object, :context => context).serialize
82
+ end
83
+
84
+ let(:null_serializer) do
85
+ ConglomerateNullSerializer.new(object, :context => context).serialize
86
+ end
87
+
88
+ let(:test_collection) { test_serializer["collection"] }
89
+
90
+ let(:extra_test_collection) { extra_test_serializer["collection"] }
91
+
92
+ let(:null_collection) { null_serializer["collection"] }
93
+
94
+ describe "#version" do
95
+ it "sets version to 1.0" do
96
+ expect(null_collection["version"]).to eq("1.0")
97
+ expect(test_collection["version"]).to eq("1.0")
98
+ end
99
+ end
100
+
101
+ describe "#href" do
102
+ it "in context, uses the block to set the collection href" do
103
+ expect(test_collection["href"]).to eq("abc")
104
+ end
105
+
106
+ it "uses 'request.original_url' if preset 'rails_current_url' used" do
107
+ expect(extra_test_collection["href"]).to eq("https://example.com/items")
108
+ end
109
+ end
110
+
111
+ describe "#query" do
112
+ it "doesn't include any query templates if none are provided" do
113
+ expect(null_collection.keys).to_not include("queries")
114
+ end
115
+
116
+ it "adds a query template to the collection" do
117
+ expect(test_collection["queries"]).to match_array(
118
+ [
119
+ {
120
+ "href" => "https://example.com/items/search",
121
+ "rel" => "search",
122
+ "data" => [
123
+ {"name" => "id", "value" => ""}
124
+ ]
125
+ }
126
+ ]
127
+ )
128
+ end
129
+ end
130
+
131
+ describe "#attribute(s)" do
132
+ context "items" do
133
+ it "skips items if there are no attributes" do
134
+ expect(null_collection.keys).to_not include("items")
135
+ end
136
+
137
+ it "doesn't have an items array if items is empty" do
138
+ test_serializer = ConglomerateTestSerializer
139
+ .new(nil, :context => context)
140
+ .serialize
141
+ test_collection = test_serializer["collection"]
142
+ expect(test_collection.keys).to_not include("items")
143
+ end
144
+
145
+ it "includes an items array if attributes and objects present" do
146
+ expect(test_collection["items"]).to eq(
147
+ [
148
+ {
149
+ "href" => "https://example.com/items/1",
150
+ "data" => [
151
+ {"name" => "description", "value" => "Tasty Burgers"},
152
+ {"name" => "id", "value" => 1},
153
+ {"name" => "event_id", "value" => 2},
154
+ {"name" => "roster_id", "value" => nil},
155
+ {"name" => "team_ids", "value" => [1,2]}
156
+ ],
157
+ "links" => [
158
+ {"rel" => "event", "href" => "https://example.com/events/2"},
159
+ {"rel" => "teams", "href" => "https://example.com/teams/1,2"}
160
+ ]
161
+ }
162
+ ]
163
+ )
164
+ end
165
+
166
+ it "doesn't include links if there are none" do
167
+ expect(extra_test_collection["items"]).to eq(
168
+ [
169
+ {
170
+ "data" => [
171
+ {"name" => "id", "value" => 1}
172
+ ]
173
+ }
174
+ ]
175
+ )
176
+ end
177
+ end
178
+
179
+ context "template" do
180
+ it "skips template if there are no attributes for a template" do
181
+ expect(null_collection.keys).to_not include("template")
182
+ end
183
+
184
+ it "includes a valid template if attributes have them" do
185
+ expect(test_collection["template"]["data"]).to match_array(
186
+ [
187
+ {"name" => "description", "value" => ""}
188
+ ]
189
+ )
190
+ end
191
+ end
192
+ end
193
+
194
+ describe "#link" do
195
+ it "skips links if there are none present" do
196
+ expect(null_collection.keys).to_not include("links")
197
+ end
198
+
199
+ it "adds links if they are present" do
200
+ expect(test_collection["links"]).to match_array(
201
+ [
202
+ {"rel" => "events", "href" => "https://example.com/events"}
203
+ ]
204
+ )
205
+ end
206
+ end
207
+
208
+ describe "#item_link" do
209
+ it "adds links if they are present" do
210
+ expect(test_collection["items"][0]["links"]).to include(
211
+ {"rel" => "teams", "href" => "https://example.com/teams/1,2"}
212
+ )
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.order = "random"
3
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conglomerate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shane Emmons
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.0.beta1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.0.beta1
55
+ description: A library to serialize Ruby objects into collection+json
56
+ email:
57
+ - oss@teamsnap.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - conglomerate.gemspec
68
+ - lib/conglomerate.rb
69
+ - lib/conglomerate/serializer.rb
70
+ - lib/conglomerate/version.rb
71
+ - spec/conglomerate_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: https://github.com/teamsnap/conglomerate
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.0
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: A library to serialize Ruby objects into collection+json
97
+ test_files:
98
+ - spec/conglomerate_spec.rb
99
+ - spec/spec_helper.rb