conglomerate 0.1.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.
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