property 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +14 -0
- data/README.rdoc +6 -2
- data/lib/property.rb +2 -1
- data/lib/property/attribute.rb +3 -2
- data/lib/property/declaration.rb +65 -17
- data/lib/property/index.rb +21 -1
- data/lib/property/properties.rb +1 -1
- data/lib/property/role.rb +16 -10
- data/lib/property/role_module.rb +83 -196
- data/lib/property/schema.rb +68 -117
- data/lib/property/stored_role.rb +13 -6
- data/lib/property/version.rb +1 -1
- data/property.gemspec +6 -4
- data/test/database.rb +1 -0
- data/test/fixtures.rb +1 -0
- data/test/shoulda_macros/index.rb +6 -3
- data/test/shoulda_macros/role.rb +26 -50
- data/test/unit/property/declaration_test.rb +34 -40
- data/test/unit/property/index_field_test.rb +56 -0
- data/test/unit/property/role_test.rb +6 -16
- data/test/unit/property/stored_role_test.rb +86 -1
- metadata +15 -4
data/History.txt
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
== 2.0.0
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
* Rewrite of the core engine to remove all the metaclass and anonymous module codes (generated memory leaks).
|
5
|
+
* Removed "actions" to create methods in a Role (you have to define them in the host class instead).
|
6
|
+
* Not defining accessor methods in Roles (using method missing instead). Accessors in Schema are defined directly in the class.
|
7
|
+
* Not checking for redefined methods anymore.
|
8
|
+
|
9
|
+
== 1.3.0 2010-11-9 (not released)
|
10
|
+
|
11
|
+
* Major enhancements
|
12
|
+
* Removed 'included_in' check (the same property can now be redefined)
|
13
|
+
* Added support for field indices (as columns in the owner table).
|
14
|
+
|
1
15
|
== 1.2.0 2010-09-26
|
2
16
|
|
3
17
|
* Major enhancements
|
data/README.rdoc
CHANGED
@@ -14,11 +14,15 @@ changes detections and migrations.
|
|
14
14
|
== Usage
|
15
15
|
|
16
16
|
You first need to create a migration to add a 'text' field named 'properties' to
|
17
|
-
your model.
|
17
|
+
your model. Choose a text format that is really long (otherwize your data will be truncated and the property will fail to decode => error). Do something like this:
|
18
18
|
|
19
19
|
class AddPropertyToContact < ActiveRecord::Migration
|
20
20
|
def self.up
|
21
|
-
|
21
|
+
if ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] == 'mysql'
|
22
|
+
execute "ALTER TABLE contacts ADD COLUMN properties LONGTEXT"
|
23
|
+
else
|
24
|
+
add_column :contacts, :properties, :text
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
28
|
def self.down
|
data/lib/property.rb
CHANGED
@@ -2,8 +2,8 @@ require 'property/attribute'
|
|
2
2
|
require 'property/dirty'
|
3
3
|
require 'property/properties'
|
4
4
|
require 'property/column'
|
5
|
+
require 'property/role_module'
|
5
6
|
require 'property/role'
|
6
|
-
require 'property/stored_role'
|
7
7
|
require 'property/schema'
|
8
8
|
require 'property/declaration'
|
9
9
|
require 'property/db'
|
@@ -11,6 +11,7 @@ require 'property/index'
|
|
11
11
|
require 'property/serialization/json'
|
12
12
|
require 'property/core_ext/time'
|
13
13
|
require 'property/base'
|
14
|
+
require 'property/stored_role'
|
14
15
|
|
15
16
|
module Property
|
16
17
|
def self.included(base)
|
data/lib/property/attribute.rb
CHANGED
@@ -19,7 +19,7 @@ module Property
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
# This is just a helper module that includes necessary code for property access, but without
|
22
|
+
# This is just a helper module that includes the necessary code for property access, but without
|
23
23
|
# the validation/save hooks.
|
24
24
|
module Base
|
25
25
|
def self.included(base)
|
@@ -92,7 +92,8 @@ module Property
|
|
92
92
|
|
93
93
|
private
|
94
94
|
def attributes_with_properties=(attributes, guard_protected_attributes = true)
|
95
|
-
property_columns = self.
|
95
|
+
property_columns = self.schema.column_names
|
96
|
+
|
96
97
|
properties = {}
|
97
98
|
|
98
99
|
attributes.keys.each do |k|
|
data/lib/property/declaration.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Property
|
2
2
|
|
3
|
-
#
|
4
|
-
# also manages property inheritence in sub-classes
|
3
|
+
# This module is used to manage property definitions (the schema) in a Class. The module
|
4
|
+
# also manages property inheritence in sub-classes by linking the schema in the sub-class with
|
5
|
+
# the schema in the superclass.
|
5
6
|
module Declaration
|
6
7
|
def self.included(base)
|
7
8
|
base.class_eval do
|
@@ -10,6 +11,8 @@ module Property
|
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
14
|
+
# This is just a helper module that includes the necessary code for property definition, but without
|
15
|
+
# the validation/save hooks.
|
13
16
|
module Base
|
14
17
|
def self.included(base)
|
15
18
|
base.class_eval do
|
@@ -17,6 +20,7 @@ module Property
|
|
17
20
|
include InstanceMethods
|
18
21
|
|
19
22
|
class << self
|
23
|
+
# Every class has it's own schema.
|
20
24
|
attr_accessor :schema
|
21
25
|
|
22
26
|
def schema
|
@@ -24,12 +28,9 @@ module Property
|
|
24
28
|
end
|
25
29
|
|
26
30
|
private
|
31
|
+
# Build schema and manage inheritance.
|
27
32
|
def make_schema
|
28
|
-
|
29
|
-
if superclass.respond_to?(:schema)
|
30
|
-
schema.has_role superclass
|
31
|
-
end
|
32
|
-
schema
|
33
|
+
Property::Schema.new(self.to_s, :class => self)
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
@@ -40,11 +41,11 @@ module Property
|
|
40
41
|
|
41
42
|
# Include a new set of property definitions (Role) into the current class schema.
|
42
43
|
# You can also provide a class to simulate multiple inheritance.
|
43
|
-
def
|
44
|
-
schema.
|
44
|
+
def include_role(role)
|
45
|
+
schema.include_role role
|
45
46
|
end
|
46
47
|
|
47
|
-
# Return true if the current object has all the roles of the given
|
48
|
+
# Return true if the current object has all the roles of the given schema or role.
|
48
49
|
def has_role?(role)
|
49
50
|
schema.has_role? role
|
50
51
|
end
|
@@ -63,7 +64,27 @@ module Property
|
|
63
64
|
# end
|
64
65
|
# end
|
65
66
|
def property(&block)
|
66
|
-
schema.
|
67
|
+
schema.property(&block)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Define property methods in a class. This is only triggered when properties are declared directly in the
|
71
|
+
# class and not through Role inclusion.
|
72
|
+
def define_property_methods(column)
|
73
|
+
attr_name = column.name
|
74
|
+
|
75
|
+
class_eval(%Q{
|
76
|
+
def #{attr_name} # def title
|
77
|
+
prop['#{attr_name}'] # prop['title']
|
78
|
+
end # end
|
79
|
+
#
|
80
|
+
def #{attr_name}? # def title?
|
81
|
+
prop['#{attr_name}'] # prop['title']
|
82
|
+
end # end
|
83
|
+
#
|
84
|
+
def #{attr_name}=(new_value) # def title=(new_value)
|
85
|
+
prop['#{attr_name}'] = new_value # prop['title'] = new_value
|
86
|
+
end # end
|
87
|
+
}, __FILE__, __LINE__)
|
67
88
|
end
|
68
89
|
end # ClassMethods
|
69
90
|
|
@@ -76,8 +97,8 @@ module Property
|
|
76
97
|
|
77
98
|
# Include a new set of property definitions (Role) into the current instance's schema.
|
78
99
|
# You can also provide a class to simulate multiple inheritance.
|
79
|
-
def
|
80
|
-
own_schema.
|
100
|
+
def include_role(role)
|
101
|
+
own_schema.include_role role
|
81
102
|
end
|
82
103
|
|
83
104
|
# Return the list of active roles. The active roles are all the Roles included
|
@@ -99,12 +120,39 @@ module Property
|
|
99
120
|
def own_schema
|
100
121
|
@own_schema ||= make_own_schema
|
101
122
|
end
|
123
|
+
|
124
|
+
# When roles are dynamically added to a model, we use method_missing to mimic property
|
125
|
+
# accessors. Since this has a cost, it is better to use 'prop' based accessors in production
|
126
|
+
# code (this is mostly helpful for testing/debugging).
|
127
|
+
def method_missing(meth, *args, &block)
|
128
|
+
method = meth.to_s
|
129
|
+
if args.empty?
|
130
|
+
if method[-1..-1] == '?'
|
131
|
+
# predicate
|
132
|
+
key = method[0..-2]
|
133
|
+
else
|
134
|
+
# reader
|
135
|
+
key = method
|
136
|
+
end
|
137
|
+
|
138
|
+
if schema.has_column?(key)
|
139
|
+
return prop[key]
|
140
|
+
end
|
141
|
+
elsif args.size == 1 && method[-1..-1] == '='
|
142
|
+
# writer
|
143
|
+
key = method[0..-2]
|
144
|
+
if schema.has_column?(key)
|
145
|
+
return prop[key] = args.first
|
146
|
+
end
|
147
|
+
end
|
148
|
+
# Not a property method
|
149
|
+
super
|
150
|
+
end
|
151
|
+
|
102
152
|
private
|
153
|
+
# Create a schema for the instance and inherit from the class
|
103
154
|
def make_own_schema
|
104
|
-
|
105
|
-
schema = Property::Schema.new(nil, this)
|
106
|
-
schema.has_role self.class
|
107
|
-
schema
|
155
|
+
Property::Schema.new(nil, :superschema => self.class.schema)
|
108
156
|
end
|
109
157
|
end # InsanceMethods
|
110
158
|
end # Declaration
|
data/lib/property/index.rb
CHANGED
@@ -4,12 +4,14 @@ module Property
|
|
4
4
|
# also manages property inheritence in sub-classes.
|
5
5
|
module Index
|
6
6
|
KEY = Property::Db.connection.quote_column_name('key')
|
7
|
+
FIELD_INDEX_REGEXP = %r{\A\.(.*)\Z}
|
7
8
|
|
8
9
|
def self.included(base)
|
9
10
|
base.class_eval do
|
10
11
|
extend ClassMethods
|
11
12
|
include InstanceMethods
|
12
|
-
|
13
|
+
before_save :property_field_index
|
14
|
+
after_save :property_index
|
13
15
|
after_destroy :property_index_destroy
|
14
16
|
end
|
15
17
|
end
|
@@ -25,6 +27,12 @@ module Property
|
|
25
27
|
module InstanceMethods
|
26
28
|
|
27
29
|
def rebuild_index!
|
30
|
+
property_field_index
|
31
|
+
|
32
|
+
if changed_without_properties?
|
33
|
+
update_without_callbacks # no validation, no callbacks
|
34
|
+
end
|
35
|
+
|
28
36
|
property_index
|
29
37
|
end
|
30
38
|
|
@@ -144,9 +152,21 @@ module Property
|
|
144
152
|
self.class.connection.execute "DELETE FROM #{index_table_name(group_name)} WHERE #{index_reader_sql(group_name)} AND #{KEY} IN (#{keys.map{|key| connection.quote(key)}.join(',')})"
|
145
153
|
end
|
146
154
|
|
155
|
+
# This method prepares the index
|
156
|
+
def property_field_index
|
157
|
+
schema.index_groups.each do |group_name, definitions|
|
158
|
+
if group_name =~ FIELD_INDEX_REGEXP
|
159
|
+
# write attribute in owner
|
160
|
+
key = definitions.first.first
|
161
|
+
self[$1] = prop[key]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
147
166
|
# This method prepares the index
|
148
167
|
def property_index
|
149
168
|
schema.index_groups.each do |group_name, definitions|
|
169
|
+
next if group_name =~ FIELD_INDEX_REGEXP
|
150
170
|
cur_indices = {}
|
151
171
|
definitions.each do |key, proc|
|
152
172
|
if key
|
data/lib/property/properties.rb
CHANGED
data/lib/property/role.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
|
-
require 'property/
|
1
|
+
require 'property/redefined_property_error'
|
2
|
+
require 'property/redefined_method_error'
|
2
3
|
|
3
4
|
module Property
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# object with the role's property definitions.
|
5
|
+
# The Role holds information on a group of property columns. The "Role" is used
|
6
|
+
# in the same way as the ruby Module: as a mixin. The Schema class "includes" roles.
|
7
7
|
class Role
|
8
|
-
attr_accessor :name
|
9
8
|
include RoleModule
|
10
9
|
|
11
|
-
|
10
|
+
# Create a new role. If a block is provided, this block can be used
|
11
|
+
# to define properties:
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# @role = Role.new('Poet') do |p|
|
15
|
+
# p.string :muse
|
16
|
+
# end
|
17
|
+
def self.new(name, opts = nil, &block)
|
12
18
|
if name.kind_of?(Hash)
|
13
|
-
obj = super(name[:name] || name['name'])
|
19
|
+
obj = super(name[:name] || name['name'], opts)
|
14
20
|
else
|
15
|
-
obj = super(name)
|
21
|
+
obj = super(name, opts)
|
16
22
|
end
|
17
23
|
|
18
24
|
if block_given?
|
@@ -22,8 +28,8 @@ module Property
|
|
22
28
|
end
|
23
29
|
|
24
30
|
# Initialize a new role with the given name
|
25
|
-
def initialize(name)
|
26
|
-
|
31
|
+
def initialize(name, opts = nil)
|
32
|
+
@name = name
|
27
33
|
initialize_role_module
|
28
34
|
end
|
29
35
|
end
|
data/lib/property/role_module.rb
CHANGED
@@ -1,49 +1,13 @@
|
|
1
|
-
require 'property/redefined_property_error'
|
2
|
-
require 'property/redefined_method_error'
|
3
|
-
|
4
1
|
module Property
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
2
|
+
# The RoleModule enables a class to hold information on a group of property columns.
|
3
|
+
# This enables classes to act in the same way as the ruby Module: as a mixin.
|
4
|
+
# The Schema class "includes" roles.
|
8
5
|
module RoleModule
|
9
|
-
|
10
|
-
|
11
|
-
# We cannot use attr_accessor to define these because we are in a module
|
12
|
-
# when the module is included in an ActiveRecord class.
|
13
|
-
#%W{name included accessor_module}.each do |name|
|
14
|
-
# class_eval %Q{
|
15
|
-
# def #{name}
|
16
|
-
# @#{name}
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# def #{name}=(value)
|
20
|
-
# @#{name} = value
|
21
|
-
# end
|
22
|
-
# }
|
23
|
-
#end
|
24
|
-
|
25
|
-
# Initialize module (should be called from within including class's initialize method).
|
26
|
-
def initialize_role_module
|
27
|
-
@included_in_schemas = []
|
28
|
-
@group_indices = []
|
29
|
-
@accessor_module = build_accessor_module
|
30
|
-
end
|
31
|
-
|
32
|
-
# List all property definitiosn for the current role
|
33
|
-
def columns
|
34
|
-
@columns ||= {}
|
35
|
-
end
|
36
|
-
|
37
|
-
# Return a list of index definitions in the form [type, key, proc_or_nil]
|
38
|
-
def indices
|
39
|
-
columns.values.select do |c|
|
40
|
-
c.indexed?
|
41
|
-
end.map do |c|
|
42
|
-
[c.index, c.name, c.index_proc]
|
43
|
-
end + @group_indices
|
6
|
+
def name
|
7
|
+
@name
|
44
8
|
end
|
45
9
|
|
46
|
-
# Return true if the
|
10
|
+
# Return true if the role contains the given column (property).
|
47
11
|
def has_column?(name)
|
48
12
|
column_names.include?(name)
|
49
13
|
end
|
@@ -53,52 +17,71 @@ module Property
|
|
53
17
|
columns.keys
|
54
18
|
end
|
55
19
|
|
56
|
-
#
|
20
|
+
# List all property columns defined for this role
|
21
|
+
def columns
|
22
|
+
defined_columns
|
23
|
+
end
|
24
|
+
|
25
|
+
# Use this method to declare properties into a Role or Schema.
|
26
|
+
#
|
57
27
|
# Example:
|
58
28
|
# @role.property.string 'phone', :default => ''
|
59
29
|
#
|
30
|
+
# You can also use the "property" method in the class to access the schema:
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# Page.property.string 'phone', :default => ''
|
34
|
+
#
|
60
35
|
# You can also use a block:
|
61
|
-
#
|
36
|
+
# Page.property do |p|
|
62
37
|
# p.string 'phone', 'name', :default => ''
|
63
38
|
# end
|
64
39
|
def property
|
65
40
|
if block_given?
|
66
|
-
yield
|
41
|
+
yield self
|
67
42
|
end
|
68
|
-
|
43
|
+
self
|
69
44
|
end
|
70
45
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
46
|
+
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
|
47
|
+
class_eval <<-EOV
|
48
|
+
def #{column_type}(*args)
|
49
|
+
options = args.extract_options!
|
50
|
+
column_names = args.flatten
|
51
|
+
default = options.delete(:default)
|
52
|
+
column_names.each { |name| add_column(Property::Column.new(name, default, '#{column_type}', options.merge(:role => self))) }
|
53
|
+
end
|
54
|
+
EOV
|
75
55
|
end
|
76
56
|
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
raise RedefinedPropertyError.new("Property '#{name}' is already defined.")
|
83
|
-
else
|
84
|
-
verify_not_defined_in_schemas_using_this_role(name)
|
85
|
-
verify_method_not_defined_in_classes_using_this_role(name)
|
86
|
-
define_property_methods(column) if column.should_create_accessors?
|
87
|
-
columns[column.name] = column
|
88
|
-
end
|
57
|
+
# This is used to serialize a non-native DB type. Use:
|
58
|
+
# p.serialize 'pet', Dog
|
59
|
+
def serialize(name, klass, options = {})
|
60
|
+
Property.validate_property_class(klass)
|
61
|
+
add_column(Property::Column.new(name, nil, klass, options.merge(:role => self)))
|
89
62
|
end
|
90
63
|
|
91
|
-
#
|
92
|
-
|
64
|
+
# This is used to create complex indices with the following syntax:
|
65
|
+
#
|
66
|
+
# p.index(:text) do |r| # r = record
|
67
|
+
# {
|
68
|
+
# "high" => "gender:#{r.gender} age:#{r.age} name:#{r.name}",
|
69
|
+
# "name_#{r.lang}" => r.name, # multi-lingual index
|
70
|
+
# }
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# The first argument is the type (used to locate the table where the data will be stored) and the block
|
74
|
+
# will be yielded with the record and should return a hash of key => value pairs.
|
75
|
+
def index(type, &block)
|
93
76
|
# type, key, proc
|
94
|
-
@group_indices << [type, nil,
|
77
|
+
@group_indices << [type, nil, block]
|
95
78
|
end
|
96
79
|
|
97
|
-
# Returns true if the
|
98
|
-
# considered to be used if any of it's
|
80
|
+
# Returns true if the role is used by the given object. A role is
|
81
|
+
# considered to be used if any of it's defined columns is not blank in the object's
|
99
82
|
# properties.
|
100
83
|
def used_in(object)
|
101
|
-
|
84
|
+
object.properties.keys & defined_columns.keys != []
|
102
85
|
end
|
103
86
|
|
104
87
|
# Returns the list of column names in the current role that are used by the
|
@@ -107,140 +90,44 @@ module Property
|
|
107
90
|
object.properties.keys & column_names
|
108
91
|
end
|
109
92
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
# options = args.extract_options!
|
119
|
-
# column_names = args
|
120
|
-
# default = options.delete(:default)
|
121
|
-
# column_names.each { |name| column(name, default, 'string', options) }
|
122
|
-
# end
|
123
|
-
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
|
124
|
-
class_eval <<-EOV
|
125
|
-
def #{column_type}(*args)
|
126
|
-
options = args.extract_options!
|
127
|
-
column_names = args.flatten
|
128
|
-
default = options.delete(:default)
|
129
|
-
column_names.each { |name| role.add_column(Property::Column.new(name, default, '#{column_type}', options.merge(:role => role))) }
|
130
|
-
end
|
131
|
-
EOV
|
132
|
-
end
|
133
|
-
|
134
|
-
# This is used to serialize a non-native DB type. Use:
|
135
|
-
# p.serialize 'pet', Dog
|
136
|
-
def serialize(name, klass, options = {})
|
137
|
-
Property.validate_property_class(klass)
|
138
|
-
role.add_column(Property::Column.new(name, nil, klass, options.merge(:role => role)))
|
139
|
-
end
|
140
|
-
|
141
|
-
# This is used to create complex indices with the following syntax:
|
142
|
-
#
|
143
|
-
# p.index(:text) do |r| # r = record
|
144
|
-
# {
|
145
|
-
# "high" => "gender:#{r.gender} age:#{r.age} name:#{r.name}",
|
146
|
-
# "name_#{r.lang}" => r.name, # multi-lingual index
|
147
|
-
# }
|
148
|
-
# end
|
149
|
-
#
|
150
|
-
# The first argument is the type (used to locate the table where the data will be stored) and the block
|
151
|
-
# will be yielded with the record and should return a hash of key => value pairs.
|
152
|
-
def index(type, &block)
|
153
|
-
role.add_index(type, block)
|
154
|
-
end
|
155
|
-
|
156
|
-
alias actions class_eval
|
157
|
-
end
|
158
|
-
end
|
159
|
-
accessor_module.role = self
|
160
|
-
accessor_module
|
161
|
-
end
|
162
|
-
|
163
|
-
def define_property_methods(column)
|
164
|
-
name = column.name
|
165
|
-
|
166
|
-
#if create_time_zone_conversion_attribute?(name, column)
|
167
|
-
# define_read_property_method_for_time_zone_conversion(name)
|
168
|
-
#else
|
169
|
-
define_read_property_method(name.to_sym, name, column)
|
170
|
-
#end
|
171
|
-
|
172
|
-
#if create_time_zone_conversion_attribute?(name, column)
|
173
|
-
# define_write_property_method_for_time_zone_conversion(name)
|
174
|
-
#else
|
175
|
-
define_write_property_method(name.to_sym)
|
176
|
-
#end
|
177
|
-
|
178
|
-
define_question_property_method(name)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Define a property reader method. Cope with nil column.
|
182
|
-
def define_read_property_method(symbol, attr_name, column)
|
183
|
-
# Unlike rails, we do not cast on read
|
184
|
-
evaluate_attribute_property_method attr_name, "def #{symbol}; prop['#{attr_name}']; end"
|
185
|
-
end
|
186
|
-
|
187
|
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
188
|
-
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
|
189
|
-
# def define_read_property_method_for_time_zone_conversion(attr_name)
|
190
|
-
# method_body = <<-EOV
|
191
|
-
# def #{attr_name}(reload = false)
|
192
|
-
# cached = @attributes_cache['#{attr_name}']
|
193
|
-
# return cached if cached && !reload
|
194
|
-
# time = properties['#{attr_name}']
|
195
|
-
# @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
|
196
|
-
# end
|
197
|
-
# EOV
|
198
|
-
# evaluate_attribute_property_method attr_name, method_body
|
199
|
-
# end
|
200
|
-
|
201
|
-
# Defines a predicate method <tt>attr_name?</tt>.
|
202
|
-
def define_question_property_method(attr_name)
|
203
|
-
evaluate_attribute_property_method attr_name, "def #{attr_name}?; prop['#{attr_name}']; end", "#{attr_name}?"
|
204
|
-
end
|
205
|
-
|
206
|
-
def define_write_property_method(attr_name)
|
207
|
-
evaluate_attribute_property_method attr_name, "def #{attr_name}=(new_value);prop['#{attr_name}'] = new_value; end", "#{attr_name}="
|
208
|
-
end
|
93
|
+
# Return a list of index definitions in the form [type, key, proc_or_nil]
|
94
|
+
def indices
|
95
|
+
columns.values.select do |c|
|
96
|
+
c.indexed?
|
97
|
+
end.map do |c|
|
98
|
+
[c.index, c.name, c.index_proc]
|
99
|
+
end + @group_indices
|
100
|
+
end
|
209
101
|
|
210
|
-
|
211
|
-
#
|
212
|
-
|
213
|
-
|
214
|
-
# def #{attr_name}=(time)
|
215
|
-
# unless time.acts_like?(:time)
|
216
|
-
# time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
|
217
|
-
# end
|
218
|
-
# time = time.in_time_zone rescue nil if time
|
219
|
-
# prop['#{attr_name}'] = time
|
220
|
-
# end
|
221
|
-
# EOV
|
222
|
-
# evaluate_attribute_property_method attr_name, method_body, "#{attr_name}="
|
223
|
-
# end
|
102
|
+
def inspect
|
103
|
+
# "#<#{self.class}:#{sprintf("0x%x", object_id)} #{@name.inspect} @klass = #{@klass.inspect} @defined_columns = #{@defined_columns.inspect}>"
|
104
|
+
"#<#{self.class}:#{sprintf("0x%x", object_id)} #{column_names.inspect}>"
|
105
|
+
end
|
224
106
|
|
225
|
-
|
226
|
-
def
|
227
|
-
|
107
|
+
protected
|
108
|
+
def initialize_role_module
|
109
|
+
@group_indices = []
|
228
110
|
end
|
229
111
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
raise RedefinedPropertyError.new("Property '#{name}' is already defined in #{schema.name}.")
|
234
|
-
end
|
235
|
-
end
|
112
|
+
# List all property columns defined for this role
|
113
|
+
def defined_columns
|
114
|
+
@defined_columns ||= {}
|
236
115
|
end
|
237
116
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
117
|
+
# @internal
|
118
|
+
def add_column(column)
|
119
|
+
name = column.name
|
120
|
+
# we do not use self.defined_columns because this triggers the load_columns_from_db in StoredRole (= inf loop).
|
121
|
+
defined_columns = (@defined_columns ||= {})
|
122
|
+
|
123
|
+
if defined_columns[name]
|
124
|
+
raise RedefinedPropertyError.new("Property '#{name}' is already defined.")
|
125
|
+
else
|
126
|
+
defined_columns[column.name] = column
|
127
|
+
if @klass && column.should_create_accessors?
|
128
|
+
@klass.define_property_methods(column)
|
242
129
|
end
|
243
130
|
end
|
244
131
|
end
|
245
|
-
end
|
246
|
-
end
|
132
|
+
end # RoleModule
|
133
|
+
end # Property
|