conglomerate 0.7.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ecec2dd728808b6548bbd891a90a1ae6939afa1
4
- data.tar.gz: a109f0656695f3abbfa1d99e4c71055cfc434c59
3
+ metadata.gz: f9f3f14b27e132968770842dd2d3fe7796a25fbc
4
+ data.tar.gz: 2174933a1f8470094242bbb92f77f967aaaa0dfc
5
5
  SHA512:
6
- metadata.gz: 0e2f0834e369486e5d9e8d16d99d3d9683736b62a195713155b5dde44101ef0375ae59405d81cec6562f1ce3a39cc6fb108e239912a2b37a75ced1b76dafeae6
7
- data.tar.gz: 466427fff930ebd97cbe510116ba80ad5075be0d15b2387c21e69475e0455b21bf26fcd6201c605533e705f6c7386d92c532e78d860edc4237fbff3e90c01a7e
6
+ metadata.gz: 080da2cec601bb44e310a6afad4dad3560bd47aa56f2de7feb0f9aef7ced1049fe5547d15028965583fe47e66486c42f9ad909458e7743ca0449635bfd211770
7
+ data.tar.gz: f5b2ea4b96a812f0f54d5b12a0f5bf5212d8af9d8441d4d8397bfe6586a63f8bfd7594eb21a0ed8236fb1fcaff88fe62b83813ce29b6a2e16b0fa9099e430218
data/.gitignore CHANGED
@@ -8,6 +8,7 @@ InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
10
  doc/
11
+ bin/
11
12
  lib/bundler/man
12
13
  pkg
13
14
  rdoc
data/conglomerate.gemspec CHANGED
@@ -6,7 +6,7 @@ require "conglomerate/version"
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "conglomerate"
8
8
  spec.version = Conglomerate::VERSION
9
- spec.authors = ["Shane Emmons"]
9
+ spec.authors = ["Shane Emmons", "Emily Dobervich"]
10
10
  spec.email = ["oss@teamsnap.com"]
11
11
  spec.summary = "A library to serialize Ruby objects into collection+json"
12
12
  spec.description = "A library to serialize Ruby objects into collection+json"
