yaks 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bee1c06eddf937efc5ff746b97d372c0a3ff5691
4
- data.tar.gz: 8079af8a09a0b0949fcd034f1df937279f203860
3
+ metadata.gz: b4ec03c9b3f3c19ac3eaca461185e507d6f7edb3
4
+ data.tar.gz: 01e2fb005d69f7add878d814e23cc76029a49969
5
5
  SHA512:
6
- metadata.gz: 7b3d2438420e029ab8548b4b63e5cc518bba5b9584cfb7ea7321c7a9ce5a5e400dccf0613d36881fc0bcd474ac40ed038051355adbeebae008543fc02e7dd9d4
7
- data.tar.gz: 49e39f0108e9d045c3225bd672da3fb544d9b08ef00382552100baab91adc13936bdf2fe1eff974dcff0e6f2e624831f457c325358d0c29c546ae61ff1df8568
6
+ metadata.gz: 8833c0d2122ad0b3cce1791ac7f4b982110595e31cb33c54db8740085404ba98cf2e4eda60cd941f77f7f82e263f415f56f80bfae82e6baeff6660ea92b6f792
7
+ data.tar.gz: 9b93205060524ffa7aa2b7a8a1d3c59491c208df0c77fd8942baa27498c17757c4117a3cf555ce2d019ce45be0a546c2de4de7b365823501fba830482579dd26
data/README.md CHANGED
@@ -8,28 +8,25 @@
8
8
  [gemnasium]: https://gemnasium.com/plexus/yaks
9
9
  [codeclimate]: https://codeclimate.com/github/plexus/yaks
10
10
 
11
- # Yak Serializers
11
+ # Yaks
12
12
 
13
- ### One Stop Hypermedia Shopping ###
13
+ The library that understands hypermedia.
14
14
 
15
- *We did the shaving for you*
15
+ Yaks consists of a resource representation that is independent of any
16
+ output type. A Yaks mapper transforms an object into a resource, which
17
+ can then be serialized into whichever output format the client
18
+ requested. These formats are presently supported:
16
19
 
