datamapper 0.2.5 → 0.3.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.
Files changed (121) hide show
  1. data/CHANGELOG +5 -1
  2. data/FAQ +96 -0
  3. data/QUICKLINKS +12 -0
  4. data/README +57 -155
  5. data/environment.rb +61 -43
  6. data/example.rb +30 -12
  7. data/lib/data_mapper.rb +6 -1
  8. data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
  9. data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
  10. data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
  11. data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
  12. data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
  14. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
  15. data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
  16. data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
  17. data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
  18. data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
  19. data/lib/data_mapper/associations.rb +75 -3
  20. data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
  21. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
  22. data/lib/data_mapper/associations/has_many_association.rb +168 -61
  23. data/lib/data_mapper/associations/has_n_association.rb +23 -3
  24. data/lib/data_mapper/attributes.rb +73 -0
  25. data/lib/data_mapper/auto_migrations.rb +2 -6
  26. data/lib/data_mapper/base.rb +5 -9
  27. data/lib/data_mapper/database.rb +4 -3
  28. data/lib/data_mapper/embedded_value.rb +66 -30
  29. data/lib/data_mapper/identity_map.rb +1 -3
  30. data/lib/data_mapper/is/tree.rb +121 -0
  31. data/lib/data_mapper/migration.rb +155 -0
  32. data/lib/data_mapper/persistence.rb +532 -218
  33. data/lib/data_mapper/property.rb +306 -0
  34. data/lib/data_mapper/query.rb +164 -0
  35. data/lib/data_mapper/support/blank.rb +2 -2
  36. data/lib/data_mapper/support/connection_pool.rb +5 -6
  37. data/lib/data_mapper/support/enumerable.rb +3 -3
  38. data/lib/data_mapper/support/errors.rb +10 -1
  39. data/lib/data_mapper/support/inflector.rb +174 -238
  40. data/lib/data_mapper/support/object.rb +54 -0
  41. data/lib/data_mapper/support/serialization.rb +19 -1
  42. data/lib/data_mapper/support/string.rb +7 -16
  43. data/lib/data_mapper/support/symbol.rb +3 -15
  44. data/lib/data_mapper/support/typed_set.rb +68 -0
  45. data/lib/data_mapper/types/base.rb +44 -0
  46. data/lib/data_mapper/types/string.rb +34 -0
  47. data/lib/data_mapper/validations/number_validator.rb +40 -0
  48. data/lib/data_mapper/validations/string_validator.rb +20 -0
  49. data/lib/data_mapper/validations/validator.rb +13 -0
  50. data/performance.rb +26 -1
  51. data/profile_data_mapper.rb +1 -1
  52. data/rakefile.rb +42 -2
  53. data/spec/acts_as_tree_spec.rb +11 -3
  54. data/spec/adapters/data_object_adapter_spec.rb +31 -0
  55. data/spec/associations/belongs_to_association_spec.rb +98 -0
  56. data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
  57. data/spec/associations/has_many_association_spec.rb +337 -0
  58. data/spec/attributes_spec.rb +23 -1
  59. data/spec/auto_migrations_spec.rb +86 -29
  60. data/spec/callbacks_spec.rb +107 -0
  61. data/spec/column_spec.rb +5 -2
  62. data/spec/count_command_spec.rb +33 -1
  63. data/spec/database_spec.rb +18 -0
  64. data/spec/dependency_spec.rb +4 -2
  65. data/spec/embedded_value_spec.rb +8 -8
  66. data/spec/fixtures/people.yaml +1 -1
  67. data/spec/fixtures/projects.yaml +10 -1
  68. data/spec/fixtures/tasks.yaml +6 -0
  69. data/spec/fixtures/tasks_tasks.yaml +2 -0
  70. data/spec/fixtures/tomatoes.yaml +1 -0
  71. data/spec/is_a_tree_spec.rb +149 -0
  72. data/spec/load_command_spec.rb +71 -9
  73. data/spec/magic_columns_spec.rb +17 -2
  74. data/spec/migration_spec.rb +267 -0
  75. data/spec/models/animal.rb +1 -1
  76. data/spec/models/candidate.rb +8 -0
  77. data/spec/models/career.rb +1 -1
  78. data/spec/models/chain.rb +8 -0
  79. data/spec/models/comment.rb +1 -1
  80. data/spec/models/exhibit.rb +1 -1
  81. data/spec/models/fence.rb +7 -0
  82. data/spec/models/fruit.rb +2 -2
  83. data/spec/models/job.rb +8 -0
  84. data/spec/models/person.rb +2 -3
  85. data/spec/models/post.rb +1 -1
  86. data/spec/models/project.rb +21 -1
  87. data/spec/models/section.rb +1 -1
  88. data/spec/models/serializer.rb +1 -1
  89. data/spec/models/task.rb +9 -0
  90. data/spec/models/tomato.rb +27 -0
  91. data/spec/models/user.rb +8 -2
  92. data/spec/models/zoo.rb +2 -7
  93. data/spec/paranoia_spec.rb +1 -1
  94. data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
  95. data/spec/postgres_spec.rb +48 -6
  96. data/spec/property_spec.rb +90 -9
  97. data/spec/query_spec.rb +71 -5
  98. data/spec/save_command_spec.rb +11 -0
  99. data/spec/spec_helper.rb +14 -11
  100. data/spec/support/blank_spec.rb +8 -0
  101. data/spec/support/inflector_spec.rb +41 -0
  102. data/spec/support/object_spec.rb +9 -0
  103. data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
  104. data/spec/support/silence_spec.rb +15 -0
  105. data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
  106. data/spec/support/struct_spec.rb +12 -0
  107. data/spec/support/typed_set_spec.rb +66 -0
  108. data/spec/table_spec.rb +3 -3
  109. data/spec/types/string.rb +81 -0
  110. data/spec/validates_uniqueness_of_spec.rb +17 -0
  111. data/spec/validations/number_validator.rb +59 -0
  112. data/spec/validations/string_validator.rb +14 -0
  113. metadata +59 -17
  114. data/do_performance.rb +0 -153
  115. data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
  116. data/lib/data_mapper/support/weak_hash.rb +0 -46
  117. data/spec/active_record_impersonation_spec.rb +0 -129
  118. data/spec/associations_spec.rb +0 -232
  119. data/spec/conditions_spec.rb +0 -49
  120. data/spec/has_many_association_spec.rb +0 -173
  121. data/spec/models/animals_exhibit.rb +0 -8
