datamapper 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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