attrio 0.1.1 → 0.2.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.
- data/README.md +118 -4
- data/lib/attrio.rb +22 -9
- data/lib/attrio/attribute.rb +9 -6
- data/lib/attrio/builders/accessor_builder.rb +5 -5
- data/lib/attrio/builders/reader_builder.rb +4 -4
- data/lib/attrio/builders/writer_builder.rb +12 -5
- data/lib/attrio/core_ext/hash.rb +7 -0
- data/lib/attrio/default_value.rb +16 -0
- data/lib/attrio/default_value/base.rb +30 -0
- data/lib/attrio/default_value/callable.rb +23 -0
- data/lib/attrio/default_value/clonable.rb +28 -0
- data/lib/attrio/default_value/symbol.rb +22 -0
- data/lib/attrio/initialize.rb +2 -9
- data/lib/attrio/reset.rb +16 -5
- data/lib/attrio/types/base.rb +0 -4
- data/lib/attrio/types/date.rb +2 -2
- data/lib/attrio/types/date_time.rb +1 -1
- data/lib/attrio/types/time.rb +1 -1
- data/lib/attrio/version.rb +2 -2
- data/spec/attrio/attrio_spec.rb +1 -11
- data/spec/attrio/default_value/callable_spec.rb +37 -0
- data/spec/attrio/default_value/clonable_spec.rb +45 -0
- data/spec/attrio/default_value/handle_spec.rb +29 -0
- data/spec/attrio/default_value/symbol_spec.rb +33 -0
- data/spec/attrio/default_value/value_spec.rb +74 -0
- data/spec/attrio/embed_value_spec.rb +59 -0
- data/spec/attrio/inspect_spec.rb +4 -4
- data/spec/attrio/reset_spec.rb +6 -4
- data/spec/attrio/type_spec.rb +14 -144
- data/spec/attrio/types/integer_spec.rb +5 -5
- data/spec/attrio/types/symbol_spec.rb +41 -3
- metadata +21 -6
- data/spec/attrio/default_value_spec.rb +0 -60
data/lib/attrio/initialize.rb
CHANGED
@@ -8,18 +8,11 @@ module Attrio
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def define_attrio_new(as)
|
11
|
-
# define_singleton_method(:new) do |*args, &block|
|
12
|
-
# obj = self.allocate
|
13
|
-
# obj.send "reset_#{as}!"
|
14
|
-
# obj.send :initialize, *args, &block
|
15
|
-
# obj
|
16
|
-
# end
|
17
|
-
|
18
11
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
19
12
|
def self.new(*args, &block)
|
20
|
-
obj = self.allocate
|
21
|
-
obj.send "reset_#{as}!"
|
13
|
+
obj = self.allocate
|
22
14
|
obj.send :initialize, *args, &block
|
15
|
+
obj.send "set_#{as}_defaults"
|
23
16
|
obj
|
24
17
|
end
|
25
18
|
EOS
|
data/lib/attrio/reset.rb
CHANGED
@@ -8,13 +8,24 @@ module Attrio
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def define_attrio_reset(as)
|
11
|
-
define_method "reset_#{as.to_s}" do
|
12
|
-
self.send(as.to_s).values.each
|
13
|
-
|
14
|
-
|
11
|
+
define_method "reset_#{as.to_s}" do |attributes = []|
|
12
|
+
self.send(as.to_s, attributes).values.each{ |attribute| self.send(attribute.writer_method_name, nil) }
|
13
|
+
self.send("set_#{as.to_s}_defaults", attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method "reset_#{as.to_s}_defaults" do |attributes = []|
|
17
|
+
self.send(as.to_s, attributes).values.select{ |attribute| !attribute.default_value.nil? }.each { |attribute| self.send(attribute.writer_method_name, nil) }
|
18
|
+
self.send("set_#{as.to_s}_defaults", attributes)
|
15
19
|
end
|
16
20
|
|
17
|
-
|
21
|
+
define_method "set_#{as.to_s}_defaults" do |attributes = []|
|
22
|
+
self.send(as.to_s, attributes).values.select{ |attribute| !attribute.default_value.nil? }.each do |attribute|
|
23
|
+
next if self.send(attribute.reader_method_name).present?
|
24
|
+
|
25
|
+
default_value = attribute.default_value.is_a?(Attrio::DefaultValue::Base) ? attribute.default_value.call(self) : attribute.default_value
|
26
|
+
self.send(attribute.writer_method_name, default_value)
|
27
|
+
end
|
28
|
+
end
|
18
29
|
end
|
19
30
|
end
|
20
31
|
end
|
data/lib/attrio/types/base.rb
CHANGED
data/lib/attrio/types/date.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
module Attrio
|
4
4
|
module Types
|
5
5
|
class Date < Base
|
6
|
-
def self.typecast(value, options = {})
|
6
|
+
def self.typecast(value, options = {})
|
7
7
|
begin
|
8
|
-
options[:format].present? ? ::Date.strptime(value, options[:format]) : ::Date.parse(value)
|
8
|
+
options[:format].present? ? ::Date.strptime(value.to_s, options[:format]) : ::Date.parse(value.to_s)
|
9
9
|
rescue ArgumentError => e
|
10
10
|
nil
|
11
11
|
end
|
@@ -5,7 +5,7 @@ module Attrio
|
|
5
5
|
class DateTime < Base
|
6
6
|
def self.typecast(value, options = {})
|
7
7
|
begin
|
8
|
-
options[:format].present? ? ::DateTime.strptime(value, options[:format]) : ::DateTime.parse(value)
|
8
|
+
options[:format].present? ? ::DateTime.strptime(value.to_s, options[:format]) : ::DateTime.parse(value.to_s)
|
9
9
|
rescue ArgumentError => e
|
10
10
|
nil
|
11
11
|
end
|
data/lib/attrio/types/time.rb
CHANGED
@@ -5,7 +5,7 @@ module Attrio
|
|
5
5
|
class Time < Base
|
6
6
|
def self.typecast(value, options = {})
|
7
7
|
begin
|
8
|
-
options[:format].present? ? ::Time.strptime(value, options[:format]) : ::Time.parse(value)
|
8
|
+
options[:format].present? ? ::Time.strptime(value.to_s, options[:format]) : ::Time.parse(value.to_s)
|
9
9
|
rescue ArgumentError => e
|
10
10
|
nil
|
11
11
|
end
|
data/lib/attrio/version.rb
CHANGED
data/spec/attrio/attrio_spec.rb
CHANGED
@@ -24,16 +24,11 @@ describe Attrio do
|
|
24
24
|
model.respond_to?(:attributes).should be_true
|
25
25
|
end
|
26
26
|
|
27
|
-
it 'should respond_to attributes=' do
|
28
|
-
model.respond_to?(:attributes=).should be_true
|
29
|
-
end
|
30
|
-
|
31
27
|
describe 'object' do
|
32
28
|
let(:object) { model.new }
|
33
29
|
|
34
30
|
it 'should respond_to reset_attributes' do
|
35
31
|
object.respond_to?(:reset_attributes).should be_true
|
36
|
-
object.respond_to?(:reset_attributes!).should be_true
|
37
32
|
end
|
38
33
|
end
|
39
34
|
end
|
@@ -52,16 +47,11 @@ describe Attrio do
|
|
52
47
|
model.respond_to?(:model_attributes).should be_true
|
53
48
|
end
|
54
49
|
|
55
|
-
it 'should respond_to model_attributes=' do
|
56
|
-
model.respond_to?(:model_attributes=).should be_true
|
57
|
-
end
|
58
|
-
|
59
50
|
describe 'object' do
|
60
51
|
let(:object) { model.new }
|
61
52
|
|
62
53
|
it 'should respond_to reset_model_attributes' do
|
63
|
-
object.respond_to?(:reset_model_attributes).should be_true
|
64
|
-
object.respond_to?(:reset_model_attributes!).should be_true
|
54
|
+
object.respond_to?(:reset_model_attributes).should be_true
|
65
55
|
end
|
66
56
|
end
|
67
57
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::DefaultValue::Callable do
|
4
|
+
subject { described_class.new(object, attribute, default_value) }
|
5
|
+
|
6
|
+
let(:model) do
|
7
|
+
Class.new do
|
8
|
+
include Attrio
|
9
|
+
|
10
|
+
define_attributes do
|
11
|
+
attr :attribute, DateTime, :default => proc{ DateTime.now }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:object){ model.new }
|
17
|
+
|
18
|
+
let(:attribute){ mock('attribute')}
|
19
|
+
let(:default_value){ mock('default_value')}
|
20
|
+
let(:response) { stub('response') }
|
21
|
+
|
22
|
+
before { default_value.stub(:call => response) }
|
23
|
+
it 'should call the value with the object and attribute' do
|
24
|
+
default_value.should_receive(:call).with(object, attribute).and_return(response)
|
25
|
+
subject.call(object)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should set attribute value to appropriate type" do
|
29
|
+
object.attribute.should be_instance_of(DateTime)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be evaluate attribute value every time" do
|
33
|
+
another_object = model.new
|
34
|
+
|
35
|
+
object.attribute.should_not be_equal(another_object.attribute)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::DefaultValue::Clonable do
|
4
|
+
subject { described_class.new(object, attribute, default_value) }
|
5
|
+
|
6
|
+
let(:model) do
|
7
|
+
Class.new do
|
8
|
+
include Attrio
|
9
|
+
|
10
|
+
define_attributes do
|
11
|
+
attr :attribute, Date, :default => Date.today
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:object){ model.new }
|
17
|
+
|
18
|
+
let(:attribute){ mock('attribute')}
|
19
|
+
let(:default_value){ mock('default_value')}
|
20
|
+
let(:instance) { mock('instance') }
|
21
|
+
let(:clone) { mock('clone') }
|
22
|
+
|
23
|
+
before { default_value.stub(:clone => clone) }
|
24
|
+
|
25
|
+
it 'should clone the value' do
|
26
|
+
default_value.should_receive(:clone).with(no_args)
|
27
|
+
subject.call(instance)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should be an instance of a cloned value' do
|
31
|
+
subject.call(instance).should be(clone)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should set attribute value to appropriate type" do
|
35
|
+
object.attribute.should be_instance_of(Date)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not be equal to clonable object" do
|
39
|
+
object.attribute.should_not be_equal(Date.today)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should have the same value as clonable object" do
|
43
|
+
object.attribute.should == Date.today
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::DefaultValue::Base, '.handle' do
|
4
|
+
subject { described_class.handle(mock('object'), mock('attribute'), default) }
|
5
|
+
|
6
|
+
context 'when default is a symbol' do
|
7
|
+
let(:default) { :symbol }
|
8
|
+
|
9
|
+
it { should be_instance_of(Attrio::DefaultValue::Symbol) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when default is a callable' do
|
13
|
+
let(:default) { Proc.new {} }
|
14
|
+
|
15
|
+
it { should be_instance_of(Attrio::DefaultValue::Callable) }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when default is a clonable' do
|
19
|
+
let(:default) { "I can be cloned" }
|
20
|
+
|
21
|
+
it { should be_instance_of(Attrio::DefaultValue::Clonable) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when default is not clonable' do
|
25
|
+
let(:default) { 1 }
|
26
|
+
|
27
|
+
it { should be_nil }
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::DefaultValue::Symbol do
|
4
|
+
subject { described_class.new(object, attribute, default_value) }
|
5
|
+
|
6
|
+
let(:model) do
|
7
|
+
Class.new do
|
8
|
+
include Attrio
|
9
|
+
|
10
|
+
define_attributes do
|
11
|
+
attr :attribute, Date, :default => :today
|
12
|
+
attr :attribute_with_non_existing_method, Symbol, :default => :non_existing_method
|
13
|
+
end
|
14
|
+
|
15
|
+
def today
|
16
|
+
Date.today
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:object){ model.new }
|
22
|
+
|
23
|
+
let(:attribute){ mock('attribute')}
|
24
|
+
let(:default_value){ mock('default_value')}
|
25
|
+
|
26
|
+
it "should set attribute value to appropriate type" do
|
27
|
+
object.attribute.should be_instance_of(Date)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have value that is returned by method in class" do
|
31
|
+
object.attribute.should == Date.today
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::DefaultValue do
|
4
|
+
let(:model) do
|
5
|
+
Class.new do
|
6
|
+
include Attrio
|
7
|
+
|
8
|
+
define_attributes do
|
9
|
+
attr :attribute_without_default_value, Date
|
10
|
+
attr :attribute_with_default_value, Integer, :default => 1
|
11
|
+
# attr :attribute_with_callable_default_value, DateTime, :default => lambda{ DateTime.now }
|
12
|
+
# attr :attribute_with_clonable_default_value, Date, :default => Date.today
|
13
|
+
# attr :attribute_with_symbol_default_value, Date, :default => :today
|
14
|
+
# attr :attribute_with_non_existing_method_default_value, Symbol, :default => :non_existing_method
|
15
|
+
end
|
16
|
+
|
17
|
+
def today
|
18
|
+
Date.today
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:object){ model.new }
|
24
|
+
|
25
|
+
context 'default value is not set' do
|
26
|
+
it "should set attribute to nil" do
|
27
|
+
object.attribute_without_default_value.should be_nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'default value is set as a not clonable object' do
|
32
|
+
it "should set attribute value to appropriate type" do
|
33
|
+
object.attribute_with_default_value.should be_instance_of(Fixnum)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be equal to attribute value" do
|
37
|
+
object.attribute_with_default_value.should be_equal(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# context 'default value is set as a callable' do
|
42
|
+
# it "should set attribute value to appropriate type" do
|
43
|
+
# object.attribute_with_callable_default_value.should be_instance_of(DateTime)
|
44
|
+
# end
|
45
|
+
|
46
|
+
# it "should be evaluate attribute value every time" do
|
47
|
+
# object.attribute_with_callable_default_value.should < another_object.attribute_with_callable_default_value
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
|
51
|
+
# context 'default value is set as a clonable object' do
|
52
|
+
# it "should set attribute value to appropriate type" do
|
53
|
+
# object.attribute_with_clonable_default_value.should be_instance_of(Date)
|
54
|
+
# end
|
55
|
+
|
56
|
+
# it "should not be equal to clonable object" do
|
57
|
+
# object.attribute_with_clonable_default_value.should_not be_equal(Date.today)
|
58
|
+
# end
|
59
|
+
|
60
|
+
# it "should have the same value as clonable object" do
|
61
|
+
# object.attribute_with_clonable_default_value.should == Date.today
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
# context 'default value is set as a symbol' do
|
66
|
+
# it "should set attribute value to appropriate type" do
|
67
|
+
# object.attribute_with_clonable_default_value.should be_instance_of(Date)
|
68
|
+
# end
|
69
|
+
|
70
|
+
# it "should have value that is returned by method in class" do
|
71
|
+
# object.attribute_with_clonable_default_value.should == Date.today
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Embedded Value' do
|
4
|
+
before do
|
5
|
+
module MassAssignment
|
6
|
+
def initialize(attributes = {})
|
7
|
+
self.attributes = attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def attributes=(attributes = {})
|
11
|
+
attributes.each do |attr,value|
|
12
|
+
self.send("#{attr}=", value) if self.respond_to?("#{attr}=")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class City
|
18
|
+
include Attrio
|
19
|
+
include MassAssignment
|
20
|
+
|
21
|
+
define_attributes do
|
22
|
+
attr :name, String
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Address
|
27
|
+
include Attrio
|
28
|
+
include MassAssignment
|
29
|
+
|
30
|
+
define_attributes do
|
31
|
+
attr :street, String
|
32
|
+
attr :zipcode, String
|
33
|
+
attr :city, City
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class User
|
38
|
+
include Attrio
|
39
|
+
include MassAssignment
|
40
|
+
|
41
|
+
define_attributes do
|
42
|
+
attr :name, String
|
43
|
+
attr :address, Address
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:address_attributes) do
|
49
|
+
{ :street => 'Sklizkova 6A', :zipcode => '170000', :city => { :name => 'Tver' } }
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should allow to pass a hash for the embedded value' do
|
53
|
+
user = User.new
|
54
|
+
user.address = address_attributes
|
55
|
+
user.address.street.should == 'Sklizkova 6A'
|
56
|
+
user.address.zipcode.should == '170000'
|
57
|
+
user.address.city.name.should == 'Tver'
|
58
|
+
end
|
59
|
+
end
|
data/spec/attrio/inspect_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Attrio::Inspect do
|
4
|
-
context '' do
|
4
|
+
context 'without options' do
|
5
5
|
let(:model) do
|
6
6
|
Class.new do
|
7
7
|
include Attrio
|
@@ -13,12 +13,12 @@ describe Attrio::Inspect do
|
|
13
13
|
|
14
14
|
let(:object) { model.new }
|
15
15
|
|
16
|
-
it 'should' do
|
16
|
+
it 'should have inspect defined by attrio' do
|
17
17
|
object.method(:inspect).source_location.first.should match(/lib\/attrio\/inspect.rb/)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
context '' do
|
21
|
+
context ':inspect option passed' do
|
22
22
|
let(:model) do
|
23
23
|
Class.new do
|
24
24
|
include Attrio
|
@@ -30,7 +30,7 @@ describe Attrio::Inspect do
|
|
30
30
|
|
31
31
|
let(:object) { model.new }
|
32
32
|
|
33
|
-
it 'should' do
|
33
|
+
it 'should not have inspect defined by attrio' do
|
34
34
|
if RUBY_ENGINE == 'rbx'
|
35
35
|
object.method(:inspect).source_location.first.should_not match(/attrio/)
|
36
36
|
else
|