property 0.9.0 → 0.9.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,9 +1,15 @@
1
+ == 0.9.1 2010-03-20
2
+
3
+ * 1 major enhancement
4
+ * Added support for custom indexer classes.
5
+ * Removed after_commit dependency (no need for an after_commit).
6
+
1
7
  == 0.9.0 2010-03-20
2
8
 
3
9
  * 3 major enhancement
4
10
  * Added simple index support.
5
11
  * Added complex index support.
6
- * Added simple hooks for indexes when properties are stored in a different model.
12
+ * Added simple hooks for indices when properties are stored in a different model.
7
13
 
8
14
  * 1 minor enhancement
9
15
  * Added 'has_column?' to schema.
data/lib/property.rb CHANGED
@@ -11,7 +11,7 @@ require 'property/serialization/json'
11
11
  require 'property/core_ext/time'
12
12
 
13
13
  module Property
14
- VERSION = '0.9.0'
14
+ VERSION = '0.9.1'
15
15
 
16
16
  def self.included(base)
17
17
  base.class_eval do
@@ -17,7 +17,7 @@ module Property
17
17
  def initialize(name)
18
18
  @name = name
19
19
  @included_in_schemas = []
20
- @group_indexes = []
20
+ @group_indices = []
21
21
  @accessor_module = build_accessor_module
22
22
  end
23
23
 
@@ -27,7 +27,7 @@ module Property
27
27
  end
28
28
 
29
29
  # Return a list of index definitions in the form [type, key, proc_or_nil]
30
- def indexes
30
+ def indices
31
31
  columns.values.select do |c|
32
32
  c.indexed?
33
33
  end.map do |c|
@@ -36,7 +36,7 @@ module Property
36
36
  else
37
37
  [c.type, c.name, c.index]
38
38
  end
39
- end + @group_indexes
39
+ end + @group_indices
40
40
  end
41
41
 
42
42
  # Return true if the Behavior contains the given column (property).
@@ -86,7 +86,7 @@ module Property
86
86
  # @internal
87
87
  def add_index(type, proc)
88
88
  # type, key, proc
89
- @group_indexes << [type, nil, proc]
89
+ @group_indices << [type, nil, proc]
90
90
  end
91
91
 
92
92
  private
@@ -120,7 +120,7 @@ module Property
120
120
  behavior.add_column(Property::Column.new(name, nil, klass, options))
121
121
  end
122
122
 
123
- # This is used to create complex indexes with the following syntax:
123
+ # This is used to create complex indices with the following syntax:
124
124
  #
125
125
  # p.index(:text) do |r| # r = record
126
126
  # {
@@ -38,7 +38,7 @@ module Property
38
38
  schema.behave_like behavior
39
39
  end
40
40
 
41
- # Use this class method to declare properties and indexes that will be used in your models.
41
+ # Use this class method to declare properties and indices that will be used in your models.
42
42
  # Example:
43
43
  # property.string 'phone', :default => '', :indexed => true
44
44
  #
@@ -1,5 +1,3 @@
1
- require 'versions/after_commit' # we need Versions gem's 'after_commit'
2
-
3
1
  module Property
4
2
 
5
3
  # Property::Declaration module is used to declare property definitions in a Class. The module
@@ -10,8 +8,8 @@ module Property
10
8
  base.class_eval do
11
9
  extend ClassMethods
12
10
  include InstanceMethods
13
- before_save :property_index
14
- before_destroy :property_index_destroy
11
+ after_save :property_index
12
+ after_destroy :property_index_destroy
15
13
  end
16
14
  end
17
15
 
@@ -21,8 +19,8 @@ module Property
21
19
  module InstanceMethods
22
20
 
23
21
  private
24
- # Retrieve the current indexes for a given group (:string, :text, etc)
25
- def get_indexes(group_name)
22
+ # Retrieve the current indices for a given group (:string, :text, etc)
23
+ def get_indices(group_name)
26
24
  return {} if new_record?
27
25
  res = {}
28
26
  Property::Db.fetch_attributes(['key', 'value'], index_table_name(group_name), index_reader_sql).each do |row|
@@ -57,39 +55,44 @@ module Property
57
55
  foreign_values = nil
58
56
 
59
57
  schema.index_groups.each do |group_name, definitions|
60
- old_indexes = get_indexes(group_name)
61
- cur_indexes = {}
58
+ cur_indices = {}
62
59
  definitions.each do |key, proc|
63
60
  if key
64
61
  value = prop[key]
65
62
  if !value.blank?
66
63
  if proc
67
- cur_indexes.merge!(proc.call(self))
64
+ cur_indices.merge!(proc.call(self))
68
65
  else
69
- cur_indexes[key] = value
66
+ cur_indices[key] = value
70
67
  end
71
68
  end
72
69
  else
73
- cur_indexes.merge!(proc.call(self))
70
+ cur_indices.merge!(proc.call(self))
74
71
  end
75
72
  end
76
73
 
77
- old_keys = old_indexes.keys
78
- cur_keys = cur_indexes.keys
74
+ if group_name.kind_of?(Class)
75
+ # Use a custom indexer
76
+ group_name.set_property_index(self, cur_indices)
77
+ else
78
+ # Add key/value pairs to the default tables
79
+ old_indices = get_indices(group_name)
80
+
81
+ old_keys = old_indices.keys
82
+ cur_keys = cur_indices.keys
79
83
 
80
- new_keys = cur_keys - old_keys
81
- del_keys = old_keys - cur_keys
82
- upd_keys = cur_keys & old_keys
84
+ new_keys = cur_keys - old_keys
85
+ del_keys = old_keys - cur_keys
86
+ upd_keys = cur_keys & old_keys
83
87
 
84
- after_commit do
85
88
  table_name = index_table_name(group_name)
86
89
 
87
90
  upd_keys.each do |key|
88
- value = cur_indexes[key]
91
+ value = cur_indices[key]
89
92
  if value.blank?
90
93
  del_keys << key
91
94
  else
92
- connection.execute "UPDATE #{table_name} SET value = #{connection.quote(cur_indexes[key])} WHERE #{reader_sql} AND key = #{connection.quote(key)}"
95
+ connection.execute "UPDATE #{table_name} SET value = #{connection.quote(cur_indices[key])} WHERE #{reader_sql} AND key = #{connection.quote(key)}"
93
96
  end
94
97
  end
95
98
 
@@ -97,7 +100,7 @@ module Property
97
100
  connection.execute "DELETE FROM #{table_name} WHERE #{reader_sql} AND key IN (#{del_keys.map{|key| connection.quote(key)}.join(',')})"
98
101
  end
99
102
 
100
- new_keys.reject! {|k| cur_indexes[k].blank? }
103
+ new_keys.reject! {|k| cur_indices[k].blank? }
101
104
  if !new_keys.empty?
102
105
  # we evaluate this now to have the id on record creation
103
106
  foreign_keys ||= index_writer.keys
@@ -107,7 +110,7 @@ module Property
107
110
  table_name,
108
111
  foreign_keys + ['key', 'value'],
109
112
  new_keys.map do |key|
110
- foreign_values + [connection.quote(key), connection.quote(cur_indexes[key])]
113
+ foreign_values + [connection.quote(key), connection.quote(cur_indices[key])]
111
114
  end
112
115
  )
113
116
  end
@@ -121,7 +124,9 @@ module Property
121
124
  foreign_key = index_foreign_key
122
125
  current_id = self.id
123
126
  schema.index_groups.each do |group_name, definitions|
124
- after_commit do
127
+ if group_name.kind_of?(Class)
128
+ group_name.delete_property_index(self)
129
+ else
125
130
  connection.execute "DELETE FROM #{index_table_name(group_name)} WHERE #{foreign_key} = #{current_id}"
126
131
  end
127
132
  end
@@ -77,7 +77,7 @@ module Property
77
77
  def index_groups
78
78
  index_groups = {}
79
79
  @behaviors.flatten.uniq.each do |b|
80
- b.indexes.each do |list|
80
+ b.indices.each do |list|
81
81
  (index_groups[list.first] ||= []) << list[1..-1]
82
82
  end
83
83
  end
data/property.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{property}
8
- s.version = "0.9.0"
8
+ s.version = "0.9.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"]
@@ -45,6 +45,7 @@ Gem::Specification.new do |s|
45
45
  "test/unit/property/declaration_test.rb",
46
46
  "test/unit/property/dirty_test.rb",
47
47
  "test/unit/property/index_complex_test.rb",
48
+ "test/unit/property/index_custom_test.rb",
48
49
  "test/unit/property/index_foreign_test.rb",
49
50
  "test/unit/property/index_simple_test.rb",
50
51
  "test/unit/property/validation_test.rb",
@@ -67,6 +68,7 @@ Gem::Specification.new do |s|
67
68
  "test/unit/property/declaration_test.rb",
68
69
  "test/unit/property/dirty_test.rb",
69
70
  "test/unit/property/index_complex_test.rb",
71
+ "test/unit/property/index_custom_test.rb",
70
72
  "test/unit/property/index_foreign_test.rb",
71
73
  "test/unit/property/index_simple_test.rb",
72
74
  "test/unit/property/validation_test.rb",
data/test/fixtures.rb CHANGED
@@ -92,10 +92,18 @@ begin
92
92
  t.string 'key'
93
93
  t.text 'value'
94
94
  end
95
+
96
+ # custom or legacy index table
97
+ create_table 'contacts' do |t|
98
+ t.integer 'employee_id'
99
+ t.string 'name'
100
+ t.string 'other_name'
101
+ end
95
102
  end
96
103
  end
97
104
 
98
105
  ActiveRecord::Base.establish_connection(:adapter=>'sqlite3', :database=>':memory:')
106
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
99
107
  ActiveRecord::Migration.verbose = false
100
108
  #PropertyMigration.migrate(:down)
101
109
  PropertyMigration.migrate(:up)
@@ -58,10 +58,10 @@ class BehaviorTest < Test::Unit::TestCase
58
58
  assert column.indexed?
59
59
  end
60
60
 
61
- should 'return a list of indexes on indexes' do
61
+ should 'return a list of indices on indices' do
62
62
  subject.property.string('rolodex', :index => true)
63
63
  subject.property.integer('foobar', :index => true)
64
- assert_equal %w{integer string}, subject.indexes.map {|i| i[0].to_s }.sort
64
+ assert_equal %w{integer string}, subject.indices.map {|i| i[0].to_s }.sort
65
65
  end
66
66
  end # A Behavior
67
67
 
@@ -53,7 +53,7 @@ class IndexComplexTest < ActiveSupport::TestCase
53
53
  assert_kind_of Hash, subject.index_groups
54
54
  end
55
55
 
56
- should 'group indexes by type' do
56
+ should 'group indices by type' do
57
57
  assert_equal %w{integer text}, subject.index_groups.keys.map(&:to_s).sort
58
58
  end
59
59
  end
