sequel 2.11.0 → 2.12.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 (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -1,539 +0,0 @@
1
- module Sequel
2
- class Model
3
- @allowed_columns = nil
4
- @association_reflections = {}
5
- @cache_store = nil
6
- @cache_ttl = nil
7
- @db = nil
8
- @db_schema = nil
9
- @dataset_methods = {}
10
- @hooks = {}
11
- @overridable_methods_module = nil
12
- @primary_key = :id
13
- @raise_on_save_failure = true
14
- @raise_on_typecast_failure = true
15
- @restrict_primary_key = true
16
- @restricted_columns = nil
17
- @simple_pk = nil
18
- @simple_table = nil
19
- @skip_superclass_validations = nil
20
- @sti_dataset = nil
21
- @sti_key = nil
22
- @strict_param_setting = true
23
- @transform = nil
24
- @typecast_empty_string_to_nil = true
25
- @typecast_on_assignment = true
26
-
27
- # Which columns should be the only columns allowed in a call to set
28
- # (default: all columns).
29
- metaattr_reader :allowed_columns
30
-
31
- # All association reflections defined for this model (default: none).
32
- metaattr_reader :association_reflections
33
-
34
- # Hash of dataset methods to add to this class and subclasses when
35
- # set_dataset is called.
36
- metaattr_reader :dataset_methods
37
-
38
- # The default primary key for classes (default: :id)
39
- metaattr_reader :primary_key
40
-
41
- # Whether to raise an error instead of returning nil on a failure
42
- # to save/create/save_changes/etc.
43
- metaattr_accessor :raise_on_save_failure
44
-
45
- # Whether to raise an error when unable to typecast data for a column
46
- # (default: true)
47
- metaattr_accessor :raise_on_typecast_failure
48
-
49
- # Which columns should not be update in a call to set
50
- # (default: no columns).
51
- metaattr_reader :restricted_columns
52
-
53
- # Should be the literal primary key column name if this Model's table has a simple primary key, or
54
- # nil if the model has a compound primary key or no primary key.
55
- metaattr_reader :simple_pk
56
-
57
- # Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
58
- # or nil otherwise.
59
- metaattr_reader :simple_table
60
-
61
- # The base dataset for STI, to which filters are added to get
62
- # only the models for the specific STI subclass.
63
- metaattr_reader :sti_dataset
64
-
65
- # The column name holding the STI key for this model
66
- metaattr_reader :sti_key
67
-
68
- # Whether new/set/update and their variants should raise an error
69
- # if an invalid key is used (either that doesn't exist or that
70
- # access is restricted to it).
71
- metaattr_accessor :strict_param_setting
72
-
73
- # Whether to typecast the empty string ('') to nil for columns that
74
- # are not string or blob.
75
- metaattr_accessor :typecast_empty_string_to_nil
76
-
77
- # Whether to typecast attribute values on assignment (default: true)
78
- metaattr_accessor :typecast_on_assignment
79
-
80
- # Dataset methods to proxy via metaprogramming
81
- DATASET_METHODS = %w'<< all avg count delete distinct eager eager_graph each each_page
82
- empty? except exclude filter first from from_self full_outer_join get graph
83
- group group_and_count group_by having import inner_join insert
84
- insert_multiple intersect interval invert_order join join_table last
85
- left_outer_join limit map multi_insert naked order order_by order_more
86
- paginate print query range reverse_order right_outer_join select
87
- select_all select_more server set set_graph_aliases single_value size to_csv to_hash
88
- transform union uniq unfiltered unordered update where with_sql'.map{|x| x.to_sym}
89
-
90
- # Instance variables that are inherited in subclasses
91
- INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup, :@cache_store=>nil,
92
- :@cache_ttl=>nil, :@dataset_methods=>:dup, :@primary_key=>nil,
93
- :@raise_on_save_failure=>nil, :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
94
- :@simple_pk=>nil, :@simple_table=>nil,
95
- :@sti_dataset=>nil, :@sti_key=>nil, :@strict_param_setting=>nil,
96
- :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
97
- :@raise_on_typecast_failure=>nil, :@association_reflections=>:dup}
98
-
99
- # Empty instance variables, for -w compliance
100
- EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@transform, :@db, :@skip_superclass_validations]
101
-
102
- # Returns the first record from the database matching the conditions.
103
- # If a hash is given, it is used as the conditions. If another
104
- # object is given, it finds the first record whose primary key(s) match
105
- # the given argument(s). If caching is used, the cache is checked
106
- # first before a dataset lookup is attempted unless a hash is supplied.
107
- def self.[](*args)
108
- args = args.first if (args.size == 1)
109
- return dataset[args] if args.is_a?(Hash)
110
- return cache_lookup(args) if @cache_store
111
- if t = simple_table and p = simple_pk
112
- with_sql("SELECT * FROM #{t} WHERE #{p} = #{dataset.literal(args)} LIMIT 1").first
113
- else
114
- dataset[primary_key_hash(args)]
115
- end
116
- end
117
-
118
- # Returns the columns in the result set in their original order.
119
- # Generally, this will used the columns determined via the database
120
- # schema, but in certain cases (e.g. models that are based on a joined
121
- # dataset) it will use Dataset#columns to find the columns, which
122
- # may be empty if the Dataset has no records.
123
- def self.columns
124
- @columns || set_columns(dataset.naked.columns)
125
- end
126
-
127
- # Creates new instance with values set to passed-in Hash, saves it
128
- # (running any callbacks), and returns the instance if the object
129
- # was saved correctly. If there was an error saving the object,
130
- # returns false.
131
- def self.create(values = {}, &block)
132
- obj = new(values, &block)
133
- return unless obj.save
134
- obj
135
- end
136
-
137
- # Returns the dataset associated with the Model class.
138
- def self.dataset
139
- @dataset || raise(Error, "No dataset associated with #{self}")
140
- end
141
-
142
- # Returns the database associated with the Model class.
143
- def self.db
144
- return @db if @db
145
- @db = self == Model ? DATABASES.first : superclass.db
146
- raise(Error, "No database associated with #{self}") unless @db
147
- @db
148
- end
149
-
150
- # Sets the database associated with the Model class.
151
- def self.db=(db)
152
- @db = db
153
- if @dataset
154
- set_dataset(db[table_name])
155
- end
156
- end
157
-
158
- # Returns the cached schema information if available or gets it
159
- # from the database.
160
- def self.db_schema
161
- @db_schema ||= get_db_schema
162
- end
163
-
164
- # If a block is given, define a method on the dataset with the given argument name using
165
- # the given block as well as a method on the model that calls the
166
- # dataset method.
167
- #
168
- # If a block is not given, define a method on the model for each argument
169
- # that calls the dataset method of the same argument name.
170
- def self.def_dataset_method(*args, &block)
171
- raise(Error, "No arguments given") if args.empty?
172
- if block_given?
173
- raise(Error, "Defining a dataset method using a block requires only one argument") if args.length > 1
174
- meth = args.first
175
- @dataset_methods[meth] = block
176
- dataset.meta_def(meth, &block)
177
- end
178
- args.each{|arg| instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__)}
179
- end
180
-
181
- # Deletes all records in the model's table.
182
- def self.delete_all
183
- dataset.delete
184
- end
185
-
186
- # Like delete_all, but invokes before_destroy and after_destroy hooks if used.
187
- def self.destroy_all
188
- dataset.destroy
189
- end
190
-
191
- # Returns a dataset with custom SQL that yields model objects.
192
- def self.fetch(*args)
193
- db.fetch(*args).set_model(self)
194
- end
195
-
196
- # Modify and return eager loading dataset based on association options
197
- def self.eager_loading_dataset(opts, ds, select, associations)
198
- ds = ds.select(*select) if select
199
- ds = ds.filter(opts[:conditions]) if opts[:conditions]
200
- ds = ds.order(*opts[:order]) if opts[:order]
201
- ds = ds.eager(opts[:eager]) if opts[:eager]
202
- ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph]
203
- ds = ds.eager(associations) unless associations.blank?
204
- ds = opts[:eager_block].call(ds) if opts[:eager_block]
205
- ds
206
- end
207
-
208
- # Finds a single record according to the supplied filter, e.g.:
209
- #
210
- # Ticket.find :author => 'Sharon' # => record
211
- def self.find(*args, &block)
212
- dataset.filter(*args, &block).first
213
- end
214
-
215
- # Like find but invokes create with given conditions when record does not
216
- # exists.
217
- def self.find_or_create(cond)
218
- find(cond) || create(cond)
219
- end
220
-
221
- # If possible, set the dataset for the model subclass as soon as it
222
- # is created. Also, inherit the INHERITED_INSTANCE_VARIABLES
223
- # from the parent class.
224
- def self.inherited(subclass)
225
- sup_class = subclass.superclass
226
- ivs = subclass.instance_variables.collect{|x| x.to_s}
227
- EMPTY_INSTANCE_VARIABLES.each{|iv| subclass.instance_variable_set(iv, nil) unless ivs.include?(iv.to_s)}
228
- INHERITED_INSTANCE_VARIABLES.each do |iv, dup|
229
- next if ivs.include?(iv.to_s)
230
- sup_class_value = sup_class.instance_variable_get(iv)
231
- sup_class_value = sup_class_value.dup if dup == :dup && sup_class_value
232
- subclass.instance_variable_set(iv, sup_class_value)
233
- end
234
- unless ivs.include?("@dataset")
235
- db
236
- begin
237
- if sup_class == Model
238
- subclass.set_dataset(subclass.implicit_table_name) unless subclass.name.blank?
239
- elsif ds = sup_class.instance_variable_get(:@dataset)
240
- subclass.set_dataset(sup_class.sti_key ? sup_class.sti_dataset.filter(sup_class.sti_key=>subclass.name.to_s) : ds.clone, :inherited=>true)
241
- end
242
- rescue
243
- nil
244
- end
245
- end
246
- hooks = subclass.instance_variable_set(:@hooks, {})
247
- sup_class.instance_variable_get(:@hooks).each{|k,v| hooks[k] = v.dup}
248
- end
249
-
250
- # Returns the implicit table name for the model class.
251
- def self.implicit_table_name
252
- name.demodulize.underscore.pluralize.to_sym
253
- end
254
-
255
- # Initializes a model instance as an existing record. This constructor is
256
- # used by Sequel to initialize model instances when fetching records.
257
- # #load requires that values be a hash where all keys are symbols. It
258
- # probably should not be used by external code.
259
- def self.load(values)
260
- new(values, true)
261
- end
262
-
263
- # Mark the model as not having a primary key. Not having a primary key
264
- # can cause issues, among which is that you won't be able to update records.
265
- def self.no_primary_key
266
- @simple_pk = @primary_key = nil
267
- end
268
-
269
- # Returns primary key attribute hash. If using a composite primary key
270
- # value such be an array with values for each primary key in the correct
271
- # order. For a standard primary key, value should be an object with a
272
- # compatible type for the key. If the model does not have a primary key,
273
- # raises an Error.
274
- def self.primary_key_hash(value)
275
- raise(Error, "#{self} does not have a primary key") unless key = @primary_key
276
- case key
277
- when Array
278
- hash = {}
279
- key.each_with_index{|k,i| hash[k] = value[i]}
280
- hash
281
- else
282
- {key => value}
283
- end
284
- end
285
-
286
- # Restrict the setting of the primary key(s) inside new/set/update. Because
287
- # this is the default, this only make sense to use in a subclass where the
288
- # parent class has used unrestrict_primary_key.
289
- def self.restrict_primary_key
290
- @restrict_primary_key = true
291
- end
292
-
293
- # Whether or not setting the primary key inside new/set/update is
294
- # restricted, true by default.
295
- def self.restrict_primary_key?
296
- @restrict_primary_key
297
- end
298
-
299
- # Serializes column with YAML or through marshalling. Arguments should be
300
- # column symbols, with an optional trailing hash with a :format key
301
- # set to :yaml or :marshal (:yaml is the default). Setting this adds
302
- # a transform to the model and dataset so that columns values will be serialized
303
- # when saved and deserialized when returned from the database.
304
- def self.serialize(*columns)
305
- format = columns.extract_options![:format] || :yaml
306
- @transform = columns.inject({}) do |m, c|
307
- m[c] = format
308
- m
309
- end
310
- @dataset.transform(@transform) if @dataset
311
- end
312
-
313
- # Whether or not the given column is serialized for this model.
314
- def self.serialized?(column)
315
- @transform ? @transform.include?(column) : false
316
- end
317
-
318
- # Set the columns to allow in new/set/update. Using this means that
319
- # any columns not listed here will not be modified. If you have any virtual
320
- # setter methods (methods that end in =) that you want to be used in
321
- # new/set/update, they need to be listed here as well (without the =).
322
- #
323
- # It may be better to use (set|update)_only instead of this in places where
324
- # only certain columns may be allowed.
325
- def self.set_allowed_columns(*cols)
326
- @allowed_columns = cols
327
- end
328
-
329
- # Sets the dataset associated with the Model class. ds can be a Symbol
330
- # (specifying a table name in the current database), or a Dataset.
331
- # If a dataset is used, the model's database is changed to the given
332
- # dataset. If a symbol is used, a dataset is created from the current
333
- # database with the table name given. Other arguments raise an Error.
334
- #
335
- # This sets the model of the the given/created dataset to the current model
336
- # and adds a destroy method to it. It also extends the dataset with
337
- # the Associations::EagerLoading methods, and assigns a transform to it
338
- # if there is one associated with the model. Finally, it attempts to
339
- # determine the database schema based on the given/created dataset.
340
- def self.set_dataset(ds, opts={})
341
- inherited = opts[:inherited]
342
- @dataset = case ds
343
- when Symbol
344
- @simple_table = db.literal(ds)
345
- db[ds]
346
- when Dataset
347
- @simple_table = nil
348
- @db = ds.db
349
- ds
350
- else
351
- raise(Error, "Model.set_dataset takes a Symbol or a Sequel::Dataset")
352
- end
353
- @dataset.set_model(self)
354
- @dataset.transform(@transform) if @transform
355
- if inherited
356
- @simple_table = sti_key ? nil : superclass.simple_table
357
- @columns = @dataset.columns rescue nil
358
- else
359
- @dataset.extend(DatasetMethods)
360
- @dataset.extend(Associations::EagerLoading)
361
- @dataset_methods.each{|meth, block| @dataset.meta_def(meth, &block)} if @dataset_methods
362
- end
363
- @db_schema = (inherited ? superclass.db_schema : get_db_schema) rescue nil
364
- self
365
- end
366
- metaalias :dataset=, :set_dataset
367
-
368
- # Sets primary key, regular and composite are possible.
369
- #
370
- # Example:
371
- # class Tagging < Sequel::Model
372
- # # composite key
373
- # set_primary_key :taggable_id, :tag_id
374
- # end
375
- #
376
- # class Person < Sequel::Model
377
- # # regular key
378
- # set_primary_key :person_id
379
- # end
380
- #
381
- # You can set it to nil to not have a primary key, but that
382
- # cause certain things not to work, see #no_primary_key.
383
- def self.set_primary_key(*key)
384
- @simple_pk = key.length == 1 ? db.literal(key.first) : nil
385
- @primary_key = (key.length == 1) ? key[0] : key.flatten
386
- end
387
-
388
- # Set the columns to restrict in new/set/update. Using this means that
389
- # any columns listed here will not be modified. If you have any virtual
390
- # setter methods (methods that end in =) that you want not to be used in
391
- # new/set/update, they need to be listed here as well (without the =).
392
- #
393
- # It may be better to use (set|update)_except instead of this in places where
394
- # only certain columns may be allowed.
395
- def self.set_restricted_columns(*cols)
396
- @restricted_columns = cols
397
- end
398
-
399
- # Makes this model a polymorphic model with the given key being a string
400
- # field in the database holding the name of the class to use. If the
401
- # key given has a NULL value or there are any problems looking up the
402
- # class, uses the current class.
403
- #
404
- # This should be used to set up single table inheritance for the model,
405
- # and it only makes sense to use this in the parent class.
406
- #
407
- # You should call sti_key after any calls to set_dataset in the model,
408
- # otherwise subclasses might not have the filters set up correctly.
409
- #
410
- # The filters that sti_key sets up in subclasses will not work if
411
- # those subclasses have further subclasses. For those middle subclasses,
412
- # you will need to call set_dataset manually with the correct filter set.
413
- def self.set_sti_key(key)
414
- m = self
415
- @sti_key = key
416
- @sti_dataset = dataset
417
- dataset.set_model(key, Hash.new{|h,k| h[k] = (k.constantize rescue m)})
418
- before_create(:set_sti_key){send("#{key}=", model.name.to_s)}
419
- end
420
-
421
- # Returns the columns as a list of frozen strings instead
422
- # of a list of symbols. This makes it possible to check
423
- # whether a column exists without creating a symbol, which
424
- # would be a memory leak if called with user input.
425
- def self.str_columns
426
- @str_columns ||= columns.map{|c| c.to_s.freeze}
427
- end
428
-
429
- # Defines a method that returns a filtered dataset. Subsets
430
- # create dataset methods, so they can be chained for scoping.
431
- # For example:
432
- #
433
- # Topic.subset(:popular, :num_posts.sql_number > 100)
434
- # Topic.subset(:recent, :created_on + 7 > Date.today)
435
- #
436
- # Allows you to do:
437
- #
438
- # Topic.filter(:username.like('%joe%')).popular.recent
439
- #
440
- # to get topics with a username that includes joe that
441
- # have more than 100 posts and were created less than
442
- # 7 days ago.
443
- def self.subset(name, *args, &block)
444
- def_dataset_method(name){filter(*args, &block)}
445
- end
446
-
447
- # Returns name of primary table for the dataset.
448
- def self.table_name
449
- dataset.opts[:from].first
450
- end
451
-
452
- # Allow the setting of the primary key(s) inside new/set/update.
453
- def self.unrestrict_primary_key
454
- @restrict_primary_key = false
455
- end
456
-
457
- # Add model methods that call dataset methods
458
- def_dataset_method(*DATASET_METHODS)
459
-
460
- ### Private Class Methods ###
461
-
462
- # Create the column accessors
463
- def self.def_column_accessor(*columns) # :nodoc:
464
- columns.each do |column|
465
- im = instance_methods.collect{|x| x.to_s}
466
- meth = "#{column}="
467
- overridable_methods_module.module_eval do
468
- define_method(column){self[column]} unless im.include?(column.to_s)
469
- unless im.include?(meth)
470
- define_method(meth) do |*v|
471
- len = v.length
472
- raise(ArgumentError, "wrong number of arguments (#{len} for 1)") unless len == 1
473
- self[column] = v.first
474
- end
475
- end
476
- end
477
- end
478
- end
479
-
480
- # Get the schema from the database, fall back on checking the columns
481
- # via the database if that will return inaccurate results or if
482
- # it raises an error.
483
- def self.get_db_schema(reload = false) # :nodoc:
484
- set_columns(nil)
485
- return nil unless @dataset
486
- schema_hash = {}
487
- ds_opts = dataset.opts
488
- single_table = ds_opts[:from] && (ds_opts[:from].length == 1) \
489
- && !ds_opts.include?(:join) && !ds_opts.include?(:sql)
490
- get_columns = proc{columns rescue []}
491
- if single_table && (schema_array = (db.schema(table_name, :reload=>reload) rescue nil))
492
- schema_array.each{|k,v| schema_hash[k] = v}
493
- if ds_opts.include?(:select)
494
- # Dataset only selects certain columns, delete the other
495
- # columns from the schema
496
- cols = get_columns.call
497
- schema_hash.delete_if{|k,v| !cols.include?(k)}
498
- cols.each{|c| schema_hash[c] ||= {}}
499
- else
500
- # Dataset is for a single table with all columns,
501
- # so set the columns based on the order they were
502
- # returned by the schema.
503
- cols = schema_array.collect{|k,v| k}
504
- set_columns(cols)
505
- # Set the primary key(s) based on the schema information
506
- pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
507
- pks.length > 0 ? set_primary_key(*pks) : no_primary_key
508
- # Also set the columns for the dataset, so the dataset
509
- # doesn't have to do a query to get them.
510
- dataset.instance_variable_set(:@columns, cols)
511
- end
512
- else
513
- # If the dataset uses multiple tables or custom sql or getting
514
- # the schema raised an error, just get the columns and
515
- # create an empty schema hash for it.
516
- get_columns.call.each{|c| schema_hash[c] = {}}
517
- end
518
- schema_hash
519
- end
520
-
521
- # Module that the class includes that holds methods the class adds for column accessors and
522
- # associations so that the methods can be overridden with super
523
- def self.overridable_methods_module # :nodoc:
524
- include(@overridable_methods_module = Module.new) unless @overridable_methods_module
525
- @overridable_methods_module
526
- end
527
-
528
- # Set the columns for this model, reset the str_columns,
529
- # and create accessor methods for each column.
530
- def self.set_columns(new_columns) # :nodoc:
531
- @columns = new_columns
532
- def_column_accessor(*new_columns) if new_columns
533
- @str_columns = nil
534
- @columns
535
- end
536
-
537
- private_class_method :def_column_accessor, :get_db_schema, :overridable_methods_module, :set_columns
538
- end
539
- end