property 0.9.0 → 0.9.1

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