@@ -0,0 +1,30 @@
1
+ module Conglomerate
2
+ class Array
3
+ include Enumerable
4
+
5
+ def initialize(type)
6
+ self.type = type
7
+ self.storage = []
8
+ end
9
+
10
+ def <<(val)
11
+ if type
12
+ raise "TypeMismatch" unless val.is_a?(type)
13
+ end
14
+
15
+ storage << val
16
+ end
17
+
18
+ def empty?
19
+ storage.empty?
20
+ end
21
+
22
+ def each(&block)
23
+ storage.each(&block)
24
+ end
25
+
26
+ private
27
+
28
+ attr_accessor :storage, :type
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ module Conglomerate
2
+ class Collection
3
+ include Particle
4
+
5
+ attribute :version, :default => "1.0"
6
+ attribute :href
7
+
8
+ attribute :template, :type => Template
9
+ attribute :error, :type => Error
10
+
11
+ array :items, :contains => Item
12
+ array :links, :contains => Link
13
+ array :queries, :contains => Query
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Conglomerate
2
+ class Datum
3
+ include Conglomerate::Particle
4
+
5
+ attribute :name, :type => String, :required => true
6
+ attribute :value
7
+ attribute :prompt, :type => String
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Conglomerate
2
+ class Error
3
+ include Conglomerate::Particle
4
+
5
+ attribute :title, :type => String
6
+ attribute :code, :type => String
7
+ attribute :message, :type => String
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Conglomerate
2
+ class Item
3
+ include Conglomerate::Particle
4
+
5
+ attribute :href, :type => String
6
+
7
+ array :data, :contains => Datum
8
+ array :links, :contains => Link
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module Conglomerate
2
+ class Link
3
+ include Conglomerate::Particle
4
+
5
+ attribute :href, :type => String, :required => true
6
+ attribute :rel, :type => String, :required => true
7
+ attribute :name, :type => String
8
+ attribute :render, :type => String
9
+ attribute :prompt, :type => String
10
+ end
11
+ end
@@ -0,0 +1,73 @@
1
+ module Conglomerate
2
+ module Particle
3
+ def self.included(object)
4
+ object.send(:extend, ClassMethods)
5
+ end
6
+
7
+ def initialize(attributes = {})
8
+ self.class.attributes.each do |attr, attr_metadata|
9
+ if attr_metadata[:required]
10
+ raise "MissingAttribute" unless attributes[attr] || attributes[attr.to_s]
11
+ end
12
+
13
+ if attr_metadata[:type] == :array
14
+ self.instance_variable_set("@#{attr}", Conglomerate::Array.new(attr_metadata[:contains]))
15
+ end
16
+ end
17
+
18
+ attributes.each do |key, value|
19
+ attrs = self.class.attributes
20
+ if attrs[key.to_sym] && attrs[key.to_sym][:type] == :array
21
+ array = value
22
+ value = Conglomerate::Array.new(attrs[key.to_sym][:contains])
23
+ array.each { |item| value << item }
24
+ self.instance_variable_set("@#{key}", value)
25
+ end
26
+
27
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ module ClassMethods
34
+ def self.extended(klass)
35
+ klass.instance_variable_set("@attributes", {})
36
+ end
37
+
38
+ def attributes
39
+ instance_variable_get("@attributes").dup
40
+ end
41
+
42
+ private
43
+
44
+ def array(attr, contains: nil, cull: true)
45
+ attribute(attr, :type => :array, :contains => contains, :cull => cull)
46
+ end
47
+
48
+ def attribute(attr, default: nil, type: nil, contains: nil, cull: true, required: nil)
49
+ instance_variable_get("@attributes")[attr] = {
50
+ :default => default,
51
+ :type => type,
52
+ :contains => contains,
53
+ :cull => cull,
54
+ :required => required
55
+ }
56
+
57
+ self.send(:attr_reader, attr)
58
+
59
+ if type != :array
60
+ self.send(:define_method, :"#{attr}=") do |val|
61
+ attr_metadata = self.class.attributes[attr]
62
+
63
+ if type = attr_metadata[:type]
64
+ raise "TypeMismatch" if !val.is_a?(type)
65
+ end
66
+
67
+ self.instance_variable_set("@#{attr}", val)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,12 @@
1
+ module Conglomerate
2
+ class Query
3
+ include Conglomerate::Particle
4
+
5
+ attribute :href, :type => String, :required => true
6
+ attribute :rel, :type => String, :required => true
7
+ attribute :name, :type => String
8
+ attribute :prompt, :type => String
9
+
10
+ array :data, :contains => Datum
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module Conglomerate
2
+ class Root
3
+ include Particle
4
+ attribute :collection
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ module Conglomerate
2
+ class Template
3
+ include Conglomerate::Particle
4
+
5
+ array :data, :contains => Datum
6
+ end
7
+ end
@@ -0,0 +1,53 @@
1
+ module Conglomerate
2
+ class TreeDeserializer
3
+ PARTICLES = [Collection, Item, Error, Template, Datum, Query, Item]
4
+
5
+ def initialize(object)
6
+ self.object = object
7
+ end
8
+
9
+ def deserialize(item = object)
10
+ raise "ObjectNotCollectionRoot" if !object["collection"]
11
+ deserialize_particle(object["collection"], Collection)
12
+ end
13
+
14
+ private
15
+
16
+ attr_accessor :object
17
+
18
+ def deserialize_particle(object, klass)
19
+ attributes = {}
20
+ attribute_info = klass.attributes
21
+
22
+ attribute_info.each do |attr, attr_metadata|
23
+ attr = attr.to_s
24
+ if object[attr]
25
+ attributes[attr] = deserialize_attribute(object[attr], attr_metadata)
26
+ end
27
+ end
28
+
29
+ particle = klass.new(attributes)
30
+ end
31
+
32
+ def deserialize_attribute(attribute, attr_metadata)
33
+ if PARTICLES.include?(attr_metadata[:type])
34
+ deserialize_particle(attribute, attr_metadata[:type])
35
+ elsif attr_metadata[:type] == :array
36
+ raise "ArrayExpected" unless attribute.is_a?(::Array)
37
+ deserialize_array(attribute, attr_metadata[:contains])
38
+ else
39
+ attribute
40
+ end
41
+ end
42
+
43
+ def deserialize_array(items, contains)
44
+ array = Conglomerate::Array.new(contains)
45
+
46
+ items.each do |item|
47
+ array << deserialize_particle(item, contains)
48
+ end
49
+
50
+ array
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,48 @@
1
+ module Conglomerate
2
+ class TreeSerializer
3
+ def initialize(object)
4
+ self.object = object
5
+ end
6
+
7
+ def serialize(item = object)
8
+ if Conglomerate::Particle === item
9
+ serialize_particle(item)
10
+ elsif Conglomerate::Array === item
11
+ serialize_array(item)
12
+ elsif Array === item
13
+ serialize_array(item)
14
+ elsif item.is_a?(Numeric) || item.is_a?(String) || item.nil?
15
+ item
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_accessor :object
22
+
23
+ def serialize_particle(particle)
24
+ attributes = particle.class.attributes
25
+
26
+ attributes.inject({}) do |hash, (attr, attr_metadata)|
27
+ hash.tap do |h|
28
+ attribute = particle.send(attr)
29
+ attribute ||= attr_metadata[:default]
30
+
31
+ unless attr_metadata[:cull] && cull_attribute(attribute)
32
+ h[attr.to_s] = serialize(attribute)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def serialize_array(array)
39
+ array.map do |item|
40
+ serialize(item)
41
+ end
42
+ end
43
+
44
+ def cull_attribute(attribute)
45
+ !attribute || (Conglomerate::Array === attribute && attribute.empty?)
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module Conglomerate
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
data/lib/conglomerate.rb CHANGED
@@ -1,6 +1,21 @@
1
+ require_relative "conglomerate/tree_serializer"
2
+ require_relative "conglomerate/array"
3
+ require_relative "conglomerate/particle"
1
4
  require_relative "conglomerate/serializer"
