property 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
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