property 0.9.1 → 1.0.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.
@@ -19,7 +19,7 @@ class Test::Unit::TestCase
19
19
  context 'with Properties' do
20
20
  setup do
21
21
  @properties = Property::Properties[
22
- 'string' => 'bar',
22
+ 'string' => "one\ntwo",
23
23
  'serialized' => Dog.new('Pavlov', 'Freud'),
24
24
  'datetime' => Time.utc(2010, 02, 12, 21, 31, 25),
25
25
  'float' => 4.3432,
data/test/test_helper.rb CHANGED
@@ -6,5 +6,7 @@ require 'shoulda'
6
6
  require 'active_record'
7
7
  require 'property'
8
8
  require 'shoulda_macros/serialization'
9
+ require 'shoulda_macros/role'
10
+ require 'shoulda_macros/index'
9
11
  require 'fixtures'
10
12
  require 'active_support/test_case'
@@ -0,0 +1,80 @@
1
+ require 'test_helper'
2
+ require 'fixtures'
3
+
4
+ # including Property::Base is like including Property but without hooks
5
+ class BaseTest < Test::Unit::TestCase
6
+
7
+ context 'An external storage including property base' do
8
+ class Version < ActiveRecord::Base
9
+ include Property::Base
10
+ belongs_to :contact, :class_name => 'BaseTest::Contact',
11
+ :foreign_key => 'employee_id'
12
+ property do |p|
13
+ p.string 'first_name'
14
+ end
15
+ end
16
+
17
+ class Contact < ActiveRecord::Base
18
+ set_table_name :employees
19
+ has_many :versions, :class_name => 'BaseTest::Version'
20
+
21
+ include Property
22
+ store_properties_in :version
23
+
24
+ property do |p|
25
+ p.string 'first_name'
26
+ p.string 'name'
27
+ end
28
+
29
+ def version
30
+ @version ||= begin
31
+ if new_record?
32
+ versions.build
33
+ else
34
+ Version.first(:conditions => ['employee_id = ?', self.id]) || versions.build
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'with properties' do
41
+ setup do
42
+ contact = Contact.create('first_name' => 'Angela', 'name' => 'Davis')
43
+ @version = Version.find(contact.version.id)
44
+ end
45
+
46
+ should 'unpack and read properties' do
47
+ assert_equal 'Angela', @version.first_name
48
+ end
49
+
50
+ should 'not execute save hooks' do
51
+ @version.prop['first_name'] = 'Angel'
52
+ assert @version.save
53
+ @version = Version.find(@version)
54
+ assert_equal 'Angela', @version.first_name
55
+ end
56
+
57
+ should 'not only have accessors for properties defined in self' do
58
+ assert_raise(ActiveRecord::UnknownAttributeError) do
59
+ @version.update_attributes('name' => 'Crenshaw')
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
@@ -6,52 +6,72 @@ class DeclarationTest < Test::Unit::TestCase
6
6
  context 'A sub-class' do
7
7
  context 'from a class with property columns' do
8
8
  setup do
9
- @klass = Developer
9
+ @ParentClass = Class.new(ActiveRecord::Base) do
10
+ include Property
11
+ property.string 'name'
12
+ def method_in_parent
13
+ end
14
+ end
15
+ end
16
+
17
+ subject do
18
+ Class.new(@ParentClass) do
19
+ property.integer 'age'
20
+ def method_in_self
21
+ end
22
+ end
10
23
  end
11
24
 
12
25
  should 'inherit property columns from parent class' do
13
- assert_equal %w{age first_name language last_name}, @klass.schema.column_names.sort
26
+ assert_equal %w{age name}, subject.schema.column_names.sort
14
27
  end
15
28
 
16
29
  should 'see its property columns in schema' do
17
- assert @klass.schema.has_column?('language')
30
+ assert subject.schema.has_column?('age')
18
31
  end
19
32
 
20
33
  should 'not back-propagate definitions to parent' do
21
- assert !@klass.superclass.schema.has_column?('language')
34
+ assert !subject.superclass.schema.has_column?('age')
22
35
  end
23
36
 
24
37
  should 'inherit new definitions in parent' do
25
- class ParentClass < ActiveRecord::Base
26
- include Property
27
- property.string 'name'
28
- end
29
-
30
- @klass = Class.new(ParentClass) do
31
- property.integer 'age'
32
- end
33
-
34
- assert_equal %w{age name}, @klass.schema.column_names.sort
35
-
36
- ParentClass.class_eval do
38
+ @ParentClass.class_eval do
37
39
  property.string 'first_name'
38
40
  end
39
41
 
40
- assert_equal %w{age first_name name}, @klass.schema.column_names.sort
42
+ assert_equal %w{age first_name name}, subject.schema.column_names.sort
41
43
  end
42
44
 
43
45
  should 'not be allowed to overwrite a property from the parent class' do
44
- assert_raise(TypeError) do
45
- @klass.class_eval do
46
- property.string 'age'
46
+ assert_raise(Property::RedefinedPropertyError) do
47
+ subject.class_eval do
48
+ property.string 'name'
47
49
  end
48
50
  end
49
51
  end
50
52
 
51
53
  should 'not be allowed to overwrite a property from the current class' do
52
- assert_raise(TypeError) do
53
- @klass.class_eval do
54
- property.string 'language'
54
+ assert_raise(Property::RedefinedPropertyError) do
55
+ subject.class_eval do
56
+ property.string 'age'
57
+ end
58
+ end
59
+ end
60
+
61
+ # This is because we include a module and the module would hide the method
62
+ should 'not be allowed to define a property with the name of a method in the parent class' do
63
+ assert_raise(Property::RedefinedMethodError) do
64
+ subject.class_eval do
65
+ property.string 'method_in_parent'
66
+ end
67
+ end
68
+ end
69
+
70
+ # This is ok because it can be useful and the module inclusion would not hide it
71
+ should 'be allowed to define a property with the same name as a method in the current class' do
72
+ assert_nothing_raised do
73
+ subject.class_eval do
74
+ property.string 'method_in_self'
55
75
  end
56
76
  end
57
77
  end
@@ -135,6 +155,16 @@ class DeclarationTest < Test::Unit::TestCase
135
155
  assert_equal Time, column.klass
136
156
  assert_equal :datetime, column.type
137
157
  end
158
+
159
+ should 'allow multiple declarations in one go' do
160
+ subject.property.string 'foo', 'bar', 'baz'
161
+ assert_equal %w{bar baz foo}, subject.schema.column_names.sort
162
+ end
163
+
164
+ should 'allow multiple declarations in an Array' do
165
+ subject.property.string ['foo', 'bar', 'baz']
166
+ assert_equal %w{bar baz foo}, subject.schema.column_names.sort
167
+ end
138
168
 
139
169
  should 'allow serialized columns' do
140
170
  Dog = Struct.new(:name, :toy) do
@@ -167,15 +197,15 @@ class DeclarationTest < Test::Unit::TestCase
167
197
  assert column.indexed?
168
198
  end
169
199
 
170
- context 'through a Behavior on an instance' do
200
+ context 'through a Role on an instance' do
171
201
  setup do
172
202
  @instance = subject.new
173
- @poet = Property::Behavior.new('Poet')
203
+ @poet = Property::Role.new('Poet')
174
204
  @poet.property do |p|
175
205
  p.string 'poem'
176
206
  end
177
207
 
178
- @instance.behave_like @poet
208
+ @instance.has_role @poet
179
209
  end
180
210
 
181
211
  should 'behave like any other property column' do
@@ -211,15 +241,44 @@ class DeclarationTest < Test::Unit::TestCase
211
241
  end
212
242
  end
213
243
 
214
- context 'On a class with a schema' do
215
- subject { Developer }
244
+ context 'A class with a schema' do
245
+ subject { Class.new(Developer) }
216
246
 
217
247
  should 'raise an exception if we ask to behave like a class without schema' do
218
- assert_raise(TypeError) { subject.behave_like String }
248
+ assert_raise(TypeError) { subject.has_role String }
219
249
  end
220
250
 
221
251
  should 'raise an exception if we ask to behave like an object' do
222
- assert_raise(TypeError) { subject.behave_like 'me' }
252
+ assert_raise(TypeError) { subject.has_role 'me' }
253
+ end
254
+
255
+ should 'raise an exception if the role redefines properties' do
256
+ @emp = Property::Role.new('empi')
257
+ @emp.property.string 'first_name'
258
+ assert_raise(Property::RedefinedPropertyError) do
259
+ subject.has_role @emp
260
+ end
261
+ end
262
+
263
+ should 'raise an exception if the role contains superclass methods' do
264
+ @emp = Property::Role.new('empi')
265
+ @emp.property.string 'method_in_parent'
266
+ assert_raise(Property::RedefinedMethodError) do
267
+ subject.has_role @emp
268
+ end
269
+ end
270
+
271
+ should 'inherit properties when asking to behave like a class' do
272
+ @class = Class.new(ActiveRecord::Base) do
273
+ include Property
274
+ property do |p|
275
+ p.string 'hop'
276
+ end
277
+ end
278
+
279
+ subject.has_role @class
280
+ assert_equal %w{language last_name hop age first_name}, subject.schema.column_names
281
+ assert subject.has_role?(@class)
223
282
  end
224
283
  end
225
284
 
@@ -57,12 +57,12 @@ class IndexForeignTest < ActiveSupport::TestCase
57
57
  end
58
58
  end
59
59
 
60
- def index_reader
60
+ def index_reader(group_name)
61
61
  {'version_id' => version.id}
62
62
  end
63
63
 
64
64
  # Foreign index: we store the 'employee_id' in the index to get back directly to non-versioned class Contact (through employee_id key).
65
- def index_writer
65
+ def index_writer(group_name)
66
66
  {'version_id' => version.id, 'employee_id' => self.id}
67
67
  end
68
68
  end
@@ -3,7 +3,7 @@ require 'fixtures'
3
3
 
4
4
  class IndexSimpleTest < ActiveSupport::TestCase
5
5
  class IndexedStringEmp < ActiveRecord::Base
6
- set_table_name :i_string_employees
6
+ set_table_name :i_special_employees
7
7
  end
8
8
 
9
9
  class IndexedIntegerEmp < ActiveRecord::Base
@@ -25,7 +25,7 @@ class IndexSimpleTest < ActiveSupport::TestCase
25
25
  alias_method_chain :save, :raise
26
26
 
27
27
  property do |p|
28
- p.string 'name', :index => true
28
+ p.string 'name', :index => :special
29
29
  p.integer 'age', :indexed => true # synonym
30
30
  end
31
31
  end
@@ -40,7 +40,7 @@ class IndexSimpleTest < ActiveSupport::TestCase
40
40
  end
41
41
 
42
42
  should 'group indices by type' do
43
- assert_equal %w{integer string}, subject.index_groups.keys.map(&:to_s).sort
43
+ assert_equal %w{integer special}, subject.index_groups.keys.map(&:to_s).sort
44
44
  end
45
45
  end
46
46
 
@@ -49,6 +49,14 @@ class IndexSimpleTest < ActiveSupport::TestCase
49
49
  Dog
50
50
  end
51
51
 
52
+ class Mongrel < Dog
53
+ attr_accessor :last_index_group_name
54
+ def create_indices(group_name, new_keys, cur_indices)
55
+ @last_index_group_name = group_name
56
+ super
57
+ end
58
+ end
59
+
52
60
  context 'on record creation' do
53
61
  should 'create index entries' do
54
62
  assert_difference('IndexedStringEmp.count', 1) do
@@ -56,6 +64,11 @@ class IndexSimpleTest < ActiveSupport::TestCase
56
64
  end
57
65
  end
58
66
 
67
+ should 'call create_indices to create index entries' do
68
+ m = Mongrel.create('name' => 'Zed')
69
+ assert_equal 'special', m.last_index_group_name
70
+ end
71
+
59
72
  should 'not create index entries for blank values' do
60
73
  assert_difference('IndexedIntegerEmp.count', 0) do
61
74
  Dog.create('name' => 'Pavlov')
@@ -91,7 +104,7 @@ class IndexSimpleTest < ActiveSupport::TestCase
91
104
  @dog.update_attributes('name' => 'Médor')
92
105
  end
93
106
  end
94
-
107
+
95
108
  should 'remove blank values' do
96
109
  assert_difference('IndexedStringEmp.count', -1) do
97
110
  @dog.update_attributes('name' => '')
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+ require 'fixtures'
3
+
4
+ class RoleTest < ActiveSupport::TestCase
5
+ should_store_property_definitions(Property::Role)
6
+
7
+
8
+ context 'A Poet role' do
9
+ setup do
10
+ @poet = Property::Role.new('Poet') do |p|
11
+ p.string 'poem', :default => :muse
12
+ p.integer 'year'
13
+
14
+ p.actions do
15
+ def muse
16
+ 'I am your muse'
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ should_insert_properties_on_has_role_poet
23
+ should_add_role_methods
24
+ should_take_part_in_used_list
25
+ should_not_maintain_indices # no indexed column defined
26
+
27
+ should 'return name on name' do
28
+ assert_equal 'Poet', @poet.name
29
+ end
30
+ end # A Poet role
31
+
32
+ context 'A Poet role with indices' do
33
+ setup do
34
+ @poet = Property::Role.new('Poet') do |p|
35
+ p.string 'poem', :index => :ml_string
36
+ p.integer 'year', :index => true
37
+ end
38
+ end
39
+
40
+ should_maintain_indices
41
+ end # A Poet role with indices
42
+
43
+
44
+ context 'A class used as role' do
45
+ class Foo < ActiveRecord::Base
46
+ include Property
47
+
48
+ property do |p|
49
+ p.string 'poem', :default => :muse
50
+ p.integer 'year'
51
+
52
+ p.actions do
53
+ def muse
54
+ 'I am your muse'
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ setup do
61
+ @poet = Foo
62
+ end
63
+
64
+ should_insert_properties_on_has_role_poet
65
+ should_add_role_methods
66
+ should_take_part_in_used_list
67
+
68
+ context 'set on a sub-class instance' do
69
+ subject do
70
+ Employee.new
71
+ end
72
+
73
+ should 'not raise an exception' do
74
+ assert_nothing_raised { subject.has_role Developer }
75
+ end
76
+ end # set on a sub-class instance
77
+ end # A class used as role
78
+ end
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+ require 'fixtures'
3
+
4
+ class StoredRoleTest < ActiveSupport::TestCase
5
+ class Column < ActiveRecord::Base
6
+ include Property::StoredColumn
7
+ end
8
+
9
+ class Role < ActiveRecord::Base
10
+ include Property::StoredRole
11
+ stored_columns_class 'StoredRoleTest::Column'
12
+ end
13
+
14
+ should_store_property_definitions(Role)
15
+
16
+ context 'A stored Role' do
17
+
18
+ context 'with column definitions' do
19
+ setup do
20
+ role = Role.create(:name => 'Poet')
21
+ role.stored_columns << Column.new(:ptype => 'string', :name => 'poem')
22
+ role.stored_columns << Column.new(:ptype => 'integer', :name => 'year')
23
+ role.save!
24
+ @poet = Role.find(role.id)
25
+ end
26
+
27
+ should_insert_properties_on_has_role_poet
28
+ should_take_part_in_used_list(false)
29
+ should_not_maintain_indices # no indexed column defined
30
+
31
+ should 'create new role columns on save' do
32
+ @poet.property do |p|
33
+ p.string 'original_language'
34
+ end
35
+
36
+ assert_difference('Role.count', 0) do
37
+ assert_difference('Column.count', 1) do
38
+ @poet.save
39
+ end
40
+ end
41
+ end
42
+
43
+ should 'return name on name' do
44
+ assert_equal 'Poet', @poet.name
45
+ end
46
+ end # with column definitions
47
+ end # A stored Role
48
+
49
+ context 'A stored Role' do
50
+
51
+ context 'with indexed column definitions' do
52
+ setup do
53
+ role = Role.create(:name => 'Poet')
54
+ role.stored_columns << Column.new(:ptype => 'string', :name => 'poem', :index => :ml_string)
55
+ role.stored_columns << Column.new(:ptype => 'integer', :name => 'year', :index => true)
56
+ role.save!
57
+ @poet = Role.find(role.id)
58
+ end
59
+
60
+ should_maintain_indices
61
+ end # with column definitions
62
+ end # A stored Role
63
+
64
+ context 'A new Role' do
65
+ subject do
66
+ Role.new('Poet') do |p|
67
+ p.string 'name'
68
+ end
69
+ end
70
+
71
+ should 'define properties with a block' do
72
+ assert_equal %w{name}, subject.column_names
73
+ end
74
+
75
+ should 'create role columns on save' do
76
+ role = subject
77
+ assert_difference('Role.count', 1) do
78
+ assert_difference('Column.count', 1) do
79
+ assert role.save
80
+ end
81
+ end
82
+ end
83
+ end # A new Role
84
+ end