yaks 0.8.0.beta2 → 0.8.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: e70fb16fe2a8b8d06d5748409ac51ce7de353cba
4
- data.tar.gz: ed91cadda25837e29c307bed1caa86b7e93bb5b4
3
+ metadata.gz: 1784c312fc3b6f4a71be14c06506fafcc0dddcef
4
+ data.tar.gz: b44cc96127c4311a2ee91a74404d702a189b54b5
5
5
  SHA512:
6
- metadata.gz: 61c520e3fa43ee9de85d54b1bf478c3d0ef68933f98c31668a8f7330bf75b1306edb1f442824434843e50d0d03068ad7f0a0a05fa8212adbf895ebd24ce97e40
7
- data.tar.gz: ad0ab6ae6c9d0d385baf8bdaeef55c6cbffdaf2a3b87fadf30e7d28f2cf0a17bfa71967396963771aeebe5decd75ef2e986ebfe634501145e4e56c18eeb5a416
6
+ metadata.gz: 10c08be52835165067815e4df2c53dde58109f704efc1b2557a4be3813321d524452ed7799481fa6c422a8be84e6abbd8e9a67e7cf796918c5621ac50e50b853
7
+ data.tar.gz: 0c2d814f5022ddf9f29d64bb7424da0de584d69a46d48fd5ab58d9397e4f3c73757ae93b0bff49a2f1ded8072b90ef7595cc35108119dbd27f23c86a3c10d5e0
data/README.md CHANGED
@@ -26,7 +26,7 @@ requested. These formats are presently supported:
26
26
  ## State of Development
27
27
 
28
28
  This library came into existence because we at