5
+ require_relative "conglomerate/link"
6
+ require_relative "conglomerate/error"
7
+ require_relative "conglomerate/datum"
8
+ require_relative "conglomerate/template"
9
+ require_relative "conglomerate/item"
10
+ require_relative "conglomerate/query"
11
+ require_relative "conglomerate/collection"
12
+ require_relative "conglomerate/tree_deserializer"
2
13
 
3
14
  module Conglomerate
15
+ def self.serialize(serializable)
16
+ Conglomerate::TreeSerializer.new(serializable).serialize
17
+ end
18
+
4
19
  def self.serializer
5
20
  Module.new do
6
21
  def self.included(descendant)
@@ -0,0 +1,40 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ describe Conglomerate::Collection do
5
+ let(:collection) { Conglomerate::Collection.new }
6
+ subject(:serialized_empty_collection) { Conglomerate.serialize(collection) }
7
+ subject(:reference_empty_collection) {
8
+ {
9
+ "version" => "1.0"
10
+ }
11
+ }
12
+
13
+ it "empty collection serializes properly" do
14
+ expect(serialized_empty_collection).to eq(reference_empty_collection)
15
+ end
16
+
17
+ context "links" do
18
+ it "serializes links correctly" do
19
+ link = Conglomerate::Link.new(
20
+ :href => "http://this.is.a.link",
21
+ :rel => "Something"
22
+ )
23
+
24
+ collection.links << link
25
+ expect(Conglomerate.serialize(collection)["links"]).to include(Conglomerate.serialize(link))
26
+ end
27
+ end
28
+
29
+ context "queries" do
30
+ it "serializes queries correctly" do
31
+ query = Conglomerate::Query.new(
32
+ :href => "http://this.is.a.query",
33
+ :rel => "Something"
34
+ )
35
+
36
+ collection.queries << query
37
+ expect(Conglomerate.serialize(collection)["queries"]).to include(Conglomerate.serialize(query))
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ describe Conglomerate::Datum do
5
+ context "required attributes" do
6
+ specify "name" do
7
+ expect { Conglomerate::Datum.new }.to raise_error("MissingAttribute")
8
+ end
9
+ end
10
+
11
+ context "optional attributes" do
12
+ specify "value" do
13
+ datum = Conglomerate::Datum.new(:name => "name")
14
+ expect(datum).to respond_to(:"value=")
15
+ end
16
+
17
+ specify "prompt" do
18
+ datum = Conglomerate::Datum.new(:name => "name")
19
+ expect(datum).to respond_to(:"prompt=")
20
+ expect { datum.prompt = 3 }.to raise_error
21
+ end
22
+ end
23
+ end
data/spec/link_spec.rb ADDED
@@ -0,0 +1,14 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ describe Conglomerate::Link do
5
+ context "required attributes" do
6
+ specify "href" do
7
+ expect { Conglomerate::Link.new(:rel => "") }.to raise_error("MissingAttribute")
8
+ end
9
+
10
+ specify "rel" do
11
+ expect { Conglomerate::Link.new(:href => "") }.to raise_error("MissingAttribute")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ class ParticleTest
5
+ include Conglomerate::Particle
6
+
7
+ attribute :default, :default => "this is the default value"
8
+ attribute :string, :type => String
9
+ end
10
+
11
+ describe Conglomerate::Particle do
12
+ subject(:serialized_particle_test) { Conglomerate.serialize(ParticleTest.new) }
13
+ subject(:mass_assigned) { ParticleTest.new(:string => "An string", :default => "Not an default") }
14
+
15
+ it "default values are exposed when serialized" do
16
+ expect(serialized_particle_test["default"]).to eq("this is the default value")
17
+ end
18
+
19
+ it "enforces type on attributes" do
20
+ particle_test = ParticleTest.new
21
+ expect {
22
+ particle_test.string = 1
23
+ }.to raise_error("TypeMismatch")
24
+ end
25
+
26
+ it "allows mass-assignment via constructor" do
27
+ expect(mass_assigned.string).to eq("An string")
28
+ expect(mass_assigned.default).to eq("Not an default")
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ describe Conglomerate::Query do
5
+ let(:query) {
6
+ Conglomerate::Query.new(
7
+ :href => "http://this.is.a.query/",
8
+ :rel => "some_query"
9
+ )
10
+ }
11
+
12
+ context "required attributes" do
13
+ specify "href" do
14
+ expect { Conglomerate::Query.new(:rel => "") }.to raise_error("MissingAttribute")
15
+ end
16
+
17
+ specify "rel" do
18
+ expect { Conglomerate::Query.new(:href => "") }.to raise_error("MissingAttribute")
19
+ end
20
+ end
21
+
22
+ context "data" do
23
+ it "serializes properly" do
24
+ datum = Conglomerate::Datum.new(:name => "name")
25
+
26
+ query.data << datum
27
+ expect(Conglomerate.serialize(query)["data"]).to include(Conglomerate.serialize(datum))
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "spec_helper"
2
+ require_relative "../lib/conglomerate"
3
+
4
+ require "json"
5
+
6
+ describe Conglomerate::TreeDeserializer do
7
+ it "deserializes collection with error" do
8
+ json = '{"collection":{"version":"1.0","href":"https://apiv3.teamsnap.com/","links":[{"rel":"root","href":"https://apiv3.teamsnap.com/"},{"rel":"self","href":"https://apiv3.teamsnap.com/"}],"error":{"message":"translation missing: en.errors.messages.unauthorized"}}}'
9
+ obj = JSON.parse(json)
10
+ deserializer = Conglomerate::TreeDeserializer.new(obj)
11
+ tree = deserializer.deserialize
12
+
13
+ expect(tree.class).to eq(Conglomerate::Collection)
14
+ expect(tree.error.class).to eq(Conglomerate::Error)
15
+ expect(tree.href).to eq("https://apiv3.teamsnap.com/")
16
+ end
17
+
18
+ it "deserializes collection with items" do
19
+ obj = {
20
+ "collection" => {
21
+ "version" => "1.0",
22
+ "href" => "http://example.com/",
23
+ "items" => [
24
+ { "href" => "http://example.com/1", "data" => [{
25
+ "name" => "is_unicorn", "value" => true
26
+ }]}
27
+ ]
28
+ }
29
+ }
30
+
31
+ deserializer = Conglomerate::TreeDeserializer.new(obj)
32
+ tree = deserializer.deserialize
33
+
34
+ expect(
35
+ tree.items.first.data.find { |datum| datum.name == "is_unicorn" }.value
36
+ ).to eq(true)
37
+ end
38
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conglomerate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Emmons
8
+ - Emily Dobervich
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-04-18 00:00:00.000000000 Z
12
+ date: 2014-05-13 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
@@ -66,10 +67,28 @@ files:
66
67
  - Rakefile
