active_type 0.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 +7 -0
- data/.gitignore +5 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/LICENSE +22 -0
- data/README.md +149 -0
- data/Rakefile +37 -0
- data/active_type.gemspec +24 -0
- data/gemfiles/Gemfile.3.2 +7 -0
- data/gemfiles/Gemfile.3.2.lock +44 -0
- data/gemfiles/Gemfile.4.0 +7 -0
- data/gemfiles/Gemfile.4.0.lock +52 -0
- data/gemfiles/Gemfile.4.1 +7 -0
- data/gemfiles/Gemfile.4.1.lock +51 -0
- data/lib/active_type/extended_record/inheritance.rb +41 -0
- data/lib/active_type/extended_record.rb +27 -0
- data/lib/active_type/no_table.rb +65 -0
- data/lib/active_type/object.rb +13 -0
- data/lib/active_type/record.rb +15 -0
- data/lib/active_type/version.rb +3 -0
- data/lib/active_type/virtual_attributes.rb +166 -0
- data/lib/active_type.rb +6 -0
- data/spec/active_type/extended_record/single_table_inheritance_spec.rb +62 -0
- data/spec/active_type/extended_record_spec.rb +118 -0
- data/spec/active_type/object_spec.rb +256 -0
- data/spec/active_type/record_spec.rb +173 -0
- data/spec/integration/sign_in_spec.rb +98 -0
- data/spec/integration/sign_up_spec.rb +90 -0
- data/spec/shared_examples/accessors.rb +24 -0
- data/spec/shared_examples/belongs_to.rb +17 -0
- data/spec/shared_examples/coercible_columns.rb +203 -0
- data/spec/shared_examples/constructor.rb +30 -0
- data/spec/shared_examples/mass_assignment.rb +26 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/database.rb +31 -0
- data/spec/support/error_on.rb +12 -0
- data/spec/support/i18n.rb +1 -0
- data/spec/support/protected_params.rb +20 -0
- data/spec/support/time_zone.rb +1 -0
- metadata +141 -0
@@ -0,0 +1,166 @@
|
|
1
|
+
module ActiveType
|
2
|
+
|
3
|
+
class InvalidAttributeNameError < StandardError; end
|
4
|
+
class MissingAttributeError < StandardError; end
|
5
|
+
|
6
|
+
module VirtualAttributes
|
7
|
+
|
8
|
+
class VirtualColumn < ActiveRecord::ConnectionAdapters::Column
|
9
|
+
|
10
|
+
def initialize(name, type)
|
11
|
+
@name = name
|
12
|
+
@type = type
|
13
|
+
end
|
14
|
+
|
15
|
+
def type_cast(value)
|
16
|
+
case @type
|
17
|
+
when :integer
|
18
|
+
case value
|
19
|
+
when FalseClass
|
20
|
+
0
|
21
|
+
when TrueClass
|
22
|
+
1
|
23
|
+
when "", nil
|
24
|
+
nil
|
25
|
+
else
|
26
|
+
value.to_i
|
27
|
+
end
|
28
|
+
when :timestamp, :datetime
|
29
|
+
if ActiveRecord::Base.time_zone_aware_attributes
|
30
|
+
time = super
|
31
|
+
if time
|
32
|
+
ActiveSupport::TimeWithZone.new(nil, Time.zone, time)
|
33
|
+
else
|
34
|
+
time
|
35
|
+
end
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class AccessorGenerator
|
47
|
+
|
48
|
+
def initialize(mod)
|
49
|
+
@module = mod
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_accessors(name)
|
53
|
+
validate_attribute_name!(name)
|
54
|
+
generate_reader(name)
|
55
|
+
generate_writer(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def generate_reader(name)
|
61
|
+
@module.module_eval <<-BODY, __FILE__, __LINE__ + 1
|
62
|
+
def #{name}
|
63
|
+
read_virtual_attribute('#{name}')
|
64
|
+
end
|
65
|
+
BODY
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_writer(name)
|
69
|
+
@module.module_eval <<-BODY, __FILE__, __LINE__ + 1
|
70
|
+
def #{name}=(value)
|
71
|
+
write_virtual_attribute('#{name}', value)
|
72
|
+
end
|
73
|
+
BODY
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_attribute_name!(name)
|
77
|
+
unless name.to_s =~ /\A[A-z0-9_]*\z/
|
78
|
+
raise InvalidAttributeNameError.new("'#{name}' is not a valid name for a virtual attribute")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
extend ActiveSupport::Concern
|
86
|
+
|
87
|
+
included do
|
88
|
+
class_attribute :virtual_columns_hash
|
89
|
+
self.virtual_columns_hash = {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(*)
|
93
|
+
@virtual_attributes = {}
|
94
|
+
@virtual_attributes_cache = {}
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
def [](name)
|
99
|
+
if self.singleton_class._has_virtual_column?(name)
|
100
|
+
read_virtual_attribute(name)
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def []=(name, value)
|
107
|
+
if self.singleton_class._has_virtual_column?(name)
|
108
|
+
write_virtual_attribute(name, value)
|
109
|
+
else
|
110
|
+
super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def read_virtual_attribute(name)
|
115
|
+
name = name.to_s
|
116
|
+
@virtual_attributes_cache[name] ||= begin
|
117
|
+
self.singleton_class._virtual_column(name).type_cast(@virtual_attributes[name])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def write_virtual_attribute(name, value)
|
122
|
+
name = name.to_s
|
123
|
+
@virtual_attributes_cache.delete(name)
|
124
|
+
@virtual_attributes[name] = value
|
125
|
+
end
|
126
|
+
|
127
|
+
module ClassMethods
|
128
|
+
|
129
|
+
def _virtual_column(name)
|
130
|
+
virtual_columns_hash[name.to_s] || begin
|
131
|
+
if defined?(super)
|
132
|
+
super
|
133
|
+
else
|
134
|
+
raise MissingAttributeError.new("Undefined attribute '#{name}'")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def _has_virtual_column?(name)
|
140
|
+
virtual_columns_hash.has_key?(name.to_s) || begin
|
141
|
+
if defined?(super)
|
142
|
+
super
|
143
|
+
else
|
144
|
+
false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def generated_virtual_attribute_methods
|
150
|
+
@generated_virtual_attribute_methods ||= begin
|
151
|
+
mod = Module.new
|
152
|
+
include mod
|
153
|
+
mod
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def attribute(name, type)
|
158
|
+
self.virtual_columns_hash = virtual_columns_hash.merge(name.to_s => VirtualColumn.new(name, type))
|
159
|
+
AccessorGenerator.new(generated_virtual_attribute_methods).generate_accessors(name)
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
data/lib/active_type.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module STISpec
|
4
|
+
|
5
|
+
class Parent < ActiveRecord::Base
|
6
|
+
self.table_name = 'sti_records'
|
7
|
+
end
|
8
|
+
|
9
|
+
class Child < Parent
|
10
|
+
end
|
11
|
+
|
12
|
+
class ExtendedChild < ActiveType::Record[Child]
|
13
|
+
end
|
14
|
+
|
15
|
+
class ExtendedExtendedChild < ActiveType::Record[ExtendedChild]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe 'ActiveType::Record[STIModel]' do
|
22
|
+
|
23
|
+
describe 'persistence' do
|
24
|
+
|
25
|
+
def should_save_and_load(save_as, load_as)
|
26
|
+
record = save_as.new(:persisted_string => "string")
|
27
|
+
record.save.should be_true
|
28
|
+
|
29
|
+
reloaded_child = load_as.find(record.id)
|
30
|
+
reloaded_child.persisted_string.should == "string"
|
31
|
+
reloaded_child.should be_a(load_as)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'can save and load the active type record' do
|
35
|
+
|
36
|
+
should_save_and_load(STISpec::ExtendedChild, STISpec::ExtendedChild)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'can save as base and load as active type record' do
|
40
|
+
should_save_and_load(STISpec::Child, STISpec::ExtendedChild)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'can save as active type and load as base record' do
|
44
|
+
should_save_and_load(STISpec::ExtendedChild, STISpec::Child)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can load via the base class and convert to active type record' do
|
48
|
+
record = STISpec::ExtendedChild.new(:persisted_string => "string")
|
49
|
+
record.save.should be_true
|
50
|
+
|
51
|
+
reloaded_child = STISpec::Child.find(record.id).becomes(STISpec::ExtendedChild)
|
52
|
+
reloaded_child.persisted_string.should == "string"
|
53
|
+
reloaded_child.should be_a(STISpec::ExtendedChild)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'can save classes further down the inheritance tree' do
|
57
|
+
should_save_and_load(STISpec::ExtendedExtendedChild, STISpec::ExtendedExtendedChild)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ExtendedRecordSpec
|
4
|
+
|
5
|
+
class BaseRecord < ActiveRecord::Base
|
6
|
+
self.table_name = 'records'
|
7
|
+
end
|
8
|
+
|
9
|
+
class BaseActiveTypeRecord < ActiveType::Record
|
10
|
+
self.table_name = 'records'
|
11
|
+
|
12
|
+
attribute :virtual_string, :string
|
13
|
+
end
|
14
|
+
|
15
|
+
class ExtendedRecord < ActiveType::Record[BaseRecord]
|
16
|
+
attribute :another_virtual_string, :string
|
17
|
+
end
|
18
|
+
|
19
|
+
class ExtendedActiveTypeRecord < ActiveType::Record[BaseActiveTypeRecord]
|
20
|
+
attribute :another_virtual_string, :string
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class ExtendedRecordWithValidations < ExtendedActiveTypeRecord
|
25
|
+
validates :persisted_string, :presence => true
|
26
|
+
validates :virtual_string, :presence => true
|
27
|
+
validates :another_virtual_string, :presence => true
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
describe "ActiveType::Record[ActiveRecord::Base]" do
|
34
|
+
|
35
|
+
subject { ExtendedRecordSpec::ExtendedRecord.new }
|
36
|
+
|
37
|
+
it 'is inherits from the base type' do
|
38
|
+
subject.should be_a(ExtendedRecordSpec::BaseRecord)
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'constructors' do
|
42
|
+
subject { ExtendedRecordSpec::ExtendedRecord }
|
43
|
+
|
44
|
+
it_should_behave_like 'ActiveRecord-like constructors', { :persisted_string => "persisted string", :another_virtual_string => "another virtual string" }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'accessors' do
|
48
|
+
it_should_behave_like 'ActiveRecord-like accessors', { :persisted_string => "persisted string", :another_virtual_string => "another virtual string" }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'persistence' do
|
52
|
+
it 'persists to the database' do
|
53
|
+
subject.persisted_string = "persisted string"
|
54
|
+
subject.save.should be_true
|
55
|
+
|
56
|
+
subject.class.find(subject.id).persisted_string.should == "persisted string"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '.find' do
|
61
|
+
it 'returns an instance of the extended model' do
|
62
|
+
subject.save
|
63
|
+
|
64
|
+
subject.class.find(subject.id).should be_a(subject.class)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
describe "ActiveType::Record[ActiveType::Record]" do
|
72
|
+
|
73
|
+
subject { ExtendedRecordSpec::ExtendedActiveTypeRecord.new }
|
74
|
+
|
75
|
+
it 'is inherits from the base type' do
|
76
|
+
subject.should be_a(ExtendedRecordSpec::BaseActiveTypeRecord)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'has the same model name as the base class' do
|
80
|
+
subject.class.model_name.singular.should == ExtendedRecordSpec::BaseActiveTypeRecord.model_name.singular
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'constructors' do
|
84
|
+
subject { ExtendedRecordSpec::ExtendedActiveTypeRecord }
|
85
|
+
|
86
|
+
it_should_behave_like 'ActiveRecord-like constructors', { :persisted_string => "persisted string", :virtual_string => "virtual string", :another_virtual_string => "another virtual string" }
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'accessors' do
|
90
|
+
it_should_behave_like 'ActiveRecord-like accessors', { :persisted_string => "persisted string", :virtual_string => "virtual string", :another_virtual_string => "another virtual string" }
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'validations' do
|
94
|
+
subject { ExtendedRecordSpec::ExtendedRecordWithValidations.new }
|
95
|
+
|
96
|
+
it { should have(1).error_on(:persisted_string) }
|
97
|
+
it { should have(1).error_on(:virtual_string) }
|
98
|
+
it { should have(1).error_on(:another_virtual_string) }
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'persistence' do
|
102
|
+
it 'persists to the database' do
|
103
|
+
subject.persisted_string = "persisted string"
|
104
|
+
subject.save.should be_true
|
105
|
+
|
106
|
+
subject.class.find(subject.id).persisted_string.should == "persisted string"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '.find' do
|
111
|
+
it 'returns an instance of the extended model' do
|
112
|
+
subject.save
|
113
|
+
|
114
|
+
subject.class.find(subject.id).should be_a(subject.class)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ObjectSpec
|
4
|
+
|
5
|
+
class Object < ActiveType::Object
|
6
|
+
|
7
|
+
attribute :virtual_string, :string
|
8
|
+
attribute :virtual_integer, :integer
|
9
|
+
attribute :virtual_time, :datetime
|
10
|
+
attribute :virtual_date, :date
|
11
|
+
attribute :virtual_boolean, :boolean
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
class ObjectWithValidations < Object
|
17
|
+
|
18
|
+
validates :virtual_string, :presence => true
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
class ObjectWithOverrides < Object
|
24
|
+
|
25
|
+
attribute :overridable_test, :string
|
26
|
+
|
27
|
+
def overridable_test
|
28
|
+
super + super
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
class InheritingObject < Object
|
35
|
+
attribute :another_virtual_string, :string
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
class IncludingObject < Object
|
40
|
+
|
41
|
+
module Module
|
42
|
+
extend ActiveSupport::Concern
|
43
|
+
|
44
|
+
included do
|
45
|
+
attribute :another_virtual_string, :string
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
include Module
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class ObjectWithCallbacks < Object
|
54
|
+
|
55
|
+
before_save :before_save_callback
|
56
|
+
before_validation :before_validation_callback
|
57
|
+
after_save :after_save_callback
|
58
|
+
|
59
|
+
def before_save_callback
|
60
|
+
end
|
61
|
+
|
62
|
+
def before_validation_callback
|
63
|
+
end
|
64
|
+
|
65
|
+
def after_save_callback
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
class Child < ActiveRecord::Base
|
71
|
+
end
|
72
|
+
|
73
|
+
class ObjectWithBelongsTo < Object
|
74
|
+
|
75
|
+
attribute :child_id, :integer
|
76
|
+
|
77
|
+
belongs_to :child
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
describe ActiveType::Object do
|
86
|
+
|
87
|
+
subject { ObjectSpec::Object.new }
|
88
|
+
|
89
|
+
describe 'constructors' do
|
90
|
+
subject { ObjectSpec::Object }
|
91
|
+
|
92
|
+
it_should_behave_like 'ActiveRecord-like constructors', { :virtual_string => "string", :virtual_integer => 100, :virtual_time => Time.now, :virtual_date => Date.today, :virtual_boolean => true }
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'mass assignment' do
|
97
|
+
it_should_behave_like 'ActiveRecord-like mass assignment', { :virtual_string => "string", :virtual_integer => 100, :virtual_time => Time.now, :virtual_date => Date.today, :virtual_boolean => true }
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'accessors' do
|
101
|
+
it_should_behave_like 'ActiveRecord-like accessors', { :virtual_string => "string", :virtual_integer => 100, :virtual_time => Time.now, :virtual_date => Date.today, :virtual_boolean => true }
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'overridable attributes' do
|
105
|
+
subject { ObjectSpec::ObjectWithOverrides.new }
|
106
|
+
|
107
|
+
it 'is possible to override attributes with super' do
|
108
|
+
subject.overridable_test = "test"
|
109
|
+
|
110
|
+
subject.overridable_test.should == "testtest"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'attribute name validation' do
|
115
|
+
it 'crashes when trying to define an invalid attribute name' do
|
116
|
+
klass = Class.new(ActiveType::Object)
|
117
|
+
expect {
|
118
|
+
klass.class_eval do
|
119
|
+
attribute :"<attr>", :string
|
120
|
+
end
|
121
|
+
}.to raise_error(ActiveType::InvalidAttributeNameError)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'coercible' do
|
126
|
+
describe 'string columns' do
|
127
|
+
it_should_behave_like 'a coercible string column', :virtual_string
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'integer columns' do
|
131
|
+
it_should_behave_like 'a coercible integer column', :virtual_integer
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'date columns' do
|
135
|
+
it_should_behave_like 'a coercible date column', :virtual_date
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'time columns' do
|
139
|
+
it_should_behave_like 'a coercible time column', :virtual_time
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'boolean columns' do
|
143
|
+
it_should_behave_like 'a coercible boolean column', :virtual_boolean
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe 'inherited classes' do
|
148
|
+
|
149
|
+
it 'sees attributes of both classes' do
|
150
|
+
object = ObjectSpec::InheritingObject.new
|
151
|
+
object.virtual_string = "string"
|
152
|
+
object.another_virtual_string = "another string"
|
153
|
+
|
154
|
+
object.virtual_string.should == "string"
|
155
|
+
object.another_virtual_string.should == "another string"
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'does not define the attribute on the parent class' do
|
159
|
+
object = ObjectSpec::Object.new
|
160
|
+
object.should_not respond_to(:another_virtual_string)
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'included modules' do
|
166
|
+
it 'sees attributes of the included module' do
|
167
|
+
object = ObjectSpec::IncludingObject.new
|
168
|
+
object.virtual_string = "string"
|
169
|
+
object.another_virtual_string = "another string"
|
170
|
+
|
171
|
+
object.virtual_string.should == "string"
|
172
|
+
object.another_virtual_string.should == "another string"
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'does not define the attribute on the parent class' do
|
176
|
+
object = ObjectSpec::Object.new
|
177
|
+
object.should_not respond_to(:another_virtual_string)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe 'validations' do
|
182
|
+
subject { ObjectSpec::ObjectWithValidations.new }
|
183
|
+
|
184
|
+
it { should have(1).error_on(:virtual_string) }
|
185
|
+
|
186
|
+
it 'has no errors if validations pass' do
|
187
|
+
subject.virtual_string = "foo"
|
188
|
+
|
189
|
+
subject.should be_valid
|
190
|
+
subject.should have(:no).errors_on(:virtual_string)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'causes #save to return false' do
|
194
|
+
subject.save.should be_false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '#belongs_to' do
|
199
|
+
subject { ObjectSpec::ObjectWithBelongsTo.new }
|
200
|
+
|
201
|
+
it_should_behave_like 'a belongs_to association', :child, ObjectSpec::Child
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#save' do
|
205
|
+
subject { ObjectSpec::ObjectWithCallbacks.new }
|
206
|
+
|
207
|
+
it "returns true" do
|
208
|
+
subject.save
|
209
|
+
end
|
210
|
+
|
211
|
+
%w[before_validation before_save after_save].each do |callback|
|
212
|
+
|
213
|
+
it "calls #{callback}" do
|
214
|
+
subject.should_receive("#{callback}_callback")
|
215
|
+
|
216
|
+
subject.save.should be_true
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
%w[before_validation before_save].each do |callback|
|
222
|
+
|
223
|
+
it "aborts the chain when #{callback} returns false" do
|
224
|
+
subject.stub("#{callback}_callback" => false)
|
225
|
+
|
226
|
+
subject.save.should be_false
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
describe '.find' do
|
234
|
+
it 'raises an error' do
|
235
|
+
expect do
|
236
|
+
ObjectSpec::Object.find(1)
|
237
|
+
end.to raise_error(ActiveRecord::RecordNotFound)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe '.all' do
|
242
|
+
it 'returns []' do
|
243
|
+
ObjectSpec::Object.all.should == []
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe '.create' do
|
248
|
+
it 'returns an object' do
|
249
|
+
object = ObjectSpec::Object.create(:virtual_string => "string")
|
250
|
+
|
251
|
+
object.should be_a(ObjectSpec::Object)
|
252
|
+
object.virtual_string.should == "string"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|