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.
- data/CHANGELOG +168 -0
- data/README.rdoc +77 -95
- data/Rakefile +100 -80
- data/bin/sequel +2 -1
- data/doc/advanced_associations.rdoc +23 -32
- data/doc/cheat_sheet.rdoc +23 -40
- data/doc/dataset_filtering.rdoc +6 -6
- data/doc/prepared_statements.rdoc +22 -22
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/schema.rdoc +3 -1
- data/doc/sharding.rdoc +8 -8
- data/doc/virtual_rows.rdoc +65 -0
- data/lib/sequel.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
- data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
- data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
- data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
- data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
- data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
- data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
- data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
- data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
- data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
- data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
- data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
- data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
- data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
- data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
- data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
- data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
- data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
- data/lib/sequel/core.rb +221 -0
- data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
- data/lib/{sequel_core → sequel}/database.rb +264 -149
- data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
- data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
- data/lib/sequel/database/schema_sql.rb +224 -0
- data/lib/{sequel_core → sequel}/dataset.rb +78 -236
- data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
- data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
- data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
- data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
- data/lib/sequel/deprecated.rb +593 -0
- data/lib/sequel/deprecated_migration.rb +91 -0
- data/lib/sequel/exceptions.rb +48 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
- data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
- data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
- data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
- data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +43 -0
- data/lib/sequel/model.rb +110 -0
- data/lib/sequel/model/associations.rb +1300 -0
- data/lib/sequel/model/base.rb +937 -0
- data/lib/sequel/model/deprecated.rb +204 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +388 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
- data/lib/sequel/model/inflections.rb +208 -0
- data/lib/sequel/model/plugins.rb +76 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/serialization.rb +117 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +384 -0
- data/lib/sequel/plugins/validation_helpers.rb +150 -0
- data/lib/{sequel_core → sequel}/sql.rb +125 -190
- data/lib/{sequel_core → sequel}/version.rb +2 -1
- data/lib/sequel_core.rb +1 -172
- data/lib/sequel_model.rb +1 -91
- data/spec/adapters/firebird_spec.rb +5 -5
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mysql_spec.rb +128 -42
- data/spec/adapters/oracle_spec.rb +47 -19
- data/spec/adapters/postgres_spec.rb +64 -52
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +12 -17
- data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
- data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
- data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
- data/spec/{sequel_core → core}/database_spec.rb +135 -99
- data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
- data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
- data/spec/core/migration_spec.rb +263 -0
- data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
- data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
- data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
- data/spec/{sequel_core → core}/schema_spec.rb +8 -10
- data/spec/{sequel_core → core}/spec_helper.rb +29 -2
- data/spec/{sequel_core → core}/version_spec.rb +0 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
- data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
- data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
- data/spec/extensions/serialization_spec.rb +109 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
- data/spec/extensions/validation_helpers_spec.rb +291 -0
- data/spec/integration/dataset_test.rb +31 -0
- data/spec/integration/eager_loader_test.rb +17 -30
- data/spec/integration/schema_test.rb +8 -5
- data/spec/integration/spec_helper.rb +17 -0
- data/spec/integration/transaction_test.rb +68 -0
- data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
- data/spec/{sequel_model → model}/associations_spec.rb +23 -10
- data/spec/{sequel_model → model}/base_spec.rb +29 -20
- data/spec/{sequel_model → model}/caching_spec.rb +16 -14
- data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
- data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/{sequel_model → model}/model_spec.rb +25 -20
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/{sequel_model → model}/record_spec.rb +121 -62
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- metadata +136 -107
- data/lib/sequel_core/core_ext.rb +0 -217
- data/lib/sequel_core/dataset/callback.rb +0 -13
- data/lib/sequel_core/dataset/schema.rb +0 -15
- data/lib/sequel_core/deprecated.rb +0 -26
- data/lib/sequel_core/exceptions.rb +0 -44
- data/lib/sequel_core/schema.rb +0 -2
- data/lib/sequel_core/schema/sql.rb +0 -325
- data/lib/sequel_model/association_reflection.rb +0 -267
- data/lib/sequel_model/associations.rb +0 -499
- data/lib/sequel_model/base.rb +0 -539
- data/lib/sequel_model/caching.rb +0 -82
- data/lib/sequel_model/dataset_methods.rb +0 -26
- data/lib/sequel_model/eager_loading.rb +0 -370
- data/lib/sequel_model/hooks.rb +0 -101
- data/lib/sequel_model/plugins.rb +0 -62
- data/lib/sequel_model/record.rb +0 -568
- data/lib/sequel_model/schema.rb +0 -49
- data/lib/sequel_model/validations.rb +0 -429
- data/spec/sequel_model/plugins_spec.rb +0 -80
data/lib/sequel_model/base.rb
DELETED
|
@@ -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
|