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 +7 -1
- data/lib/property.rb +1 -1
- data/lib/property/behavior.rb +5 -5
- data/lib/property/declaration.rb +1 -1
- data/lib/property/index.rb +27 -22
- data/lib/property/schema.rb +1 -1
- data/property.gemspec +3 -1
- data/test/fixtures.rb +8 -0
- data/test/unit/property/behavior_test.rb +2 -2
- data/test/unit/property/index_complex_test.rb +1 -1
- data/test/unit/property/index_custom_test.rb +100 -0
- data/test/unit/property/index_simple_test.rb +2 -2
- metadata +4 -2
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
|
|
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
data/lib/property/behavior.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Property
|
|
|
17
17
|
def initialize(name)
|
|
18
18
|
@name = name
|
|
19
19
|
@included_in_schemas = []
|
|
20
|
-
@
|
|
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
|
|
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 + @
|
|
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
|
-
@
|
|
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
|
|
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
|
# {
|
data/lib/property/declaration.rb
CHANGED
|
@@ -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
|
|
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
|
#
|
data/lib/property/index.rb
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
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
|
|
25
|
-
def
|
|
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
|
-
|
|
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
|
-
|
|
64
|
+
cur_indices.merge!(proc.call(self))
|
|
68
65
|
else
|
|
69
|
-
|
|
66
|
+
cur_indices[key] = value
|
|
70
67
|
end
|
|
71
68
|
end
|
|
72
69
|
else
|
|
73
|
-
|
|
70
|
+
cur_indices.merge!(proc.call(self))
|
|
74
71
|
end
|
|
75
72
|
end
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 =
|
|
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(
|
|
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|
|
|
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(
|
|
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
|
-
|
|
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
|
data/lib/property/schema.rb
CHANGED
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
9
|
-
version: 0.9.
|
|
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
|