@@ -1,4 +1,310 @@
1
+
1
2
  module DataMapper
3
+
4
+ # :include:/QUICKLINKS
5
+ #
6
+ # = Properties
7
+ # A model's properties are not derived from database structure.
8
+ # Instead, properties are declared inside it's model's class definition,
9
+ # which map to (or generate) fields in a database.
10
+ #
11
+ # Defining properties explicitly in a model has several advantages.
12
+ # It centralizes information about the model
13
+ # in a single location, rather than having to dig out migrations, xml,
14
+ # or other config files. It also provides the ability to use Ruby's
15
+ # access control functions. Finally, since Datamapper only cares about
16
+ # properties explicitly defined in your models, Datamappers plays well
17
+ # with legacy databases and shares databases easily with other
18
+ # applications.
19
+ #
20
+ # == Declaring Properties
21
+ # Inside your class, you call the property method for each property you want to add.
22
+ # The only two required arguments are the name and type, everything else is optional.
23
+ #
24
+ # class Post < DataMapper::Base
25
+ # property :title, :string, :nullable => false # Cannot be null
26
+ # property :publish, :boolen, :default => false # Default value for new records
27
+ # is false
28
+ # end
29
+ #
30
+ # == Limiting Access
31
+ # Property access control is uses the same terminology Ruby does. Properties are
32
+ # public by default, but can also be declared private or protected as needed
33
+ # (via the :accessor option).
34
+ #
35
+ # class Post < DataMapper::Base
36
+ # property :title, :string, :accessor => :private # Both reader and writer are private
37
+ # property :body, :text, :accessor => :protected # Both reader and writer are protected
38
+ # end
39
+ #
40
+ # Access control is also analogous to Ruby getters, setters, and accessors, and can
41
+ # be declared using :reader and :writer, in addition to :accessor.
42
+ #
43
+ # class Post < DataMapper::Base
44
+ # property :title, :string, :writer => :private # Only writer is private
45
+ # property :tags, :string, :reader => :protected # Only reader is protected
46
+ # end
47
+ #
48
+ # == Overriding Accessors
49
+ # The accessor for any property can be overridden in the same manner that Ruby class accessors
50
+ # can be. After the property is defined, just add your custom accessor:
51
+ #
52
+ # class Post < DataMapper::Base
53
+ # property :title, :string
54
+ #
55
+ # def title=(new_title)
56
+ # raise ArgumentError if new_title != 'Luke is Awesome'
57
+ # @title = new_title
58
+ # end
59
+ # end
60
+ #
61
+ # == Lazy Loading
62
+ # By default, some properties are not loaded when an object is fetched in Datamapper.
63
+ # These lazily loaded properties are fetched on demand when their accessor is called
64
+ # for the first time (as it is often unnecessary to instantiate -every- property
65
+ # -every- time an object is loaded). For instance, text fields are lazy loading by
66
+ # default, although you can over-ride this behavior if you wish:
67
+ #
68
+ # Example:
69
+ #
70
+ # class Post < DataMapper::Base
71
+ # property :title, :string # Loads normally
72
+ # property :body, :text # Is lazily loaded by default
73
+ # end
74
+ #
75
+ # If you want to over-ride the lazy loading on any field you can set it to true or
76
+ # false with the :lazy option.
77
+ #
78
+ # class Post < DataMapper::Base
79
+ # property :title, :string # Loads normally
80
+ # property :body, :text, :lazy => false # The default is now over-ridden
81
+ # end
82
+ #
83
+ # Delaying the request for lazy-loaded attributes even applies to objects accessed through
84
+ # associations. In a sense, Datamapper anticipates that you will likely be iterating
85
+ # over objects in associations and rolls all of the load commands for lazy-loaded
86
+ # properties into one request from the database.
87
+ #
88
+ # Example:
89
+ #
90
+ # Widget[1].components # loads when the post object is pulled from database, by default
91
+ # Widget[1].components.first.body # loads the values for the body property on all objects in the
92
+ # association, rather than just this one.
93
+ #
94
+ # == Keys
95
+ # Properties can be declared as primary or natural keys on a table. By default,
96
+ # Datamapper will assume <tt>:id</tt> and create it if you don't have it.
97
+ # You can, however, declare a property as the primary key of the table:
98
+ #
99
+ # property :legacy_pk, :string, :key => true
100
+ #
101
+ # This is roughly equivalent to Activerecord's <tt>set_primary_key</tt>, though
102
+ # non-integer data types may be used, thus Datamapper supports natural keys.
103
+ # When a property is declared as a natural key, accessing the object using the
104
+ # indexer syntax <tt>Class[key]</tt> remains valid.
105
+ #
106
+ # User[1] when :id is the primary key on the users table
107
+ # User['bill'] when :name is the primary (natural) key on the users table
108
+ #
109
+ # == Inferred Validations
110
+ # When properties are declared with specific column restrictions, Datamapper
111
+ # will infer a few validation rules for values assigned to that property.
112
+ #
113
+ # property :title, :string, :length => 250
114
+ # # => infers 'validates_length_of :title, :minimum => 0, :maximum => 250'
115
+ #
116
+ # property :title, :string, :nullable => false
117
+ # # => infers 'validates_presence_of :title
118
+ #
119
+ # property :email, :string, :format => :email_address
120
+ # # => infers 'validates_format_of :email, :with => :email_address
121
+ #
122
+ # property :title, :string, :length => 255, :nullable => false
123
+ # # => infers both 'validates_length_of' as well as 'validates_presence_of'
124
+ # # better: property :title, :string, :length => 1..255
125
+ #
126
+ # For more information about validations, visit the Validatable documentation.
127
+ # == Embedded Values
128
+ # As an alternative to extraneous has_one relationships, consider using an
129
+ # EmbeddedValue.
130
+ #
131
+ # == Misc. Notes
132
+ # * Properties declared as strings will default to a length of 50, rather than 255
133
+ # (typical max varchar column size). To overload the default, pass
134
+ # <tt>:length => 255</tt> or <tt>:length => 0..255</tt>. Since Datamapper does
135
+ # not introspect for properties, this means that legacy database tables may need
136
+ # their <tt>:string</tt> columns defined with a <tt>:length</tt> so that DM does
137
+ # not inadvertantly truncate data.
138
+ # * You may declare a Property with the data-type of <tt>:class</tt>.
139
+ # see SingleTableInheritance for more on how to use <tt>:class</tt> columns.
2
140
  class Property
