attributor 4.0.0 → 4.0.1

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: 64d1a212c179718aacc7837f4484062a4987e4d3
4
- data.tar.gz: 5d2f971e22f191497e2f29bb95e8f84e73a8a52f
3
+ metadata.gz: bcf6ceb7deb1d4db6a3370de07f1af676c4ffd27
4
+ data.tar.gz: 28e0d3d5091ab8c1663275ae4f5c2c46927cd186
5
5
  SHA512:
6
- metadata.gz: f33c468a57b20fd6e91bf58747513b19856d9c62b2c61ddf96e718a91bb0cfa9e3fa1f85c62442e5735df3280707d3adc70bf1b11c4f9c90b7ff5511668affe3
7
- data.tar.gz: 14084572c335799325e89195620520176ff57360bf1732a3eee9c86a00fce331a842ca8790a2aaa2743eccd49cc012a84bc7d5aec5f525659a108885ef54543e
6
+ metadata.gz: 322a5d22a2b1fcc8166c011b88fb09f4367f585e1c1fab94ef457a3132c2e0427b1a1cdf1aaeae67d5c653c813d4a11212f8f2be77250cab15e1bed711981cf4
7
+ data.tar.gz: 83e5bee88b8119a4af387a767ba8a64580cbc79feb72cd7fe3870be707c2a7fb9d78a91ddcc0227a6b05c549980851b5a863b05c47862c312e9cd35b9fb1b075
data/.gitignore CHANGED
@@ -16,3 +16,5 @@ doc
16
16
 
17
17
  Gemfile.lock
18
18
  pkg
19
+
20
+ *.swp
@@ -1,6 +1,11 @@
1
1
  # Attributor Changelog
2
2
 
3
- ## next
3
+ ## next
4
+
5
+
6
+ ## 4.0.1
7
+
8
+ * `Attribute#check_option!` now calls `load` on any provided value.
4
9
 
5
10
 
6
11
  ## 4.0.0 next
data/Rakefile CHANGED
@@ -14,6 +14,14 @@ RSpec::Core::RakeTask.new do |spec|
14
14
  spec.pattern = FileList['spec/**/*_spec.rb']
15
15
  end
16
16
 
17
+ desc "console"
18
+ task :console do
19
+ require 'bundler'
20
+ Bundler.require(:default, :development, :test)
21
+ require_relative 'lib/attributor'
22
+ pry
23
+ end
24
+
17
25
  task :default => :spec
18
26
 
19
27
  require 'yard'
@@ -10,17 +10,18 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ["Josep M. Blanquer","Dane Jensen"]
11
11
  spec.summary = "A powerful attribute and type management library for Ruby"
12
12
  spec.email = ["blanquer@gmail.com","dane.jensen@gmail.com"]
13
-
13
+
14
14
  spec.homepage = "https://github.com/rightscale/attributor"
15
15
  spec.license = "MIT"
16
16
  spec.required_ruby_version = ">=2.1"
17
-
17
+
18
18
  spec.require_paths = ["lib"]
19
19
  spec.files = `git ls-files -z`.split("\x0")
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
-
21
+
22
22
  spec.add_runtime_dependency(%q<hashie>, ["~> 3"])
23
23
  spec.add_runtime_dependency(%q<randexp>, ["~> 0"])
24
+ spec.add_runtime_dependency(%q<activesupport>, ['>= 3'])
24
25
  spec.add_development_dependency(%q<rspec>, ["< 2.99"])
25
26
  spec.add_development_dependency(%q<yard>, ["~> 0.8.7"])
26
27
  spec.add_development_dependency(%q<backports>, ["~> 3"])
@@ -45,7 +45,7 @@ module Attributor
45
45
  end
46
46
 
47
47
  def self.type_name(type)
48
- return self.type_name(type.class) unless type.kind_of?(Class)
48
+ return self.type_name(type.class) unless type.kind_of?(::Class)
49
49
 
50
50
  type.ancestors.find { |k| k.name && !k.name.empty? }.name
51
51
  end
@@ -75,7 +75,7 @@ module Attributor
75
75
  end
76
76
 
77
77
  MODULE_PREFIX = "Attributor\:\:".freeze
78
- MODULE_PREFIX_REGEX = Regexp.new(MODULE_PREFIX)
78
+ MODULE_PREFIX_REGEX = ::Regexp.new(MODULE_PREFIX)
79
79
 
80
80
  require_relative 'attributor/families/numeric'
81
81
  require_relative 'attributor/families/temporal'