29
- [Ticketsolve](https://www.ticketsolve.com) wanted to build a rich
29
+ [Ticketsolve](http://www.ticketsolve.com) wanted to build a rich
30
30
  hypermedia API, and were dissatisfied with any of the existing Ruby
31
31
  solutions at the time (November 2013).
32
32
 
@@ -37,7 +37,7 @@ supporting hypermedia formats in Ruby.
37
37
  Yaks can be used in production today, as we do, but until 1.0 is
38
38
  released there will regularly be breaking changes, as we figure out
39
39
  the best way to do things. These are all documented clearly in the
40
- [changelog](CHANGLOG.md). At this point we recommend locking to an
40
+ [changelog](/CHANGELOG.md). At this point we recommend locking to an
41
41
  exact version number.
42
42
 
43
43
  ## Concepts
@@ -138,6 +138,30 @@ class CommentMapper < Yaks::Mapper
138
138
  end
139
139
  ```
140
140
 
141
+ ### Forms
142
+
143
+ Mapper can contain form defintions, for formats that support them. The
144
+ form DSL mimics the HTML5 field and attribute names.
145
+
146
+ ```ruby
147
+ class PostMapper < Yaks::Mapper
148
+ attributes :id, :body, :date
149
+
150
+ form :add_comment do
151
+ action '/api/comments'
152
+ method 'POST'
153
+ media_type 'application/json'
154
+
155
+ text :body
156
+ hidden :post_id, value: -> { object.id }
157
+ end
158
+ end
159
+ ```
160
+
161
+ TODO: add more info on form element types, attributes, conditional
162
+ rendering of forms, dynamic form sections, ...
163
+
164
+
141
165
  #### Filtering
142
166
 
143
167
  You can override `#attributes`, or `#associations`.
@@ -196,11 +220,16 @@ end
196
220
 
197
221
  ```
198
222
 
199
- You can pass a symbol instead of a template, in that case the symbol will be used as a method name on the object to retrieve the link. You can override this behavior just like with attributes.
223
+ You can pass a proc instead of a template, in that case the proc will
224
+ be resolved in the context of the mapper. What this means is that, if
225
+ the proc takes no arguments, it will be evaluated with the mapper
226
+ instance as the value of `self`. If the proc does take an argument,
227
+ then it will receive the mapper instance, and will be evaluated as a
228
+ closure, i.e. with access to the scope in which it was defined.
200
229
 
201
230
  ```ruby
202
231
  class FooMapper < Yaks::Mapper
203
- link 'http://api.foo.com/rels/go_home', :home_url
232
+ link 'http://api.foo.com/rels/go_home', -> { home_url }
204
233
  # by default calls object.home_url
205
234
 
206
235
  def home_url
@@ -209,6 +238,21 @@ class FooMapper < Yaks::Mapper
209
238
  end
210
239
  ```
211
240
 
241
+
242
+ To only include links based on certain conditions, add an `:if`
243
+ option, passing it a block. The block will be resolved in the context
244
+ of the mapper, as explained before.
245
+
246
+ For example, say you want to notify the consumer of your API that upon
247
+ confirming an order, the previously held cart is no longer valid, you
248
+ could use the IANA standard `invalidates` rel to communicate this.
249
+
250
+ ``` ruby
251
+ class OrderMapper < BaseMapper
252
+ link :invalidates, '/api/cart', if: ->{ env['api.invalidate_cart'] }
253
+ end
254
+ ```
255
+
212
256
  ### Associations
213
257
 
214
258
  Use `has_one` for an association that returns a single object, or `has_many` for embedding a collection.
@@ -218,6 +262,7 @@ Options
218
262
  * `:mapper` : Use a specific for each instance, will be derived from the class name if omitted (see Policy vs Configuration)
219
263
  * `:collection_mapper` : For mapping the collection as a whole, this defaults to Yaks::CollectionMapper, but you can subclass it for example to add links or attributes on the collection itself
220
264
  * `:rel` : Set the relation (symbol or URI) this association has with the object. Will be derived from the association name and the configured rel_template if ommitted
265
+ * `:if`: Only render the association if a condition holds
221
266
  * `:link_if`: Conditionally render the association as a link. A `:href` option is required
222
267
 
223
268
  ```ruby
@@ -77,11 +77,7 @@ module Yaks
77
77
  protected
78
78
 
79
79
  def form_is_query?(form)
80
- method_is_get?(form.method) && !form.action.nil?
81
- end
82
-
83
- def method_is_get?(method)
84
- !method.nil? && method.downcase.to_sym === :get
80
+ form.method?(:get) && form.has_action?
85
81
  end
86
82
 
87
83
  def template_form_exists?(resource)
@@ -6,7 +6,8 @@ module Yaks
6
6
  item_mapper: Undefined,
7
7
  rel: Undefined,
8
8
  href: Undefined,
9
- link_if: Undefined
9
+ link_if: Undefined,
10
+ if: Undefined
10
11
  ),
11
12
  Util
12
13
 
@@ -21,6 +22,7 @@ module Yaks
21
22
  end
22
23
 
23
24
  def add_to_resource(resource, parent_mapper, context)
25
+ return resource if self.if != Undefined && !parent_mapper.expand_value(self.if)
24
26
  AssociationMapper.new(parent_mapper, self, context).call(resource)
25
27
  end
26
28
 
@@ -15,6 +15,7 @@ module Yaks
15
15
  )
16
16
  end
17
17
  def_forward :dynamic
18
+ def_forward :condition
18
19
  end
19
20
 
20
21
  def_delegators :config, :name, :action, :title, :method,
@@ -36,6 +37,7 @@ module Yaks
36
37
  include Concord.new(:config)
37
38
 
38
39
  def add_to_resource(resource, mapper, _context)
40
+ return resource if config.if && !mapper.expand_value(config.if)
39
41
  resource.add_form(to_resource(mapper))
40
42
  end
41
43
 
@@ -9,12 +9,17 @@ module Yaks
9
9
  method: nil,
10
10
  media_type: nil,
11
11
  fields: [],
12
- dynamic_blocks: []
12
+ dynamic_blocks: [],
13
+ if: nil
13
14
  )
14
15
 
15
16
  def dynamic(&blk)
16
17
  append_to(:dynamic_blocks, blk)
17
18
  end
19
+
20
+ def condition(prc = nil, &blk)
21
+ with(if: prc || blk)
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -28,14 +28,14 @@ module Yaks
28
28
  Resource::Form::Field.new(
29
29
  resource_attributes.each_with_object({}) do |attr, attrs|
30
30
  attrs[attr] = mapper.expand_value(public_send(attr))
31
- end.merge(options: resource_options)
31
+ end.merge(options: resource_options(mapper))
32
32
  )
