datamapper 0.1.1 → 0.2.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/CHANGELOG +65 -0
- data/README +193 -1
- data/do_performance.rb +153 -0
- data/environment.rb +45 -0
- data/example.rb +119 -22
- data/lib/data_mapper.rb +36 -16
- data/lib/data_mapper/adapters/abstract_adapter.rb +8 -0
- data/lib/data_mapper/adapters/data_object_adapter.rb +360 -0
- data/lib/data_mapper/adapters/mysql_adapter.rb +30 -179
- data/lib/data_mapper/adapters/postgresql_adapter.rb +90 -199
- data/lib/data_mapper/adapters/sql/coersion.rb +32 -3
- data/lib/data_mapper/adapters/sql/commands/conditions.rb +97 -128
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +234 -231
- data/lib/data_mapper/adapters/sql/commands/loader.rb +99 -0
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +30 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +68 -6
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +6 -3
- data/lib/data_mapper/adapters/sql/mappings/table.rb +71 -42
- data/lib/data_mapper/adapters/sql/quoting.rb +8 -2
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +32 -201
- data/lib/data_mapper/associations.rb +21 -7
- data/lib/data_mapper/associations/belongs_to_association.rb +96 -80
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +158 -67
- data/lib/data_mapper/associations/has_many_association.rb +96 -78
- data/lib/data_mapper/associations/has_n_association.rb +64 -0
- data/lib/data_mapper/associations/has_one_association.rb +49 -79
- data/lib/data_mapper/associations/reference.rb +47 -0
- data/lib/data_mapper/base.rb +216 -50
- data/lib/data_mapper/callbacks.rb +71 -24
- data/lib/data_mapper/{session.rb → context.rb} +20 -8
- data/lib/data_mapper/database.rb +176 -45
- data/lib/data_mapper/embedded_value.rb +65 -0
- data/lib/data_mapper/identity_map.rb +12 -4
- data/lib/data_mapper/support/active_record_impersonation.rb +12 -8
- data/lib/data_mapper/support/enumerable.rb +8 -0
- data/lib/data_mapper/support/serialization.rb +13 -0
- data/lib/data_mapper/support/string.rb +1 -12
- data/lib/data_mapper/support/symbol.rb +3 -0
- data/lib/data_mapper/validations/unique_validator.rb +1 -2
- data/lib/data_mapper/validations/validation_helper.rb +18 -1
- data/performance.rb +109 -34
- data/plugins/can_has_sphinx/LICENSE +23 -0
- data/plugins/can_has_sphinx/README +4 -0
- data/plugins/can_has_sphinx/REVISION +1 -0
- data/plugins/can_has_sphinx/Rakefile +22 -0
- data/plugins/can_has_sphinx/init.rb +1 -0
- data/plugins/can_has_sphinx/install.rb +1 -0
- data/plugins/can_has_sphinx/lib/acts_as_sphinx.rb +123 -0
- data/plugins/can_has_sphinx/lib/sphinx.rb +460 -0
- data/plugins/can_has_sphinx/scripts/sphinx.sh +47 -0
- data/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake +41 -0
- data/plugins/dataobjects/REVISION +1 -0
- data/plugins/dataobjects/Rakefile +7 -0
- data/plugins/dataobjects/do.rb +246 -0
- data/plugins/dataobjects/do_mysql.rb +179 -0
- data/plugins/dataobjects/do_postgres.rb +181 -0
- data/plugins/dataobjects/do_sqlite3.rb +153 -0
- data/plugins/dataobjects/spec/do_spec.rb +150 -0
- data/plugins/dataobjects/spec/spec_helper.rb +81 -0
- data/plugins/dataobjects/swig_mysql/do_mysql.bundle +0 -0
- data/plugins/dataobjects/swig_mysql/extconf.rb +33 -0
- data/plugins/dataobjects/swig_mysql/mysql_c.c +18800 -0
- data/plugins/dataobjects/swig_mysql/mysql_c.i +8 -0
- data/plugins/dataobjects/swig_mysql/mysql_supp.i +46 -0
- data/plugins/dataobjects/swig_postgres/Makefile +146 -0
- data/plugins/dataobjects/swig_postgres/extconf.rb +29 -0
- data/plugins/dataobjects/swig_postgres/postgres_c.bundle +0 -0
- data/plugins/dataobjects/swig_postgres/postgres_c.c +8185 -0
- data/plugins/dataobjects/swig_postgres/postgres_c.i +73 -0
- data/plugins/dataobjects/swig_sqlite/db +0 -0
- data/plugins/dataobjects/swig_sqlite/extconf.rb +9 -0
- data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +4725 -0
- data/plugins/dataobjects/swig_sqlite/sqlite_c.i +168 -0
- data/rakefile.rb +45 -23
- data/spec/acts_as_tree_spec.rb +39 -0
- data/spec/associations_spec.rb +220 -0
- data/spec/attributes_spec.rb +15 -0
- data/spec/base_spec.rb +44 -0
- data/spec/callbacks_spec.rb +45 -0
- data/spec/can_has_sphinx.rb +6 -0
- data/spec/coersion_spec.rb +34 -0
- data/spec/conditions_spec.rb +49 -0
- data/spec/conversions_to_yaml_spec.rb +17 -0
- data/spec/count_command_spec.rb +11 -0
- data/spec/delete_command_spec.rb +1 -1
- data/spec/embedded_value_spec.rb +23 -0
- data/spec/fixtures/animals_exhibits.yaml +2 -0
- data/spec/fixtures/people.yaml +18 -1
- data/spec/{legacy.rb → legacy_spec.rb} +3 -3
- data/spec/load_command_spec.rb +157 -20
- data/spec/magic_columns_spec.rb +9 -0
- data/spec/mock_adapter.rb +20 -0
- data/spec/models/animal.rb +1 -1
- data/spec/models/animals_exhibit.rb +6 -0
- data/spec/models/exhibit.rb +2 -0
- data/spec/models/person.rb +26 -1
- data/spec/models/project.rb +19 -0
- data/spec/models/sales_person.rb +1 -0
- data/spec/models/section.rb +6 -0
- data/spec/models/zoo.rb +3 -1
- data/spec/query_spec.rb +9 -0
- data/spec/save_command_spec.rb +65 -1
- data/spec/schema_spec.rb +89 -0
- data/spec/single_table_inheritance_spec.rb +27 -0
- data/spec/spec_helper.rb +9 -55
- data/spec/{symbolic_operators.rb → symbolic_operators_spec.rb} +9 -5
- data/spec/{validates_confirmation_of.rb → validates_confirmation_of_spec.rb} +4 -3
- data/spec/{validates_format_of.rb → validates_format_of_spec.rb} +5 -4
- data/spec/{validates_length_of.rb → validates_length_of_spec.rb} +8 -7
- data/spec/{validates_uniqueness_of.rb → validates_uniqueness_of_spec.rb} +7 -10
- data/spec/{validations.rb → validations_spec.rb} +24 -6
- data/tasks/drivers.rb +20 -0
- data/tasks/fixtures.rb +42 -0
- metadata +181 -42
- data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +0 -140
- data/lib/data_mapper/adapters/sql/commands/delete_command.rb +0 -113
- data/lib/data_mapper/adapters/sql/commands/save_command.rb +0 -141
- data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +0 -33
- data/lib/data_mapper/adapters/sql_adapter.rb +0 -163
- data/lib/data_mapper/associations/advanced_has_many_association.rb +0 -55
- data/lib/data_mapper/support/blank_slate.rb +0 -3
- data/lib/data_mapper/support/proc.rb +0 -69
- data/lib/data_mapper/support/struct.rb +0 -26
- data/lib/data_mapper/unit_of_work.rb +0 -38
- data/spec/basic_finder.rb +0 -67
- data/spec/belongs_to.rb +0 -47
- data/spec/has_and_belongs_to_many.rb +0 -25
- data/spec/has_many.rb +0 -34
- data/spec/new_record.rb +0 -24
- data/spec/sub_select.rb +0 -16
- data/spec/support/string_spec.rb +0 -7
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module DataMapper
|
|
2
|
+
module Associations
|
|
3
|
+
|
|
4
|
+
class HasNAssociation
|
|
5
|
+
|
|
6
|
+
attr_reader :adapter, :table, :options
|
|
7
|
+
|
|
8
|
+
OPTIONS = [
|
|
9
|
+
:class,
|
|
10
|
+
:class_name,
|
|
11
|
+
:foreign_key
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
def initialize(klass, association_name, options)
|
|
15
|
+
@adapter = database.adapter
|
|
16
|
+
@table = adapter.table(klass)
|
|
17
|
+
@association_name = association_name.to_sym
|
|
18
|
+
@options = options || Hash.new
|
|
19
|
+
|
|
20
|
+
define_accessor(klass)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def name
|
|
24
|
+
@association_name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def constant
|
|
28
|
+
@associated_class || @associated_class = if @options.has_key?(:class) || @options.has_key?(:class_name)
|
|
29
|
+
associated_class_name = (@options[:class] || @options[:class_name])
|
|
30
|
+
if associated_class_name.kind_of?(String)
|
|
31
|
+
Kernel.const_get(Inflector.classify(associated_class_name))
|
|
32
|
+
else
|
|
33
|
+
associated_class_name
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
Kernel.const_get(Inflector.classify(@association_name))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def foreign_key
|
|
41
|
+
@foreign_key || @foreign_key = begin
|
|
42
|
+
association_table[@options[:foreign_key] || table.default_foreign_key]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def association_table
|
|
47
|
+
@association_table || (@association_table = adapter.table(constant))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_sql
|
|
51
|
+
"JOIN #{association_table.to_sql} ON #{foreign_key.to_sql(true)} = #{table.key.to_sql(true)}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def association_columns
|
|
55
|
+
association_table.columns.reject { |column| column.lazy? }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def finder_options
|
|
59
|
+
@finder_options || @finder_options = @options.reject { |k,v| self.class::OPTIONS.include?(k) }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -1,107 +1,77 @@
|
|
|
1
|
+
require 'data_mapper/associations/has_n_association'
|
|
2
|
+
|
|
1
3
|
module DataMapper
|
|
2
4
|
module Associations
|
|
3
5
|
|
|
4
|
-
class HasOneAssociation
|
|
5
|
-
|
|
6
|
-
def initialize(instance, association_name, options)
|
|
7
|
-
@instance = instance
|
|
8
|
-
@association_name = association_name
|
|
9
|
-
@options = options
|
|
10
|
-
|
|
11
|
-
@associated_class = if options.has_key?(:class) || options.has_key?(:class_name)
|
|
12
|
-
associated_class_name = (options[:class] || options[:class_name])
|
|
13
|
-
if associated_class_name.kind_of?(String)
|
|
14
|
-
Kernel.const_get(Inflector.classify(associated_class_name))
|
|
15
|
-
else
|
|
16
|
-
associated_class_name
|
|
17
|
-
end
|
|
18
|
-
else
|
|
19
|
-
Kernel.const_get(Inflector.classify(association_name))
|
|
20
|
-
end
|
|
21
|
-
end
|
|
6
|
+
class HasOneAssociation < HasNAssociation
|
|
22
7
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# Define the association instance method (i.e. Exhibit#zoo)
|
|
8
|
+
# Define the association instance method (i.e. Project#tasks)
|
|
9
|
+
def define_accessor(klass)
|
|
26
10
|
klass.class_eval <<-EOS
|
|
27
|
-
def create_#{association_name}(options
|
|
28
|
-
#{association_name}_association.create(options)
|
|
11
|
+
def create_#{@association_name}(options)
|
|
12
|
+
#{@association_name}_association.create(options)
|
|
29
13
|
end
|
|
30
14
|
|
|
31
|
-
def build_#{association_name}(options
|
|
32
|
-
#{association_name}_association.build(options)
|
|
15
|
+
def build_#{@association_name}(options)
|
|
16
|
+
#{@association_name}_association.build(options)
|
|
33
17
|
end
|
|
34
18
|
|
|
35
|
-
def #{association_name}
|
|
36
|
-
#
|
|
37
|
-
#{association_name}_association.find
|
|
19
|
+
def #{@association_name}
|
|
20
|
+
#{@association_name}_association.instance
|
|
38
21
|
end
|
|
39
22
|
|
|
40
|
-
def #{association_name}=(value)
|
|
41
|
-
#{association_name}_association.set(value)
|
|
23
|
+
def #{@association_name}=(value)
|
|
24
|
+
#{@association_name}_association.set(value)
|
|
42
25
|
end
|
|
43
26
|
|
|
44
27
|
private
|
|
45
|
-
def #{association_name}_association
|
|
46
|
-
@#{association_name} || (@#{association_name} = HasOneAssociation.new(self,
|
|
28
|
+
def #{@association_name}_association
|
|
29
|
+
@#{@association_name}_association || (@#{@association_name}_association = DataMapper::Associations::HasOneAssociation::Instance.new(self, #{@association_name.inspect}))
|
|
47
30
|
end
|
|
48
31
|
EOS
|
|
49
|
-
|
|
50
32
|
end
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
34
|
+
class Instance < Associations::Reference
|
|
35
|
+
|
|
36
|
+
def instance
|
|
37
|
+
@associated || @associated = begin
|
|
38
|
+
if @instance.loaded_set.nil?
|
|
39
|
+
nil
|
|
40
|
+
else
|
|
41
|
+
# Temp variable for the instance variable name.
|
|
42
|
+
setter_method = "#{@association_name}=".to_sym
|
|
43
|
+
instance_variable_name = "@#{association.foreign_key}".to_sym
|
|
60
44
|
|
|
61
|
-
|
|
45
|
+
set = @instance.loaded_set.group_by { |instance| instance.key }
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
# Fetch the foreign objects for all instances in the current object's loaded-set.
|
|
48
|
+
@instance.session.all(association.constant, association.foreign_key => set.keys).each do |assoc|
|
|
49
|
+
set[assoc.instance_variable_get(instance_variable_name)].first.send(setter_method, assoc)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@associated
|
|
53
|
+
end
|
|
54
|
+
|
|
66
55
|
end
|
|
67
56
|
end
|
|
68
|
-
|
|
69
|
-
return @result
|
|
70
|
-
end
|
|
71
57
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
def create(options)
|
|
59
|
+
@associated = association.constant.new(options)
|
|
60
|
+
if @associated.save
|
|
61
|
+
@associated.send("#{@associated_class.foreign_key}=", @instance.key)
|
|
62
|
+
end
|
|
77
63
|
end
|
|
78
|
-
end
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def set(val)
|
|
85
|
-
@result = val
|
|
86
|
-
end
|
|
65
|
+
def build(options)
|
|
66
|
+
@associated = association.constant.new(options)
|
|
67
|
+
end
|
|
87
68
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
69
|
+
def set(val)
|
|
70
|
+
@associated = val
|
|
71
|
+
end
|
|
91
72
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
module HasOne
|
|
95
|
-
def self.included(base)
|
|
96
|
-
base.extend(ClassMethods)
|
|
97
|
-
end
|
|
73
|
+
end # class Instance
|
|
98
74
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
end
|
|
107
|
-
end
|
|
75
|
+
end # class HasOneAssociation
|
|
76
|
+
end # module Associations
|
|
77
|
+
end # module DataMapper
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module DataMapper
|
|
2
|
+
|
|
3
|
+
module Associations
|
|
4
|
+
|
|
5
|
+
# Reference is an abstract-class providing the boiler-plate for
|
|
6
|
+
# the association proxies (ie: HasManyAssociation::Set, or
|
|
7
|
+
# HasOneAssociation::Instance)
|
|
8
|
+
# The proxies need to access the defining Association instances
|
|
9
|
+
# to obtain mapping information. This class provides convenient
|
|
10
|
+
# access to said Association.
|
|
11
|
+
#
|
|
12
|
+
# EXAMPLE:
|
|
13
|
+
# class Zoo
|
|
14
|
+
# has_many :exhibits
|
|
15
|
+
# end
|
|
16
|
+
# The +has_many+ declaration instantiates a
|
|
17
|
+
# DataMapper::Associations::HasManyAssociation and adds it to the
|
|
18
|
+
# DataMapper::Adapters::Sql::Mappings::Table#associations array for
|
|
19
|
+
# the Table representing Zoo.
|
|
20
|
+
#
|
|
21
|
+
# Zoo.new.exhibits
|
|
22
|
+
# +exhibits+ above returns an instance of
|
|
23
|
+
# DataMapper::Associations::HasManyAssociation::Set. This instance
|
|
24
|
+
# needs to access the actual HasManyAssociation instance in order
|
|
25
|
+
# to access the mapping information within. The DataMapper::Associations::Reference
|
|
26
|
+
# abstract-class for the Set provides the Reference#association method in order to
|
|
27
|
+
# provide easy access to this information.
|
|
28
|
+
class Reference
|
|
29
|
+
|
|
30
|
+
# +instance+ is a mapped object instance. ie: #<Zoo:0x123456 ...>
|
|
31
|
+
# +association_name+ is the Symbol used to look up the Association
|
|
32
|
+
# instance within the DataMapper::Adapters::Sql::Mappings::Table
|
|
33
|
+
def initialize(instance, association_name)
|
|
34
|
+
@instance, @association_name = instance, association_name.to_sym
|
|
35
|
+
@instance.loaded_associations << self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# #association provides lazily initialized access to the declared
|
|
39
|
+
# Association.
|
|
40
|
+
def association
|
|
41
|
+
@association || (@association = @instance.session.table(@instance.class).associations[@association_name])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
data/lib/data_mapper/base.rb
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
require 'data_mapper/unit_of_work'
|
|
2
1
|
require 'data_mapper/support/active_record_impersonation'
|
|
2
|
+
require 'data_mapper/support/serialization'
|
|
3
3
|
require 'data_mapper/validations/validation_helper'
|
|
4
4
|
require 'data_mapper/associations'
|
|
5
5
|
require 'data_mapper/callbacks'
|
|
6
|
+
require 'data_mapper/embedded_value'
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'ferret'
|
|
10
|
+
rescue LoadError
|
|
11
|
+
end
|
|
6
12
|
|
|
7
13
|
module DataMapper
|
|
8
14
|
|
|
@@ -11,56 +17,123 @@ module DataMapper
|
|
|
11
17
|
# This probably needs to be protected
|
|
12
18
|
attr_accessor :loaded_set
|
|
13
19
|
|
|
14
|
-
include
|
|
20
|
+
include CallbacksHelper
|
|
15
21
|
include Support::ActiveRecordImpersonation
|
|
22
|
+
include Support::Serialization
|
|
16
23
|
include Validations::ValidationHelper
|
|
17
24
|
include Associations
|
|
18
25
|
|
|
26
|
+
# Track classes that inherit from DataMapper::Base.
|
|
27
|
+
def self.subclasses
|
|
28
|
+
@subclasses || (@subclasses = [])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.auto_migrate!
|
|
32
|
+
subclasses.each do |subclass|
|
|
33
|
+
subclass.auto_migrate!
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
19
37
|
def self.inherited(klass)
|
|
20
|
-
klass
|
|
38
|
+
DataMapper::Base::subclasses << klass
|
|
39
|
+
klass.send(:undef_method, :id)
|
|
21
40
|
|
|
22
41
|
# When this class is sub-classed, copy the declared columns.
|
|
23
42
|
klass.class_eval do
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
|
|
44
|
+
def self.auto_migrate!
|
|
45
|
+
if self::subclasses.empty?
|
|
46
|
+
database.schema[self].drop!
|
|
47
|
+
database.save(self)
|
|
48
|
+
else
|
|
49
|
+
schema = database.schema
|
|
50
|
+
columns = self::subclasses.inject(schema[self].columns) do |span, subclass|
|
|
51
|
+
span + schema[subclass].columns
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
table_name = schema[self].name.to_s
|
|
55
|
+
table = schema[table_name]
|
|
56
|
+
columns.each do |column|
|
|
57
|
+
table.add_column(column.name, column.type, column.options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
table.drop!
|
|
61
|
+
table.create!
|
|
31
62
|
end
|
|
32
|
-
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.subclasses
|
|
66
|
+
@subclasses || (@subclasses = [])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.inherited(subclass)
|
|
70
|
+
self::subclasses << subclass
|
|
33
71
|
end
|
|
34
72
|
end
|
|
35
73
|
end
|
|
36
74
|
|
|
75
|
+
# Allows you to override the table name for a model.
|
|
76
|
+
# EXAMPLE:
|
|
77
|
+
# class WorkItem
|
|
78
|
+
# set_table_name 't_work_item_list'
|
|
79
|
+
# end
|
|
37
80
|
def self.set_table_name(value)
|
|
38
81
|
database.schema[self].name = value
|
|
39
82
|
end
|
|
40
83
|
|
|
41
84
|
def initialize(details = nil)
|
|
42
85
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
instance_variable_set("@#{key}", value)
|
|
48
|
-
end
|
|
86
|
+
case details
|
|
87
|
+
when Hash then self.attributes = details
|
|
88
|
+
when DataMapper::Base then self.attributes = details.attributes
|
|
89
|
+
when NilClass then nil
|
|
49
90
|
end
|
|
50
91
|
end
|
|
51
92
|
|
|
93
|
+
# Adds property accessors for a field that you'd like to be able to modify. The DataMapper doesn't
|
|
94
|
+
# use the table schema to infer accessors, you must explicity call #property to add field accessors
|
|
95
|
+
# to your model.
|
|
96
|
+
# EXAMPLE:
|
|
97
|
+
# class CellProvider
|
|
98
|
+
# property :name, :string
|
|
99
|
+
# property :rating, :integer
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# att = CellProvider.new(:name => 'AT&T')
|
|
103
|
+
# att.rating = 3
|
|
104
|
+
# puts att.name, att.rating
|
|
105
|
+
#
|
|
106
|
+
# => AT&T
|
|
107
|
+
# => 3
|
|
52
108
|
def self.property(name, type, options = {})
|
|
53
109
|
mapping = database.schema[self].add_column(name, type, options)
|
|
54
110
|
property_getter(name, mapping)
|
|
55
111
|
property_setter(name, mapping)
|
|
112
|
+
|
|
113
|
+
if MAGIC_PROPERTIES.has_key?(name)
|
|
114
|
+
class_eval(&MAGIC_PROPERTIES[name])
|
|
115
|
+
end
|
|
116
|
+
|
|
56
117
|
return name
|
|
57
118
|
end
|
|
58
119
|
|
|
120
|
+
MAGIC_PROPERTIES = {
|
|
121
|
+
:updated_at => lambda { before_save { |x| x.updated_at = Time::now } },
|
|
122
|
+
:updated_on => lambda { before_save { |x| x.updated_on = Date::today } },
|
|
123
|
+
:created_at => lambda { before_create { |x| x.created_at = Time::now } },
|
|
124
|
+
:created_on => lambda { before_create { |x| x.created_on = Date::today } }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def self.embed(class_or_name, &block)
|
|
128
|
+
EmbeddedValue::define(self, class_or_name, &block)
|
|
129
|
+
end
|
|
130
|
+
|
|
59
131
|
def self.property_getter(name, mapping)
|
|
60
132
|
if mapping.lazy?
|
|
61
133
|
class_eval <<-EOS
|
|
62
134
|
def #{name}
|
|
63
|
-
lazy_load!(
|
|
135
|
+
lazy_load!(#{name.inspect})
|
|
136
|
+
@#{name}
|
|
64
137
|
end
|
|
65
138
|
EOS
|
|
66
139
|
else
|
|
@@ -83,34 +156,114 @@ module DataMapper
|
|
|
83
156
|
end
|
|
84
157
|
end
|
|
85
158
|
|
|
86
|
-
|
|
87
|
-
|
|
159
|
+
# Lazy-loads the attributes for a loaded_set, then overwrites the accessors
|
|
160
|
+
# for the named methods so that the lazy_loading is skipped the second time.
|
|
161
|
+
def lazy_load!(*names)
|
|
162
|
+
|
|
163
|
+
reset_attribute = lambda do |instance|
|
|
164
|
+
singleton_class = (class << instance; self end)
|
|
165
|
+
names.each do |name|
|
|
166
|
+
singleton_class.send(:attr_accessor, name)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
88
169
|
|
|
89
|
-
|
|
170
|
+
unless new_record? || loaded_set.nil?
|
|
171
|
+
session.all(
|
|
172
|
+
self.class,
|
|
173
|
+
:select => ([:id] + names),
|
|
174
|
+
:reload => true,
|
|
175
|
+
:id => loaded_set.map(&:id)
|
|
176
|
+
).each(&reset_attribute)
|
|
177
|
+
else
|
|
178
|
+
reset_attribute[self]
|
|
179
|
+
end
|
|
90
180
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def new_record?
|
|
184
|
+
@new_record.nil? || @new_record
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def loaded_attributes
|
|
188
|
+
pairs = {}
|
|
94
189
|
|
|
95
|
-
session.
|
|
96
|
-
|
|
190
|
+
session.table(self).columns.each do |column|
|
|
191
|
+
pairs[column.name] = instance_variable_get(column.instance_variable_name)
|
|
97
192
|
end
|
|
98
193
|
|
|
99
|
-
|
|
194
|
+
pairs
|
|
100
195
|
end
|
|
101
|
-
|
|
196
|
+
|
|
102
197
|
def attributes
|
|
103
|
-
|
|
104
|
-
|
|
198
|
+
pairs = {}
|
|
199
|
+
|
|
200
|
+
session.table(self).columns.each do |column|
|
|
201
|
+
lazy_load!(column.name) if column.lazy?
|
|
202
|
+
value = instance_variable_get(column.instance_variable_name)
|
|
203
|
+
pairs[column.name] = column.type == :class ? value.to_s : value
|
|
105
204
|
end
|
|
205
|
+
|
|
206
|
+
pairs
|
|
106
207
|
end
|
|
107
208
|
|
|
209
|
+
# Mass-assign mapped fields.
|
|
108
210
|
def attributes=(values_hash)
|
|
211
|
+
table = session.schema[self.class]
|
|
212
|
+
|
|
109
213
|
values_hash.reject do |key, value|
|
|
110
214
|
protected_attribute? key
|
|
111
215
|
end.each_pair do |key, value|
|
|
112
|
-
|
|
216
|
+
if column = table[key]
|
|
217
|
+
instance_variable_set(column.instance_variable_name, value)
|
|
218
|
+
else
|
|
219
|
+
send("#{key}=", value)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def dirty?(name = nil)
|
|
225
|
+
if name.nil?
|
|
226
|
+
session.table(self).columns.any? do |column|
|
|
227
|
+
if value = self.instance_variable_get(column.instance_variable_name)
|
|
228
|
+
value.hash != original_hashes[column.name]
|
|
229
|
+
else
|
|
230
|
+
false
|
|
231
|
+
end
|
|
232
|
+
end || loaded_associations.any? do |loaded_association|
|
|
233
|
+
if loaded_association.respond_to?(:dirty?)
|
|
234
|
+
loaded_association.dirty?
|
|
235
|
+
else
|
|
236
|
+
false
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
else
|
|
240
|
+
key = name.kind_of?(Symbol) ? name : name.to_sym
|
|
241
|
+
self.instance_variable_get("@#{name}").hash != original_hashes[key]
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def dirty_attributes
|
|
246
|
+
pairs = {}
|
|
247
|
+
|
|
248
|
+
if new_record?
|
|
249
|
+
session.table(self).columns.each do |column|
|
|
250
|
+
unless (value = instance_variable_get(column.instance_variable_name)).nil?
|
|
251
|
+
pairs[column.name] = value
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
else
|
|
255
|
+
session.table(self).columns.each do |column|
|
|
256
|
+
if (value = instance_variable_get(column.instance_variable_name)).hash != original_hashes[column.name]
|
|
257
|
+
pairs[column.name] = value
|
|
258
|
+
end
|
|
259
|
+
end
|
|
113
260
|
end
|
|
261
|
+
|
|
262
|
+
pairs
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def original_hashes
|
|
266
|
+
@original_hashes || (@original_hashes = {})
|
|
114
267
|
end
|
|
115
268
|
|
|
116
269
|
def protected_attribute?(key)
|
|
@@ -121,12 +274,33 @@ module DataMapper
|
|
|
121
274
|
@protected_attributes ||= []
|
|
122
275
|
end
|
|
123
276
|
|
|
277
|
+
def self.index
|
|
278
|
+
@index || @index = Ferret::Index::Index.new(:path => "#{database.adapter.index_path}/#{name}")
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def self.reindex!
|
|
282
|
+
all.each do |record|
|
|
283
|
+
index << record.attributes
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def self.search(phrase)
|
|
288
|
+
ids = []
|
|
289
|
+
|
|
290
|
+
query = "#{database.schema[self].columns.map(&:name).join('|')}:\"#{phrase}\""
|
|
291
|
+
|
|
292
|
+
index.search_each(query) do |document_id, score|
|
|
293
|
+
ids << index[document_id][:id]
|
|
294
|
+
end
|
|
295
|
+
return all(:id => ids)
|
|
296
|
+
end
|
|
297
|
+
|
|
124
298
|
def self.protect(*keys)
|
|
125
299
|
keys.each { |key| protected_attributes << key.to_sym }
|
|
126
300
|
end
|
|
127
301
|
|
|
128
302
|
def self.foreign_key
|
|
129
|
-
|
|
303
|
+
Inflector.underscore(self.name) + "_id"
|
|
130
304
|
end
|
|
131
305
|
|
|
132
306
|
def inspect
|
|
@@ -141,6 +315,10 @@ module DataMapper
|
|
|
141
315
|
"#<%s:0x%x @new_record=%s, %s>" % [self.class.name, (object_id * 2), new_record?, inspected_attributes.join(', ')]
|
|
142
316
|
end
|
|
143
317
|
|
|
318
|
+
def loaded_associations
|
|
319
|
+
@loaded_associations || @loaded_associations = []
|
|
320
|
+
end
|
|
321
|
+
|
|
144
322
|
def session=(value)
|
|
145
323
|
@session = value
|
|
146
324
|
end
|
|
@@ -149,6 +327,12 @@ module DataMapper
|
|
|
149
327
|
@session || ( @session = database )
|
|
150
328
|
end
|
|
151
329
|
|
|
330
|
+
def key=(value)
|
|
331
|
+
key_column = session.schema[self.class].key
|
|
332
|
+
@__key = key_column.type_cast_value(value)
|
|
333
|
+
instance_variable_set(key_column.instance_variable_name, @__key)
|
|
334
|
+
end
|
|
335
|
+
|
|
152
336
|
def key
|
|
153
337
|
@__key || @__key = begin
|
|
154
338
|
key_column = session.schema[self.class].key
|
|
@@ -156,24 +340,6 @@ module DataMapper
|
|
|
156
340
|
end
|
|
157
341
|
end
|
|
158
342
|
|
|
159
|
-
# Callbacks associated with this class.
|
|
160
|
-
def self.callbacks
|
|
161
|
-
@callbacks || ( @callbacks = Callbacks.new )
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Declare helpers for the standard callbacks
|
|
165
|
-
DataMapper::Callbacks::EVENTS.each do |name|
|
|
166
|
-
class_eval <<-EOS
|
|
167
|
-
def self.#{name}(string = nil, &block)
|
|
168
|
-
if string.nil?
|
|
169
|
-
callbacks.add(:#{name}, &block)
|
|
170
|
-
else
|
|
171
|
-
callbacks.add(:#{name}, string)
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
EOS
|
|
175
|
-
end
|
|
176
|
-
|
|
177
343
|
end
|
|
178
344
|
|
|
179
345
|
end
|