@@ -91,11 +91,13 @@ module Attributor
91
91
  require_relative 'attributor/types/time'
92
92
  require_relative 'attributor/types/date'
93
93
  require_relative 'attributor/types/date_time'
94
+ require_relative 'attributor/types/regexp'
94
95
  require_relative 'attributor/types/float'
95
96
  require_relative 'attributor/types/collection'
96
97
  require_relative 'attributor/types/hash'
97
98
  require_relative 'attributor/types/model'
98
99
  require_relative 'attributor/types/struct'
100
+ require_relative 'attributor/types/class'
99
101
 
100
102
 
101
103
  require_relative 'attributor/types/csv'
@@ -67,7 +67,7 @@ module Attributor
67
67
  defined_val.call
68
68
  end
69
69
  else
70
- defined_val
70
+ defined_val
71
71
  end
72
72
  value = val #Need to load?
73
73
  end
@@ -124,7 +124,7 @@ module Attributor
124
124
  description[:type] = self.type.describe(shallow, example: example )
125
125
  # Move over any example from the type, into the attribute itself
126
126
  if ( ex = description[:type].delete(:example) )
127
- description[:example] = self.dump( ex ).to_s
127
+ description[:example] = self.dump(ex)
128
128
  end
129
129
 
130
130
  description
@@ -286,6 +286,7 @@ module Attributor
286
286
  raise AttributorException.new("Allowed set of values requires an array. Got (#{definition})") unless definition.is_a? ::Array
287
287
  when :default
288
288
  raise AttributorException.new("Default value doesn't have the correct attribute type. Got (#{definition.inspect})") unless self.type.valid_type?(definition) || definition.kind_of?(Proc)
289
+ self.options[:default] = self.load(definition) unless definition.kind_of?(Proc)
289
290
  when :description
290
291
  raise AttributorException.new("Description value must be a string. Got (#{definition})") unless definition.is_a? ::String
291
292
  when :required