33
33
  end
34
34
 
35
- def resource_options
35
+ def resource_options(mapper)
36
36
  # make sure all empty options arrays are the same instance,
37
37
  # makes for prettier #pp
38
- options.empty? ? options : options.map(&:to_resource)
38
+ options.empty? ? options : options.map {|opt| opt.to_resource_field_option(mapper) }
39
39
  end
40
40
 
41
41
  # All attributes that can be converted 1-to-1 to
@@ -10,8 +10,12 @@ module Yaks
10
10
  new(opts.merge(value: value))
11
11
  end
12
12
 
13
- def to_resource
14
- Resource::Form::Field::Option.new(to_h)
13
+ def to_resource_field_option(mapper)
14
+ Resource::Form::Field::Option.new(
15
+ value: mapper.expand_value(value),
16
+ label: mapper.expand_value(label),
17
+ selected: mapper.expand_value(selected),
18
+ )
15
19
  end
16
20
  end
17
21
  end
@@ -55,6 +55,8 @@ module Yaks
55
55
  end
56
56
 
57
57
  def map_to_resource_link(mapper)
58
+ return unless mapper.expand_value(options.fetch(:if, true))
59
+
58
60
  uri = mapper.expand_uri(template, options.fetch(:expand, true))
59
61
  return if uri.nil?
60
62
 
@@ -69,7 +71,7 @@ module Yaks
69
71
  options = options()
70
72
  options = options.merge(title: Resolve(options[:title], mapper)) if options.key?(:title)
71
73
  options = options.merge(templated: true) if templated?
72
- options.reject{|key| [:expand, :replace].include? key }
74
+ options.reject{|key| [:expand, :replace, :if].include? key }
73
75
  end
74
76
 
75
77
  end
@@ -14,8 +14,8 @@ module Yaks
14
14
  end
15
15
  end
16
16
 
17
- def fields_flat(fields = fields)
18
- fields.each_with_object([]) do |field, acc|
17
+ def fields_flat(fs = fields)
18
+ fs.each_with_object([]) do |field, acc|
19
19
  if field.type.equal? :fieldset
20
20
  acc.concat(fields_flat field.fields)
21
21
  else
@@ -23,6 +23,14 @@ module Yaks
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ def method?(meth)
28
+ !method.nil? && method.downcase.to_sym === meth.downcase.to_sym
29
+ end
30
+
31
+ def has_action?
32
+ !action.nil?
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -1,3 +1,3 @@
1
1
  module Yaks
2
- VERSION = '0.8.0.beta2'
2
+ VERSION = '0.8.0'
3
3
  end
@@ -7,7 +7,8 @@ RSpec.describe Yaks::Mapper::Association do
7
7
  item_mapper: mapper,
8
8
  rel: rel,
9
9
  href: href,
10
- link_if: link_if
10
+ link_if: link_if,
11
+ if: self.if
11
12
  )
12
13
  end
13
14
 
@@ -16,6 +17,7 @@ RSpec.describe Yaks::Mapper::Association do
16
17
  let(:rel) { Yaks::Undefined }
17
18
  let(:href) { Yaks::Undefined }
18
19
  let(:link_if) { Yaks::Undefined }
20
+ let(:if) { Yaks::Undefined }
19
21
 
20
22
  its(:name) { should equal :shoes }
21
23
  its(:item_mapper) { should equal Yaks::Mapper }
@@ -45,6 +47,23 @@ RSpec.describe Yaks::Mapper::Association do
45
47
  it 'should delegate to AssociationMapper' do
46
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
50
+
51
+ context 'with a truthy condition' do
52
+ let(:if) { ->{ true } }
53
+
54
+ it 'should add the association' do
55
+ expect(association.add_to_resource(Yaks::Resource.new, parent_mapper, yaks_context).subresources.length).to be 1
56
+ end
57
+ end
58
+
59
+ context 'with a falsey condition' do
60
+ let(:if) { ->{ false } }
61
+
62
+ it 'should not add the association' do
63
+ expect(association.add_to_resource(Yaks::Resource.new, parent_mapper, yaks_context).subresources.length).to be 0
64
+ end
65
+ end
66
+
48
67
  end