@@ -0,0 +1,100 @@
1
+ require 'test_helper'
2
+ require 'fixtures'
3
+
4
+ class IndexCustomTest < ActiveSupport::TestCase
5
+ class IndexedContact < ActiveRecord::Base
6
+ set_table_name :contacts
7
+
8
+ def self.set_property_index(person, indices)
9
+ if index = first(:conditions => ['employee_id = ?', person.id])
10
+ index.update_attributes(indices)
11
+ else
12
+ create(indices.merge(:employee_id => person.id))
13
+ end
14
+ end
15
+
16
+ def self.delete_property_index(person)
17
+ delete_all(['employee_id = ?', person.id])
18
+ end
19
+ end
20
+
21
+ # Index definition class with a legacy table for indices
22
+ class Person < ActiveRecord::Base
23
+ include Property
24
+ set_table_name :employees
25
+
26
+ property do |p|
27
+ p.string 'name'
28
+ p.string 'first_name'
29
+
30
+ p.index(IndexedContact) do |r| # r = record
31
+ {
32
+ 'name' => "#{r.name}!", # just to test renaming
33
+ 'other_name' => r.first_name
34
+ }
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'A schema from a class with a custom indexer' do
40
+ subject do
41
+ Person.schema
42
+ end
43
+
44
+ should 'return a Hash on index_groups' do
45
+ assert_kind_of Hash, subject.index_groups
46
+ end
47
+
48
+ should 'group indices by type' do
49
+ assert_equal %w{IndexCustomTest::IndexedContact}, subject.index_groups.keys.map(&:to_s).sort
50
+ end
51
+ end
52
+
53
+ context 'A class with complex index definition' do
54
+ subject do
55
+ Person
56
+ end
57
+
58
+ context 'on record creation' do
59
+ should 'create index entries in the custom table' do
60
+ assert_difference('IndexedContact.count', 1) do
61
+ Person.create('name' => 'Sadr', 'first_name' => 'Shadi')
62
+ end
63
+ end
64
+
65
+ should 'store key and value pairs linked to the model' do
66
+ person = Person.create('name' => 'Sadr', 'first_name' => 'Shadi')
67
+ index = IndexedContact.first(:conditions => {:employee_id => person.id})
68
+ assert_equal 'Sadr!', index.name
69
+ assert_equal 'Shadi', index.other_name
70
+ end
71
+ end
72
+
73
+ context 'on record update' do
74
+ setup do
75
+ @person = Person.create('name' => 'Sadr', 'first_name' => 'Shadi')
76
+ end
77
+
78
+ should 'update index entries' do
79
+ index = IndexedContact.first(:conditions => {:employee_id => @person.id})
80
+ assert_difference('IndexedContact.count', 0) do
81
+ @person.update_attributes('first_name' => 'Shiva', 'name' => 'Nazar Ahari')
82
+ end
83
+
84
+ index = IndexedContact.find(index.id) # reload (make sure the record has been updated, not recreated)
85
+
86
+ assert_equal 'Nazar Ahari!', index.name
87
+ assert_equal 'Shiva', index.other_name
88
+ end
89
+ end
90
+
91
+ context 'on record destruction' do
92
+ should 'remove index entry' do
93
+ person = Person.create('first_name' => 'Gaspard', 'name' => 'Bucher')
94
+ assert_difference('IndexedContact.count', -1) do
95
+ person.destroy
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -39,7 +39,7 @@ class IndexSimpleTest < ActiveSupport::TestCase
39
39
  assert_kind_of Hash, subject.index_groups
40
40
  end
41
41
 
42
- should 'group indexes by type' do
42
+ should 'group indices by type' do
43
43
  assert_equal %w{integer string}, subject.index_groups.keys.map(&:to_s).sort
44
44
  end
45
45
  end
@@ -116,7 +116,7 @@ class IndexSimpleTest < ActiveSupport::TestCase
116
116
  @dog = Dog.create('name' => 'Pavlov')
117
117
  end
118
118
 
119
- should 'not alter indexes' do
119
+ should 'not alter indices' do
120
120
  assert_difference('IndexedIntegerEmp.count', 0) do
121
121
  assert_raises(Exception) do
122
122
  @dog.update_attributes('name' => 'raise')
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 0
9
- version: 0.9.0
8
+ - 1
9
+ version: 0.9.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Renaud Kern
@@ -80,6 +80,7 @@ files:
80
80
  - test/unit/property/declaration_test.rb
81
81
  - test/unit/property/dirty_test.rb
82
82
  - test/unit/property/index_complex_test.rb
83
+ - test/unit/property/index_custom_test.rb
83
84
  - test/unit/property/index_foreign_test.rb
84
85
  - test/unit/property/index_simple_test.rb
85
86
  - test/unit/property/validation_test.rb
@@ -125,6 +126,7 @@ test_files:
125
126
  - test/unit/property/declaration_test.rb
126
127
  - test/unit/property/dirty_test.rb
127
128
  - test/unit/property/index_complex_test.rb
129
+ - test/unit/property/index_custom_test.rb
128
130
  - test/unit/property/index_foreign_test.rb
129
131
  - test/unit/property/index_simple_test.rb
130
132
  - test/unit/property/validation_test.rb