oat 0.1.2 → 0.2.2
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +27 -0
- data/README.md +26 -6
- data/gemfiles/Gemfile.as-1.4.4 +5 -0
- data/gemfiles/Gemfile.as-2.3.x +5 -0
- data/gemfiles/Gemfile.as-3.0.x +6 -0
- data/gemfiles/Gemfile.as-3.1.x +6 -0
- data/gemfiles/Gemfile.as-3.2.x +5 -0
- data/gemfiles/Gemfile.as-4.0.x +5 -0
- data/lib/oat/adapter.rb +3 -3
- data/lib/oat/adapters/hal.rb +4 -4
- data/lib/oat/adapters/json_api.rb +13 -6
- data/lib/oat/adapters/siren.rb +4 -4
- data/lib/oat/serializer.rb +2 -2
- data/lib/oat/version.rb +1 -1
- data/lib/support/class_attribute.rb +68 -0
- data/oat.gemspec +1 -1
- data/spec/adapters/hal_spec.rb +62 -47
- data/spec/adapters/json_api_spec.rb +76 -48
- data/spec/adapters/siren_spec.rb +95 -67
- data/spec/fixtures.rb +2 -1
- data/spec/serializer_spec.rb +22 -8
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b724a91b70428b2d8119bb6bdd9a30c814ac10d
|
4
|
+
data.tar.gz: 043edf3fd0dcb99075cf01660851e8a183270d1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7662b374492e8c20c8e3fbe030d1b08bbcd27665cb4230a4c696142b1e80d6d298eb4870977943ac99d12d20517792c97240d88602b28f29c2bd0974f3bf7455
|
7
|
+
data.tar.gz: a4887962d0788adfcb7992a79e21c884ddb92734c690b99f77c9541278003db4c7fbe5c5f1ffae2696c114c58a7236ce621a619ac9c44b3c8d151b4eafee6b47
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,3 +1,30 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 2.0.0
|
4
|
+
- 1.9.3
|
5
|
+
- 1.9.2
|
6
|
+
- 1.8.7
|
7
|
+
|
8
|
+
gemfile:
|
9
|
+
- gemfiles/Gemfile.as-2.3.x
|
10
|
+
- gemfiles/Gemfile.as-3.0.x
|
11
|
+
- gemfiles/Gemfile.as-3.1.x
|
12
|
+
- gemfiles/Gemfile.as-3.2.x
|
13
|
+
- gemfiles/Gemfile.as-4.0.x
|
14
|
+
|
15
|
+
matrix:
|
16
|
+
include:
|
17
|
+
- gemfile: gemfiles/Gemfile.as-1.4.4
|
18
|
+
rvm: 1.8.7
|
19
|
+
exclude:
|
20
|
+
- gemfile: gemfiles/Gemfile.as-2.3.x
|
21
|
+
rvm: 2.0.0
|
22
|
+
- gemfile: gemfiles/Gemfile.as-4.0.x
|
23
|
+
rvm: 1.8.7
|
24
|
+
- gemfile: gemfiles/Gemfile.as-4.0.x
|
25
|
+
rvm: 1.9.2
|
26
|
+
allow_failures:
|
27
|
+
- gemfile: gemfiles/Gemfile.as-3.0.x
|
28
|
+
rvm: 1.8.7
|
29
|
+
- gemfile: gemfiles/Gemfile.as-1.4.4
|
30
|
+
rvm: 1.8.7
|
data/README.md
CHANGED
@@ -46,7 +46,7 @@ Serializers require a single object as argument, which can be a model instance,
|
|
46
46
|
The full serializer signature is `item`, `context`, `adapter_class`.
|
47
47
|
|
48
48
|
* `item` a model or presenter instance. It is available in your serializer's schema as `item`.
|
49
|
-
* `context` (optional) a context
|
49
|
+
* `context` (optional) a context hash that is passed to the serializer and sub-serializers as the `context` variable. Useful if you need to pass request-specific data.
|
50
50
|
* `adapter_class` (optional) A serializer's adapter can be configured at class-level or passed here to the initializer. Useful if you want to switch adapters based on request data. More on this below.
|
51
51
|
|
52
52
|
### Defining Properties
|
@@ -350,32 +350,52 @@ end
|
|
350
350
|
|
351
351
|
In frameworks like Rails, you'll probably want to use the URL helpers created by the `routes.rb` file. Two options:
|
352
352
|
|
353
|
-
### Pass a context
|
353
|
+
### Pass a context hash to serializers
|
354
354
|
|
355
|
-
You can pass a context
|
355
|
+
You can pass a context hash as second argument to serializers. This object will be passed to nested serializers too. For example, you can pass the controller instance itself.
|
356
356
|
|
357
357
|
```ruby
|
358
358
|
# users_controller.rb
|
359
359
|
|
360
360
|
def show
|
361
361
|
user = User.find(params[:id])
|
362
|
-
render json: UserSerializer.new(user, self)
|
362
|
+
render json: UserSerializer.new(user, controller: self)
|
363
363
|
end
|
364
364
|
```
|
365
365
|
|
366
366
|
Then, in the `UserSerializer`:
|
367
|
+
|
367
368
|
```ruby
|
368
369
|
class ProductSerializer < Oat::Serializer
|
369
370
|
adapter Oat::Adapters::HAL
|
370
371
|
|
371
372
|
schema do
|
372
|
-
# `context` is the controller, which responds to URL helpers.
|
373
|
-
link :self, href: context.product_url(item)
|
373
|
+
# `context[:controller]` is the controller, which responds to URL helpers.
|
374
|
+
link :self, href: context[:controller].product_url(item)
|
374
375
|
...
|
375
376
|
end
|
376
377
|
end
|
377
378
|
```
|
378
379
|
|
380
|
+
The context hash is passed down to each nested serializer called by a parent. In some cases, you might want to include extra context information for one or more nested serializers. This can be done by passing options into your call to `entity` or `entities`.
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
class CategorySerializer < Oat::Serializer
|
384
|
+
adapter Oat::Adapters::HAL
|
385
|
+
|
386
|
+
schema do
|
387
|
+
map_properties :id, :name
|
388
|
+
|
389
|
+
# category entities
|
390
|
+
# passing this option ensures that only direct children are embedded within
|
391
|
+
# the parent serialized category
|
392
|
+
entities :subcategories, item.subcategories, CategorySerializer, embedded: true if context[:embedded]
|
393
|
+
end
|
394
|
+
end
|
395
|
+
```
|
396
|
+
|
397
|
+
The additional options are merged into the current context before being passed down to the nested serializer.
|
398
|
+
|
379
399
|
### Mixin Rails' routing module
|
380
400
|
|
381
401
|
Alternatively, you can mix in Rails routing helpers directly into your serializers.
|
data/lib/oat/adapter.rb
CHANGED
@@ -21,17 +21,17 @@ module Oat
|
|
21
21
|
props.to_hash
|
22
22
|
end
|
23
23
|
|
24
|
-
def serializer_from_block_or_class(obj, serializer_class = nil, &block)
|
24
|
+
def serializer_from_block_or_class(obj, serializer_class = nil, context_options = {}, &block)
|
25
25
|
return nil if obj.nil?
|
26
26
|
|
27
27
|
if block_given?
|
28
28
|
serializer_class = Class.new(serializer.class)
|
29
29
|
serializer_class.adapter self.class
|
30
|
-
s = serializer_class.new(obj, serializer.context, serializer.adapter_class, serializer.top)
|
30
|
+
s = serializer_class.new(obj, serializer.context.merge(context_options), serializer.adapter_class, serializer.top)
|
31
31
|
serializer.top.instance_exec(obj, s, &block)
|
32
32
|
s.to_hash
|
33
33
|
else
|
34
|
-
serializer_class.new(obj, serializer.context, serializer.adapter_class).to_hash
|
34
|
+
serializer_class.new(obj, serializer.context.merge(context_options), serializer.adapter_class).to_hash
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/oat/adapters/hal.rb
CHANGED
@@ -14,13 +14,13 @@ module Oat
|
|
14
14
|
data[key] = value
|
15
15
|
end
|
16
16
|
|
17
|
-
def entity(name, obj, serializer_class = nil, &block)
|
18
|
-
data[:_embedded][name] = serializer_from_block_or_class(obj, serializer_class, &block)
|
17
|
+
def entity(name, obj, serializer_class = nil, context_options = {}, &block)
|
18
|
+
data[:_embedded][name] = serializer_from_block_or_class(obj, serializer_class, context_options, &block)
|
19
19
|
end
|
20
20
|
|
21
|
-
def entities(name, collection, serializer_class = nil, &block)
|
21
|
+
def entities(name, collection, serializer_class = nil, context_options = {}, &block)
|
22
22
|
data[:_embedded][name] = collection.map do |obj|
|
23
|
-
serializer_from_block_or_class(obj, serializer_class, &block)
|
23
|
+
serializer_from_block_or_class(obj, serializer_class, context_options, &block)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -1,5 +1,12 @@
|
|
1
1
|
# http://jsonapi.org/format/#url-based-json-api
|
2
|
+
require 'active_support/inflector'
|
2
3
|
require 'active_support/core_ext/string/inflections'
|
4
|
+
unless defined?(String.new.pluralize)
|
5
|
+
class String
|
6
|
+
include ActiveSupport::CoreExtensions::String::Inflections
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
3
10
|
module Oat
|
4
11
|
module Adapters
|
5
12
|
class JsonAPI < Oat::Adapter
|
@@ -25,22 +32,22 @@ module Oat
|
|
25
32
|
data[key] = value
|
26
33
|
end
|
27
34
|
|
28
|
-
def entity(name, obj, serializer_class = nil, &block)
|
35
|
+
def entity(name, obj, serializer_class = nil, context_options = {}, &block)
|
29
36
|
@entities[name.to_s.pluralize.to_sym] ||= []
|
30
|
-
ent = entity_without_root(obj, serializer_class, &block)
|
37
|
+
ent = entity_without_root(obj, serializer_class, context_options, &block)
|
31
38
|
if ent
|
32
39
|
link name, :href => ent[:id]
|
33
40
|
@entities[name.to_s.pluralize.to_sym] << ent
|
34
41
|
end
|
35
42
|
end
|
36
43
|
|
37
|
-
def entities(name, collection, serializer_class = nil, &block)
|
44
|
+
def entities(name, collection, serializer_class = nil, context_options = {}, &block)
|
38
45
|
link_name = name.to_s.pluralize.to_sym
|
39
46
|
data[:links][link_name] = []
|
40
47
|
|
41
48
|
collection.each do |obj|
|
42
49
|
@entities[link_name] ||= []
|
43
|
-
ent = entity_without_root(obj, serializer_class, &block)
|
50
|
+
ent = entity_without_root(obj, serializer_class, context_options, &block)
|
44
51
|
if ent
|
45
52
|
data[:links][link_name] << ent[:id]
|
46
53
|
@entities[link_name] << ent
|
@@ -59,8 +66,8 @@ module Oat
|
|
59
66
|
|
60
67
|
attr_reader :root_name
|
61
68
|
|
62
|
-
def entity_without_root(obj, serializer_class = nil, &block)
|
63
|
-
ent = serializer_from_block_or_class(obj, serializer_class, &block)
|
69
|
+
def entity_without_root(obj, serializer_class = nil, context_options = {}, &block)
|
70
|
+
ent = serializer_from_block_or_class(obj, serializer_class, context_options, &block)
|
64
71
|
ent.values.first.first if ent
|
65
72
|
end
|
66
73
|
|
data/lib/oat/adapters/siren.rb
CHANGED
@@ -26,14 +26,14 @@ module Oat
|
|
26
26
|
data[:properties][key] = value
|
27
27
|
end
|
28
28
|
|
29
|
-
def entity(name, obj, serializer_class = nil, &block)
|
30
|
-
ent = serializer_from_block_or_class(obj, serializer_class, &block)
|
29
|
+
def entity(name, obj, serializer_class = nil, context_options = {}, &block)
|
30
|
+
ent = serializer_from_block_or_class(obj, serializer_class, context_options, &block)
|
31
31
|
data[:entities] << ent if ent
|
32
32
|
end
|
33
33
|
|
34
|
-
def entities(name, collection, serializer_class = nil, &block)
|
34
|
+
def entities(name, collection, serializer_class = nil, context_options = {}, &block)
|
35
35
|
collection.each do |obj|
|
36
|
-
entity name, obj, serializer_class, &block
|
36
|
+
entity name, obj, serializer_class, context_options, &block
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
data/lib/oat/serializer.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'support/class_attribute'
|
2
2
|
module Oat
|
3
3
|
class Serializer
|
4
4
|
|
@@ -20,7 +20,7 @@ module Oat
|
|
20
20
|
|
21
21
|
attr_reader :item, :context, :adapter_class, :adapter
|
22
22
|
|
23
|
-
def initialize(item, context =
|
23
|
+
def initialize(item, context = {}, _adapter_class = nil, parent_serializer = nil)
|
24
24
|
@item, @context = item, context
|
25
25
|
@parent_serializer = parent_serializer
|
26
26
|
@adapter_class = _adapter_class || self.class.adapter
|
data/lib/oat/version.rb
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
begin
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
|
+
rescue LoadError
|
4
|
+
module Kernel
|
5
|
+
# Returns the object's singleton class.
|
6
|
+
def singleton_class
|
7
|
+
class << self
|
8
|
+
self
|
9
|
+
end
|
10
|
+
end unless respond_to?(:singleton_class) # exists in 1.9.2
|
11
|
+
|
12
|
+
# class_eval on an object acts like singleton_class.class_eval.
|
13
|
+
def class_eval(*args, &block)
|
14
|
+
singleton_class.class_eval(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Module
|
19
|
+
def remove_possible_method(method)
|
20
|
+
if method_defined?(method) || private_method_defined?(method)
|
21
|
+
remove_method(method)
|
22
|
+
end
|
23
|
+
rescue NameError
|
24
|
+
# If the requested method is defined on a superclass or included module,
|
25
|
+
# method_defined? returns true but remove_method throws a NameError.
|
26
|
+
# Ignore this.
|
27
|
+
end
|
28
|
+
|
29
|
+
def redefine_method(method, &block)
|
30
|
+
remove_possible_method(method)
|
31
|
+
define_method(method, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Class
|
36
|
+
def class_attribute(*attrs)
|
37
|
+
attrs.each do |name|
|
38
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
39
|
+
def self.#{name}() nil end
|
40
|
+
def self.#{name}?() !!#{name} end
|
41
|
+
|
42
|
+
def self.#{name}=(val)
|
43
|
+
singleton_class.class_eval do
|
44
|
+
remove_possible_method(:#{name})
|
45
|
+
define_method(:#{name}) { val }
|
46
|
+
end
|
47
|
+
|
48
|
+
if singleton_class?
|
49
|
+
class_eval do
|
50
|
+
remove_possible_method(:#{name})
|
51
|
+
def #{name}
|
52
|
+
defined?(@#{name}) ? @#{name} : singleton_class.#{name}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
val
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def singleton_class?
|
65
|
+
ancestors.first != self
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/oat.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "activesupport"
|
21
|
+
spec.add_dependency "activesupport"
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.3"
|
23
23
|
spec.add_development_dependency "rake"
|
24
24
|
spec.add_development_dependency "rspec"
|
data/spec/adapters/hal_spec.rb
CHANGED
@@ -5,63 +5,78 @@ describe Oat::Adapters::HAL do
|
|
5
5
|
|
6
6
|
include Fixtures
|
7
7
|
|
8
|
-
|
8
|
+
let(:serializer) { serializer_class.new(user, {:name => 'some_controller'}, Oat::Adapters::HAL) }
|
9
|
+
let(:hash) { serializer.to_hash }
|
9
10
|
|
10
11
|
describe '#to_hash' do
|
11
12
|
it 'produces a HAL-compliant hash' do
|
12
|
-
|
13
|
+
expect(hash).to include(
|
13
14
|
# properties
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
15
|
+
:id => user.id,
|
16
|
+
:name => user.name,
|
17
|
+
:age => user.age,
|
18
|
+
:controller_name => 'some_controller',
|
19
|
+
:message_from_above => nil
|
20
|
+
)
|
21
|
+
|
22
|
+
# links
|
23
|
+
expect(hash.fetch(:_links)).to include(:self => { :href => "http://foo.bar.com/#{user.id}" })
|
24
|
+
|
25
|
+
# HAL Spec says href is REQUIRED
|
26
|
+
expect(hash.fetch(:_links)).not_to include(:empty)
|
27
|
+
expect(hash.fetch(:_embedded)).to include(:manager, :friends)
|
28
|
+
|
29
|
+
# embedded manager
|
30
|
+
expect(hash.fetch(:_embedded).fetch(:manager)).to include(
|
31
|
+
:id => manager.id,
|
32
|
+
:name => manager.name,
|
33
|
+
:age => manager.age,
|
34
|
+
:_links => { :self => { :href => "http://foo.bar.com/#{manager.id}" } }
|
35
|
+
)
|
36
|
+
|
37
|
+
# embedded friends
|
38
|
+
expect(hash.fetch(:_embedded).fetch(:friends).size).to be 1
|
39
|
+
expect(hash.fetch(:_embedded).fetch(:friends).first).to include(
|
40
|
+
:id => friend.id,
|
41
|
+
:name => friend.name,
|
42
|
+
:age => friend.age,
|
43
|
+
:controller_name => 'some_controller',
|
44
|
+
:message_from_above => "Merged into parent's context",
|
45
|
+
:_links => { :self => { :href => "http://foo.bar.com/#{friend.id}" } }
|
46
|
+
)
|
39
47
|
end
|
40
48
|
|
41
49
|
context 'with a nil entity relationship' do
|
42
50
|
let(:manager) { nil }
|
43
51
|
|
44
52
|
it 'produces a HAL-compliant hash' do
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
53
|
+
# properties
|
54
|
+
expect(hash).to include(
|
55
|
+
:id => user.id,
|
56
|
+
:name => user.name,
|
57
|
+
:age => user.age,
|
58
|
+
:controller_name => 'some_controller',
|
59
|
+
:message_from_above => nil
|
60
|
+
)
|
61
|
+
|
62
|
+
expect(hash.fetch(:_links)).to include(:self => { :href => "http://foo.bar.com/#{user.id}" })
|
63
|
+
|
64
|
+
# HAL Spec says href is REQUIRED
|
65
|
+
expect(hash.fetch(:_links)).not_to include(:empty)
|
66
|
+
expect(hash.fetch(:_embedded)).to include(:manager, :friends)
|
67
|
+
|
68
|
+
expect(hash.fetch(:_embedded).fetch(:manager)).to be_nil
|
69
|
+
|
70
|
+
# embedded friends
|
71
|
+
expect(hash.fetch(:_embedded).fetch(:friends).size).to be 1
|
72
|
+
expect(hash.fetch(:_embedded).fetch(:friends).first).to include(
|
73
|
+
:id => friend.id,
|
74
|
+
:name => friend.name,
|
75
|
+
:age => friend.age,
|
76
|
+
:controller_name => 'some_controller',
|
77
|
+
:message_from_above => "Merged into parent's context",
|
78
|
+
:_links => { :self => { :href => "http://foo.bar.com/#{friend.id}" } }
|
79
|
+
)
|
65
80
|
end
|
66
81
|
end
|
67
82
|
end
|
@@ -5,69 +5,97 @@ describe Oat::Adapters::JsonAPI do
|
|
5
5
|
|
6
6
|
include Fixtures
|
7
7
|
|
8
|
-
|
8
|
+
let(:serializer) { serializer_class.new(user, {:name => 'some_controller'}, Oat::Adapters::JsonAPI) }
|
9
|
+
let(:hash) { serializer.to_hash }
|
9
10
|
|
10
11
|
describe '#to_hash' do
|
11
12
|
it 'produces a JSON-API compliant hash' do
|
12
|
-
|
13
|
+
# the user being serialized
|
14
|
+
users = hash.fetch(:users)
|
15
|
+
expect(users.size).to be 1
|
16
|
+
expect(users.first).to include(
|
17
|
+
:id => user.id,
|
18
|
+
:name => user.name,
|
19
|
+
:age => user.age,
|
20
|
+
:controller_name => 'some_controller',
|
21
|
+
:message_from_above => nil
|
22
|
+
)
|
23
|
+
|
24
|
+
expect(users.first.fetch(:links)).to include(
|
25
|
+
:self => "http://foo.bar.com/#{user.id}",
|
26
|
+
# these links are added by embedding entities
|
27
|
+
:manager => manager.id,
|
28
|
+
:friends => [friend.id]
|
29
|
+
)
|
30
|
+
|
13
31
|
# embedded friends
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
32
|
+
linked_friends = hash.fetch(:linked).fetch(:friends)
|
33
|
+
expect(linked_friends.size).to be 1
|
34
|
+
expect(linked_friends.first).to include(
|
35
|
+
:id => friend.id,
|
36
|
+
:name => friend.name,
|
37
|
+
:age => friend.age,
|
38
|
+
:controller_name => 'some_controller',
|
39
|
+
:message_from_above => "Merged into parent's context"
|
40
|
+
)
|
21
41
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
m[:links][:self].should == "http://foo.bar.com/#{manager.id}"
|
28
|
-
end
|
42
|
+
expect(linked_friends.first.fetch(:links)).to include(
|
43
|
+
:self => "http://foo.bar.com/#{friend.id}",
|
44
|
+
:empty => nil,
|
45
|
+
:friends => []
|
46
|
+
)
|
29
47
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
h[:links][:friends].should == [friend.id]
|
40
|
-
end
|
48
|
+
# embedded manager
|
49
|
+
linked_managers = hash.fetch(:linked).fetch(:managers)
|
50
|
+
expect(linked_managers.size).to be 1
|
51
|
+
expect(linked_managers.first).to include(
|
52
|
+
:id => manager.id,
|
53
|
+
:name => manager.name,
|
54
|
+
:age => manager.age,
|
55
|
+
:links => { :self => "http://foo.bar.com/#{manager.id}" }
|
56
|
+
)
|
41
57
|
end
|
42
58
|
|
43
59
|
context 'with a nil entity relationship' do
|
44
60
|
let(:manager) { nil }
|
45
61
|
|
46
62
|
it 'produces a JSON-API compliant hash' do
|
47
|
-
|
63
|
+
# the user being serialized
|
64
|
+
users = hash.fetch(:users)
|
65
|
+
expect(users.size).to be 1
|
66
|
+
expect(users.first).to include(
|
67
|
+
:id => user.id,
|
68
|
+
:name => user.name,
|
69
|
+
:age => user.age,
|
70
|
+
:controller_name => 'some_controller',
|
71
|
+
:message_from_above => nil
|
72
|
+
)
|
73
|
+
|
74
|
+
expect(users.first.fetch(:links)).not_to include(:manager)
|
75
|
+
expect(users.first.fetch(:links)).to include(
|
76
|
+
:self => "http://foo.bar.com/#{user.id}",
|
77
|
+
# these links are added by embedding entities
|
78
|
+
:friends => [friend.id]
|
79
|
+
)
|
48
80
|
# embedded friends
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
81
|
+
linked_friends = hash.fetch(:linked).fetch(:friends)
|
82
|
+
expect(linked_friends.size).to be 1
|
83
|
+
expect(linked_friends.first).to include(
|
84
|
+
:id => friend.id,
|
85
|
+
:name => friend.name,
|
86
|
+
:age => friend.age,
|
87
|
+
:controller_name => 'some_controller',
|
88
|
+
:message_from_above => "Merged into parent's context"
|
89
|
+
)
|
56
90
|
|
57
|
-
|
58
|
-
|
91
|
+
expect(linked_friends.first.fetch(:links)).to include(
|
92
|
+
:self => "http://foo.bar.com/#{friend.id}",
|
93
|
+
:empty => nil,
|
94
|
+
:friends => []
|
95
|
+
)
|
59
96
|
|
60
|
-
|
61
|
-
|
62
|
-
h[:name].should == user.name
|
63
|
-
h[:age].should == user.age
|
64
|
-
h[:controller_name].should == 'some_controller'
|
65
|
-
# links
|
66
|
-
h[:links][:self].should == "http://foo.bar.com/#{user.id}"
|
67
|
-
# these links are added by embedding entities
|
68
|
-
h[:links].should_not include(:manager)
|
69
|
-
h[:links][:friends].should == [friend.id]
|
70
|
-
end
|
97
|
+
# embedded manager
|
98
|
+
hash.fetch(:linked).fetch(:managers).should be_empty
|
71
99
|
end
|
72
100
|
end
|
73
101
|
end
|
data/spec/adapters/siren_spec.rb
CHANGED
@@ -5,82 +5,110 @@ describe Oat::Adapters::Siren do
|
|
5
5
|
|
6
6
|
include Fixtures
|
7
7
|
|
8
|
-
|
8
|
+
let(:serializer) { serializer_class.new(user, {:name => 'some_controller'}, Oat::Adapters::Siren) }
|
9
|
+
let(:hash) { serializer.to_hash }
|
9
10
|
|
10
11
|
describe '#to_hash' do
|
11
12
|
it 'produces a Siren-compliant hash' do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
13
|
+
expect(hash.fetch(:class)).to match_array(['user'])
|
14
|
+
|
15
|
+
expect(hash.fetch(:properties)).to include(
|
16
|
+
:id => user.id,
|
17
|
+
:name => user.name,
|
18
|
+
:age => user.age,
|
19
|
+
:controller_name => 'some_controller',
|
20
|
+
:message_from_above => nil
|
21
|
+
)
|
22
|
+
|
23
|
+
expect(hash.fetch(:links).size).to be 2
|
24
|
+
expect(hash.fetch(:links)).to include(
|
25
|
+
{ :rel => [:self], :href => "http://foo.bar.com/#{user.id}" },
|
26
|
+
{ :rel => [:empty], :href => nil }
|
27
|
+
)
|
28
|
+
|
29
|
+
expect(hash.fetch(:entities).size).to be 2
|
30
|
+
|
31
|
+
# embedded friends
|
32
|
+
embedded_friends = hash.fetch(:entities).select{ |o| o[:class].include? "user" }
|
33
|
+
expect(embedded_friends.size).to be 1
|
34
|
+
expect(embedded_friends.first.fetch(:properties)).to include(
|
35
|
+
:id => friend.id,
|
36
|
+
:name => friend.name,
|
37
|
+
:age => friend.age,
|
38
|
+
:controller_name => 'some_controller',
|
39
|
+
:message_from_above => "Merged into parent's context"
|
40
|
+
)
|
41
|
+
expect(embedded_friends.first.fetch(:links).first).to include(
|
42
|
+
:rel => [:self],
|
43
|
+
:href => "http://foo.bar.com/#{friend.id}"
|
44
|
+
)
|
45
|
+
|
46
|
+
embedded_managers = hash.fetch(:entities).select{ |o| o[:class].include? "manager" }
|
47
|
+
expect(embedded_managers.size).to be 1
|
48
|
+
expect(embedded_managers.first.fetch(:properties)).to include(
|
49
|
+
:id => manager.id,
|
50
|
+
:name => manager.name,
|
51
|
+
:age => manager.age
|
52
|
+
)
|
53
|
+
expect(embedded_managers.first.fetch(:links).first).to include(
|
54
|
+
:rel => [:self],
|
55
|
+
:href => "http://foo.bar.com/#{manager.id}"
|
56
|
+
)
|
57
|
+
|
58
|
+
# action close_account
|
59
|
+
actions = hash.fetch(:actions)
|
60
|
+
expect(actions.size).to be 1
|
61
|
+
expect(actions.first).to include(
|
62
|
+
:name => :close_account,
|
63
|
+
:href => "http://foo.bar.com/#{user.id}/close_account",
|
64
|
+
:class => ['danger', 'irreversible'],
|
65
|
+
:method => 'DELETE'
|
66
|
+
)
|
67
|
+
expect(actions.first.fetch(:fields)).to include(
|
68
|
+
:name => :current_password,
|
69
|
+
:type => :password
|
70
|
+
)
|
54
71
|
end
|
55
72
|
|
56
73
|
context 'with a nil entity relationship' do
|
57
74
|
let(:manager) { nil }
|
58
75
|
|
59
76
|
it 'produces a Siren-compliant hash' do
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
expect(hash.fetch(:class)).to match_array(['user'])
|
78
|
+
|
79
|
+
expect(hash.fetch(:properties)).to include(
|
80
|
+
:id => user.id,
|
81
|
+
:name => user.name,
|
82
|
+
:age => user.age,
|
83
|
+
:controller_name => 'some_controller',
|
84
|
+
:message_from_above => nil
|
85
|
+
)
|
86
|
+
|
87
|
+
expect(hash.fetch(:links).size).to be 2
|
88
|
+
expect(hash.fetch(:links)).to include(
|
89
|
+
{ :rel => [:self], :href => "http://foo.bar.com/#{user.id}" },
|
90
|
+
{ :rel => [:empty], :href => nil }
|
91
|
+
)
|
92
|
+
|
93
|
+
expect(hash.fetch(:entities).size).to be 1
|
94
|
+
|
95
|
+
# embedded friends
|
96
|
+
embedded_friends = hash.fetch(:entities).select{ |o| o[:class].include? "user" }
|
97
|
+
expect(embedded_friends.size).to be 1
|
98
|
+
expect(embedded_friends.first.fetch(:properties)).to include(
|
99
|
+
:id => friend.id,
|
100
|
+
:name => friend.name,
|
101
|
+
:age => friend.age,
|
102
|
+
:controller_name => 'some_controller',
|
103
|
+
:message_from_above => "Merged into parent's context"
|
104
|
+
)
|
105
|
+
expect(embedded_friends.first.fetch(:links).first).to include(
|
106
|
+
:rel => [:self],
|
107
|
+
:href => "http://foo.bar.com/#{friend.id}"
|
108
|
+
)
|
109
|
+
|
110
|
+
embedded_managers = hash.fetch(:entities).select{ |o| o[:class].include? "manager" }
|
111
|
+
expect(embedded_managers.size).to be 0
|
84
112
|
end
|
85
113
|
end
|
86
114
|
end
|
data/spec/fixtures.rb
CHANGED
@@ -18,9 +18,10 @@ module Fixtures
|
|
18
18
|
map_properties :name, :age
|
19
19
|
properties do |attrs|
|
20
20
|
attrs.controller_name context[:name]
|
21
|
+
attrs.message_from_above context[:message]
|
21
22
|
end
|
22
23
|
|
23
|
-
entities :friends, item.friends, klass
|
24
|
+
entities :friends, item.friends, klass, :message => "Merged into parent's context"
|
24
25
|
|
25
26
|
entity :manager, item.manager do |manager, s|
|
26
27
|
s.type 'manager'
|
data/spec/serializer_spec.rb
CHANGED
@@ -52,17 +52,31 @@ describe Oat::Serializer do
|
|
52
52
|
Oat::VERSION.should_not be_nil
|
53
53
|
end
|
54
54
|
|
55
|
+
describe "#context" do
|
56
|
+
it "is a hash by default" do
|
57
|
+
expect(@sc.new(user1).context).to be_a Hash
|
58
|
+
end
|
59
|
+
|
60
|
+
it "can be set like an options hash" do
|
61
|
+
serializer = @sc.new(user1, :controller => double(:name => "Fancy"))
|
62
|
+
expect(serializer.context.fetch(:controller).name).to eq "Fancy"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
55
66
|
describe '#to_hash' do
|
56
67
|
it 'builds Hash from item and context with attributes as defined in adapter' do
|
57
68
|
serializer = @sc.new(user1, :name => 'some_controller')
|
58
|
-
serializer.to_hash.
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
69
|
+
expect(serializer.to_hash.fetch(:attributes)).to include(
|
70
|
+
:special => 'Hello',
|
71
|
+
:id => user1.id,
|
72
|
+
:name => user1.name,
|
73
|
+
:age => user1.age,
|
74
|
+
:controller_name => 'some_controller'
|
75
|
+
)
|
76
|
+
|
77
|
+
expect(serializer.to_hash.fetch(:links)).to include(
|
78
|
+
:self => "http://foo.bar.com/#{user1.id}"
|
79
|
+
)
|
66
80
|
end
|
67
81
|
end
|
68
82
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,6 +83,12 @@ files:
|
|
83
83
|
- LICENSE.txt
|
84
84
|
- README.md
|
85
85
|
- Rakefile
|
86
|
+
- gemfiles/Gemfile.as-1.4.4
|
87
|
+
- gemfiles/Gemfile.as-2.3.x
|
88
|
+
- gemfiles/Gemfile.as-3.0.x
|
89
|
+
- gemfiles/Gemfile.as-3.1.x
|
90
|
+
- gemfiles/Gemfile.as-3.2.x
|
91
|
+
- gemfiles/Gemfile.as-4.0.x
|
86
92
|
- lib/oat.rb
|
87
93
|
- lib/oat/adapter.rb
|
88
94
|
- lib/oat/adapters/hal.rb
|
@@ -91,6 +97,7 @@ files:
|
|
91
97
|
- lib/oat/props.rb
|
92
98
|
- lib/oat/serializer.rb
|
93
99
|
- lib/oat/version.rb
|
100
|
+
- lib/support/class_attribute.rb
|
94
101
|
- oat.gemspec
|
95
102
|
- spec/adapters/hal_spec.rb
|
96
103
|
- spec/adapters/json_api_spec.rb
|