49
68
 
50
69
  describe '#render_as_link?' do
@@ -0,0 +1,19 @@
1
+ RSpec.describe Yaks::Mapper::Form::Field::Option do
2
+ include_context 'yaks context'
3
+
4
+ let(:mapper_class) do
5
+ Class.new(Yaks::Mapper) do
6
+ def color
7
+ :yellow
8
+ end
9
+ end
10
+ end
11
+
12
+ let(:mapper) { mapper_class.new(yaks_context) }
13
+
14
+ let(:option) { described_class.new(value: ->{color}, label: :color) }
15
+
16
+ it 'should expand procs in the context of the mapper' do
17
+ expect(option.to_resource_field_option(mapper)).to eql Yaks::Resource::Form::Field::Option.new(value: :yellow, label: :color, selected: false)
18
+ end
19
+ end
@@ -1,4 +1,6 @@
1
1
  RSpec.describe Yaks::Mapper::Form do
2
+ include_context 'yaks context'
3
+
2
4
  let(:form) { described_class.create( full_args ) }
3
5
  let(:name) { :the_name }
4
6
  let(:full_args) { {name: name}.merge(args) }
@@ -20,8 +22,8 @@ RSpec.describe Yaks::Mapper::Form do
20
22
  end
21
23
 
22
24
  describe '#add_to_resource' do
23
- let(:resource) { form.new.add_to_resource(Yaks::Resource.new, Yaks::Mapper.new(nil), nil) }
24
-
25
+ let(:mapper) { Yaks::Mapper.new(yaks_context) }
26
+ let(:resource) { form.new.add_to_resource(Yaks::Resource.new, mapper, nil) }
25
27
 
26
28
  context 'with fields' do
27
29
  let(:fields) {
@@ -34,7 +36,23 @@ RSpec.describe Yaks::Mapper::Form do
34
36
  )
35
37
  ]
36
38
  }
39
+ end
40
+
41
+ context 'with a truthy condition' do
42
+ let(:form) { described_class.create { condition { true }}}
37
43
 
44
+ it 'should add the form' do
45
+ expect(form.add_to_resource(Yaks::Resource.new, mapper, nil).forms.length).to be 1
46
+ end
38
47
  end
48
+
49
+ context 'with a falsey condition' do
50
+ let(:form) { described_class.create { condition { false }}}
51
+
52
+ it 'should not add the form' do
53
+ expect(form.add_to_resource(Yaks::Resource.new, mapper, nil).forms.length).to be 0
54
+ end
55
+ end
56
+
39
57
  end
40
58
  end
@@ -68,6 +68,26 @@ RSpec.describe Yaks::Mapper::Link do
68
68
  )
69
69
  end
70
70
  end
71
+
72
+ context 'with :if defined and resolving to true' do
73
+ let(:options) { { if: ->{ true } } }
74
+
75
+ it 'should render the link' do
76
+ expect(link.add_to_resource(Yaks::Resource.new, mapper, yaks_context)).to eql(
77
+ Yaks::Resource.new(links: [Yaks::Resource::Link.new(rel: :next, uri: "/foo/bar/3/4")])
78
+ )
79
+ end
80
+ end
81
+
82
+ context 'with :if defined and resolving to false' do
83
+ let(:options) { { if: ->{ false } } }
84
+
85
+ it 'should not render the link' do
86
+ expect(link.add_to_resource(Yaks::Resource.new, mapper, yaks_context)).to eql(
87
+ Yaks::Resource.new
88
+ )
89
+ end
90
+ end
71
91
  end
72
92
 
73
93
  describe '#rel?' do
@@ -174,6 +194,38 @@ RSpec.describe Yaks::Mapper::Link do
174
194
  expect(resource_link).to be_nil
175
195
  end
176
196
  end
