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 +4 -4
- data/CHANGELOG.md +17 -4
- data/README.md +5 -1
- data/attributor.gemspec +4 -2
- data/lib/attributor/extras/field_selector.rb +44 -0
- data/lib/attributor/extras/field_selector/parser.rb +11 -0
- data/lib/attributor/extras/field_selector/transformer.rb +30 -0
- data/lib/attributor/type.rb +1 -9
- data/lib/attributor/types/class.rb +1 -0
- data/lib/attributor/types/hash.rb +6 -2
- data/lib/attributor/types/string.rb +6 -2
- data/lib/attributor/types/struct.rb +1 -1
- data/lib/attributor/types/uri.rb +4 -0
- data/lib/attributor/version.rb +1 -1
- data/spec/extras/field_selector/field_selector_spec.rb +36 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/types/class_spec.rb +8 -0
- data/spec/types/string_spec.rb +1 -0
- data/spec/types/uri_spec.rb +6 -0
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28036c8d33153157e7118cae29dd5d0736248cb2
|
4
|
+
data.tar.gz: ea81f5e962a1f8f63f34dad0e41e4bd1899aebb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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<
|
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
|
data/lib/attributor/type.rb
CHANGED
@@ -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(
|
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
|
|
@@ -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,
|
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,
|
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
|
-
|
22
|
-
|
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
|
|
data/lib/attributor/types/uri.rb
CHANGED
data/lib/attributor/version.rb
CHANGED
@@ -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 '
|
8
|
-
|
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
|
data/spec/types/class_spec.rb
CHANGED
@@ -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) }
|
data/spec/types/string_spec.rb
CHANGED
data/spec/types/uri_spec.rb
CHANGED
@@ -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
|
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-
|
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:
|
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
|