property 1.2.0 → 2.0.0
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 +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
|