67
68
  - conglomerate.gemspec
68
69
  - lib/conglomerate.rb
70
+ - lib/conglomerate/array.rb
71
+ - lib/conglomerate/collection.rb
72
+ - lib/conglomerate/datum.rb
73
+ - lib/conglomerate/error.rb
74
+ - lib/conglomerate/item.rb
75
+ - lib/conglomerate/link.rb
76
+ - lib/conglomerate/particle.rb
77
+ - lib/conglomerate/query.rb
78
+ - lib/conglomerate/root.rb
69
79
  - lib/conglomerate/serializer.rb
80
+ - lib/conglomerate/template.rb
81
+ - lib/conglomerate/tree_deserializer.rb
82
+ - lib/conglomerate/tree_serializer.rb
70
83
  - lib/conglomerate/version.rb
84
+ - spec/collection_spec.rb
71
85
  - spec/conglomerate_spec.rb
86
+ - spec/datum_spec.rb
87
+ - spec/link_spec.rb
88
+ - spec/particle_spec.rb
89
+ - spec/query_spec.rb
72
90
  - spec/spec_helper.rb
91
+ - spec/tree_deserializer_spec.rb
73
92
  homepage: https://github.com/teamsnap/conglomerate
74
93
  licenses:
75
94
  - MIT
@@ -95,5 +114,11 @@ signing_key:
95
114
  specification_version: 4
96
115
  summary: A library to serialize Ruby objects into collection+json
97
116
  test_files:
117
+ - spec/collection_spec.rb
98
118
  - spec/conglomerate_spec.rb
119
+ - spec/datum_spec.rb
120
+ - spec/link_spec.rb
121
+ - spec/particle_spec.rb
122
+ - spec/query_spec.rb
99
123
  - spec/spec_helper.rb
124
+ - spec/tree_deserializer_spec.rb