141
+
142
+ # NOTE: check is only for psql, so maybe the postgres adapter should define
143
+ # its own property options. currently it will produce a warning tho since
144
+ # PROPERTY_OPTIONS is a constant
145
+ PROPERTY_OPTIONS = [
146
+ :public, :protected, :private, :accessor, :reader, :writer,
147
+ :lazy, :default, :nullable, :key, :serial, :column, :size, :length,
148
+ :format, :index, :check, :ordinal, :auto_validation
149
+ ]
150
+
151
+ VISIBILITY_OPTIONS = [:public, :protected, :private]
152
+
153
+ def initialize(klass, name, type, options)
154
+
155
+ @klass, @name, @type, @options = klass, name.to_sym, type, options
156
+ @symbolized_name = name.to_s.sub(/\?$/, '').to_sym
157
+
158
+ validate_type!
159
+ validate_options!
160
+ determine_visibility!
161
+
162
+ database.schema[klass].add_column(@symbolized_name, @type, @options)
163
+ klass::ATTRIBUTES << @symbolized_name
164
+
165
+ create_getter!
166
+ create_setter!
167
+ auto_validations! unless @options[:auto_validation] == false
168
+
169
+ end
170
+
171
+ def validate_type! # :nodoc:
172
+ adapter_class = database.adapter.class
173
+ raise ArgumentError.new("#{@type.inspect} is not a supported type in the database adapter. Valid types are:\n #{adapter_class::TYPES.keys.inspect}") unless adapter_class::TYPES.has_key?(@type)
174
+ end
175
+
176
+ def validate_options! # :nodoc:
177
+ @options.each_pair do |k,v|
178
+ raise ArgumentError.new("#{k.inspect} is not a supported option in DataMapper::Property::PROPERTY_OPTIONS") unless PROPERTY_OPTIONS.include?(k)
179
+ end
180
+ end
181
+
182
+ def determine_visibility! # :nodoc:
183
+ @reader_visibility = @options[:reader] || @options[:accessor] || :public
184
+ @writer_visibility = @options[:writer] || @options[:accessor] || :public
185
+ @writer_visibility = :protected if @options[:protected]
186
+ @writer_visibility = :private if @options[:private]
187
+ raise(ArgumentError.new, "property visibility must be :public, :protected, or :private") unless VISIBILITY_OPTIONS.include?(@reader_visibility) && VISIBILITY_OPTIONS.include?(@writer_visibility)
188
+ end
189
+
190
+ # defines the getter for the property
191
+ def create_getter!
192
+ if lazy?
193
+ klass.class_eval <<-EOS
194
+ #{reader_visibility.to_s}
195
+ def #{name}
196
+ lazy_load!(#{name.inspect})
197
+ class << self;
198
+ attr_accessor #{name.inspect}
199
+ end
200
+ @#{name}
201
+ end
202
+ EOS
203
+ else
204
+ klass.class_eval <<-EOS
205
+ #{reader_visibility.to_s}
206
+ def #{name}
207
+ #{instance_variable_name}
208
+ end
209
+ EOS
210
+ end
211
+ if type == :boolean
212
+ klass.class_eval <<-EOS
213
+ #{reader_visibility.to_s}
214
+ def #{name.to_s.ensure_ends_with('?')}
215
+ #{instance_variable_name}
216
+ end
217
+ EOS
218
+ end
219
+ rescue SyntaxError
220
+ raise SyntaxError.new(column)
221
+ end
222
+
223
+ # defines the setter for the property
224
+ def create_setter!
225
+ if lazy?
226
+ klass.class_eval <<-EOS
227
+ #{writer_visibility.to_s}
228
+ def #{name}=(value)
229
+ class << self;
230
+ attr_accessor #{name.inspect}
231
+ end
232
+ @#{name} = value
233
+ end
234
+ EOS
235
+ else
236
+ klass.class_eval <<-EOS
237
+ #{writer_visibility.to_s}
238
+ def #{name}=(value)
239
+ #{instance_variable_name} = value
240
+ end
241
+ EOS
242
+ end
243
+ rescue SyntaxError
244
+ raise SyntaxError.new(column)
245
+ end
246
+
247
+ # NOTE: :length may also be used in place of :size
248
+ AUTO_VALIDATIONS = {
249
+ :nullable => lambda { |k,v| "validates_presence_of :#{k}" if v == false },
250
+ :size => lambda { |k,v| "validates_length_of :#{k}, " + (v.is_a?(Range) ? ":minimum => #{v.first}, :maximum => #{v.last}" : ":maximum => #{v}") },
251
+ :format => lambda { |k, v| "validates_format_of :#{k}, :with => #{v.inspect}" }
252
+ }
253
+
254
+ AUTO_VALIDATIONS[:length] = AUTO_VALIDATIONS[:size].dup
255
+
256
+ # defines the inferred validations given a property definition.
257
+ def auto_validations!
258
+ AUTO_VALIDATIONS.each do |key, value|
259
+ next unless options.has_key?(key)
260
+ validation = value.call(name, options[key])
261
+ next if validation.empty?
262
+ klass.class_eval <<-EOS
263
+ begin
264
+ #{validation}
265
+ rescue ArgumentError => e
266
+ throw e unless e.message =~ /specify a unique key/
267
+ end
268
+ EOS
269
+ end
270
+ end
271
+
272
+ def klass
273
+ @klass
274
+ end
275
+
276
+ def column
277
+ column = database.table(klass)[@name]
278
+ raise StandardError.new("#{@name.inspect} is not a valid column name") unless column
279
+ return column
280
+ end
281
+
282
+ def name
283
+ @name
284
+ end
285
+
286
+ def instance_variable_name # :nodoc:
287
+ column.instance_variable_name
288
+ end
289
+
290
+ def type
291
+ column.type
292
+ end
293
+
294
+ def options
295
+ column.options
296
+ end
297
+
298
+ def reader_visibility # :nodoc:
299
+ @reader_visibility
300
+ end
301
+
302
+ def writer_visibility # :nodoc:
303
+ @writer_visibility
304
+ end
305
+
306
+ def lazy?
307
+ column.lazy?
308
+ end
3
309
  end
4
310
  end
@@ -0,0 +1,164 @@
1
+ module DataMapper
2
+
3
+ # This class handles option parsing and SQL generation.
4
+ class Query
5
+
6
+ # These are the standard finder options
7
+ OPTIONS = [
8
+ :select, :offset, :limit, :include, :reload, :conditions, :join, :order, :after_row_materialization
9
+ ]
10
+
11
+ def initialize(adapter, klass, options = {})
12
+ # Set some of the standard options
13
+ @adapter, @klass = adapter, klass
14
+ @from = @adapter.table(@klass)
15
+ @parameters = []
16
+ @joins = []
17
+
18
+ # Parse simple options
19
+ @limit = options.fetch(:limit, nil)
20
+ @offset = options.fetch(:offset, nil)
21
+ @reload = options.fetch(:reload, false)
22
+ @order = options.fetch(:order, nil)
23
+ @after_row_materialization = options.fetch(:after_row_materialization, nil)
24
+
25
+ # Parse :include option
26
+ @includes = case include_options = options[:include]
27
+ when Array then include_options.dup
28
+ when Symbol then [include_options]
29
+ when NilClass then []
30
+ else raise ArgumentError.new(":include must be an Array, Symbol or nil, but was #{include_options.inspect}")
31
+ end
32
+
33
+ # Include lazy columns if specified in :include option
34
+ @columns = @from.columns.select do |column|
35
+ !column.lazy? || @includes.delete(column.name)
36
+ end
37
+
38
+ # Qualify columns with their table name if joins are present
39
+ @qualify = !@includes.empty?
40
+
41
+ # Generate SQL for joins
42
+ @includes.each do |association_name|
43
+ association = @from.associations[association_name]
44
+ @joins << association.to_sql
45
+ @columns += association.associated_table.columns.select do |column|
46
+ !column.lazy?
47
+ end
48
+ end
49
+
50
+ # Prepare conditions for parsing
51
+ @conditions = []
52
+
53
+ # Each non-standard option is assumed to be a column
54
+ options.each_pair do |k,v|
55
+ unless OPTIONS.include?(k)
56
+ append_condition(k, v)
57
+ end
58
+ end
59
+
60
+ # If a :conditions option is present, parse it
61
+ if conditions_option = options[:conditions]
62
+ if conditions_option.is_a?(String)
63
+ @conditions << conditions_option
64
+ else
65
+ append_condition(*conditions_option)
66
+ end
67
+ end
68
+
69
+ # If the table is paranoid, add a filter to the conditions
70
+ if @from.paranoid?
71
+ @conditions << "#{@from.paranoid_column.to_sql(qualify?)} IS NULL OR #{@from.paranoid_column.to_sql(qualify?)} > #{@adapter.class::SYNTAX[:now]}"
72
+ end
73
+
74
+ end
75
+
76
+ # SQL for query
77
+ def to_sql
78
+ sql = "SELECT #{columns.map { |column| column.to_sql(qualify?) }.join(', ')} FROM #{from.to_sql}"
79
+
80
+ sql << " " << joins.join($/) unless joins.empty?
81
+ sql << " WHERE (#{conditions.join(") AND (")})" unless conditions.empty?
82
+ return sql
83
+ end
84
+
85
+ # Parameters for query
86
+ def parameters
87
+ @parameters
88
+ end
89
+
90
+ private
91
+
92
+ # Conditions for the query, in the form of an Array of Strings
93
+ def conditions
94
+ @conditions
95
+ end
96
+
97
+ # Determines wether columns should be qualified with their table-names.
98
+ def qualify?
99
+ @qualify
100
+ end
101
+
102
+ # The primary table in the FROM clause of the query
103
+ def from
104
+ @from
105
+ end
106
+
107
+ # SQL for any joins in the query
108
+ def joins
109
+ @joins
110
+ end
111
+
112
+ # Column mappings to be selected
113
+ def columns
114
+ @columns
115
+ end
116
+
117
+ def append_condition(clause, value)
118
+ case clause
119
+ when Symbol::Operator then
120
+ operator = case clause.type
121
+ when :gt then '>'
122
+ when :gte then '>='
123
+ when :lt then '<'
124
+ when :lte then '<='
125
+ when :not then inequality_operator(value)
126
+ when :eql then equality_operator(value)
127
+ when :like then equality_operator(value, 'LIKE')
128
+ when :in then equality_operator(value)
129
+ else raise ArgumentError.new('Operator type not supported')
130
+ end
131
+ @conditions << "#{from[clause].to_sql(qualify?)} #{operator} ?"
132
+ @parameters << value
133
+ when Symbol then
134
+ @conditions << "#{from[clause].to_sql(qualify?)} #{equality_operator(value)} ?"
135
+ @parameters << value
136
+ when String then
137
+ @conditions << clause
138
+ value.each { |v| @parameters << v }
139
+ when Mappings::Column then
140
+ @conditions << "#{clause.to_sql(qualify?)} #{equality_operator(value)} ?"
141
+ @parameters << value
142
+ else raise "CAN HAS CRASH? #{clause.inspect}"
143
+ end
144
+ end
145
+
146
+ def equality_operator(value, default = '=')
147
+ case value
148
+ when NilClass then 'IS'
149
+ when Array then 'IN'
150
+ else default
151
+ end
152
+ end
153
+
154
+ def inequality_operator(value, default = '<>')
155
+ case value
156
+ when NilClass then 'IS NOT'
157
+ when Array then 'NOT IN'
158
+ else default
159
+ end
160
+ end
161
+
162
+
163
+ end # class Query
164
+ end # module DataMapper