197
+
198
+ context 'with a if lambda resolving to true' do
199
+ let(:options) { { if: -> { add_link? } } }
200
+
201
+ before do
202
+ mapper_class.class_eval do
203
+ def add_link?
204
+ true
205
+ end
206
+ end
207
+ end
208
+
209
+ it 'should evaluate the lambda in the context of the mapper' do
210
+ expect(resource_link).to be_a(Yaks::Resource::Link)
211
+ end
212
+ end
213
+
214
+ context 'with a if lambda resolving to false' do
215
+ let(:options) { { if: -> { add_link? } } }
216
+
217
+ before do
218
+ mapper_class.class_eval do
219
+ def add_link?
220
+ false
221
+ end
222
+ end
223
+ end
224
+
225
+ it 'should evaluate the lambda in the context of the mapper' do
226
+ expect(resource_link).to be_nil
227
+ end
228
+ end
177
229
  end
178
230
 
179
231
  describe '.create' do
@@ -49,4 +49,39 @@ RSpec.describe Yaks::Resource::Form do
49
49
  end
50
50
  end
51
51
 
52
+ describe "#method?" do
53
+ it 'should return true if method matches' do
54
+ form_sym = Yaks::Resource::Form.new(name: :foo, method: :get)
55
+ form_str = Yaks::Resource::Form.new(name: :foo, method: 'GET')
56
+
57
+ expect(form_sym.method?(:get)).to eq(true)
58
+ expect(form_sym.method?('get')).to eq(true)
59
+ expect(form_str.method?(:get)).to eq(true)
60
+ expect(form_str.method?('GET')).to eq(true)
61
+ end
62
+
63
+ it 'should return false if method does not match' do
64
+ form_sym = Yaks::Resource::Form.new(name: :foo, method: :get)
65
+ form_str = Yaks::Resource::Form.new(name: :foo, method: 'GET')
66
+
67
+ expect(form_sym.method?(:post)).to eq(false)
68
+ expect(form_sym.method?('patch')).to eq(false)
69
+ expect(form_str.method?(:delete)).to eq(false)
70
+ expect(form_str.method?('PUT')).to eq(false)
71
+ end
72
+ end
73
+
74
+ describe "#has_action?" do
75
+ it 'should return true if form has an action url' do
76
+ form = Yaks::Resource::Form.new(name: :foo, action: "/my-action")
77
+
78
+ expect(form.has_action?).to eq(true)
79
+ end
80
+
81
+ it 'should return false if form has not an action url' do
82
+ form = Yaks::Resource::Form.new(name: :foo)
83
+
84
+ expect(form.has_action?).to eq(false)
85
+ end
86
+ end
52
87
  end
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.8.0.beta2
4
+ version: 0.8.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: 2015-01-14 00:00:00.000000000 Z
11
+ date: 2015-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inflection
@@ -339,6 +339,7 @@ files:
339
339
  - spec/unit/yaks/mapper/association_spec.rb
340
340
  - spec/unit/yaks/mapper/attribute_spec.rb
341
341
  - spec/unit/yaks/mapper/config_spec.rb
342
+ - spec/unit/yaks/mapper/form/field/option_spec.rb
342
343
  - spec/unit/yaks/mapper/form/field_spec.rb
343
344
  - spec/unit/yaks/mapper/form_spec.rb
344
345
  - spec/unit/yaks/mapper/has_many_spec.rb
@@ -374,9 +375,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
374
375
  version: '0'
375
376
  required_rubygems_version: !ruby/object:Gem::Requirement
376
377
  requirements:
377
- - - ">"
378
+ - - ">="
378
379
  - !ruby/object:Gem::Version
379
- version: 1.3.1
380
+ version: '0'
380
381
  requirements: []
381
382
  rubyforge_project:
382
383
  rubygems_version: 2.2.2
@@ -429,6 +430,7 @@ test_files:
429
430
  - spec/unit/yaks/mapper/association_spec.rb
430
431
  - spec/unit/yaks/mapper/attribute_spec.rb
431
432
  - spec/unit/yaks/mapper/config_spec.rb
433
+ - spec/unit/yaks/mapper/form/field/option_spec.rb
432
434
  - spec/unit/yaks/mapper/form/field_spec.rb
433
435
  - spec/unit/yaks/mapper/form_spec.rb
434
436
  - spec/unit/yaks/mapper/has_many_spec.rb