property 2.1.0 → 2.1.1

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/History.txt CHANGED
@@ -1,8 +1,20 @@
1
+ == 2.1.1 2010-11-23
2
+
3
+ * Major enhancements
4
+ * Not raising an error on invalid attributes.
5
+ * Avoid bad attributes being used to 'send' messages during error message building.
6
+ * Not setting an error for blank values.
7
+
8
+ * Minor enhancements
9
+ * Made defined_columns public.
10
+ * Fixed a bug where all columns would be parsed for each role.
11
+ * Fixed a bug where group_indices would be overwritten during initialization.
12
+
1
13
  == 2.1.0 2010-11-15
2
14
 
3
15
  * Major enhancements
4
16
  * Added support for custom Schema classes through SchemaModule and StoredSchema.
5
-
17
+
6
18
  == 2.0.0 2010-11-10
7
19
 
8
20
  * Major enhancements
@@ -94,16 +94,17 @@ module Property
94
94
  def attributes_with_properties=(attributes, guard_protected_attributes = true)
95
95
  property_columns = schema.column_names
96
96
 
97
- properties = {}
97
+ model_attrs = {}
98
98
 
99
99
  attributes.keys.each do |k|
100
- if property_columns.include?(k)
101
- properties[k] = attributes.delete(k)
100
+ if respond_to?("#{k}=")
101
+ model_attrs[k] = attributes.delete(k)
102
102
  end
103
103
  end
104
-
105
- self.properties = properties
106
- self.attributes_without_properties = attributes
104
+
105
+ # Properties validation will add errors on invalid keys.
106
+ self.properties = attributes
107
+ self.attributes_without_properties = model_attrs
107
108
  end
108
109
  end # InstanceMethods
109
110
  end # Attribute
@@ -1,4 +1,18 @@
1
1
  module Property
2
+ class AttributeError < ActiveRecord::Error
3
+ def default_options
4
+ options.reverse_merge :scope => [:activerecord, :errors],
5
+ :model => @base.class.human_name,
6
+ :attribute => @base.class.human_attribute_name(attribute.to_s)
7
+ end
8
+
9
+ # SECURITY: MAKE SURE WE DO NOT SEND.
10
+ # Value is already in 'options'.
11
+ def value
12
+ nil
13
+ end
14
+ end
15
+
2
16
  class Properties < Hash
3
17
  attr_accessor :owner
4
18
  include Property::DirtyProperties
@@ -49,8 +63,12 @@ module Property
49
63
  bad_keys.each do |key|
50
64
  if original_hash[key] == self[key]
51
65
  # ignore invalid legacy value
66
+ elsif self[key].blank?
67
+ # ignore blank values
68
+ self.delete(key)
52
69
  else
53
- errors.add("#{key}", 'property not declared')
70
+ # We use our own Error class to make sure 'send' is not used on error keys.
71
+ errors.add(key, Property::AttributeError.new(@owner, key, nil, :message => 'property not declared', :value => self[key]))
54
72
  end
55
73
  end
56
74
 
data/lib/property/role.rb CHANGED
@@ -30,7 +30,6 @@ module Property
30
30
  # Initialize a new role with the given name
31
31
  def initialize(name, opts = nil)
32
32
  @name = name
33
- initialize_role_module
34
33
  end
35
34
  end
36
35
  end
@@ -74,7 +74,7 @@ module Property
74
74
  # will be yielded with the record and should return a hash of key => value pairs.
75
75
  def index(type, &block)
76
76
  # type, key, proc
77
- @group_indices << [type, nil, block]
77
+ group_indices << [type, nil, block]
78
78
  end
79
79
 
80
80
  # Returns true if the role is used by the given object. A role is
@@ -90,13 +90,13 @@ module Property
90
90
  object.properties.keys & column_names
91
91
  end
92
92
 
93
- # Return a list of index definitions in the form [type, key, proc_or_nil]
94
- def indices
95
- columns.values.select do |c|
93
+ # Return a list of index definitions from the defined columns in the form [type, key, proc_or_nil]
94
+ def defined_indices
95
+ defined_columns.values.select do |c|
96
96
  c.indexed?