17
- Yaks is a tool for turning your domain models into Hypermedia resources.
18
-
19
- There are at the moment a number of competing media types for building Hypermedia APIs. These all add a layer of semantics on top of a low level serialization format such as JSON or XML. Even though they each have their own design goals, the core features mostly overlap. They typically provide a way to represent resources (entities), and resource collections, consisting of
20
-
21
- * Data in key-value format, possibly with composite values
22
- * Embedded resources
23
- * Links to related resources
24
- * Outbound links that have a specific relation to the resource
25
-
26
- They might also contain extra control data to specify possible future interactions, not unlike HTML forms.
27
-
28
- These different media types for Hypermedia clients and servers base themselves on the same set of internet standards, such as [RFC4288 Media types](http://tools.ietf.org/html/rfc4288), [RFC5988 Web Linking](http://tools.ietf.org/html/rfc5988), [RFC6906 The "profile" link relation](http://tools.ietf.org/search/rfc6906) and [RFC6570 URI Templates](http://tools.ietf.org/html/rfc6570).
20
+ * HAL
21
+ * JSON API
22
+ * Collection+JSON
23
+ * HTML
24
+ * HALO
29
25
 
30
26
  ## Concepts
31
27
 
32
- Yaks is a processing pipeline, you create and configure the pipeline, then feed data through it.
28
+ Yaks is a processing pipeline, you create and configure the pipeline,
29
+ then feed data through it.
33
30
 
34
31
  ``` ruby
35
32
  yaks = Yaks.new do
@@ -42,7 +39,7 @@ yaks = Yaks.new do
42
39
  end
43
40
  end
44
41
 
45
- yaks.call(data) # => JSON
42
+ yaks.call(data) # => '{"foo": "bar", "_links": { ... }}'
46
43
  ```
47
44
 
48
45
  Yaks performs this serialization in three steps
@@ -386,10 +383,6 @@ default_format :collection_json
386
383
 
387
384
  Subresources aren't mapped because Collection+JSON doesn't really have that concept, and the other way around templates and queries don't exist (yet) in Yaks.
388
385
 
389
- ### More formats
390
-
391
- Are planned... at the moment HAL is the only format I actually use, so it's the one that's best supported. Adding formats that follow the resource=(attributes, links, subresources) structure or a subset thereof is straightforward. More features, e.g. forms/actions such as used in Mason might be added in the future.
392
-
393
386
  ## Hooks
394
387
 
395
388
  It is possible to hook into the Yaks pipeline to perform extra processing steps before, after, or around each step. It also possible to skip a step.
@@ -481,25 +474,19 @@ Yaks by default "primitivizes" symbols (as strings), and classes that include En
481
474
 
482
475
  Yaks is used in production by [Ticketsolve](http://www.ticketsolve.com/). You can find an example API endpoint [here](http://leicestersquaretheatre.ticketsolve.com/api).
483
476
 
484
- Get in touch if you like to see your name and API here.
485
-
486
477
  ## Demo
487
478
 
488
- You can find an example app at [Yakports](https://github.com/plexus/yakports), or browse the HAL api directly using the [HAL browser](http://yaks-airports.herokuapp.com/browser.html).
489
-
490
- ## Acknowledgment
491
-
492
- The mapper syntax is largely borrowed from ActiveModel::Serializers, which in turn closely mimics the syntax of ActiveRecord models. It's a great concise syntax that still offers plenty of flexibility, so to not reinvent the wheel I've stuck to the existing syntax as far as practical, although there are several extensions and deviations.
493
-
494
- ## Lightweight
495
-
496
- Yaks is a lean library. It only depends on a few other tiny libraries (inflection, concord, uri_template). It has no core extensions (monkey patches). There is deliberately no built-in "integration" with existing frameworks, since the API is simply enough. You just call it.
479
+ You can find an outdated example app at [Yakports](https://github.com/plexus/yakports), or browse the HAL api directly using the [HAL browser](http://yaks-airports.herokuapp.com/browser.html).
497
480
 
498
- If this approach sounds appealing, have a look at [microrb.com](http://microrb.com/).
481
+ ## Standards Based
499
482
 
500
- ## Is it any good
483
+ Yaks is based on internet standards, including
501
484
 
502
- [Yes](https://news.ycombinator.com/item?id=3067434)
485
+ * [RFC4288 Media types](http://tools.ietf.org/html/rfc4288)
486
+ * [RFC5988 Web Linking](http://tools.ietf.org/html/rfc5988)
487
+ * [RFC6906 The "profile" link relation](http://tools.ietf.org/search/rfc6906)
488
+ * [RFC6570 URI Templates](http://tools.ietf.org/html/rfc6570)
489
+ * [RFC4229 HTTP Header Field Registrations](http://tools.ietf.org/html/rfc4229).
503
490
 
504
491
  ## How to contribute
505
492
 
@@ -5,7 +5,20 @@ module Yaks
5
5
 
6
6
  BreakingChanges = {
7
7
 
8
- '0.5.0' => %q~
8
+ '0.7.0' => %q~
9
+ Breaking Changes in Yaks 0.7.0
10
+ ==============================
11
+ Yaks::Resource#subresources is now an array, not a hash. The rel is
12
+ stored on the resource itself as Yaks::Resource#rels (an array). This
13
+ should only be of concern if you implement custom output formats
14
+
15
+ The general signature of all processing steps (mapper, formatter,
16
+ hooks) has changed to incldue a second parameter, the rack env. If you
17
+ have custom implementations of any of these, or hooks that are not
18
+ specified as ruby blocks, you will need to take this into account
19
+ ~,
20
+
21
+ '0.5.0' => %q~
9
22
 
10
23
  Breaking Changes in Yaks 0.5.0
11
24
  ==============================
@@ -26,7 +39,7 @@ for full documentation.
26
39
 
27
40
  ~,
28
41
 
29
- '0.4.3' => %q~
42
+ '0.4.3' => %q~
30
43
 
31
44
  Breaking Changes in Yaks 0.4.3
32
45
  ==============================
@@ -16,7 +16,9 @@ module Yaks
16
16
  end
17
17
  }
18
18
 
19
- attrs[ :collection_rel ] = collection_rel
19
+ if context[:mapper_stack].empty?
20
+ attrs[:rels] = [collection_rel]
21
+ end
20
22
 
21
23
  map_attributes(
22
24
  map_links(
@@ -5,25 +5,20 @@ module Yaks
5
5
  # A collection can be the top-level result of an API call, like all posts to
6
6
  # a blog, or a subresource collection, like the comments on a post result.
7
7
  #
8
- # Some formats treat everything like a collection, and a single resource as a
9
- # collection of one. Others treat every top level response as singular, e.g.
10
- # a single "collection of orders". Because of this Resource and
11
- # CollectionResource can both be iterated with #each, for the "everything is
12
- # a collection crowd", and they both respond to `links`, `attributes` and
13
- # `subresources`, so they can both be approached like a singular resource.
14
- #
15
- # In the second case a collection has a single "subresource", being its
16
- # members.
17
8
  class CollectionResource < Resource
18
- include attributes.add(members: [], collection_rel: 'members')
9
+ include attributes.add(members: [])
19
10
 
20
11
  extend Forwardable
21
- def_delegators :members, :each
12
+ def_delegators :members, :each, :map, :each_with_object
22
13
 
23
14
  # @return [Boolean]
24
15
  def collection?
25
16
  true
26
17
  end
27
18
 
19
+ def seq
20
+ self
21
+ end
22
+
28
23
  end
29
24
  end
data/lib/yaks/config.rb CHANGED
@@ -47,6 +47,10 @@ module Yaks
47
47
  @policy ||= @policy_class.new(@policy_options)
48
48
  end
49
49
 
50
+ def runner(object, options)
51
+ Runner.new(config: self, object: object, options: options)
52
+ end
53
+
50
54
  # Main entry point into yaks
51
55
  #
52
56
  # @param object [Object] The object to serialize
@@ -58,7 +62,7 @@ module Yaks
58
62
  # @option item_mapper [Class] Mapper class to use for items in a top-level collection
59
63
  #
60
64
  def call(object, options = {})
61
- Runner.new(config: self, object: object, options: options).call
65
+ runner(object, options).call
62
66
  end
63
67
  alias serialize call
64
68
  end
data/lib/yaks/format.rb CHANGED
@@ -20,7 +20,7 @@ module Yaks
20
20
 
21
21
  # @param [Yaks::Resource] resource
22
22
  # @return [Hash]
23
- def call(resource)
23
+ def call(resource, env = {})
24
24
  serialize_resource(resource)
25
25
  end
26
26
  alias serialize call
@@ -19,7 +19,7 @@ module Yaks
19
19
  # @param [Yaks::Resource] resource
20
20
  # @return [Array]
21
21
  def serialize_items(resource)
22
- resource.map do |item|
22
+ resource.seq.map do |item|
23
23
  attrs = item.attributes.map do |name, value|
24
24
  {
25
25
  name: name,
@@ -40,7 +40,7 @@ module Yaks
40
40
 
41
41
  if resource.collection?
42
42
  result = result.merge(_embedded:
43
- serialize_embedded(resource.collection_rel => resource))
43
+ serialize_embedded([resource]))
44
44
  elsif resource.subresources.any?
45
45
  result = result.merge(_embedded:
46
46
  serialize_embedded(resource.subresources))
@@ -79,12 +79,14 @@ module Yaks
79
79
  # @param [Array] subresources
80
80
  # @return [Hash]
81
81
  def serialize_embedded(subresources)
82
- subresources.each_with_object({}) do |(rel, resources), memo|
83
- memo[rel] = if resources.collection?
84
- resources.map( &method(:serialize_resource) )
85
- else
86
- serialize_resource(resources) unless resources.null_resource?
87
- end
82
+ subresources.each_with_object({}) do |sub, memo|
83
+ memo[sub.rels.first] = if sub.collection?
84
+ sub.map( &method(:serialize_resource) )
85
+ elsif sub.null_resource?
86
+ nil
87
+ else
88
+ serialize_resource(sub)
89
+ end
88
90
  end
89
91
  end
90
92
 
@@ -9,11 +9,11 @@ module Yaks
9
9
 
10
10
  # @param [Yaks::Resource] resource
11
11
  # @return [Hash]
12
- def call(resource)
13
- main_collection = resource.map(&method(:serialize_resource))
12
+ def call(resource, env = {})
13
+ main_collection = resource.seq.map(&method(:serialize_resource))
14
14
 
15
15
  { pluralize(resource.type) => main_collection }.tap do |serialized|
16
- linked = resource.each_with_object({}) do |res, hsh|
16
+ linked = resource.seq.each_with_object({}) do |res, hsh|
17
17
  serialize_linked_subresources(res.subresources, hsh)
18
18
  end
19
19
  serialized.merge!(linked: linked) unless linked.empty?
@@ -39,8 +39,8 @@ module Yaks
39
39
  # @param [Yaks::Resource] subresource
40
40
  # @return [Hash]
41
41
  def serialize_links(subresources)
42
- subresources.each_with_object({}) do |(_name, resource), hsh|
43
- next if resource.instance_of? NullResource
42
+ subresources.each_with_object({}) do |resource, hsh|
43
+ next if resource.null_resource?
44
44
  key = resource.collection? ? pluralize(resource.type) : resource.type
45
45
  hsh[key] = serialize_link(resource)
46
46
  end
@@ -56,7 +56,7 @@ module Yaks
56
56
  # @param [Hash] hsh
57
57
  # @return [Hash]
58
58
  def serialize_linked_subresources(subresources, hsh)
59
- subresources.values.each do |resources|
59
+ subresources.each do |resources|
60
60
  serialize_linked_resources(resources, hsh)
61
61
  end
62
62
  end
@@ -64,8 +64,8 @@ module Yaks
64
64
  # @param [Array] resources
65
65
  # @param [Hash] linked
66
66
  # @return [Hash]
67
- def serialize_linked_resources(resources, linked)
68
- resources.each_with_object(linked) do |resource, memo|
67
+ def serialize_linked_resources(subresource, linked)
68
+ subresource.seq.each_with_object(linked) do |resource, memo|
69
69
  serialize_subresource(resource, memo)
70
70
  end
71
71
  end
data/lib/yaks/mapper.rb CHANGED
@@ -36,7 +36,7 @@ module Yaks
36
36
  self.class.mapper_name(policy)
37
37
  end
38
38
 
39
- def call(object)
39
+ def call(object, env = {})
40
40
  @object = object
41
41
 
42
42
  return NullResource.new if object.nil?
@@ -34,8 +34,8 @@ module Yaks
34
34
 
35
35
  def add_subresource(resource)
36
36
  object = parent_mapper.load_association(association.name)
37
- subresource = association.map_resource(object, context)
38
- resource.add_subresource(rel, subresource)
37
+ subresource = association.map_resource(object, context).add_rel(rel)
38
+ resource.add_subresource(subresource)
39
39
  end
40
40
  end
41
41
  end
@@ -1,10 +1,13 @@
1
1
  module Yaks
2
2
  class NullResource < Resource
3
- include Equalizer.new(:collection?)
3
+ include attributes.add(collection: false),
4
+ Equalizer.new(:rels, :collection)
4
5
 
5
6
  def initialize(opts = {})
6
- super()
7
- @collection = opts.fetch(:collection) { false }
7
+ _opts = {}
8
+ _opts[:rels] = opts[:rels] if opts.key?(:rels)
9
+ _opts[:collection] = opts[:collection] if opts.key?(:collection)
10
+ super(_opts)
8
11
  end
9
12
 
10
13
  def each
@@ -19,6 +22,15 @@ module Yaks
19
22
  true
20
23
  end
21
24
 
25
+ def seq
26
+ []
27
+ end
28
+
29
+ def map
30
+ return [] if collection?
31
+ raise UnsupportedOperationError, "Operation #{__method__} not supported on #{self.class}"
32
+ end
33
+
22
34
  def update_attributes(_new_attrs)
23
35
  raise UnsupportedOperationError, "Operation #{__method__} not supported on #{self.class}"
24
36
  end
@@ -31,7 +43,7 @@ module Yaks
31
43
  raise UnsupportedOperationError, "Operation #{__method__} not supported on #{self.class}"
32
44
  end
33
45
 
34
- def add_subresource(_rel, _subresource)
46
+ def add_subresource(_subresource)
35
47
  raise UnsupportedOperationError, "Operation #{__method__} not supported on #{self.class}"
36
48
  end
37
49
  end
@@ -26,7 +26,7 @@ module Yaks
26
26
  object
27
27
  end
28
28
 
29
- p.map Symbol do |object|
29
+ p.map Symbol, URI do |object|
30
30
  object.to_s
31
31
  end
32
32
 
data/lib/yaks/resource.rb CHANGED
@@ -1,17 +1,25 @@
1
1
  module Yaks
2
2
  class Resource
3
3
  include Attributes.new(
4
- type: nil, links: [], attributes: {}, subresources: {}, controls: []
5
- ),
6
- Enumerable
4
+ type: nil,
5
+ rels: [],
6
+ links: [],
7
+ attributes: {},
8
+ subresources: [],
9
+ controls: []
10
+ )
11
+
12
+ def initialize(attrs = {})
13
+ raise attrs.inspect if attrs.key?(:subresources) && !attrs[:subresources].instance_of?(Array)
14
+ super
15
+ end
7
16
 
8
17
  def [](attr)
9
18
  attributes[attr]
10
19
  end
11
20
 
12
- def each
13
- return to_enum unless block_given?
14
- yield self
21
+ def seq
22
+ [self]
15
23
  end
16
24
 
17
25
  def self_link
@@ -23,23 +31,27 @@ module Yaks
23
31
  def collection?
24
32
  false
25
33
  end
34
+ alias collection collection?
26
35
 
27
36
  def null_resource?
28
37
  false
29
38
  end
30
39
 
31
- def collection_rel
32
- raise UnsupportedOperationError, "Only Yaks::CollectionResource has a collection_rel"
33
- end
34
-
35
40
  def members
36
41
  raise UnsupportedOperationError, "Only Yaks::CollectionResource has members"
37
42
  end
43
+ alias each members
44
+ alias map members
45
+ alias each_with_object members
38
46
 
39
47
  def update_attributes(new_attrs)
40
48
  update(attributes: @attributes.merge(new_attrs))
41
49
  end
42
50
 
51
+ def add_rel(rel)
52
+ append_to(:rels, rel)
53
+ end
54
+
43
55
  def add_link(link)
44
56
  append_to(:links, link)
45
57
  end
@@ -48,8 +60,30 @@ module Yaks
48
60
  append_to(:controls, control)
49
61
  end
50
62
 
51
- def add_subresource(rel, subresource)
52
- update(subresources: @subresources.merge(rel => subresource))
63
+ def add_subresource(subresource)
64
+ append_to(:subresources, subresource)
65
+ end
66
+
67
+ def pp
68
+ indent = ->(str) { str.lines.map {|l| " #{l}"}.join }
69
+ format = ->(val) { val.respond_to?(:pp) ? val.pp : val.inspect }
70
+
71
+ fmt_attrs = self.class.attributes.attributes.map do |attr|
72
+ value = public_send(attr)
73
+ fmt_val = case value
74
+ when Array
75
+ if value.inspect.length < 50
76
+ value.inspect
77
+ else
78
+ "[\n#{indent[value.map(&format).join(",\n")]}\n]"
79
+ end
80
+ else
81
+ format[value]
82
+ end
83
+ "#{attr}=#{fmt_val}"
84
+ end.join("\n")
85
+
86
+ "#<#{self.class.name}\n#{indent[fmt_attrs]}\n>"
53
87
  end
54
88
  end
55
89
  end
data/lib/yaks/runner.rb CHANGED
@@ -5,11 +5,12 @@ module Yaks
5
5
  include Adamantium::Flat
6
6
  extend Forwardable
7
7
 
8
- def_delegators :config, :policy, :default_format, :format_options, :primitivize, :serializers, :hooks
8
+ def_delegators :config, :policy, :default_format, :format_options, :primitivize, :serializers
9
9
 
10
10
  def call
11
- steps.inject(object) {|memo, (_, step)| step.call(memo) }
11
+ steps.inject(object) {|memo, (_, step)| step.call(memo, env) }
12
12
  end
13
+ alias result call
13
14
 
14
15
  def context
15
16
  {
@@ -33,6 +34,14 @@ module Yaks
33
34
  end
34
35
  memoize :format_class
35
36
 
37
+ def media_type
38
+ format_class.media_type
39
+ end
40
+
41
+ def format
42
+ format_class.format_name
43
+ end
44
+
36
45
  def steps
37
46
  insert_hooks(
38
47
  [[ :map, mapper ],
@@ -55,7 +64,7 @@ module Yaks
55
64
  memoize :formatter
56
65
 
57
66
  def primitivizer
58
- ->(input) do
67
+ proc do |input|
59
68
  if format_class.serializer.equal? :json
60
69
  primitivize.call(input)
61
70
  else
@@ -77,6 +86,10 @@ module Yaks
77
86
  end
78
87
  memoize :serializer
79
88
 
89
+ def hooks
90
+ config.hooks + options.fetch(:hooks, [])
91
+ end
92
+
80
93
  def insert_hooks(steps)
81
94
  hooks.inject(steps) do |steps, (type, target_step, name, hook)|
82
95
  steps.flat_map do |step_name, callable|
@@ -6,7 +6,7 @@ module Yaks
6
6
  end
7
7
 
8
8
  def self.all
9
- @serializers ||= {json: JSON.method(:pretty_generate)}
9
+ @serializers ||= {json: ->(data, env) { JSON.pretty_generate(data) }}
10
10
  end
11
11
  end
12
12
  end
data/lib/yaks/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yaks
2
- VERSION = '0.6.2'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -2,7 +2,7 @@ RSpec.shared_examples_for 'JSON output format' do |yaks, format, name|
2
2
  let(:input) { load_yaml_fixture(name) }
3
3
  let(:output) { load_json_fixture("#{name}.#{format}") }
4
4
 
5
- subject { yaks.serialize(input) }
5
+ subject { yaks.call(input) }
6
6
 
7
7
  it { should deep_eql output }
8
8
  end
@@ -12,17 +12,56 @@ RSpec.describe 'Mapping domain models to Resource objects' do
12
12
  its(:attributes) { should eql(id: 1, name: 'john') }
13
13
  its(:links) { should eql [ Yaks::Resource::Link.new(rel: :copyright, uri: '/api/copyright/2024') ] }
14
14
 
15
+ specify {
16
+
17
+ subject.subresources == [
18
+ Yaks::Resource.new(
19
+ type:'pet_peeve',
20
+ rels: ['rel:pet_peeve'],
21
+ attributes: {id: 4, type: 'parsing with regexps'}
22
+ ),
23
+ Yaks::CollectionResource.new(
24
+ type: 'pet',
25
+ rels: ['rel:pets'],
26
+ members: [
27
+ Yaks::Resource.new(
28
+ type: 'pet',
29
+ attributes: {:id => 2, :species => 'dog', :name => 'boingboing'}
30
+ ),
31
+ Yaks::Resource.new(
32
+ type: 'pet',
33
+ attributes: {:id => 3, :species => 'cat', :name => 'wassup'}
34
+ )
35
+ ]
36
+ )
37
+ ]
38
+
39
+
40
+ }
41
+
15
42
  its(:subresources) {
16
43
  should eq(
17
- "rel:pet_peeve" => Yaks::Resource.new(type:'pet_peeve', attributes: {id: 4, type: 'parsing with regexps'}),
18
- "rel:pets" => Yaks::CollectionResource.new(
19
- type: 'pet',
20
- members: [
21
- Yaks::Resource.new(type: 'pet', attributes: {:id => 2, :species => "dog", :name => "boingboing"}),
22
- Yaks::Resource.new(type: 'pet', attributes: {:id => 3, :species => "cat", :name => "wassup"})
23
- ],
24
- collection_rel: 'rel:pets'
25
- )
44
+ [
45
+ Yaks::Resource.new(
46
+ type:'pet_peeve',
47
+ rels: ['rel:pet_peeve'],
48
+ attributes: {id: 4, type: 'parsing with regexps'}
49
+ ),
50
+ Yaks::CollectionResource.new(
51
+ type: 'pet',
52
+ rels: ['rel:pets'],
53
+ members: [
54
+ Yaks::Resource.new(
55
+ type: 'pet',
56
+ attributes: {:id => 2, :species => 'dog', :name => 'boingboing'}
57
+ ),
58
+ Yaks::Resource.new(
59
+ type: 'pet',
60
+ attributes: {:id => 3, :species => 'cat', :name => 'wassup'}
61
+ )
62
+ ]
63
+ )
64
+ ]
26
65
  )
27
66
  }
28
67
  end
@@ -3,7 +3,7 @@ RSpec.shared_context 'collection resource' do
3
3
  Yaks::CollectionResource.new(
4
4
  links: links,
5
5
  members: members,
6
- collection_rel: 'http://api.example.com/rels/plants'
6
+ rels: ['http://api.example.com/rels/plants']
7
7
  )
8
8
  end
9
9
  let(:links) { [] }
@@ -23,7 +23,7 @@ RSpec.describe Yaks::CollectionMapper do
23
23
  links: [],
24
24
  attributes: {},
25
25
  members: [],
26
- collection_rel: 'rel:the_types'
26
+ rels: ['rel:the_types']
27
27
  )
28
28
  end
29
29
 
@@ -44,7 +44,7 @@ RSpec.describe Yaks::CollectionMapper do
44
44
  Yaks::Resource.new(type: 'pet', attributes: {:id => 2, :species => "dog", :name => "boingboing"}),
45
45
  Yaks::Resource.new(type: 'pet', attributes: {:id => 3, :species => "cat", :name => "wassup"})
46
46
  ],
47
- collection_rel: 'rel:pets'
47
+ rels: ['rel:pets']
48
48
  )
49
49
  end
50
50
  end
@@ -68,7 +68,7 @@ RSpec.describe Yaks::CollectionMapper do
68
68
  Yaks::Resource.new(type: 'pet', attributes: {:id => 2, :species => "dog", :name => "boingboing"}),
69
69
  Yaks::Resource.new(type: 'pet', attributes: {:id => 3, :species => "cat", :name => "wassup"})
70
70
  ],
71
- collection_rel: 'rel:pets'
71
+ rels: ['rel:pets']
72
72
  )
73
73
  end
74
74
  end
@@ -93,7 +93,7 @@ RSpec.describe Yaks::CollectionMapper do
93
93
  links: [],
94
94
  attributes: { foo: 123, bar: 'pi la~~~' },
95
95
  members: [],
96
- collection_rel: 'rel:the_types'
96
+ rels: ['rel:the_types']
97
97
  )
98
98
  end
99
99
  end
@@ -111,7 +111,7 @@ RSpec.describe Yaks::CollectionMapper do
111
111
  links: [ Yaks::Resource::Link.new(rel: :self, uri: 'http://api.example.com/orders') ],
112
112
  attributes: { },
113
113
  members: [],
114
- collection_rel: 'rel:the_types'
114
+ rels: ['rel:the_types']
115
115
  )
116
116
  end
117
117
  end
@@ -138,7 +138,7 @@ RSpec.describe Yaks::CollectionMapper do
138
138
  members: [
139
139
  Yaks::Resource.new(type: 'pet', attributes: {:id => 3, :species => "cat", :name => "wassup"})
140
140
  ],
141
- collection_rel: 'rel:pets'
141
+ rels: ['rel:pets']
142
142
  )
143
143
  end
144
144
  end
@@ -146,18 +146,18 @@ RSpec.describe Yaks::CollectionMapper do
146
146
  context 'with an empty collection' do
147
147
 
148
148
  context 'without an item_mapper specified' do
149
- let(:context) { Yaks::Util.slice_hash(super(), :policy, :env) }
149
+ let(:context) { Yaks::Util.slice_hash(super(), :policy, :env, :mapper_stack) }
150
150
 
151
151
  it 'should use a rel of "collection"' do
152
- expect(mapper.([]).collection_rel).to eq 'collection'
152
+ expect(mapper.([]).rels).to eq ['collection']
153
153
  end
154
154
  end
155
155
 
156
156
  context 'with an item_mapper specified' do
157
- let(:context) { Yaks::Util.slice_hash(super(), :policy, :env, :item_mapper) }
157
+ let(:context) { Yaks::Util.slice_hash(super(), :policy, :env, :mapper_stack, :item_mapper) }
158
158
 
159
159
  it 'should derive the collection rel from the item mapper' do
160
- expect(mapper.([]).collection_rel).to eq 'rel:the_types'
160
+ expect(mapper.([]).rels).to eq ['rel:the_types']
161
161
  end
162
162
  end
163
163
 
@@ -12,8 +12,8 @@ RSpec.describe Yaks::CollectionResource do
12
12
  its(:links) { should eql [] }
13
13
  its(:attributes) { should eql({}) }
14
14
  its(:members) { should eql [] }
15
- its(:subresources) { should eql({}) }
16
- its(:collection_rel) { should eql('members') }
15
+ its(:subresources) { should eql [] }
16
+ its(:rels) { should eql [] }
17
17
  end
18
18
 
19
19
  context 'with a full constructor' do
@@ -32,7 +32,7 @@ RSpec.describe Yaks::CollectionResource do
32
32
  attributes: { customer: 'John Doe', price: 10.00 }
33
33
  )
34
34
  ],
35
- collection_rel: 'http://api.example.org/rels/orders'
35
+ rels: ['http://api.example.org/rels/orders']
36
36
  }
37
37
  }
38
38
 
@@ -51,9 +51,9 @@ RSpec.describe Yaks::CollectionResource do
51
51
  )
52
52
  ]
53
53
  }
54
- its(:collection_rel) { should eq 'http://api.example.org/rels/orders'}
54
+ its(:rels) { should eq ['http://api.example.org/rels/orders'] }
55
55
 
56
- its(:subresources) { should eql({}) }
56
+ its(:subresources) { should eql [] }
57
57
 
58
58
  end
59
59
  end
@@ -39,16 +39,14 @@ RSpec.describe Yaks::Mapper::AssociationMapper do
39
39
  stub(association).render_as_link?(parent_mapper) { false }
40
40
  stub(association).map_rel(policy) { 'rels:the_rel' }
41
41
  stub(association).name { :the_name }
42
- stub(association).map_resource(:the_object, association_mapper.context) { :the_resource }
42
+ stub(association).map_resource(:the_object, association_mapper.context) { Yaks::Resource.new }
43
43
 
44
44
  stub(parent_mapper).load_association(:the_name) { :the_object }
45
45
  end
46
46
 
47
- it 'should render a link' do
47
+ it 'should render a subresource' do
48
48
  expect(association_mapper.call(Yaks::Resource.new)).to eql Yaks::Resource.new(
49
- subresources: {
50
- 'rels:the_rel' => :the_resource
51
- }
49
+ subresources: [ Yaks::Resource.new(rels: ['rels:the_rel']) ]
52
50
  )
53
51
  end
54
52
 
@@ -19,14 +19,14 @@ RSpec.describe Yaks::Mapper::Association do
19
19
  let(:href) { Yaks::Undefined }
20
20
  let(:link_if) { Yaks::Undefined }
21
21
 
22
- its(:name) { should equal :shoes }
22
+ its(:name) { should equal :shoes }
23
23
  its(:item_mapper) { should equal Yaks::Mapper }
24
24
 
25
25
  context 'with a minimal constructor' do
26
26
  subject(:association) { described_class.new(name: :foo) }
27
27
 
28
28
  its(:name) { should be :foo }
29
- its(:item_mapper) { should be Yaks::Undefined }
29
+ its(:item_mapper) { should be Yaks::Undefined }
30
30
  its(:rel) { should be Yaks::Undefined }
31
31
  its(:href) { should be Yaks::Undefined }
32
32
  its(:link_if) { should be Yaks::Undefined }
@@ -38,12 +38,14 @@ RSpec.describe Yaks::Mapper::Association do
38
38
  describe '#add_to_resource' do
39
39
  let(:object) { fake(:shoes => []) }
40
40
  let(:rel) { 'rel:shoes' }
41
+
41
42
  before do
42
43
  parent_mapper.call(object)
44
+ stub(association).map_resource(any_args) { Yaks::Resource.new }
43
45
  end
