yaks 0.0.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 +7 -0
- data/.gitignore +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +59 -0
- data/README.md +106 -0
- data/lib/yaks/dumper.rb +23 -0
- data/lib/yaks/fold_ams_compat.rb +33 -0
- data/lib/yaks/fold_json_api.rb +61 -0
- data/lib/yaks/primitivize.rb +24 -0
- data/lib/yaks/serializable_association.rb +21 -0
- data/lib/yaks/serializable_collection.rb +10 -0
- data/lib/yaks/serializable_object.rb +18 -0
- data/lib/yaks/serializer/class_methods.rb +76 -0
- data/lib/yaks/serializer.rb +64 -0
- data/lib/yaks/util.rb +21 -0
- data/lib/yaks/version.rb +3 -0
- data/lib/yaks.rb +43 -0
- data/spec/integration_spec.rb +57 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/fixtures.rb +6 -0
- data/spec/support/models.rb +23 -0
- data/spec/support/serializers.rb +14 -0
- data/spec/yaks/json_api_folder_spec.rb +63 -0
- data/spec/yaks/serializer_spec.rb +12 -0
- data/yaks.gemspec +28 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 44bddcd54510d2f8a9fbd3a928874218eb12f946
|
4
|
+
data.tar.gz: 3d6578acf4bb8b7bb22e6b81e84e747321b6a456
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 48980782c7367fef882d2bc46cde48df3d13280fd19e196246461c774e7f81f2d300800a3c3f7c6f1560fd0ecf8b60afc9628f787110768f372b7c250766269d
|
7
|
+
data.tar.gz: 9733522792a868b261d48e429db1416104b62b2b8c80689a33b9c8dc8ede9d70d5173f16cded562b01adfa7aa5827cb0e59b1fac84ed0722538f9c409470420e
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.bundle
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
yaks (0.0.0)
|
5
|
+
concord (~> 0.1.4)
|
6
|
+
hamster (~> 0.4.3)
|
7
|
+
inflection (~> 1.0.0)
|
8
|
+
|
9
|
+
PATH
|
10
|
+
remote: /home/arne/github/hamster
|
11
|
+
specs:
|
12
|
+
hamster (0.4.3)
|
13
|
+
|
14
|
+
GEM
|
15
|
+
remote: https://rubygems.org/
|
16
|
+
specs:
|
17
|
+
adamantium (0.1.0)
|
18
|
+
ice_nine (~> 0.9)
|
19
|
+
thread_safe (~> 0.1.2)
|
20
|
+
atomic (1.1.14)
|
21
|
+
axiom-types (0.0.5)
|
22
|
+
descendants_tracker (~> 0.0.1)
|
23
|
+
ice_nine (~> 0.9)
|
24
|
+
backports (3.3.5)
|
25
|
+
coercible (0.2.0)
|
26
|
+
backports (~> 3.0, >= 3.1.0)
|
27
|
+
descendants_tracker (~> 0.0.1)
|
28
|
+
concord (0.1.4)
|
29
|
+
adamantium (~> 0.1)
|
30
|
+
equalizer (~> 0.0.7)
|
31
|
+
descendants_tracker (0.0.3)
|
32
|
+
diff-lcs (1.2.5)
|
33
|
+
equalizer (0.0.8)
|
34
|
+
ice_nine (0.10.0)
|
35
|
+
inflection (1.0.0)
|
36
|
+
rspec (2.14.1)
|
37
|
+
rspec-core (~> 2.14.0)
|
38
|
+
rspec-expectations (~> 2.14.0)
|
39
|
+
rspec-mocks (~> 2.14.0)
|
40
|
+
rspec-core (2.14.7)
|
41
|
+
rspec-expectations (2.14.4)
|
42
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
43
|
+
rspec-mocks (2.14.4)
|
44
|
+
thread_safe (0.1.3)
|
45
|
+
atomic
|
46
|
+
virtus (1.0.0)
|
47
|
+
axiom-types (~> 0.0.5)
|
48
|
+
coercible (~> 0.2)
|
49
|
+
descendants_tracker (~> 0.0.1)
|
50
|
+
equalizer (~> 0.0.7)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
hamster!
|
57
|
+
rspec
|
58
|
+
virtus
|
59
|
+
yaks!
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Yankee Alpha Kilo Serializers
|
2
|
+
|
3
|
+
```ruby
|
4
|
+
class ShowSerializer < Yaks::Serialize
|
5
|
+
attributes :id, :name, :description, :dates
|
6
|
+
|
7
|
+
has_many :events
|
8
|
+
has_one :event_category
|
9
|
+
|
10
|
+
def description
|
11
|
+
object.description(:long)
|
12
|
+
end
|
13
|
+
|
14
|
+
def dates
|
15
|
+
events.map(&:day)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class EventSerializer < Yaks::Serializer
|
20
|
+
attributes :id, :name
|
21
|
+
end
|
22
|
+
|
23
|
+
class EventCategorySerializer < Yaks::Serializer
|
24
|
+
attributes :id, :name
|
25
|
+
end
|
26
|
+
|
27
|
+
json = JSON.dump(
|
28
|
+
Yaks::Dumper.new(format: :json_api).dump('shows', Show.upcoming)
|
29
|
+
)
|
30
|
+
```
|
31
|
+
|
32
|
+
## Non-Features
|
33
|
+
|
34
|
+
* No core extensions
|
35
|
+
* Minimal dependencies
|
36
|
+
* Only serializes what explicitly has a Serializer, will never call to_json/as_json
|
37
|
+
* Adding extra output formats does not require altering existing code
|
38
|
+
|
39
|
+
## Formats
|
40
|
+
|
41
|
+
* :json_api
|
42
|
+
|
43
|
+
```json
|
44
|
+
{
|
45
|
+
"shows" : [
|
46
|
+
{
|
47
|
+
"dates" : [ "next Sunday" ],
|
48
|
+
"name" : "Piglet gets his groove back",
|
49
|
+
"id" : 5,
|
50
|
+
"description" : "Once in a lifetime...",
|
51
|
+
"links" : {
|
52
|
+
"event_category" : 2,
|
53
|
+
"events" : [ 7 ]
|
54
|
+
}
|
55
|
+
}
|
56
|
+
],
|
57
|
+
"linked" : {
|
58
|
+
"event_categories" : [
|
59
|
+
{
|
60
|
+
"name" : "Drama",
|
61
|
+
"id" : 2
|
62
|
+
}
|
63
|
+
],
|
64
|
+
"events" : [
|
65
|
+
{
|
66
|
+
"name" : "Sneak preview",
|
67
|
+
"id" : 7
|
68
|
+
}
|
69
|
+
]
|
70
|
+
}
|
71
|
+
}
|
72
|
+
```
|
73
|
+
|
74
|
+
* :ams_compat
|
75
|
+
|
76
|
+
```json
|
77
|
+
{
|
78
|
+
"shows" : [
|
79
|
+
{
|
80
|
+
"dates" : [ "next Sunday" ],
|
81
|
+
"name" : "Piglet gets his groove back",
|
82
|
+
"id" : 5,
|
83
|
+
"description" : null,
|
84
|
+
"event_ids" : [ 7 ],
|
85
|
+
"event_category_id" : 2
|
86
|
+
}
|
87
|
+
],
|
88
|
+
"event_categories" : [{
|
89
|
+
"name" : "Drama",
|
90
|
+
"id" : 2
|
91
|
+
}],
|
92
|
+
"events" : [{
|
93
|
+
"Name" : "Sneak preview",
|
94
|
+
"id" : 7
|
95
|
+
}]
|
96
|
+
}
|
97
|
+
|
98
|
+
## Maturity
|
99
|
+
|
100
|
+
Infantile. Crazy what these kids get up to, though.
|
101
|
+
|
102
|
+
requires current master of Hamster.
|
103
|
+
|
104
|
+
## License
|
105
|
+
|
106
|
+
MIT
|
data/lib/yaks/dumper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Dumper
|
3
|
+
include Util
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@lookup = options.fetch(:serializer_lookup) { Yaks.default_serializer_lookup }
|
7
|
+
format = options.fetch(:format) { :json_api }
|
8
|
+
@format = Yaks.const_get("Fold#{camelize(format.to_s)}") if format.is_a?(Symbol)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump(type, objects)
|
13
|
+
serializer = @lookup.(type).new(@options.merge(root_key: type))
|
14
|
+
Primitivize.(
|
15
|
+
@format.new(
|
16
|
+
serializer.serializable_collection(objects)
|
17
|
+
).fold
|
18
|
+
)
|
19
|
+
end
|
20
|
+
alias call dump
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Yaks
|
2
|
+
class FoldAmsCompat < FoldJsonApi
|
3
|
+
include Concord.new(:collection)
|
4
|
+
include Util
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegator :collection, :root_key
|
8
|
+
|
9
|
+
def fold
|
10
|
+
Hamster.hash(
|
11
|
+
root_key => collection.map(&method(:fold_object)),
|
12
|
+
).merge(
|
13
|
+
fold_associated_objects
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def fold_object(object)
|
20
|
+
object.attributes.merge(link_ids(object))
|
21
|
+
end
|
22
|
+
|
23
|
+
def fold_association_ids(hash, association)
|
24
|
+
name = singular(association.name.to_s)
|
25
|
+
if association.one?
|
26
|
+
hash.put(name + '_id', association.identities.first)
|
27
|
+
else
|
28
|
+
hash.put(name + '_ids', association.identities)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Yaks
|
2
|
+
class FoldJsonApi
|
3
|
+
include Concord.new(:collection)
|
4
|
+
include Util
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegator :collection, :root_key
|
8
|
+
|
9
|
+
def fold
|
10
|
+
Hamster.hash(
|
11
|
+
root_key => collection.map(&method(:fold_object)),
|
12
|
+
"linked" => fold_associated_objects
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def fold_object(object)
|
19
|
+
if object.has_associated_objects?
|
20
|
+
object.attributes.merge(Hamster.hash(links: link_ids(object)))
|
21
|
+
else
|
22
|
+
object.attributes
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def link_ids(object)
|
27
|
+
object.associations.reduce(
|
28
|
+
Hamster.hash,
|
29
|
+
&method(:fold_association_ids)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def fold_association_ids(hash, association)
|
34
|
+
if association.one?
|
35
|
+
hash.put(association.name, association.identities.first)
|
36
|
+
else
|
37
|
+
hash.put(association.name, association.identities)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def fold_associated_objects
|
42
|
+
association_names = Hamster.set(*
|
43
|
+
collection.flat_map do |object|
|
44
|
+
object.associations.map{|ass| [ass.name, ass.one?] }
|
45
|
+
end
|
46
|
+
)
|
47
|
+
Hamster.hash(
|
48
|
+
association_names.map do |name, one|
|
49
|
+
[
|
50
|
+
one ? pluralize(name.to_s) : name,
|
51
|
+
Hamster.set(*
|
52
|
+
collection.flat_map do |object|
|
53
|
+
object.associated_objects(name)
|
54
|
+
end
|
55
|
+
).map(&method(:fold_object))
|
56
|
+
]
|
57
|
+
end
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Primitivize
|
3
|
+
def self.call(object)
|
4
|
+
new.call(object)
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(object)
|
8
|
+
case object
|
9
|
+
when String, TrueClass, FalseClass, NilClass, Numeric
|
10
|
+
object
|
11
|
+
when Symbol
|
12
|
+
object.to_s
|
13
|
+
when Hash, Hamster::Hash
|
14
|
+
object.to_enum(:each).with_object({}) do |(key, value), output|
|
15
|
+
output[self.(key)] = self.(value)
|
16
|
+
end
|
17
|
+
when Enumerable, Hamster::Enumerable
|
18
|
+
object.map(&method(:call)).to_a
|
19
|
+
else
|
20
|
+
raise "don't know how to turn #{object.class} (#{object.inspect}) into a primitive"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Yaks
|
2
|
+
class SerializableAssociation
|
3
|
+
include Concord.new(:collection, :one)
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegator :collection, :root_key, :name
|
7
|
+
def_delegators :collection, :identity_key, :map, :objects, :empty?
|
8
|
+
|
9
|
+
def one?
|
10
|
+
one
|
11
|
+
end
|
12
|
+
|
13
|
+
def identities
|
14
|
+
map(&method(:identity))
|
15
|
+
end
|
16
|
+
|
17
|
+
def identity(object)
|
18
|
+
object[identity_key]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Yaks
|
2
|
+
class SerializableObject
|
3
|
+
include Concord.new(:attributes, :associations)
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
public :attributes, :associations
|
7
|
+
|
8
|
+
def_delegator :attributes, :[]
|
9
|
+
|
10
|
+
def associated_objects(association_name)
|
11
|
+
associations.detect {|association| association_name == association.name }.objects
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_associated_objects?
|
15
|
+
!associations.all?(&:empty?)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Serializer
|
3
|
+
module ClassMethods
|
4
|
+
include Util
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def inherited(desc)
|
9
|
+
attributes
|
10
|
+
has_one
|
11
|
+
end
|
12
|
+
|
13
|
+
def attributes(*attrs)
|
14
|
+
_attributes.concat attrs
|
15
|
+
|
16
|
+
attrs.each do |attr|
|
17
|
+
unless method_defined?(attr)
|
18
|
+
define_method attr do
|
19
|
+
object.send attr
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_one(*attrs)
|
26
|
+
_associations.concat(attrs.map {|a| [:has_one, a] })
|
27
|
+
attrs.each do |attr|
|
28
|
+
unless method_defined?(attr)
|
29
|
+
define_method attr do
|
30
|
+
object.send attr
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def has_many(*attrs)
|
37
|
+
_associations.concat(attrs.map {|a| [:has_many, a] })
|
38
|
+
attrs.each do |attr|
|
39
|
+
unless method_defined?(attr)
|
40
|
+
define_method attr do
|
41
|
+
object.send attr
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def root_key(key)
|
48
|
+
@root_key = key
|
49
|
+
end
|
50
|
+
|
51
|
+
def identity_key(id_key)
|
52
|
+
@identity_key = id_key
|
53
|
+
end
|
54
|
+
|
55
|
+
public
|
56
|
+
|
57
|
+
def _attributes
|
58
|
+
@attributes ||= []
|
59
|
+
end
|
60
|
+
|
61
|
+
def _associations
|
62
|
+
@associations ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
def _root_key
|
66
|
+
@root_key ||
|
67
|
+
pluralize(underscore(self.name.sub(/Serializer$/, '')))
|
68
|
+
end
|
69
|
+
|
70
|
+
def _identity_key
|
71
|
+
@identity_key || :id
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Serializer
|
3
|
+
include Yaks
|
4
|
+
extend ClassMethods
|
5
|
+
|
6
|
+
attr_accessor :object
|
7
|
+
attr_reader :serializer_lookup, :root_key
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@serializer_lookup = options.fetch(:serializer_lookup) { Yaks.default_serializer_lookup }
|
11
|
+
@root_key = options.fetch(:root_key) { self.class._root_key }
|
12
|
+
end
|
13
|
+
|
14
|
+
def identity_key
|
15
|
+
self.class._identity_key
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
self.class._attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def associations
|
23
|
+
self.class._associations
|
24
|
+
end
|
25
|
+
|
26
|
+
def filter(attributes)
|
27
|
+
attributes
|
28
|
+
end
|
29
|
+
|
30
|
+
def serializable_collection(enumerable)
|
31
|
+
SerializableCollection.new(root_key, identity_key, enumerable.map(&method(:serializable_object)))
|
32
|
+
end
|
33
|
+
|
34
|
+
def serializable_object(object)
|
35
|
+
self.object = object
|
36
|
+
SerializableObject.new(
|
37
|
+
serializable_attributes(object),
|
38
|
+
serializable_associations(object)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def serializable_attributes(object)
|
43
|
+
Hash(filter(attributes).map {|attr| [attr, send(attr)] })
|
44
|
+
end
|
45
|
+
|
46
|
+
def serializable_associations(object)
|
47
|
+
Hamster.enumerate(filter(associations.map(&:last)).each).map do |name|
|
48
|
+
type = associations.detect {|type, n| name == n }.first
|
49
|
+
if type == :has_one
|
50
|
+
obj = send(name)
|
51
|
+
serializer = serializer_lookup.(obj).new
|
52
|
+
objects = List(serializer.serializable_object(obj))
|
53
|
+
else
|
54
|
+
serializer = nil
|
55
|
+
objects = Hamster.enumerate(send(name).each).map do |obj|
|
56
|
+
serializer ||= serializer_lookup.(obj).new
|
57
|
+
serializer.serializable_object(obj)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
SerializableAssociation.new( SerializableCollection.new(name, :id, objects), type == :has_one )
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/yaks/util.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Yaks
|
2
|
+
module Util
|
3
|
+
extend self
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators Inflection, :singular, :singularize, :pluralize
|
7
|
+
|
8
|
+
def underscore(str)
|
9
|
+
str.gsub(/::/, '/')
|
10
|
+
.gsub(/(?<!^)([A-Z])(?=[a-z$])|(?<=[a-z])([A-Z])/, '_\1\2')
|
11
|
+
.tr("-", "_")
|
12
|
+
.downcase!
|
13
|
+
end
|
14
|
+
|
15
|
+
def camelize(str)
|
16
|
+
str.gsub(/\/(.?)/) { "::#{ $1.upcase }" }
|
17
|
+
.gsub!(/(?:^|_)(.)/) { $1.upcase }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/yaks/version.rb
ADDED
data/lib/yaks.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'hamster'
|
4
|
+
require 'concord'
|
5
|
+
require 'inflection'
|
6
|
+
|
7
|
+
module Yaks
|
8
|
+
Undefined = Object.new
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def default_serializer_lookup(obj = Undefined)
|
12
|
+
return method(:default_serializer_lookup) if obj == Undefined
|
13
|
+
if obj.respond_to?(:to_str)
|
14
|
+
Object.const_get("#{Util.singular(Util.camelize(obj.to_str))}Serializer")
|
15
|
+
else
|
16
|
+
Object.const_get("#{obj.class.name}Serializer")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def List(*args)
|
22
|
+
Hamster.list(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def Hash(*args)
|
26
|
+
Hamster.hash(*args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def Set(*args)
|
30
|
+
Hamster.set(*args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'yaks/util'
|
35
|
+
require 'yaks/serializable_collection'
|
36
|
+
require 'yaks/serializable_object'
|
37
|
+
require 'yaks/serializable_association'
|
38
|
+
require 'yaks/fold_json_api'
|
39
|
+
require 'yaks/fold_ams_compat'
|
40
|
+
require 'yaks/serializer/class_methods'
|
41
|
+
require 'yaks/serializer'
|
42
|
+
require 'yaks/primitivize'
|
43
|
+
require 'yaks/dumper'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module IntegrationRunner
|
4
|
+
def run!
|
5
|
+
specify do
|
6
|
+
tests.each do |type, objects, result|
|
7
|
+
expect(Yaks::Dumper.new(format: format).call(type, objects)).to eq result
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Yaks, 'integration tests' do
|
14
|
+
include_context 'fixtures'
|
15
|
+
|
16
|
+
context 'json api' do
|
17
|
+
extend IntegrationRunner
|
18
|
+
|
19
|
+
let(:format) { :json_api }
|
20
|
+
let(:tests) do
|
21
|
+
[
|
22
|
+
['friends', [john],
|
23
|
+
{
|
24
|
+
"friends" => [
|
25
|
+
{ "name" => "john",
|
26
|
+
"id" => 1,
|
27
|
+
"links" => {
|
28
|
+
'pets' => [2, 3],
|
29
|
+
'pet_peeve' => 4
|
30
|
+
}
|
31
|
+
}
|
32
|
+
],
|
33
|
+
"linked" => {
|
34
|
+
"pets" => [
|
35
|
+
{ "name" => "boingboing",
|
36
|
+
"species" => "dog",
|
37
|
+
"id" => 2
|
38
|
+
},
|
39
|
+
{ "name" => "wassup",
|
40
|
+
"species" => "cat",
|
41
|
+
"id" => 3
|
42
|
+
}
|
43
|
+
],
|
44
|
+
"pet_peeve" => [
|
45
|
+
{ "id" => 4,
|
46
|
+
"type" => "parsing with regexps"}
|
47
|
+
]
|
48
|
+
}
|
49
|
+
}
|
50
|
+
]
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
run!
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
ROOT = Pathname(__FILE__).join('../..')
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(ROOT.join('lib'))
|
6
|
+
|
7
|
+
require 'yaks'
|
8
|
+
require 'virtus'
|
9
|
+
|
10
|
+
require_relative 'support/models'
|
11
|
+
require_relative 'support/serializers'
|
12
|
+
require_relative 'support/fixtures'
|
@@ -0,0 +1,6 @@
|
|
1
|
+
shared_context 'fixtures' do
|
2
|
+
let(:john) { Friend.new(id: 1, name: 'john', pets: [boingboing, wassup], pet_peeve: regexps) }
|
3
|
+
let(:boingboing) { Pet.new(id: 2, name: 'boingboing', species: 'dog') }
|
4
|
+
let(:wassup) { Pet.new(id: 3, name: 'wassup', species: 'cat') }
|
5
|
+
let(:regexps) { PetPeeve.new(id: 4, type: 'parsing with regexps') }
|
6
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Pet
|
2
|
+
include Virtus.model
|
3
|
+
|
4
|
+
attribute :id, Integer
|
5
|
+
attribute :name, String
|
6
|
+
attribute :species, String
|
7
|
+
end
|
8
|
+
|
9
|
+
class PetPeeve
|
10
|
+
include Virtus.model
|
11
|
+
|
12
|
+
attribute :id, Integer
|
13
|
+
attribute :type, String
|
14
|
+
end
|
15
|
+
|
16
|
+
class Friend
|
17
|
+
include Virtus.model
|
18
|
+
|
19
|
+
attribute :id, Integer
|
20
|
+
attribute :name, String
|
21
|
+
attribute :pets, Array[Pet]
|
22
|
+
attribute :pet_peeve, PetPeeve
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class FriendSerializer < Yaks::Serializer
|
2
|
+
attributes :id, :name
|
3
|
+
|
4
|
+
has_many :pets
|
5
|
+
has_one :pet_peeve
|
6
|
+
end
|
7
|
+
|
8
|
+
class PetSerializer < Yaks::Serializer
|
9
|
+
attributes :id, :name, :species
|
10
|
+
end
|
11
|
+
|
12
|
+
class PetPeeveSerializer < Yaks::Serializer
|
13
|
+
attributes :id, :type
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Yaks
|
4
|
+
describe FoldJsonApi do
|
5
|
+
let(:collection) {
|
6
|
+
SerializableCollection.new(
|
7
|
+
'friends',
|
8
|
+
:id,
|
9
|
+
Hamster.list(
|
10
|
+
SerializableObject.new(
|
11
|
+
Hamster.hash(
|
12
|
+
id: '3',
|
13
|
+
name: 'john'
|
14
|
+
),
|
15
|
+
Hamster.list(
|
16
|
+
SerializableAssociation.new(
|
17
|
+
SerializableCollection.new(
|
18
|
+
'pets', :id,
|
19
|
+
Hamster.list(
|
20
|
+
SerializableObject.new(Hamster.hash(
|
21
|
+
id: '3',
|
22
|
+
pet_name: 'wabi'
|
23
|
+
),
|
24
|
+
Hamster.list
|
25
|
+
),
|
26
|
+
SerializableObject.new(Hamster.hash(
|
27
|
+
id: '4',
|
28
|
+
pet_name: 'sabi'
|
29
|
+
),
|
30
|
+
Hamster.list
|
31
|
+
)
|
32
|
+
)
|
33
|
+
)
|
34
|
+
)
|
35
|
+
)
|
36
|
+
)
|
37
|
+
)
|
38
|
+
)
|
39
|
+
}
|
40
|
+
|
41
|
+
specify do
|
42
|
+
expect( FoldJsonApi.new(collection).fold ).to eq(
|
43
|
+
Hamster.hash(
|
44
|
+
"friends" => Hamster.list(
|
45
|
+
Hamster.hash(
|
46
|
+
"id" => "3",
|
47
|
+
"name" => "john",
|
48
|
+
"links" => Hamster.hash(
|
49
|
+
"pets" => Hamster.list("3", "4")
|
50
|
+
),
|
51
|
+
)
|
52
|
+
),
|
53
|
+
"linked" => Hamster.hash(
|
54
|
+
"pets" => Hamster.set(
|
55
|
+
Hamster.hash("pet_name" => "sabi", "id" => "4"),
|
56
|
+
Hamster.hash("pet_name" => "wabi", "id" => "3")
|
57
|
+
)
|
58
|
+
)
|
59
|
+
)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/yaks.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../lib/yaks/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = 'yaks'
|
7
|
+
gem.version = Yaks::VERSION
|
8
|
+
gem.authors = [ 'Arne Brasseur' ]
|
9
|
+
gem.email = [ 'arne@arnebrasseur.net' ]
|
10
|
+
gem.description = 'Serialize to JSON-API and similar'
|
11
|
+
gem.summary = gem.description
|
12
|
+
gem.homepage = 'https://github.com/plexus/yaks'
|
13
|
+
gem.license = 'MIT'
|
14
|
+
|
15
|
+
gem.require_paths = %w[lib]
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.test_files = `git ls-files -- spec`.split($/)
|
18
|
+
gem.extra_rdoc_files = %w[README.md]
|
19
|
+
|
20
|
+
gem.add_runtime_dependency 'hamster' , '~> 0.4.3' # 128k , flog: 1851.8
|
21
|
+
gem.add_runtime_dependency 'inflection' , '~> 1.0.0' # 12k , flog: 226.8
|
22
|
+
gem.add_runtime_dependency 'concord' , '~> 0.1.4' # 8k , flog: 62.3
|
23
|
+
|
24
|
+
# For comparison, ActiveSupport has a flog score of 11134.7
|
25
|
+
|
26
|
+
gem.add_development_dependency 'virtus'
|
27
|
+
gem.add_development_dependency 'rspec'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yaks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arne Brasseur
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hamster
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: inflection
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: concord
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.1.4
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.1.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: virtus
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Serialize to JSON-API and similar
|
84
|
+
email:
|
85
|
+
- arne@arnebrasseur.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files:
|
89
|
+
- README.md
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- Gemfile
|
93
|
+
- Gemfile.lock
|
94
|
+
- README.md
|
95
|
+
- lib/yaks.rb
|
96
|
+
- lib/yaks/dumper.rb
|
97
|
+
- lib/yaks/fold_ams_compat.rb
|
98
|
+
- lib/yaks/fold_json_api.rb
|
99
|
+
- lib/yaks/primitivize.rb
|
100
|
+
- lib/yaks/serializable_association.rb
|
101
|
+
- lib/yaks/serializable_collection.rb
|
102
|
+
- lib/yaks/serializable_object.rb
|
103
|
+
- lib/yaks/serializer.rb
|
104
|
+
- lib/yaks/serializer/class_methods.rb
|
105
|
+
- lib/yaks/util.rb
|
106
|
+
- lib/yaks/version.rb
|
107
|
+
- spec/integration_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
- spec/support/fixtures.rb
|
110
|
+
- spec/support/models.rb
|
111
|
+
- spec/support/serializers.rb
|
112
|
+
- spec/yaks/json_api_folder_spec.rb
|
113
|
+
- spec/yaks/serializer_spec.rb
|
114
|
+
- yaks.gemspec
|
115
|
+
homepage: https://github.com/plexus/yaks
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata: {}
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 2.0.3
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: Serialize to JSON-API and similar
|
139
|
+
test_files:
|
140
|
+
- spec/integration_spec.rb
|
141
|
+
- spec/spec_helper.rb
|
142
|
+
- spec/support/fixtures.rb
|
143
|
+
- spec/support/models.rb
|
144
|
+
- spec/support/serializers.rb
|
145
|
+
- spec/yaks/json_api_folder_spec.rb
|
146
|
+
- spec/yaks/serializer_spec.rb
|
147
|
+
has_rdoc:
|