yaks 0.0.0 → 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 +13 -5
- data/.gitignore +1 -0
- data/.travis.yml +23 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +32 -6
- data/README.md +182 -80
- data/examples/hal01.rb +126 -0
- data/examples/jsonapi01.rb +68 -0
- data/examples/jsonapi02.rb +62 -0
- data/examples/jsonapi03.rb +86 -0
- data/lib/yaks.rb +55 -25
- data/lib/yaks/collection_mapper.rb +33 -0
- data/lib/yaks/collection_resource.rb +65 -0
- data/lib/yaks/default_policy.rb +13 -0
- data/lib/yaks/hal_serializer.rb +59 -0
- data/lib/yaks/json_api_serializer.rb +59 -0
- data/lib/yaks/link_lookup.rb +23 -0
- data/lib/yaks/mapper.rb +59 -0
- data/lib/yaks/mapper/association.rb +43 -0
- data/lib/yaks/mapper/class_methods.rb +36 -0
- data/lib/yaks/mapper/config.rb +79 -0
- data/lib/yaks/mapper/has_many.rb +15 -0
- data/lib/yaks/mapper/has_one.rb +10 -0
- data/lib/yaks/mapper/link.rb +74 -0
- data/lib/yaks/mapper/lookup.rb +19 -0
- data/lib/yaks/mapper/map_links.rb +17 -0
- data/lib/yaks/null_resource.rb +28 -0
- data/lib/yaks/primitivize.rb +34 -15
- data/lib/yaks/profile_registry.rb +60 -0
- data/lib/yaks/rel_registry.rb +20 -0
- data/lib/yaks/resource.rb +28 -0
- data/lib/yaks/resource/link.rb +21 -0
- data/lib/yaks/serializer.rb +11 -53
- data/lib/yaks/shared_options.rb +15 -0
- data/lib/yaks/util.rb +76 -5
- data/lib/yaks/version.rb +1 -1
- data/spec/integration/map_to_resource_spec.rb +30 -0
- data/spec/json/hal_plant_collection.json +34 -0
- data/spec/spec_helper.rb +10 -1
- data/spec/support/friends_mapper.rb +29 -0
- data/spec/support/pet_mapper.rb +5 -0
- data/spec/support/pet_peeve_mapper.rb +3 -0
- data/spec/support/serializers.rb +11 -11
- data/spec/support/shared_contexts.rb +47 -0
- data/spec/support/shorthands.rb +22 -0
- data/spec/yaks/collection_resource_spec.rb +9 -0
- data/spec/yaks/hal_serializer_spec.rb +9 -0
- data/spec/yaks/mapper/association_spec.rb +21 -0
- data/spec/yaks/mapper/class_methods_spec.rb +28 -0
- data/spec/yaks/mapper/config_spec.rb +77 -0
- data/spec/yaks/mapper/has_one_spec.rb +16 -0
- data/spec/yaks/mapper/link_spec.rb +42 -0
- data/spec/yaks/mapper/map_links_spec.rb +46 -0
- data/spec/yaks/mapper_spec.rb +47 -0
- data/spec/yaks/resource_spec.rb +23 -0
- data/yaks.gemspec +6 -3
- metadata +115 -27
- data/lib/yaks/dumper.rb +0 -23
- data/lib/yaks/fold_ams_compat.rb +0 -33
- data/lib/yaks/fold_json_api.rb +0 -61
- data/lib/yaks/serializable_association.rb +0 -21
- data/lib/yaks/serializable_collection.rb +0 -10
- data/lib/yaks/serializable_object.rb +0 -18
- data/lib/yaks/serializer/class_methods.rb +0 -76
- data/spec/integration_spec.rb +0 -57
- data/spec/yaks/json_api_folder_spec.rb +0 -63
- data/spec/yaks/serializer_spec.rb +0 -12
@@ -0,0 +1,28 @@
|
|
1
|
+
module Yaks
|
2
|
+
class NullResource
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def each
|
6
|
+
return to_enum unless block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def attributes
|
10
|
+
Yaks::Hash()
|
11
|
+
end
|
12
|
+
|
13
|
+
def links
|
14
|
+
Yaks::List()
|
15
|
+
end
|
16
|
+
|
17
|
+
def subresources
|
18
|
+
Yaks::Hash()
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](*)
|
22
|
+
end
|
23
|
+
|
24
|
+
def collection?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/yaks/primitivize.rb
CHANGED
@@ -1,24 +1,43 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Primitivize
|
3
|
+
include Concord.new(:object)
|
4
|
+
|
5
|
+
# TODO Global config, make this extensible in a per-instance way
|
6
|
+
MAPPINGS = {}
|
7
|
+
|
3
8
|
def self.call(object)
|
4
|
-
new
|
9
|
+
new(object).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.map(*types, &blk)
|
13
|
+
types.each do |type|
|
14
|
+
MAPPINGS[type] = blk
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
map String, TrueClass, FalseClass, NilClass, Numeric do
|
19
|
+
object
|
20
|
+
end
|
21
|
+
|
22
|
+
map Symbol do
|
23
|
+
object.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
map Hash, Hamster::Hash do
|
27
|
+
object.to_enum(:each).with_object({}) do |(key, value), output|
|
28
|
+
output[self.class.(key)] = self.class.(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
map Enumerable, Hamster::Enumerable do
|
33
|
+
object.map(&self.class.method(:call)).to_a
|
5
34
|
end
|
6
35
|
|
7
|
-
def call
|
8
|
-
|
9
|
-
|
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"
|
36
|
+
def call
|
37
|
+
MAPPINGS.each do |pattern, block|
|
38
|
+
return instance_eval(&block) if pattern === object
|
21
39
|
end
|
40
|
+
raise "don't know how to turn #{object.class} (#{object.inspect}) into a primitive"
|
22
41
|
end
|
23
42
|
end
|
24
43
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Yaks
|
3
|
+
# RFC6906 The "profile" link relation http://tools.ietf.org/search/rfc6906
|
4
|
+
class ProfileRegistry
|
5
|
+
class << self
|
6
|
+
include Util
|
7
|
+
|
8
|
+
def create(&blk)
|
9
|
+
blk ||= ->{}
|
10
|
+
Class.new(self).tap(&σ(:instance_eval, &blk)).new
|
11
|
+
end
|
12
|
+
|
13
|
+
def profile(type, uri)
|
14
|
+
profiles {|reg| reg.put(type, uri)}
|
15
|
+
end
|
16
|
+
|
17
|
+
def profiles
|
18
|
+
@profiles ||= Yaks::Hash()
|
19
|
+
@profiles = yield(@profiles) if block_given?
|
20
|
+
@profiles
|
21
|
+
end
|
22
|
+
|
23
|
+
def inherited(child)
|
24
|
+
child.profiles { @profiles }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_by_type(type)
|
29
|
+
self.class.profiles[type]
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_by_uri(by_uri)
|
33
|
+
self.class.profiles.detect {|type, uri| uri == by_uri}.first
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class NullProfileRegistry
|
38
|
+
def find_by_type(type)
|
39
|
+
type.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_by_uri(uri)
|
43
|
+
uri.to_sym
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class TemplateProfileRegistry
|
48
|
+
def initialize(template)
|
49
|
+
@template = URITemplate.new(template)
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_by_type(type)
|
53
|
+
@template.expand(type: type)
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_by_uri(uri)
|
57
|
+
@template.extract(uri).fetch(:type) { uri }.to_sym
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Yaks
|
2
|
+
class RelRegistry
|
3
|
+
end
|
4
|
+
|
5
|
+
class TemplateRelRegistry < RelRegistry
|
6
|
+
def initialize(template)
|
7
|
+
@template = URITemplate.new(template)
|
8
|
+
end
|
9
|
+
|
10
|
+
def lookup(source, destination)
|
11
|
+
@template.expand(:src => source, :dest => destination)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NullRelRegistry < TemplateRelRegistry
|
16
|
+
def initialize
|
17
|
+
super('rel:src={src}&dest={dest}')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Resource
|
3
|
+
include Equalizer.new(:links, :attributes, :subresources)
|
4
|
+
include Enumerable, LinkLookup
|
5
|
+
|
6
|
+
attr_reader :attributes, :links, :subresources
|
7
|
+
|
8
|
+
def initialize(attributes, links, subresources)
|
9
|
+
@attributes = Yaks::Hash(attributes)
|
10
|
+
@links = Yaks::List(links)
|
11
|
+
@subresources = Yaks::Hash(subresources)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](attr)
|
15
|
+
attributes[attr]
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
return to_enum unless block_given?
|
20
|
+
yield self
|
21
|
+
end
|
22
|
+
|
23
|
+
def collection?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Resource
|
3
|
+
class Link
|
4
|
+
include Equalizer.new(:rel, :uri, :options)
|
5
|
+
|
6
|
+
attr_reader :rel, :uri, :options
|
7
|
+
|
8
|
+
def initialize(rel, uri, options)
|
9
|
+
@rel, @uri, @options = rel, uri, options
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
options[:name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def templated?
|
17
|
+
options.fetch(:templated) { false }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/yaks/serializer.rb
CHANGED
@@ -1,64 +1,22 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Serializer
|
3
|
-
|
4
|
-
|
3
|
+
extend Forwardable
|
4
|
+
include Util, SharedOptions
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
attr_reader :resource, :options
|
7
|
+
def_delegators :resource, :links, :attributes, :subresources
|
8
8
|
|
9
|
-
|
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
|
9
|
+
protected :resource, :links, :attributes, :subresources, :options
|
29
10
|
|
30
|
-
def
|
31
|
-
|
11
|
+
def initialize(resource, options = {})
|
12
|
+
@resource = resource
|
13
|
+
@options = YAKS_DEFAULT_OPTIONS.merge(options)
|
32
14
|
end
|
33
15
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
serializable_attributes(object),
|
38
|
-
serializable_associations(object)
|
39
|
-
)
|
16
|
+
def profile_name
|
17
|
+
(profile = resource.profile) &&
|
18
|
+
profile_registry.find_by_uri(profile)
|
40
19
|
end
|
41
20
|
|
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
21
|
end
|
64
22
|
end
|
data/lib/yaks/util.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module Yaks
|
2
3
|
module Util
|
3
4
|
extend self
|
@@ -7,14 +8,84 @@ module Yaks
|
|
7
8
|
|
8
9
|
def underscore(str)
|
9
10
|
str.gsub(/::/, '/')
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
.gsub(/(?<!^|\/)([A-Z])(?=[a-z$])|(?<=[a-z])([A-Z])/, '_\1\2')
|
12
|
+
.tr("-", "_")
|
13
|
+
.downcase
|
13
14
|
end
|
14
15
|
|
15
16
|
def camelize(str)
|
16
|
-
str.gsub(/\/(.?)/)
|
17
|
-
|
17
|
+
str.gsub(/\/(.?)/) { "::#{ $1.upcase }" }
|
18
|
+
.gsub!(/(?:^|_)(.)/) { $1.upcase }
|
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
|
+
|
33
|
+
# Turn what is maybe a Proc into its result (or itself)
|
34
|
+
#
|
35
|
+
# When input can be either a value or a proc that returns a value,
|
36
|
+
# this conversion function can be used to resolve the thing to a
|
37
|
+
# value.
|
38
|
+
#
|
39
|
+
# The proc can be evaluated (instance_evaled) in a certain context,
|
40
|
+
# or evaluated as a closure.
|
41
|
+
#
|
42
|
+
# @param [Object|Proc] maybe_proc
|
43
|
+
# A proc or a plain value
|
44
|
+
# @param [Object] context
|
45
|
+
# (optional) A context used to instance_eval the proc
|
46
|
+
def Resolve(maybe_proc, context = nil)
|
47
|
+
if maybe_proc.respond_to?(:to_proc)
|
48
|
+
if context
|
49
|
+
if maybe_proc.to_proc.arity > 0
|
50
|
+
context.instance_eval(&maybe_proc)
|
51
|
+
else
|
52
|
+
# In case it's a lambda with zero arity instance_eval fails
|
53
|
+
context.instance_exec(&maybe_proc)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
maybe_proc.()
|
57
|
+
end
|
58
|
+
else
|
59
|
+
maybe_proc
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def curry_method(name)
|
64
|
+
method(name).to_proc.curry
|
65
|
+
end
|
66
|
+
alias μ curry_method
|
67
|
+
|
68
|
+
def identity_function
|
69
|
+
->(x) {x}
|
70
|
+
end
|
71
|
+
alias ι identity_function
|
72
|
+
|
73
|
+
def juxt(*procs)
|
74
|
+
->(*args) { procs.map &σ(:call, *args) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def curry_symbol(symbol, *args, &blk)
|
78
|
+
->(obj) { obj.method(symbol).to_proc.curry.(*args, &blk) }
|
79
|
+
end
|
80
|
+
alias σ curry_symbol
|
81
|
+
alias send_with_args curry_symbol
|
82
|
+
|
83
|
+
def extract_options(args)
|
84
|
+
if args.last.is_a? Hash
|
85
|
+
[args.take(args.count-1), args.last]
|
86
|
+
else
|
87
|
+
[args, {}]
|
88
|
+
end
|
18
89
|
end
|
19
90
|
|
20
91
|
end
|
data/lib/yaks/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Mapping domain models to Resource objects' do
|
4
|
+
include_context 'fixtures'
|
5
|
+
include_context 'shorthands'
|
6
|
+
|
7
|
+
subject { mapper.to_resource }
|
8
|
+
let(:mapper) { FriendMapper.new(john) }
|
9
|
+
|
10
|
+
|
11
|
+
it { should be_a Yaks::Resource }
|
12
|
+
its(:attributes) { should eq Yaks::Hash(id: 1, name: 'john') }
|
13
|
+
its(:links) { should eq Yaks::List(
|
14
|
+
resource_link[:profile, 'friend'],
|
15
|
+
resource_link[:copyright, '/api/copyright/2024']
|
16
|
+
)}
|
17
|
+
|
18
|
+
its(:subresources) {
|
19
|
+
should eq Yaks::Hash(
|
20
|
+
"rel:src=friend&dest=pet_peeve" => resource[{id: 4, type: 'parsing with regexps'}, [resource_link[:profile, 'pet_peeve']]],
|
21
|
+
"rel:src=friend&dest=pets" => Yaks::CollectionResource.new(
|
22
|
+
[resource_link[:profile, 'pet']],
|
23
|
+
[
|
24
|
+
resource[{:id => 2, :species => "dog", :name => "boingboing"}, [resource_link[:profile, 'pet']]],
|
25
|
+
resource[{:id => 3, :species => "cat", :name => "wassup"}, [resource_link[:profile, 'pet']]]
|
26
|
+
]
|
27
|
+
)
|
28
|
+
)
|
29
|
+
}
|
30
|
+
end
|