44
46
 
45
47
  it 'should delegate to AssociationMapper' do
46
- expect(association.add_to_resource(Yaks::Resource.new, parent_mapper, yaks_context)).to eql Yaks::Resource.new(subresources: {'rel:shoes' => nil} )
48
+ expect(association.add_to_resource(Yaks::Resource.new, parent_mapper, yaks_context)).to eql Yaks::Resource.new(subresources: [Yaks::Resource.new(rels: ['rel:shoes'])])
47
49
  end
48
50
  end
49
51
 
@@ -28,22 +28,22 @@ RSpec.describe Yaks::Mapper::HasMany do
28
28
  }
29
29
 
30
30
  it 'should map the subresources' do
31
- expect(closet_mapper.call(closet).subresources).to eql(
32
- "http://foo/shoes" => Yaks::CollectionResource.new(
31
+ expect(closet_mapper.call(closet).subresources).to eql([
32
+ Yaks::CollectionResource.new(
33
33
  type: 'shoe',
34
34
  members: [
35
35
  Yaks::Resource.new(type: 'shoe', attributes: {:size => 9, :color => :blue}),
36
36
  Yaks::Resource.new(type: 'shoe', attributes: {:size => 11.5, :color => :red})
37
37
  ],
38
- collection_rel: 'rel:shoes'
38
+ rels: ['http://foo/shoes']
39
39
  )
40
- )
40
+ ])
41
41
  end
42
42
 
43
43
  it 'should map nil to a NullResource collection' do
44
- expect(closet_mapper.call(fake(shoes: nil)).subresources).to eql(
45
- 'http://foo/shoes' => Yaks::NullResource.new(collection: true)
46
- )
44
+ expect(closet_mapper.call(fake(shoes: nil)).subresources).to eql([
45
+ Yaks::NullResource.new(collection: true, rels: ['http://foo/shoes'])
46
+ ])
47
47
  end
48
48
 
49
49
  context 'without an explicit mapper' do
@@ -40,9 +40,9 @@ RSpec.describe Yaks::Mapper::HasOne do
40
40
  it 'should derive one based on policy' do
41
41
  expect(subresource).to eql(
42
42
  Yaks::Resource.new(
43
- subresources: {
44
- 'http://rel' => Yaks::Resource.new(type: 'author', attributes: {name: name})
45
- }
43
+ subresources: [
44
+ Yaks::Resource.new(type: 'author', attributes: {name: name}, rels: ['http://rel'])
45
+ ]
46
46
  )
47
47
  )
48
48
  end
@@ -98,14 +98,14 @@ RSpec.describe Yaks::Mapper do
98
98
 
99
99
 
100
100
  it 'should have the subresource in the resource' do
101
- expect(resource.subresources).to eq("http://foo.bar/rels/widgets" => Yaks::Resource.new(type: 'widget', attributes: {:type => "super_widget"}))
101
+ expect(resource.subresources).to eq([Yaks::Resource.new(type: 'widget', attributes: {:type => 'super_widget'}, rels: ['http://foo.bar/rels/widgets'])])
102
102
  end
103
103
 
104
104
  context 'with explicit mapper and rel' do
105
105
  it 'should delegate to the given mapper' do
106
- expect(resource.subresources).to eq(
107
- "http://foo.bar/rels/widgets" => Yaks::Resource.new(type: 'widget', attributes: {:type => "super_widget"})
108
- )
106
+ expect(resource.subresources).to eq([
107
+ Yaks::Resource.new(type: 'widget', attributes: {:type => 'super_widget'}, rels: ['http://foo.bar/rels/widgets'])
108
+ ])
109
109
  end
110
110
  end
111
111
 
@@ -121,9 +121,9 @@ RSpec.describe Yaks::Mapper do
121
121
  end
122
122
 
123
123
  it 'should derive the mapper based on policy' do
124
- expect(resource.subresources).to eq(
125
- "http://foo.bar/rels/widgets" => Yaks::Resource.new(type: 'widget', attributes: {:type => "super_widget"})
126
- )
124
+ expect(resource.subresources).to eq([
125
+ Yaks::Resource.new(type: 'widget', attributes: {:type => 'super_widget'}, rels: ['http://foo.bar/rels/widgets'])
126
+ ])
127
127
  end
128
128
  end
129
129
 
@@ -139,9 +139,9 @@ RSpec.describe Yaks::Mapper do
139
139
  end
140
140
 
141
141
  it 'should derive the rel based on policy' do
142
- expect(resource.subresources).to eq(
143
- "http://rel/rel" => Yaks::Resource.new(type: 'widget', attributes: {:type => "super_widget"})
144
- )
142
+ expect(resource.subresources).to eq([
143
+ Yaks::Resource.new(type: 'widget', attributes: {:type => 'super_widget'}, rels: ['http://rel/rel'])
144
+ ])
145
145
  end
146
146
  end
147
147
 
@@ -155,7 +155,7 @@ RSpec.describe Yaks::Mapper do
155
155
  end
156
156
 
157
157
  it 'should not map the resource' do
158
- expect(resource.subresources).to eq({})
158
+ expect(resource.subresources).to eq([])
159
159
  end
160
160
  end
161
161
  end
@@ -3,9 +3,9 @@ require 'spec_helper'
3
3
  RSpec.describe Yaks::NullResource do
4
4
  subject(:null_resource) { described_class.new }
5
5
 
6
- its(:attributes) { should eq Hash[] }
6
+ its(:attributes) { should eq({}) }
7
7
  its(:links) { should eq [] }
8
- its(:subresources) { should eq Hash[] }
8
+ its(:subresources) { should eq [] }
9
9
  its(:collection?) { should be false }
10
10
  its(:null_resource?) { should be true }
11
11
 
@@ -49,7 +49,7 @@ RSpec.describe Yaks::NullResource do
49
49
  end
50
50
 
51
51
  it 'should not allow adding subresources' do
52
- expect { null_resource.add_subresource(nil, nil) }.to raise_error(
52
+ expect { null_resource.add_subresource(nil) }.to raise_error(
53
53
  Yaks::UnsupportedOperationError, "Operation add_subresource not supported on Yaks::NullResource"
54
54
  )
55
55
  end
@@ -10,7 +10,7 @@ RSpec.describe Yaks::Resource do
10
10
  its(:type) { should be_nil }
11
11
  its(:attributes) { should eql({}) }
12
12
  its(:links) { should eql [] }
13
- its(:subresources) { should eql({}) }
13
+ its(:subresources) { should eql [] }
14
14
  its(:self_link) { should be_nil }
15
15
  its(:null_resource?) { should be false }
16
16
  its(:collection?) { should be false }
@@ -48,17 +48,16 @@ RSpec.describe Yaks::Resource do
48
48
  end
49
49
 
50
50
  context 'with subresources' do
51
- let(:init_opts) { { subresources: { 'comments' => [Yaks::Resource.new(type: 'comment')] } } }
52
- its(:subresources) { should eql 'comments' => [Yaks::Resource.new(type: 'comment')] }
51
+ let(:init_opts) { { subresources: [ Yaks::Resource.new(type: 'comment', rels: ['comments']) ] } }
52
+ its(:subresources) { should eql [Yaks::Resource.new(type: 'comment', rels: ['comments'])] }
53
53
 
54
- it 'should return an enumerator for #each' do
55
- expect(resource.each.with_index.to_a).to eq [ [resource, 0] ]
54
+ it 'should return an enumerable for #seq' do
55
+ expect(resource.seq.each.with_index.to_a).to eq [ [resource, 0] ]
56
56
  end
57
57
  end
58
58
 
59
-
60
59
  it 'should act as a collection of one' do
61
- expect(resource.each.to_a).to eql [resource]
60
+ expect(resource.seq.each.to_a).to eql [resource]
62
61
  end
63
62
 
64
63
  describe 'persistent updates' do
@@ -66,7 +65,7 @@ RSpec.describe Yaks::Resource do
66
65
  Yaks::Resource.new(
67
66
  attributes: {x: :y},
68
67
  links: [:one],
69
- subresources: {foo_rel: :subres}
68
+ subresources: [ :subres ]
70
69
  )
71
70
  }
72
71
 
@@ -75,18 +74,18 @@ RSpec.describe Yaks::Resource do
75
74
  resource
76
75
  .update_attributes(foo: :bar)
77
76
  .add_link(:a_link)
78
- .add_subresource(:rel, :a_subresource)
77
+ .add_subresource(:a_subresource)
79
78
  .update_attributes(foo: :baz)
80
79
  ).to eq Yaks::Resource.new(
81
80
  attributes: {x: :y, foo: :baz},
82
81
  links: [:one, :a_link],
83
- subresources: {foo_rel: :subres, rel: :a_subresource}
82
+ subresources: [:subres, :a_subresource]
84
83
  )
