yaks 0.8.3 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -0
- data/lib/yaks.rb +4 -2
- data/lib/yaks/attributes.rb +1 -1
- data/lib/yaks/builder.rb +10 -10
- data/lib/yaks/collection_mapper.rb +1 -1
- data/lib/yaks/config.rb +1 -1
- data/lib/yaks/configurable.rb +41 -2
- data/lib/yaks/format/json_api.rb +21 -35
- data/lib/yaks/mapper.rb +2 -1
- data/lib/yaks/mapper/form.rb +6 -29
- data/lib/yaks/mapper/form/config.rb +37 -3
- data/lib/yaks/mapper/form/dynamic_field.rb +17 -0
- data/lib/yaks/mapper/form/field.rb +13 -10
- data/lib/yaks/mapper/form/field/option.rb +2 -1
- data/lib/yaks/mapper/form/fieldset.rb +7 -29
- data/lib/yaks/reader/hal.rb +0 -1
- data/lib/yaks/reader/json_api.rb +49 -0
- data/lib/yaks/version.rb +1 -1
- data/spec/acceptance/acceptance_spec.rb +1 -0
- data/spec/json/confucius.json_api.json +19 -20
- data/spec/unit/yaks/attributes_spec.rb +37 -1
- data/spec/unit/yaks/builder_spec.rb +34 -3
- data/spec/unit/yaks/collection_mapper_spec.rb +20 -1
- data/spec/unit/yaks/collection_resource_spec.rb +7 -0
- data/spec/unit/yaks/configurable_spec.rb +84 -0
- data/spec/unit/yaks/format/json_api_spec.rb +91 -2
- data/spec/unit/yaks/mapper/form/field_spec.rb +63 -5
- data/spec/unit/yaks/mapper/form/fieldset_spec.rb +30 -0
- data/spec/unit/yaks/mapper/form_spec.rb +25 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1351b2c236dc9fb68ff5bbe9c365f7addb38cad
|
4
|
+
data.tar.gz: d8baafb1e55bc4bf5e926e012dac9bd20d7284f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f5732d430846954a651d3aff1145cf07610098d23c0ba2b28bfe7626b3e3c2c3c741d6f50d84459360a1986d820f10604d115bd3336ccad6dac2386bf808142
|
7
|
+
data.tar.gz: 7ce7ea4c6bdf3c0e0fc2ae11ecee61813c4be3b8eefe01316153ba427d6522496f2d92e3efcbf2c29f60bea9db3a93b158caf4d667472835438c284ae14df08f
|
data/README.md
CHANGED
@@ -12,8 +12,15 @@
|
|
12
12
|
|
13
13
|
# Yaks
|
14
14
|
|
15
|
+
![](logo.png)
|
16
|
+
|
15
17
|
The library that understands hypermedia.
|
16
18
|
|
19
|
+
Yaks takes your data and transforms it into hypermedia formats such as
|
20
|
+
HAL, JSON-API, or HTML. It allows you to build APIs that are
|
21
|
+
discoverable and browsable. It is built from the ground up around
|
22
|
+
linked resources, a concept central to the architecture of the web.
|
23
|
+
|
17
24
|
Yaks consists of a resource representation that is independent of any
|
18
25
|
output type. A Yaks mapper transforms an object into a resource, which
|
19
26
|
can then be serialized into whichever output format the client
|
@@ -482,6 +489,11 @@ implemented. It is also not the most suitable format for Yaks
|
|
482
489
|
feature-set due to its strong convention-driven nature and weak
|
483
490
|
support for hypermedia.
|
484
491
|
|
492
|
+
At this time, The JSON-API specification has not reached a 1.0 release.
|
493
|
+
Some changes to the Yaks JSON-API formatter may still be required
|
494
|
+
before it is completely compatible with the latest version of the
|
495
|
+
specification.
|
496
|
+
|
485
497
|
If you would like to see better JSON-API support, get in touch. We
|
486
498
|
might be able to work something out.
|
487
499
|
|
data/lib/yaks.rb
CHANGED
@@ -12,11 +12,11 @@ require 'rack/accept'
|
|
12
12
|
|
13
13
|
require 'yaks/version'
|
14
14
|
require 'yaks/util'
|
15
|
+
require 'yaks/attributes'
|
15
16
|
require 'yaks/configurable'
|
16
17
|
require 'yaks/fp'
|
17
18
|
require 'yaks/fp/callable'
|
18
19
|
require 'yaks/primitivize'
|
19
|
-
require 'yaks/attributes'
|
20
20
|
require 'yaks/builder'
|
21
21
|
require 'yaks/errors'
|
22
22
|
|
@@ -75,10 +75,11 @@ require 'yaks/mapper/has_one'
|
|
75
75
|
require 'yaks/mapper/has_many'
|
76
76
|
require 'yaks/mapper/attribute'
|
77
77
|
require 'yaks/mapper/link'
|
78
|
-
require 'yaks/mapper/form/config'
|
79
78
|
require 'yaks/mapper/form/field/option'
|
80
79
|
require 'yaks/mapper/form/field'
|
81
80
|
require 'yaks/mapper/form/fieldset'
|
81
|
+
require 'yaks/mapper/form/dynamic_field'
|
82
|
+
require 'yaks/mapper/form/config'
|
82
83
|
require 'yaks/mapper/form'
|
83
84
|
require 'yaks/mapper/config'
|
84
85
|
require 'yaks/mapper'
|
@@ -98,5 +99,6 @@ require 'yaks/format/json_api'
|
|
98
99
|
require 'yaks/format/collection_json'
|
99
100
|
|
100
101
|
require 'yaks/reader/hal'
|
102
|
+
require 'yaks/reader/json_api'
|
101
103
|
require 'yaks/pipeline'
|
102
104
|
require 'yaks/runner'
|
data/lib/yaks/attributes.rb
CHANGED
data/lib/yaks/builder.rb
CHANGED
@@ -7,8 +7,8 @@ module Yaks
|
|
7
7
|
#
|
8
8
|
# # This code
|
9
9
|
# Form.create(:search)
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# .method("POST")
|
11
|
+
# .action("/search")
|
12
12
|
#
|
13
13
|
# # Can be written as
|
14
14
|
# Builder.new(Form, [:method, :action]).create(:search) do
|
@@ -19,6 +19,13 @@ module Yaks
|
|
19
19
|
class Builder
|
20
20
|
include Configurable
|
21
21
|
|
22
|
+
def initialize(klass, methods = [], &block)
|
23
|
+
@klass = klass
|
24
|
+
@methods = methods
|
25
|
+
def_forward *methods if methods.any?
|
26
|
+
instance_eval(&block) if block
|
27
|
+
end
|
28
|
+
|
22
29
|
def create(*args, &block)
|
23
30
|
build(@klass.create(*args), &block)
|
24
31
|
end
|
@@ -29,15 +36,8 @@ module Yaks
|
|
29
36
|
@config
|
30
37
|
end
|
31
38
|
|
32
|
-
def initialize(klass, methods = [], &block)
|
33
|
-
@klass = klass
|
34
|
-
@methods = methods
|
35
|
-
def_forward *methods if methods.any?
|
36
|
-
instance_eval(&block) if block
|
37
|
-
end
|
38
|
-
|
39
39
|
def inspect
|
40
|
-
"#<Builder #{@klass} #{@methods
|
40
|
+
"#<Builder #{@klass} #{@methods}>"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
data/lib/yaks/config.rb
CHANGED
@@ -18,7 +18,7 @@ module Yaks
|
|
18
18
|
|
19
19
|
deprecated_alias :namespace, :mapper_namespace
|
20
20
|
|
21
|
-
def format_options(format
|
21
|
+
def format_options(format, options = Undefined)
|
22
22
|
with(format_options_hash: format_options_hash.merge(format => options))
|
23
23
|
end
|
24
24
|
|
data/lib/yaks/configurable.rb
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
module Yaks
|
2
|
+
# A "Configurable" class is one that keeps a configuration in a
|
3
|
+
# separate immutable object, of type class::Config. say you have
|
4
|
+
#
|
5
|
+
# class MyMapper < Yaks::Mapper
|
6
|
+
# # use yaks configuration DSL in here
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# The links, associations, etc, that you set up for MyMapper, will
|
10
|
+
# be available in MyMapper.config, which is an instance of
|
11
|
+
# Yaks::Mapper::Config.
|
12
|
+
#
|
13
|
+
# Each configuration step, like `link`, `has_many`, will replace
|
14
|
+
# MyMapper.config with an updated version, discarding the old
|
15
|
+
# config.
|
16
|
+
#
|
17
|
+
# By extending Configurable, a number of "macros" become available
|
18
|
+
# to describe the DSL that subclasses can use. See the docs for
|
19
|
+
# `def_set`. `def_forward`, and `def_add`.
|
2
20
|
module Configurable
|
3
21
|
attr_accessor :config
|
4
22
|
|
@@ -10,14 +28,28 @@ module Yaks
|
|
10
28
|
child.config = config
|
11
29
|
end
|
12
30
|
|
31
|
+
# Create a DSL method to set a certain config property. The
|
32
|
+
# generated method will take either a plain value, or a block,
|
33
|
+
# which will be captured and stored instead.
|
13
34
|
def def_set(*method_names)
|
14
35
|
method_names.each do |method_name|
|
15
|
-
define_singleton_method method_name do |arg|
|
16
|
-
|
36
|
+
define_singleton_method method_name do |arg = Undefined, &block|
|
37
|
+
if arg == Undefined && block
|
38
|
+
self.config = config.update(method_name => block)
|
39
|
+
elsif arg == Undefined
|
40
|
+
raise ArgumentError, "wrong number of arguments (0 for 1)"
|
41
|
+
else
|
42
|
+
self.config = config.update(method_name => arg)
|
43
|
+
end
|
17
44
|
end
|
18
45
|
end
|
19
46
|
end
|
20
47
|
|
48
|
+
# Forward a method to the config object. This assumes the method
|
49
|
+
# will return an updated config instance.
|
50
|
+
#
|
51
|
+
# Either takes a list of methods to forward, or a mapping (hash)
|
52
|
+
# of source to destination method name.
|
21
53
|
def def_forward(method_names, *args)
|
22
54
|
unless method_names.is_a? Hash
|
23
55
|
def_forward([method_names, *args].map{|name| {name => name}}.inject(:merge))
|
@@ -30,6 +62,13 @@ module Yaks
|
|
30
62
|
end
|
31
63
|
end
|
32
64
|
|
65
|
+
# Generate a DSL method that creates a certain type of domain
|
66
|
+
# object, and adds it to a list on the config.
|
67
|
+
#
|
68
|
+
# def_add :fieldset, create: Fieldset, append_to: :fields
|
69
|
+
#
|
70
|
+
# This will generate a `fieldset` method, which will call
|
71
|
+
# `Fieldset.create`, and append the result to `config.fields`
|
33
72
|
def def_add(name, options)
|
34
73
|
define_singleton_method name do |*args, &block|
|
35
74
|
defaults = options.fetch(:defaults, {})
|
data/lib/yaks/format/json_api.rb
CHANGED
@@ -7,12 +7,12 @@ module Yaks
|
|
7
7
|
|
8
8
|
# @param [Yaks::Resource] resource
|
9
9
|
# @return [Hash]
|
10
|
-
def call(resource,
|
10
|
+
def call(resource, _env = nil)
|
11
11
|
main_collection = resource.seq.map(&method(:serialize_resource))
|
12
12
|
|
13
|
-
{
|
14
|
-
linked = resource.seq.each_with_object(
|
15
|
-
serialize_linked_subresources(res.subresources,
|
13
|
+
{ data: main_collection }.tap do |serialized|
|
14
|
+
linked = resource.seq.each_with_object([]) do |res, array|
|
15
|
+
serialize_linked_subresources(res.subresources, array)
|
16
16
|
end
|
17
17
|
serialized.merge!(linked: linked) unless linked.empty?
|
18
18
|
end
|
@@ -21,11 +21,10 @@ module Yaks
|
|
21
21
|
# @param [Yaks::Resource] resource
|
22
22
|
# @return [Hash]
|
23
23
|
def serialize_resource(resource)
|
24
|
-
result = resource.attributes
|
24
|
+
result = {type: pluralize(resource.type).to_sym}.merge(resource.attributes)
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
26
|
+
links = serialize_links(resource.subresources)
|
27
|
+
result[:links] = links unless links.empty?
|
29
28
|
|
30
29
|
if resource.self_link && !result.key?(:href)
|
31
30
|
result[:href] = resource.self_link.uri
|
@@ -39,29 +38,32 @@ module Yaks
|
|
39
38
|
def serialize_links(subresources)
|
40
39
|
subresources.each_with_object({}) do |resource, hsh|
|
41
40
|
next if resource.null_resource?
|
42
|
-
|
43
|
-
hsh[key] = serialize_link(resource)
|
41
|
+
hsh[resource.type] = serialize_link(resource)
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
45
|
# @param [Yaks::Resource] resource
|
48
46
|
# @return [Array, String]
|
49
47
|
def serialize_link(resource)
|
50
|
-
resource.collection?
|
48
|
+
if resource.collection?
|
49
|
+
{type: resource.type, ids: resource.map(&send_with_args(:[], :id))}
|
50
|
+
else
|
51
|
+
{type: pluralize(resource.type), id: resource[:id]}
|
52
|
+
end
|
51
53
|
end
|
52
54
|
|
53
55
|
# @param [Hash] subresources
|
54
|
-
# @param [
|
55
|
-
# @return [
|
56
|
-
def serialize_linked_subresources(subresources,
|
56
|
+
# @param [Array] array
|
57
|
+
# @return [Array]
|
58
|
+
def serialize_linked_subresources(subresources, array)
|
57
59
|
subresources.each do |resources|
|
58
|
-
serialize_linked_resources(resources,
|
60
|
+
serialize_linked_resources(resources, array)
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
62
64
|
# @param [Array] resources
|
63
|
-
# @param [
|
64
|
-
# @return [
|
65
|
+
# @param [Array] linked
|
66
|
+
# @return [Array]
|
65
67
|
def serialize_linked_resources(subresource, linked)
|
66
68
|
subresource.seq.each_with_object(linked) do |resource, memo|
|
67
69
|
serialize_subresource(resource, memo)
|
@@ -74,32 +76,16 @@ module Yaks
|
|
74
76
|
# @param [Hash] linked
|
75
77
|
# @return [Hash]
|
76
78
|
def serialize_subresource(resource, linked)
|
77
|
-
|
78
|
-
set = linked.fetch(key) { Set.new }
|
79
|
-
linked[key] = (set << serialize_resource(resource))
|
79
|
+
linked << serialize_resource(resource)
|
80
80
|
serialize_linked_subresources(resource.subresources, linked)
|
81
81
|
end
|
82
82
|
|
83
83
|
def inverse
|
84
|
-
|
84
|
+
Yaks::Reader::JsonAPI.new
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
88
|
class Reader
|
89
|
-
def call(data, env)
|
90
|
-
type = data.detect do |key, value|
|
91
|
-
key unless key == "links"
|
92
|
-
end
|
93
|
-
|
94
|
-
CollectionResource.new(
|
95
|
-
type: type,
|
96
|
-
members: map_to_resource(data[type], )
|
97
|
-
)
|
98
|
-
end
|
99
|
-
|
100
|
-
def inverse
|
101
|
-
JsonApi.new
|
102
|
-
end
|
103
89
|
end
|
104
90
|
end
|
105
91
|
end
|
data/lib/yaks/mapper.rb
CHANGED
data/lib/yaks/mapper/form.rb
CHANGED
@@ -1,22 +1,8 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Mapper
|
3
3
|
class Form
|
4
|
-
extend
|
4
|
+
extend Forwardable, Util
|
5
5
|
|
6
|
-
ConfigBuilder = Builder.new(Config) do
|
7
|
-
def_set :action, :title, :method, :media_type
|
8
|
-
def_add :field, create: Field::Builder, append_to: :fields
|
9
|
-
def_add :fieldset, create: Fieldset, append_to: :fields
|
10
|
-
HTML5Forms::INPUT_TYPES.each do |type|
|
11
|
-
def_add(type,
|
12
|
-
create: Field::Builder,
|
13
|
-
append_to: :fields,
|
14
|
-
defaults: { type: type }
|
15
|
-
)
|
16
|
-
end
|
17
|
-
def_forward :dynamic
|
18
|
-
def_forward :condition
|
19
|
-
end
|
20
6
|
|
21
7
|
def_delegators :config, :name, :action, :title, :method,
|
22
8
|
:media_type, :fields, :dynamic_blocks
|
@@ -28,28 +14,22 @@ module Yaks
|
|
28
14
|
options[:name] = args.first
|
29
15
|
end
|
30
16
|
|
31
|
-
new(
|
17
|
+
new(config: Config.build(options, &block))
|
32
18
|
end
|
33
19
|
|
34
20
|
############################################################
|
35
21
|
# instance
|
36
22
|
|
37
|
-
include
|
23
|
+
include Attributes.new(:config)
|
38
24
|
|
39
25
|
def add_to_resource(resource, mapper, _context)
|
40
26
|
return resource if config.if && !mapper.expand_value(config.if)
|
41
|
-
resource.add_form(
|
27
|
+
resource.add_form(to_resource_form(mapper))
|
42
28
|
end
|
43
29
|
|
44
|
-
|
45
|
-
|
46
|
-
def to_resource(mapper)
|
47
|
-
config = dynamic_blocks.inject(self.config) do |config, block|
|
48
|
-
ConfigBuilder.build(config, mapper.object, &block)
|
49
|
-
end
|
50
|
-
|
30
|
+
def to_resource_form(mapper)
|
51
31
|
attrs = {
|
52
|
-
fields:
|
32
|
+
fields: config.to_resource_fields(mapper),
|
53
33
|
action: mapper.expand_uri(config.action, true)
|
54
34
|
}
|
55
35
|
|
@@ -60,9 +40,6 @@ module Yaks
|
|
60
40
|
Resource::Form.new(attrs)
|
61
41
|
end
|
62
42
|
|
63
|
-
def resource_fields(fields, mapper)
|
64
|
-
fields.map { |field| field.to_resource(mapper) }
|
65
|
-
end
|
66
43
|
end
|
67
44
|
end
|
68
45
|
end
|
@@ -9,17 +9,51 @@ module Yaks
|
|
9
9
|
method: nil,
|
10
10
|
media_type: nil,
|
11
11
|
fields: [],
|
12
|
-
dynamic_blocks: [],
|
13
12
|
if: nil
|
14
13
|
)
|
15
14
|
|
16
|
-
def
|
17
|
-
|
15
|
+
def self.builder
|
16
|
+
@builder ||= Builder.new(self) do
|
17
|
+
def_set :action, :title, :method, :media_type
|
18
|
+
def_add :field, create: Field::Builder, append_to: :fields
|
19
|
+
def_add :fieldset, create: Fieldset, append_to: :fields
|
20
|
+
HTML5Forms::INPUT_TYPES.each do |type|
|
21
|
+
def_add(type,
|
22
|
+
create: Field::Builder,
|
23
|
+
append_to: :fields,
|
24
|
+
defaults: { type: type })
|
25
|
+
end
|
26
|
+
def_add :dynamic, create: DynamicField, append_to: :fields
|
27
|
+
def_forward :condition
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Builder expects a `create' method. Alias to constructor
|
32
|
+
def self.create(options)
|
33
|
+
new(options)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Build up a configuration based on an initial set of
|
37
|
+
# attributes, and a configuration block
|
38
|
+
def self.build(options = {}, &block)
|
39
|
+
builder.create(options, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Build up a configuration based on a config block. Provide an
|
43
|
+
# object to be supplied to the block
|
44
|
+
def self.build_with_object(object, &block)
|
45
|
+
builder.build(new, object, &block)
|
18
46
|
end
|
19
47
|
|
20
48
|
def condition(prc = nil, &blk)
|
21
49
|
with(if: prc || blk)
|
22
50
|
end
|
51
|
+
|
52
|
+
def to_resource_fields(mapper)
|
53
|
+
fields.flat_map do |field|
|
54
|
+
field.to_resource_fields(mapper)
|
55
|
+
end
|
56
|
+
end
|
23
57
|
end
|
24
58
|
end
|
25
59
|
end
|