97
97
  end.map do |c|
98
98
  [c.index, c.name, c.index_proc]
99
- end + @group_indices
99
+ end + group_indices
100
100
  end
101
101
 
102
102
  def inspect
@@ -104,14 +104,14 @@ module Property
104
104
  "#<#{self.class}:#{sprintf("0x%x", object_id)} #{defined_columns.keys.inspect}>"
105
105
  end
106
106
 
107
- protected
108
- def initialize_role_module
109
- @group_indices = []
110
- end
107
+ # List all property columns defined for this role
108
+ def defined_columns
109
+ @defined_columns ||= {}
110
+ end
111
111
 
112
- # List all property columns defined for this role
113
- def defined_columns
114
- @defined_columns ||= {}
112
+ protected
113
+ def group_indices
114
+ @group_indices ||= []
115
115
  end
116
116
 
117
117
  # @internal
@@ -45,7 +45,7 @@ module Property
45
45
  def index_groups
46
46
  index_groups = {}
47
47
  @roles.flatten.uniq.each do |b|
48
- b.indices.each do |list|
48
+ b.defined_indices.each do |list|
49
49
  (index_groups[list.first] ||= []) << list[1..-1]
50
50
  end
51
51
  end
@@ -22,9 +22,6 @@ module Property
22
22
  base.class_eval do
23
23
  after_save :update_columns
24
24
  validates_presence_of :name
25
- def after_initialize
26
- initialize_role_module
27
- end
28
25
 
29
26
  extend ClassMethods
30
27
 
@@ -45,12 +42,6 @@ module Property
45
42
 
46
43
  obj
47
44
  end
48
-
49
- # Initialize a new role with the given name
50
- def initialize(*args)
51
- super
52
- initialize_role_module
53
- end
54
45
  end
55
46
  end # included
56
47
 
@@ -72,7 +63,6 @@ module Property
72
63
 
73
64
  private
74
65
  def load_columns_from_db
75
- initialize_role_module
76
66
  @columns_from_db_loaded = true
77
67
  @original_columns = {}
78
68
  stored_columns.each do |column|
@@ -1,3 +1,3 @@
1
1
  module Property
2
- VERSION = '2.1.0'
2
+ VERSION = '2.1.1'
3
3
  end
data/property.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{property}
8
- s.version = "2.1.0"
8
+ s.version = "2.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Renaud Kern", "Gaspard Bucher"]
12
- s.date = %q{2010-11-15}
12
+ s.date = %q{2010-11-23}
13
13
  s.description = %q{Wrap model properties into a single database column and declare properties from within the model.}
14
14
  s.email = %q{gaspard@teti.ch}
