attributor 4.0.1 → 4.1.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: bcf6ceb7deb1d4db6a3370de07f1af676c4ffd27
4
- data.tar.gz: 28e0d3d5091ab8c1663275ae4f5c2c46927cd186
3
+ metadata.gz: 28036c8d33153157e7118cae29dd5d0736248cb2
4
+ data.tar.gz: ea81f5e962a1f8f63f34dad0e41e4bd1899aebb2
5
5
  SHA512:
6
- metadata.gz: 322a5d22a2b1fcc8166c011b88fb09f4367f585e1c1fab94ef457a3132c2e0427b1a1cdf1aaeae67d5c653c813d4a11212f8f2be77250cab15e1bed711981cf4
7
- data.tar.gz: 83e5bee88b8119a4af387a767ba8a64580cbc79feb72cd7fe3870be707c2a7fb9d78a91ddcc0227a6b05c549980851b5a863b05c47862c312e9cd35b9fb1b075
6
+ metadata.gz: dacc030e86045803473c59ad89fce1d3ae1e7dd12fab67d42dbcde02e23477861e4cd45b0fdd3fd0d9628daec92b57d812d55f3ed2b60d10f86a2372446ff8c3
7
+ data.tar.gz: 27c876734a439ff0df35a2970dbd57b90821b16ab9710d99391b2948be0052ffb91d18b2730d99fbbb827739ad742aaedffa2c66b5a388de12b90e9e84ec58b5
data/CHANGELOG.md CHANGED
@@ -1,14 +1,28 @@
1
1
  # Attributor Changelog
2
2
 
3
- ## next
4
-
3
+ ## next
4
+
5
+ ## 4.1.0
6
+
7
+ * Added a `Class` type (useful to avoid demodulization coercions etc...)
8
+ * Added `Attributor::FieldSelector` type for parsing hierarchical field
9
+ selection hashes from a string. This is similar to the partial `fields`
10
+ parameter in Google APIs, or the `fields` parameter in the Facebook's Graph
11
+ API.
12
+ * For example: the string `'one,two(a,b)'` would select two top-level fields
13
+ named 'one' and 'two', retrieving the entire contents of 'one', and only
14
+ the 'a' and 'b' sub-fields for 'two'. The type will parse the above string
15
+ into the hash: `{one: true, two: {a: true, b: true}}`.
16
+ * This type is not automatically required by Attributor. To require it use:
17
+ `require 'attributor/extras/field_selector'.
18
+ * This type also depends upon the 'parslet' gem.
5
19
 
6
20
  ## 4.0.1
7
21
 
8
22
  * `Attribute#check_option!` now calls `load` on any provided value.
9
23
 
10
24
 
11
- ## 4.0.0 next
25
+ ## 4.0.0
12
26
 
13
27
  * Changed the expectation of the value for an `:example` option of an attribute:
14
28
  * Before, passing an array of values would indicate that those were a few possible examples for it.
@@ -154,4 +168,3 @@
154
168
  * procs that take 2 arguments now receive the context as the second argument.
155
169
  * Circular references are now detected and handled in validation and dumping.
156
170
  * Fixed bug with Model attribute accessors when using false values.
157
-
data/README.md CHANGED
@@ -1,7 +1,11 @@
1
- # Attributor [![TravisCI][travis-img-url]][travis-ci-url]
1
+ # Attributor [![TravisCI][travis-img-url]][travis-ci-url] [![Coverage Status][coveralls-img-url]][coveralls-url] [![Dependency Status][gemnasium-img-url]][gemnasium-url]
2
2
 
3
3
  [travis-img-url]: https://travis-ci.org/rightscale/attributor.svg?branch=master
4
4
  [travis-ci-url]:https://travis-ci.org/rightscale/attributor
5
+ [coveralls-img-url]:https://coveralls.io/repos/rightscale/attributor/badge.svg?branch=master&service=github
6
+ [coveralls-url]:https://coveralls.io/github/rightscale/attributor?branch=master
7
+ [gemnasium-img-url]:https://gemnasium.com/rightscale/attributor.svg
8
+ [gemnasium-url]:https://gemnasium.com/rightscale/attributor
5
9
 
6
10
  An Attribute management, self documenting framework, designed for getting rid of most of your parameter handling boilerplate.
7
11
  While initially designed to be the backbone for parameter handling in REST services, attribute management can be applied in many other areas.
data/attributor.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_runtime_dependency(%q<hashie>, ["~> 3"])
23
23
  spec.add_runtime_dependency(%q<randexp>, ["~> 0"])
24
24
  spec.add_runtime_dependency(%q<activesupport>, ['>= 3'])
25
+
25
26
  spec.add_development_dependency(%q<rspec>, ["< 2.99"])
26
27
  spec.add_development_dependency(%q<yard>, ["~> 0.8.7"])
27
28
  spec.add_development_dependency(%q<backports>, ["~> 3"])
@@ -29,12 +30,13 @@ Gem::Specification.new do |spec|
29
30
  spec.add_development_dependency(%q<redcarpet>, ["< 3.0"])
30
31
  spec.add_development_dependency(%q<bundler>, [">= 0"])
31
32
  spec.add_development_dependency(%q<rake-notes>, ["~> 0"])
32
- spec.add_development_dependency(%q<simplecov>, ["~> 0"])
33
+ spec.add_development_dependency(%q<coveralls>)
33
34
  spec.add_development_dependency(%q<guard>, ["~> 2"])
34
35
  spec.add_development_dependency(%q<guard-rspec>, ["~> 4"])
35
36
  spec.add_development_dependency(%q<pry>, ["~> 0"])
36
37
  spec.add_development_dependency(%q<pry-byebug>, ["~> 1"])
37
38
  spec.add_development_dependency(%q<pry-stack_explorer>, ["~> 0"])
38
39
  spec.add_development_dependency(%q<fuubar>, ["~> 1"])
39
- end
40
40
 
41
+ spec.add_development_dependency(%q<parslet>, [">= 0"])
42
+ end
@@ -0,0 +1,44 @@
1
+ begin
2
+ require 'parslet'
3
+ rescue LoadError
4
+ warn "Attributor::FieldSelector requires the 'parslet' gem, which can not be found. " +
5
+ "Please make sure it's in your Gemfile or installed in your system."
6
+ end
7
+
8
+ module Attributor
9
+ class FieldSelector
10
+ require 'attributor/extras/field_selector/parser'
11
+ require 'attributor/extras/field_selector/transformer'
12
+
13
+ include Attributor::Type
14
+
15
+ def self.native_type
16
+ ::Hash
17
+ end
18
+
19
+ def self.example(_context = nil, _options = {})
20
+ 3.times.each_with_object([]) do |_i, array|
21
+ array << /\w{5,8}/.gen
22
+ end.join(',')
23
+ end
24
+
25
+ def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
26
+ return nil if value.nil?
27
+ return value if self.valid_type? value
28
+ return {} if value.empty?
29
+
30
+ parsed = Parser.new.parse(value)
31
+ Transformer.new.apply(parsed)
32
+ rescue
33
+ raise CoercionError, context: context, from: value.class, to: self, value: value
34
+ end
35
+
36
+ def self.validate(_value, _context = nil, _attribute)
37
+ [].freeze
38
+ end
39
+
40
+ def self.valid_type?(value)
41
+ return true if value.is_a?(native_type) || value.is_a?(self.class)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ module Attributor
2
+ class FieldSelector
3
+ class Parser < Parslet::Parser
4
+ rule(:simple_name) { match('[a-zA-Z0-9_]').repeat(1) }
5
+ rule(:item) { simple_name.as(:field) >> parenthesized.repeat(0).as(:children) }
6
+ rule(:parenthesized) { str('(') >> csv >> str(')') }
7
+ rule(:csv) { (item >> (str(',') >> item).repeat(0)).as(:csv) }
8
+ root(:csv)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module Attributor
2
+ class FieldSelector
3
+ class Transformer < Parslet::Transform
4
+
5
+ rule(field: simple(:field_token), children: subtree(:children_tree)) do
6
+ cs = if children_tree.empty?
7
+ true
8
+ else
9
+ children_tree.each_with_object({}) do |item, hash|
10
+ hash.merge! item
11
+ end
12
+ end
13
+ { field_token.to_sym => cs }
14
+ end
15
+
16
+ rule(csv: subtree(:csv_tree)) do
17
+ case csv_tree
18
+ when ::Hash
19
+ csv_tree
20
+ when Array
21
+ csv_tree.each_with_object({}) do |item, hash|
22
+ hash.merge! item
23
+ end
24
+ else
25
+ raise "Oops...didn't know this could happen! (this is not a Hash or an Array?). Got a #{csv_tree.class.name} : #{csv_tree.inspect}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,11 +4,10 @@ module Attributor
4
4
  # TODO: should this be a mixin since it is an abstract class?
5
5
  module Type
6
6
 
7
- def self.included( klass )
7
+ def self.included(klass)
8
8
  klass.extend(ClassMethods)
9
9
  end
10
10
 
11
-
12
11
  module ClassMethods
13
12
 
14
13
  # Does this type support the generation of subtypes?
@@ -58,13 +57,6 @@ module Attributor
58
57
  # Default, overridable example function