85
84
 
86
85
  expect(resource).to eq Yaks::Resource.new(
87
86
  attributes: {x: :y},
88
87
  links: [:one],
89
- subresources: {foo_rel: :subres}
88
+ subresources: [:subres]
90
89
  )
91
90
  end
92
91
  end
@@ -113,14 +112,6 @@ RSpec.describe Yaks::Resource do
113
112
  end
114
113
  end
115
114
 
116
- describe '#collection_rel' do
117
- it 'should raise unsupported operation error' do
118
- expect { resource.collection_rel }.to raise_error(
119
- Yaks::UnsupportedOperationError, "Only Yaks::CollectionResource has a collection_rel"
120
- )
121
- end
122
- end
123
-
124
115
  describe '#members' do
125
116
  it 'should raise unsupported operation error' do
126
117
  expect { resource.members }.to raise_error(
@@ -13,8 +13,8 @@ RSpec.describe Yaks::Runner do
13
13
  let(:runner) {
14
14
  Class.new(described_class) do
15
15
  def steps
16
- [ [:step1, ->(x) { x + 35 }],
17
- [:step2, ->(x) { "#{x} #{x}"}] ]
16
+ [ [:step1, proc { |x| x + 35 }],
17
+ [:step2, proc { |x| "#{x} #{x}" }] ]
18
18
  end
19
19
  end.new(object: object, config: config, options: options)
20
20
  }
@@ -219,12 +219,12 @@ RSpec.describe Yaks::Runner do
219
219
  }