15
15
  s.extra_rdoc_files = [
@@ -53,7 +53,19 @@ class Test::Unit::TestCase
53
53
  end
54
54
  end
55
55
  end
56
- end # An instance of Person
56
+
57
+ should 'build a group of indices' do
58
+ assert_equal Hash['ml_string'=>[['poem', nil]], 'integer'=>[['year', nil]]], subject.schema.index_groups
59
+ end
60
+
61
+ should 'build indices array' do
62
+ assert_equal [['integer', 'year', nil], ['ml_string', 'poem', nil]], @poet.defined_indices
63
+ end
64
+
65
+ should 'only use defined propertys to build indices array' do
66
+ assert_equal [], subject.schema.defined_indices
67
+ end
68
+ end # assigned to an instance of Dummy
57
69
  end
58
70
 
59
71
  def self.should_not_maintain_indices
@@ -54,10 +54,10 @@ class Test::Unit::TestCase
54
54
  assert column.indexed?
55
55
  end
56
56
 
57
- should 'return a list of indices on indices' do
57
+ should 'return a list of indices on defined_indices' do
58
58
  subject.property.string('rolodex', :index => true)
59
59
  subject.property.integer('foobar', :index => true)
60
- assert_equal %w{integer string}, subject.indices.map {|i| i[0].to_s }.sort
60
+ assert_equal %w{integer string}, subject.defined_indices.map {|i| i[0].to_s }.sort
61
61
  end
62
62
 
63
63
  context 'created with a Hash' do
@@ -259,7 +259,7 @@ class AttributeTest < Test::Unit::TestCase
259
259
  should 'call native methods' do
260
260
  assert_equal 'please', subject.backup
261
261
  end
262
- end
262
+ end # Setting attributes
263
263
 
264
264
  context 'Initializing an object' do
265
265
  subject { Version.new('foo'=>'bar', 'title'=>'test', 'backup' => 'please') }
@@ -281,7 +281,7 @@ class AttributeTest < Test::Unit::TestCase
281
281
  setup do
282
282
  version = Version.create('title' => 'first', 'tic' => 'tac')
283
283
  @version = Version.find(version.id)
284
- assert subject.update_attributes('foo'=>'bar', 'title'=>'test', 'backup' => 'please')
284
+ assert subject.update_attributes(:foo=>'bar', 'title'=>'test', 'backup' => 'please')
285
285
  end
286
286
 
287
287
  subject { @version }
@@ -35,6 +35,10 @@ class IndexSimpleTest < ActiveSupport::TestCase
35
35
  should 'group indices by type' do
36
36
  assert_equal %w{integer special}, subject.index_groups.keys.map(&:to_s).sort
37
37
  end
38
+
39
+ should 'not contain duplicates' do
40
+ assert_equal Hash["special"=>[["name", nil]], "integer"=>[["age", nil]]], subject.index_groups
41
+ end
38
42
  end
39
43
 
40
44
  context 'A class with a simple index definition' do
@@ -27,6 +27,7 @@ class RoleTest < ActiveSupport::TestCase
27
27
  @poet = Property::Role.new('Poet') do |p|
28
28
  p.string 'poem', :index => :ml_string
29
29
  p.integer 'year', :index => true
30
+ p.integer 'age'
30
31
  end
31
32
  end
32
33
 
@@ -10,23 +10,53 @@ class ValidationTest < Test::Unit::TestCase
10
10
  property.float 'boat'
11
11
  property.string 'bird_name'
12
12
  property.serialize 'cat', Cat
13
+
14
+ def infamous
15
+ raise Exception.new("Should not send!")
16
+ end
13
17
  end
14
18
 
15
19
  subject { Pirate.create }
16
20
 
17
21
  context 'without a property column' do
18
- should 'raise ActiveRecord::UnknownAttributeError when using attributes=' do
19
- assert_raise(ActiveRecord::UnknownAttributeError) do
22
+ context 'set with attributes=' do
23
+ should 'not raise an error' do
20
24
  subject.update_attributes('infamous' => 'dictator')
21
25
  end
22
- end
23
26
 
24
- should 'set an error message on the property when set directly' do
27
+ should 'set an error message' do
28
+ subject.update_attributes('infamous' => 'dictator')
29
+ assert_equal subject.errors['infamous'], 'property not declared'
30
+ end
31
+ end # set with attributes=
32
+
33
+ should 'set an error message' do
25
34
  subject.prop['infamous'] = 'dictator'
26
35
  assert !subject.save
27
36
  assert_equal subject.errors['infamous'], 'property not declared'
28
37
  end
29
-
38
+
39
+ should 'not send to get value in error message' do
40
+ subject.prop['infamous'] = 'dictator'
41
+ assert !subject.save
42
+ assert_nothing_raised do
43
+ subject.errors.each_error do |key, error|
44
+ assert_equal 'Infamous property not declared', error.full_message
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'with blank value' do
50
+
51
+ should 'not set an error message' do
52
+ subject.prop['infamous'] = ''
53
+ subject.prop['greedy'] = nil
54
+ assert subject.save
55
+ assert_nil subject.errors['infamous']
56
+ assert_nil subject.errors['greedy']
57
+ end
58
+ end # with blank value
59
+
30
60
  context 'with dirty' do
31
61
  should 'validate if property was not changed' do
32
62
  subject.prop.instance_eval do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: property
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
8
  - 1
9
- - 0
10
- version: 2.1.0
9
+ - 1
10
+ version: 2.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Renaud Kern
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-11-15 00:00:00 +01:00
19
+ date: 2010-11-23 00:00:00 +01:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency