attributor 4.0.1 → 4.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 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