220
220
 
221
221
  it 'should try to find an explicitly configured serializer' do
222
- expect(runner.serializer.call('42')).to eql 'serialized 42'
222
+ expect(runner.serializer.call('42', {})).to eql 'serialized 42'
223
223
  end
224
224
  end
225
225
 
226
226
  it 'should fall back to the policy' do
227
- expect(runner.serializer.call([1,2,3])).to eql "[\n 1,\n 2,\n 3\n]"
227
+ expect(runner.serializer.call([1,2,3], {})).to eql "[\n 1,\n 2,\n 3\n]"
228
228
  end
229
229
  end
230
230
 
@@ -11,7 +11,7 @@ RSpec.describe Yaks::Serializer do
11
11
  end
12
12
 
13
13
  it 'should by default have a serializer for JSON' do
14
- expect(Yaks::Serializer.all[:json].call([1,2,3])).to eql "[\n 1,\n 2,\n 3\n]"
14
+ expect(Yaks::Serializer.all[:json].call([1,2,3], {})).to eql "[\n 1,\n 2,\n 3\n]"
15
15
  end
16
16
 
17
17
  it 'should warn when registering a key again' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yaks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arne Brasseur
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-05 00:00:00.000000000 Z
11
+ date: 2014-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inflection
@@ -346,7 +346,18 @@ homepage: https://github.com/plexus/yaks
346
346
  licenses:
347
347
  - MIT
348
348
  metadata: {}
349
- post_install_message:
349
+ post_install_message: |2
350
+
351
+ Breaking Changes in Yaks 0.7.0
352
+ ==============================
353
+ Yaks::Resource#subresources is now an array, not a hash. The rel is
354
+ stored on the resource itself as Yaks::Resource#rels (an array). This
355
+ should only be of concern if you implement custom output formats
356
+
357
+ The general signature of all processing steps (mapper, formatter,
358
+ hooks) has changed to incldue a second parameter, the rack env. If you
359
+ have custom implementations of any of these, or hooks that are not
360
+ specified as ruby blocks, you will need to take this into account
350
361
  rdoc_options: []
351
362
  require_paths:
352
363
  - lib