@@ -0,0 +1,56 @@
1
+ require 'active_support'
2
+
3
+ require_relative '../exceptions'
4
+
5
+
6
+ module Attributor
7
+ class Class
8
+ include Type
9
+
10
+ def self.native_type
11
+ return ::Class
12
+ end
13
+
14
+ def self.load(value, context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
15
+ return @klass || nil if value.nil?
16
+
17
+ # Must be given a String object or nil
18
+ unless value.kind_of?(::String) || value.nil?
19
+ raise IncompatibleTypeError, context: context, value_type: value.class, type: self
20
+ end
21
+
22
+ value = "::" + value if value[0..1] != '::'
23
+ result = value.constantize
24
+
25
+ # Class given must match class specified when type created using .of() method
26
+ unless @klass.nil? || result == @klass
27
+ raise LoadError, "Error loading class #{value} for attribute with " +
28
+ "defined class #{@klass} while loading #{Attributor.humanize_context(context)}."
29
+ end
30
+
31
+ result
32
+ end
33
+
34
+ def self.example(context=nil, options:{})
35
+ @klass.nil? ? "MyClass" : @klass.name
36
+ end
37
+
38
+ # Create a Class attribute type of a specific Class.
39
+ #
40
+ # @param klass [Class] optional, defines the class of this attribute, if constant
41
+ #
42
+ # @return anonymous class with specified type of collection members
43
+ #
44
+ # @example Class.of(Factory)
45
+ #
46
+ def self.of(klass)
47
+ ::Class.new(self) do
48
+ @klass = klass
49
+ end
50
+ end
51
+
52
+ def self.family
53
+ 'string'
54
+ end
55
+ end
56
+ end
@@ -16,7 +16,7 @@ module Attributor
16
16
  unless resolved_type.ancestors.include?(Attributor::Type)
17
17
  raise Attributor::AttributorException.new("Collections can only have members that are Attributor::Types")
18
18
  end
19
- Class.new(self) do
19
+ ::Class.new(self) do
20
20
  @member_type = resolved_type
21
21
  end
22
22
  end
@@ -53,7 +53,7 @@ module Attributor
53
53
  def self.member_attribute
54
54
  @member_attribute ||= begin
55
55
  self.construct(nil,{})
56
-
56
+
57
57
  @member_attribute
58
58
  end
59
59
  end
@@ -129,7 +129,7 @@ module Attributor
129
129
 
130
130
  # @example Hash.of(key: String, value: Integer)
131
131
  def self.of(key: @key_type, value: @value_type)
132
- Class.new(self) do
132
+ ::Class.new(self) do
133
133
  self.key_type = key
134
134
  self.value_type = value
135
135
  @keys = {}
@@ -164,8 +164,8 @@ module Attributor
164
164
  example_depth = context.size
165
165
 
166
166
  self.keys.each do |sub_attribute_name, sub_attribute|
167
-
168
-
167
+
168
+
169
169
  if sub_attribute.attributes
170
170
  # TODO: add option to raise an exception in this case?
171
171
  next if example_depth > MAX_EXAMPLE_DEPTH
@@ -12,7 +12,7 @@ module Attributor
12
12
  raise AttributorException, "#{type.name} does not have attribute with name '#{identity_name}'"
13
13
  end
14
14
 
15
- Class.new(self) do
15
+ ::Class.new(self) do
16
16
  @member_attribute = identity_attribute
17
17
  @member_type = identity_attribute.type
18
18
  end
@@ -0,0 +1,30 @@
1
+ require_relative '../exceptions'
2
+
3
+ module Attributor
4
+ class Regexp
5
+ include Type
6
+
7
+ def self.native_type
8
+ return ::Regexp
9
+ end
10
+
11
+ def self.load(value, context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
12
+ unless value.kind_of?(::String) || value.nil?
13
+ raise IncompatibleTypeError, context: context, value_type: value.class, type: self
14
+ end
15
+
16
+ value && ::Regexp.new(value)
17
+ rescue
18
+ super
19
+ end
20
+
21
+ def self.example(context=nil, options:{})
22
+ ::Regexp.new(/^pattern\d{0,3}$/).to_s
23
+ end
24
+
25
+ def self.family
26
+ 'string'
27
+ end
28
+
29
+ end
30
+ end
@@ -1,8 +1,8 @@
1
1
 
2
2
  module Attributor
3
3
  class Struct < Attributor::Model
4
-
5
- def self.constructable?
4
+
5
+ def self.constructable?
6
6
  true
7
7
  end
8
8
 
@@ -26,7 +26,7 @@ module Attributor
26
26
  end
27
27
  end
28
28
 
29
- Class.new(self) do
29
+ ::Class.new(self) do
30
30
  attributes options, &attribute_definition
31
31
  end
32
32
 
@@ -39,12 +39,26 @@ module Attributor
39
39
  end
40
40
 
41
41
  def self.validate(value,context=Attributor::DEFAULT_ROOT_CONTEXT,attribute)
42
- []
42
+ errors = []
43
+
44
+ if attribute && (definition = attribute.options[:path])
45
+ unless value.path =~ attribute.options[:path]
46
+ errors << "#{Attributor.humanize_context(context)} value (#{value}) does not match path (#{definition.inspect})"
47
+ end
48
+ end
49
+ errors
43
50
  end
44
51
 
45
- def check_option!(name, definition)
46
- # No options are supported
47
- :unknown
52
+ def self.check_option!(name, definition)
53
+ case name
54
+ when :path
55
+ unless definition.is_a? ::Regexp
56
+ raise AttributorException.new("Value for option :path is not a Regexp object. Got (#{definition.inspect})")
57
+ end
58
+ :ok
59
+ else
60
+ :unknown
61
+ end
48
62
  end
49
63
 
50
64
  end
@@ -1,3 +1,3 @@
1
1
  module Attributor
2
- VERSION = "4.0.0"
2
+ VERSION = "4.0.1"
3
3
  end
@@ -87,3 +87,10 @@ class Address < Attributor::Model
87
87
  end
88
88
 
89
89
 
90
+ class Post < Attributor::Model
91
+ attributes do
92
+ attribute :title, String
93
+ attribute :tags, Attributor::Collection.of(String)
94
+ end
95
+ end
96
+
@@ -0,0 +1,58 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+ require 'backports'
3
+
4
+ describe Attributor::Class do
5
+
6
+ subject(:type) { Attributor::Class }
7
+
8
+ its(:native_type) { should be(::Class) }
9
+ its(:family) { should == 'string' }
10
+
11
+ context '.example' do
12
+ its(:example) { should be_a(::String) }
13
+
14
+ context 'when created using .of method' do
15
+ let(:klass) { Integer }
16
+ subject(:type) { Attributor::Class.of(klass) }
17
+
18
+ its(:example) { should eq(klass.to_s) }
19
+ end
20
+ end
21
+
22
+ context '.load' do
23
+ let(:value) { nil }
24
+
25
+ context 'for incoming String values' do
26
+ ['Object', '::Object', '::Hash', 'Attributor::Struct'].each do |value|
27
+ it "loads '#{value}' as #{eval(value)}" do
28
+ type.load(value).should eq(value.constantize)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when created using .of method' do
34
+ let(:klass) { Integer }
35
+ subject(:type) { Attributor::Class.of(klass) }
36
+
37
+ it "loads 'Integer' as Integer" do
38
+ type.load('Integer').should eq(Integer)
39
+ end
40
+
41
+ it "returns specified class for nil" do
42
+ type.load(nil).should be(klass)
43
+ end
44
+
45
+ it "raises when given a class that doesn't match specified class" do
46
+ expect { type.load('Float') }.to raise_exception(Attributor::LoadError)
47
+ end
48
+ end
49
+
50
+ it 'returns nil for nil' do
51
+ type.load(nil).should be(nil)
52
+ end
53
+
54
+ it 'raises when given a non-String' do
55
+ expect {type.load(1)}.to raise_exception(Attributor::IncompatibleTypeError)
56
+ end
57
+ end
58
+ end
@@ -564,8 +564,8 @@ context 'attributes' do
564
564
  description[:attributes].keys.should =~ type.keys.keys
565
565
  description[:attributes].each do |name,sub_description|
566
566
  sub_description.should have_key(:example)
567
- val = type.attributes[name].dump( example[name] ).to_s
568
- sub_description[:example].should eq( val )
567
+ val = type.attributes[name].dump(example[name])
568
+ sub_description[:example].should eq val
569
569
  end
570
570
  end
571
571
  end
@@ -242,6 +242,30 @@ describe Attributor::Model do
242
242
  #raise_error(Attributor::AttributorException, /Unknown attributes.*#{context.join('.')}/)
243
243
  end
244
244
  end
245
+
246
+ context 'loading with default values' do
247
+ let(:reference) { Post }
248
+ let(:options) { {reference: reference} }
249
+
250
+ let(:attribute_definition) do
251
+ proc do
252
+ attribute :title
253
+ attribute :tags, default: ['stuff', 'things']
254
+ end
255
+ end
256
+
257
+ let(:struct) { Attributor::Struct.construct(attribute_definition, options)}
258
+
259
+ let(:data) { {title: 'my post'} }
260
+
261
+ subject(:loaded) { struct.load(data) }
262
+
263
+
264
+ it 'validates' do
265
+ expect(loaded.validate).to be_empty
266
+ end
267
+
268
+ end
245
269
  end
246
270
 
247
271
  end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+
3
+ describe Attributor::Regexp do
4
+
5
+ subject(:type) { Attributor::Regexp }
6
+
7
+ its(:native_type) { should be(::Regexp) }
8
+ its(:example) { should be_a(::String) }
9
+ its(:family) { should == 'string' }
10
+
11
+ context '.load' do
12
+ let(:value) { nil }
13
+
14
+ it 'returns nil for nil' do
15
+ type.load(nil).should be(nil)
16
+ end
17
+
18
+ context 'for incoming String values' do
19
+
20
+ { 'foo' => /foo/, '^pattern$' => /^pattern$/ }.each do |value, expected|
21
+ it "loads '#{value}' as #{expected.inspect}" do
22
+ type.load(value).should eq(expected)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -1,12 +1,103 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+
1
3
  describe Attributor::URI do
2
4
 
3
5
  subject(:type) { Attributor::URI }
4
6
 
5
7
  its(:native_type) { should be ::URI::Generic }
6
8
 
7
- it 'check_option!' do
8
- # No options supported thus far
9
- expect(type.check_option!(:foo, nil )).to be(:unknown)
9
+ context '.example' do
10
+ it 'returns a valid URI' do
11
+ expect(type.example).to be_kind_of(URI)
12
+ end
10
13
  end
11
14
 
15
+ context '.load' do
16
+ subject(:load) { type.load(value) }
17
+
18
+ context 'given a nil' do
19
+ let(:value) { nil }
20
+ it 'returns a nil' do
21
+ expect(subject).to be_nil
22
+ end
23
+ end
24
+
25
+ context 'given a string' do
26
+ let(:value) { 'string' }
27
+ it 'returns a URI object' do
28
+ expect(subject).to be_kind_of(URI)
29
+ end
30
+ end
31
+
32
+ context 'given a URI object' do
33
+ let(:value) { URI.parse('string') }
34
+ it 'returns itself' do
35
+ expect(subject).to eq(value)
36
+ end
37
+ end
38
+
39
+ context 'given a value not its native_type' do
40
+ let(:value) { Class.new }
41
+ it 'raises an error' do
42
+ expect { subject }.to raise_error(Attributor::CoercionError)
43
+ end
44
+ end
45
+ end
46
+
47
+ context '.validate' do
48
+ let(:uri) { URI.parse('http://www.example.com/something/foo') }
49
+ let(:attribute) { nil }
50
+ subject(:validate) { type.validate(uri, ['root'], attribute) }
51
+
52
+ context 'when given a valid URI' do
53
+ it 'does not return any errors' do
54
+ expect(subject).to be_empty
55
+ end
56
+
57
+ context 'when given a path option' do
58
+ let(:attribute) { Attributor::Attribute.new(type, path: /^\//) }
59
+
60
+ context 'given a URI that matches the path regex' do
61
+ it 'does not return any errors' do
62
+ expect(subject).to be_empty
63
+ end
64
+ end
65
+
66
+ context 'given a URI that does not match the path regex' do
67
+ let(:uri) { URI.parse('www.example.com/something/foo') }
68
+ it 'returns an errors array' do
69
+ expect(subject).to_not be_empty
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ context '.check_option!' do
77
+ let(:options) { {} }
78
+ subject(:attribute) { Attributor::Attribute.new(type, options) }
79
+
80
+ context 'for path option' do
81
+ context 'given a regex definition' do
82
+ let(:options) { {path: Regexp.new('a-z')} }
83
+ it 'checks successfully' do
84
+ expect(subject).to be_kind_of(Attributor::Attribute)
85
+ end
86
+ end
87
+
88
+ context 'given any definition other than regex' do
89
+ let(:options) { {path: 1} }
90
+ it 'raises an exception' do
91
+ expect { subject }.to raise_error(Attributor::AttributorException)
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'for any other option' do
97
+ let(:options) { {something: 1} }
98
+ it 'raises an exception' do
99
+ expect { subject }.to raise_error(Attributor::AttributorException)
100
+ end
101
+ end
102
+ end
12
103
  end
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.0
4
+ version: 4.0.1
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-07-08 00:00:00.000000000 Z
12
+ date: 2015-08-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hashie
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: activesupport
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '3'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '3'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: rspec
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -265,6 +279,7 @@ files:
265
279
  - lib/attributor/type.rb
266
280
  - lib/attributor/types/bigdecimal.rb
267
281
  - lib/attributor/types/boolean.rb
282
+ - lib/attributor/types/class.rb
268
283
  - lib/attributor/types/collection.rb
269
284
  - lib/attributor/types/container.rb
270
285
  - lib/attributor/types/csv.rb
@@ -277,6 +292,7 @@ files:
277
292
  - lib/attributor/types/integer.rb
278
293
  - lib/attributor/types/model.rb
279
294
  - lib/attributor/types/object.rb
295
+ - lib/attributor/types/regexp.rb
280
296
  - lib/attributor/types/string.rb
281
297
  - lib/attributor/types/struct.rb
282
298
  - lib/attributor/types/symbol.rb
@@ -295,6 +311,7 @@ files:
295
311
  - spec/type_spec.rb
296
312
  - spec/types/bigdecimal_spec.rb
297
313
  - spec/types/boolean_spec.rb
314
+ - spec/types/class_spec.rb
298
315
  - spec/types/collection_spec.rb
299
316
  - spec/types/container_spec.rb
300
317
  - spec/types/csv_spec.rb
@@ -306,6 +323,7 @@ files:
306
323
  - spec/types/ids_spec.rb
307
324
  - spec/types/integer_spec.rb
308
325
  - spec/types/model_spec.rb
326
+ - spec/types/regexp_spec.rb
309
327
  - spec/types/string_spec.rb
310
328
  - spec/types/struct_spec.rb
311
329
  - spec/types/tempfile_spec.rb
@@ -347,6 +365,7 @@ test_files:
347
365
  - spec/type_spec.rb
348
366
  - spec/types/bigdecimal_spec.rb
349
367
  - spec/types/boolean_spec.rb
368
+ - spec/types/class_spec.rb
350
369
  - spec/types/collection_spec.rb
351
370
  - spec/types/container_spec.rb
352
371
  - spec/types/csv_spec.rb
@@ -358,6 +377,7 @@ test_files:
358
377
  - spec/types/ids_spec.rb
359
378
  - spec/types/integer_spec.rb
360
379
  - spec/types/model_spec.rb
380
+ - spec/types/regexp_spec.rb
361
381
  - spec/types/string_spec.rb
362
382
  - spec/types/struct_spec.rb
363
383
  - spec/types/tempfile_spec.rb