59
58
  def example(context=nil, options:{})
60
59
  raise AttributorException.new("#{self} must implement #example")
61
- # return options[:example] if options.has_key? :example
62
- # return options[:default] if options.has_key? :default
63
- # if options.has_key? :values
64
- # vals = options[:values]
65
- # return vals[rand(vals.size)]
66
- # end
67
- # return nil
68
60
  end
69
61
 
70
62
 
@@ -12,6 +12,7 @@ module Attributor
12
12
  end
13
13
 
14
14
  def self.load(value, context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
15
+ return value if value.is_a?(self.native_type)
15
16
  return @klass || nil if value.nil?
16
17
 
17
18
  # Must be given a String object or nil
@@ -388,11 +388,11 @@ module Attributor
388
388
  hash.set(self.extra_keys, v, context: sub_context, recurse: recurse)
389
389
  end
390
390
 
391
- object.each do |k,v|
391
+ object.each do |k,val|
392
392
  next if k == self.extra_keys
393
393
 
394
394
  sub_context = self.generate_subcontext(context,k)
395
- hash.set(k, v, context: sub_context, recurse: recurse)
395
+ hash.set(k, val, context: sub_context, recurse: recurse)
396
396
  end
397
397
 
398
398
  # handle default values for missing keys
@@ -447,6 +447,10 @@ module Attributor
447
447
  @contents[k]
448
448
  end
449
449
 
450
+ def _get_attr(k)
451
+ self[k]
452
+ end
453
+
450
454
  def []=(k,v)
451
455
  @contents[k] = v
452
456
  end
@@ -18,8 +18,12 @@ module Attributor
18
18
 
19
19
  def self.example(context=nil, options:{})
20
20
  if options[:regexp]
21
- # It may fail to generate an example, see bug #72.
22
- options[:regexp].gen rescue ('Failed to generate example for %s' % options[:regexp].inspect)
21
+ begin
22
+ # It may fail to generate an example, see bug #72.
23
+ options[:regexp].gen
24
+ rescue => e
25
+ 'Failed to generate example for %s : %s' % [ options[:regexp].inspect, e.message]
26
+ end
23
27
  else
24
28
  /\w+/.gen
25
29
  end
@@ -45,7 +45,7 @@ module Attributor
45
45
 
46
46
  # Two structs are equal if their attributes are equal
47
47
  def ==(other_object)
48
- return false if other_object == nil
48
+ return false if other_object == nil || !other_object.respond_to?(:attributes)
49
49
  self.attributes == other_object.attributes
50
50
  end
51
51
 
@@ -38,6 +38,10 @@ module Attributor
38
38
  end
39
39
  end
40
40
 
41
+ def self.dump(value, **opts)
42
+ value.to_s
43
+ end
44
+
41
45
  def self.validate(value,context=Attributor::DEFAULT_ROOT_CONTEXT,attribute)
42
46
  errors = []
43
47
 
@@ -1,3 +1,3 @@
1
1
  module Attributor
2
- VERSION = "4.0.1"
2
+ VERSION = "4.1.0"
3
3
  end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'attributor/extras/field_selector'
3
+
4
+ describe Attributor::FieldSelector do
5
+ subject(:type) { Attributor::FieldSelector }
6
+
7
+ it 'loads a Hash' do
8
+ expect(subject.load('one,two')).to be_kind_of(::Hash)
9
+ end
10
+
11
+ context 'loading all the test combinations' do
12
+ cases = {
13
+ nil => nil,
14
+ '' => {},
15
+ 'one' => { one: true },
16
+ 'one,two,three' => { one: true, two: true, three: true },
17
+ 'one,two(a,b),three' => { one: true, two: { a: true, b: true }, three: true },
18
+ 'one,two(a,b,c(A,B)),three' => {
19
+ one: true,
20
+ two: {
21
+ a: true,
22
+ b: true,
23
+ c: { A: true, B: true }
24
+ },
25
+ three: true
26
+ }
27
+ }
28
+
29
+ cases.each do |fields, result|
30
+ it "loads #{fields.inspect}" do
31
+ loaded = subject.load(fields)
32
+ loaded.should eq result
33
+ end
34
+ end
35
+ end
36
+ end
data/spec/spec_helper.rb CHANGED
@@ -4,8 +4,8 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
4
  $LOAD_PATH.unshift(File.dirname(__FILE__))
5
5
 
6
6
  # Configure simplecov gem (must be here at top of file)
7
- require 'simplecov'
8
- SimpleCov.start do
7
+ require 'coveralls'
8
+ Coveralls.wear! do
9
9
  add_filter 'spec' # Don't include RSpec stuff
10
10
  add_group 'Types', 'lib/attributor/types'
11
11
  end
@@ -30,6 +30,14 @@ describe Attributor::Class do
30
30
  end
31
31
  end
32
32
 
33
+ context 'for incoming Class values' do
34
+ [Object, ::Object, ::Hash, Attributor::Struct].each do |value|
35
+ it "loads '#{value}' as #{value}" do
36
+ type.load(value).should eq(value)
37
+ end
38
+ end
39
+ end
40
+
33
41
  context 'when created using .of method' do
34
42
  let(:klass) { Integer }
35
43
  subject(:type) { Attributor::Class.of(klass) }
@@ -24,6 +24,7 @@ describe Attributor::String do
24
24
  expect {
25
25
  val = Attributor::String.example(options:{regexp: regex})
26
26
  val.should be_a(::String)
27
+ val.should =~ /Failed to generate.+is too vague/
27
28
  }.to_not raise_error
28
29
  end
29
30
 
@@ -12,6 +12,12 @@ describe Attributor::URI do
12
12
  end
13
13
  end
14
14
 
15
+ context '.dump' do
16
+ let(:example){ type.example }
17
+ it 'uses the underlying URI to_s' do
18
+ expect(type.dump(example)).to eq(example.to_s)
19
+ end
20
+ end
15
21
  context '.load' do
16
22
  subject(:load) { type.load(value) }
17
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attributor
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep M. Blanquer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-08-27 00:00:00.000000000 Z
12
+ date: 2015-10-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hashie
@@ -152,17 +152,17 @@ dependencies:
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
154
  - !ruby/object:Gem::Dependency
155
- name: simplecov
155
+ name: coveralls
156
156
  requirement: !ruby/object:Gem::Requirement
157
157
  requirements:
158
- - - "~>"
158
+ - - ">="
159
159
  - !ruby/object:Gem::Version
160
160
  version: '0'
161
161
  type: :development
162
162
  prerelease: false
163
163
  version_requirements: !ruby/object:Gem::Requirement
164
164
  requirements:
165
- - - "~>"
165
+ - - ">="
166
166
  - !ruby/object:Gem::Version
167
167
  version: '0'
168
168
  - !ruby/object:Gem::Dependency
@@ -249,6 +249,20 @@ dependencies:
249
249
  - - "~>"
250
250
  - !ruby/object:Gem::Version
251
251
  version: '1'
252
+ - !ruby/object:Gem::Dependency
253
+ name: parslet
254
+ requirement: !ruby/object:Gem::Requirement
255
+ requirements:
256
+ - - ">="
257
+ - !ruby/object:Gem::Version
258
+ version: '0'
259
+ type: :development
260
+ prerelease: false
261
+ version_requirements: !ruby/object:Gem::Requirement
262
+ requirements:
263
+ - - ">="
264
+ - !ruby/object:Gem::Version
265
+ version: '0'
252
266
  description:
253
267
  email:
254
268
  - blanquer@gmail.com
@@ -274,6 +288,9 @@ files:
274
288
  - lib/attributor/example_mixin.rb
275
289
  - lib/attributor/exceptions.rb
276
290
  - lib/attributor/extensions/randexp.rb
291
+ - lib/attributor/extras/field_selector.rb
292
+ - lib/attributor/extras/field_selector/parser.rb
293
+ - lib/attributor/extras/field_selector/transformer.rb
277
294
  - lib/attributor/families/numeric.rb
278
295
  - lib/attributor/families/temporal.rb
279
296
  - lib/attributor/type.rb
@@ -304,6 +321,7 @@ files:
304
321
  - spec/attribute_spec.rb
305
322
  - spec/attributor_spec.rb
306
323
  - spec/dsl_compiler_spec.rb
324
+ - spec/extras/field_selector/field_selector_spec.rb
307
325
  - spec/families_spec.rb
308
326
  - spec/spec_helper.rb
309
327
  - spec/support/hashes.rb
@@ -349,7 +367,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
349
367
  version: '0'
350
368
  requirements: []
351
369
  rubyforge_project:
352
- rubygems_version: 2.4.5
370
+ rubygems_version: 2.4.5.1
353
371
  signing_key:
354
372
  specification_version: 4
355
373
  summary: A powerful attribute and type management library for Ruby
@@ -358,6 +376,7 @@ test_files:
358
376
  - spec/attribute_spec.rb
359
377
  - spec/attributor_spec.rb
360
378
  - spec/dsl_compiler_spec.rb
379
+ - spec/extras/field_selector/field_selector_spec.rb
361
380
  - spec/families_spec.rb
362
381
  - spec/spec_helper.rb
363
382
  - spec/support/hashes.rb