viking-sequel 3.10.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 +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- 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/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
module Sequel
|
2
|
+
# The default exception class for exceptions raised by Sequel.
|
3
|
+
# All exception classes defined by Sequel are descendants of this class.
|
4
|
+
class Error < ::StandardError
|
5
|
+
# If this exception wraps an underlying exception, the underlying
|
6
|
+
# exception is held here.
|
7
|
+
attr_accessor :wrapped_exception
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raised when the adapter requested doesn't exist or can't be loaded.
|
11
|
+
class AdapterNotFound < Error ; end
|
12
|
+
|
13
|
+
# Generic error raised by the database adapters, indicating a
|
14
|
+
# problem originating from the database server. Usually raised
|
15
|
+
# because incorrect SQL syntax is used.
|
16
|
+
class DatabaseError < Error; end
|
17
|
+
|
18
|
+
# Error raised when the Sequel is unable to connect to the database with the
|
19
|
+
# connection parameters it was given.
|
20
|
+
class DatabaseConnectionError < DatabaseError; end
|
21
|
+
|
22
|
+
# Error that should be raised by adapters when they determine that the connection
|
23
|
+
# to the database has been lost. Instructs the connection pool code to
|
24
|
+
# remove that connection from the pool so that other connections can be acquired
|
25
|
+
# automatically.
|
26
|
+
class DatabaseDisconnectError < DatabaseError; end
|
27
|
+
|
28
|
+
# Raised on an invalid operation, such as trying to update or delete
|
29
|
+
# a joined or grouped dataset.
|
30
|
+
class InvalidOperation < Error; end
|
31
|
+
|
32
|
+
# Raised when attempting an invalid type conversion.
|
33
|
+
class InvalidValue < Error ; end
|
34
|
+
|
35
|
+
# Raised when the connection pool cannot acquire a database connection
|
36
|
+
# before the timeout.
|
37
|
+
class PoolTimeout < Error ; end
|
38
|
+
|
39
|
+
# Exception that you should raise to signal a rollback of the current transaction.
|
40
|
+
# The transaction block will catch this exception, rollback the current transaction,
|
41
|
+
# and won't reraise it.
|
42
|
+
class Rollback < Error ; end
|
43
|
+
|
44
|
+
class Error
|
45
|
+
AdapterNotFound = Sequel::AdapterNotFound
|
46
|
+
InvalidOperation = Sequel::InvalidOperation
|
47
|
+
InvalidValue = Sequel::InvalidValue
|
48
|
+
PoolTimeoutError = Sequel::PoolTimeout
|
49
|
+
Rollback = Sequel::Rollback
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# The blank extension adds the blank? method to all objects (e.g. Object#blank?).
|
2
|
+
|
3
|
+
class FalseClass
|
4
|
+
# false is always blank
|
5
|
+
def blank?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Object
|
11
|
+
# Objects are blank if they respond true to empty?
|
12
|
+
def blank?
|
13
|
+
respond_to?(:empty?) && empty?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class NilClass
|
18
|
+
# nil is always blank
|
19
|
+
def blank?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Numeric
|
25
|
+
# Numerics are never blank (not even 0)
|
26
|
+
def blank?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class String
|
32
|
+
# Strings are blank if they are empty or include only whitespace
|
33
|
+
def blank?
|
34
|
+
strip.empty?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class TrueClass
|
39
|
+
# true is never blank
|
40
|
+
def blank?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# The inflector extension adds inflection instance methods to String, which allows the easy transformation of
|
2
|
+
# words from singular to plural, class names to table names, modularized class
|
3
|
+
# names to ones without, and class names to foreign keys. It exists for
|
4
|
+
# backwards compatibility to legacy Sequel code.
|
5
|
+
|
6
|
+
class String
|
7
|
+
# This module acts as a singleton returned/yielded by String.inflections,
|
8
|
+
# which is used to override or specify additional inflection rules. Examples:
|
9
|
+
#
|
10
|
+
# String.inflections do |inflect|
|
11
|
+
# inflect.plural /^(ox)$/i, '\1\2en'
|
12
|
+
# inflect.singular /^(ox)en/i, '\1'
|
13
|
+
#
|
14
|
+
# inflect.irregular 'octopus', 'octopi'
|
15
|
+
#
|
16
|
+
# inflect.uncountable "equipment"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
|
20
|
+
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
|
21
|
+
# already have been loaded.
|
22
|
+
module Inflections
|
23
|
+
@plurals, @singulars, @uncountables = [], [], []
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for plurization.
|
27
|
+
attr_reader :plurals
|
28
|
+
|
29
|
+
# Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for singularization.
|
30
|
+
attr_reader :singulars
|
31
|
+
|
32
|
+
# Array of strings for words were the singular form is the same as the plural form
|
33
|
+
attr_reader :uncountables
|
34
|
+
end
|
35
|
+
|
36
|
+
# Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
|
37
|
+
# the options are: :plurals, :singulars, :uncountables
|
38
|
+
#
|
39
|
+
# Examples:
|
40
|
+
# clear :all
|
41
|
+
# clear :plurals
|
42
|
+
def self.clear(scope = :all)
|
43
|
+
case scope
|
44
|
+
when :all
|
45
|
+
@plurals, @singulars, @uncountables = [], [], []
|
46
|
+
else
|
47
|
+
instance_variable_set("@#{scope}", [])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
|
52
|
+
# for strings, not regular expressions. You simply pass the irregular in singular and plural form.
|
53
|
+
#
|
54
|
+
# Examples:
|
55
|
+
# irregular 'octopus', 'octopi'
|
56
|
+
# irregular 'person', 'people'
|
57
|
+
def self.irregular(singular, plural)
|
58
|
+
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
|
59
|
+
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
|
60
|
+
end
|
61
|
+
|
62
|
+
# Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
|
63
|
+
# The replacement should always be a string that may include references to the matched data from the rule.
|
64
|
+
#
|
65
|
+
# Example:
|
66
|
+
# plural(/(x|ch|ss|sh)$/i, '\1es')
|
67
|
+
def self.plural(rule, replacement)
|
68
|
+
@plurals.insert(0, [rule, replacement])
|
69
|
+
end
|
70
|
+
|
71
|
+
# Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
|
72
|
+
# The replacement should always be a string that may include references to the matched data from the rule.
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
# singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
76
|
+
def self.singular(rule, replacement)
|
77
|
+
@singulars.insert(0, [rule, replacement])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add uncountable words that shouldn't be attempted inflected.
|
81
|
+
#
|
82
|
+
# Examples:
|
83
|
+
# uncountable "money"
|
84
|
+
# uncountable "money", "information"
|
85
|
+
# uncountable %w( money information rice )
|
86
|
+
def self.uncountable(*words)
|
87
|
+
(@uncountables << words).flatten!
|
88
|
+
end
|
89
|
+
|
90
|
+
Sequel.require('default_inflections', 'model')
|
91
|
+
instance_eval(&Sequel::DEFAULT_INFLECTIONS_PROC)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Yield the Inflections module if a block is given, and return
|
95
|
+
# the Inflections module.
|
96
|
+
def self.inflections
|
97
|
+
yield Inflections if block_given?
|
98
|
+
Inflections
|
99
|
+
end
|
100
|
+
|
101
|
+
# By default, camelize converts the string to UpperCamelCase. If the argument to camelize
|
102
|
+
# is set to :lower then camelize produces lowerCamelCase.
|
103
|
+
#
|
104
|
+
# camelize will also convert '/' to '::' which is useful for converting paths to namespaces
|
105
|
+
#
|
106
|
+
# Examples
|
107
|
+
# "active_record".camelize #=> "ActiveRecord"
|
108
|
+
# "active_record".camelize(:lower) #=> "activeRecord"
|
109
|
+
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
|
110
|
+
# "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
|
111
|
+
def camelize(first_letter_in_uppercase = :upper)
|
112
|
+
s = gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
113
|
+
s[0...1] = s[0...1].downcase unless first_letter_in_uppercase == :upper
|
114
|
+
s
|
115
|
+
end
|
116
|
+
alias_method :camelcase, :camelize
|
117
|
+
|
118
|
+
# Singularizes and camelizes the string. Also strips out all characters preceding
|
119
|
+
# and including a period (".").
|
120
|
+
#
|
121
|
+
# Examples
|
122
|
+
# "egg_and_hams".classify #=> "EggAndHam"
|
123
|
+
# "post".classify #=> "Post"
|
124
|
+
# "schema.post".classify #=> "Post"
|
125
|
+
def classify
|
126
|
+
sub(/.*\./, '').singularize.camelize
|
127
|
+
end
|
128
|
+
|
129
|
+
# Constantize tries to find a declared constant with the name specified
|
130
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
131
|
+
# or is not initialized.
|
132
|
+
#
|
133
|
+
# Examples
|
134
|
+
# "Module".constantize #=> Module
|
135
|
+
# "Class".constantize #=> Class
|
136
|
+
def constantize
|
137
|
+
raise(NameError, "#{inspect} is not a valid constant name!") unless m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(self)
|
138
|
+
Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Replaces underscores with dashes in the string.
|
142
|
+
#
|
143
|
+
# Example
|
144
|
+
# "puni_puni".dasherize #=> "puni-puni"
|
145
|
+
def dasherize
|
146
|
+
gsub(/_/, '-')
|
147
|
+
end
|
148
|
+
|
149
|
+
# Removes the module part from the expression in the string
|
150
|
+
#
|
151
|
+
# Examples
|
152
|
+
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
|
153
|
+
# "Inflections".demodulize #=> "Inflections"
|
154
|
+
def demodulize
|
155
|
+
gsub(/^.*::/, '')
|
156
|
+
end
|
157
|
+
|
158
|
+
# Creates a foreign key name from a class name.
|
159
|
+
# +use_underscore+ sets whether the method should put '_' between the name and 'id'.
|
160
|
+
#
|
161
|
+
# Examples
|
162
|
+
# "Message".foreign_key #=> "message_id"
|
163
|
+
# "Message".foreign_key(false) #=> "messageid"
|
164
|
+
# "Admin::Post".foreign_key #=> "post_id"
|
165
|
+
def foreign_key(use_underscore = true)
|
166
|
+
"#{demodulize.underscore}#{'_' if use_underscore}id"
|
167
|
+
end
|
168
|
+
|
169
|
+
# Capitalizes the first word and turns underscores into spaces and strips _id.
|
170
|
+
# Like titleize, this is meant for creating pretty output.
|
171
|
+
#
|
172
|
+
# Examples
|
173
|
+
# "employee_salary" #=> "Employee salary"
|
174
|
+
# "author_id" #=> "Author"
|
175
|
+
def humanize
|
176
|
+
gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns the plural form of the word in the string.
|
180
|
+
#
|
181
|
+
# Examples
|
182
|
+
# "post".pluralize #=> "posts"
|
183
|
+
# "octopus".pluralize #=> "octopi"
|
184
|
+
# "sheep".pluralize #=> "sheep"
|
185
|
+
# "words".pluralize #=> "words"
|
186
|
+
# "the blue mailman".pluralize #=> "the blue mailmen"
|
187
|
+
# "CamelOctopus".pluralize #=> "CamelOctopi"
|
188
|
+
def pluralize
|
189
|
+
result = dup
|
190
|
+
Inflections.plurals.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(downcase)
|
191
|
+
result
|
192
|
+
end
|
193
|
+
|
194
|
+
# The reverse of pluralize, returns the singular form of a word in a string.
|
195
|
+
#
|
196
|
+
# Examples
|
197
|
+
# "posts".singularize #=> "post"
|
198
|
+
# "octopi".singularize #=> "octopus"
|
199
|
+
# "sheep".singluarize #=> "sheep"
|
200
|
+
# "word".singluarize #=> "word"
|
201
|
+
# "the blue mailmen".singularize #=> "the blue mailman"
|
202
|
+
# "CamelOctopi".singularize #=> "CamelOctopus"
|
203
|
+
def singularize
|
204
|
+
result = dup
|
205
|
+
Inflections.singulars.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(downcase)
|
206
|
+
result
|
207
|
+
end
|
208
|
+
|
209
|
+
# Underscores and pluralizes the string.
|
210
|
+
#
|
211
|
+
# Examples
|
212
|
+
# "RawScaledScorer".tableize #=> "raw_scaled_scorers"
|
213
|
+
# "egg_and_ham".tableize #=> "egg_and_hams"
|
214
|
+
# "fancyCategory".tableize #=> "fancy_categories"
|
215
|
+
def tableize
|
216
|
+
underscore.pluralize
|
217
|
+
end
|
218
|
+
|
219
|
+
# Capitalizes all the words and replaces some characters in the string to create
|
220
|
+
# a nicer looking title. Titleize is meant for creating pretty output.
|
221
|
+
#
|
222
|
+
# titleize is also aliased as as titlecase
|
223
|
+
#
|
224
|
+
# Examples
|
225
|
+
# "man from the boondocks".titleize #=> "Man From The Boondocks"
|
226
|
+
# "x-men: the last stand".titleize #=> "X Men: The Last Stand"
|
227
|
+
def titleize
|
228
|
+
underscore.humanize.gsub(/\b([a-z])/){|x| x[-1..-1].upcase}
|
229
|
+
end
|
230
|
+
alias_method :titlecase, :titleize
|
231
|
+
|
232
|
+
# The reverse of camelize. Makes an underscored form from the expression in the string.
|
233
|
+
# Also changes '::' to '/' to convert namespaces to paths.
|
234
|
+
#
|
235
|
+
# Examples
|
236
|
+
# "ActiveRecord".underscore #=> "active_record"
|
237
|
+
# "ActiveRecord::Errors".underscore #=> active_record/errors
|
238
|
+
def underscore
|
239
|
+
gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
240
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# The LooserTypecasting extension changes the float and integer typecasting to
|
2
|
+
# use the looser .to_f and .to_i instead of the more strict Kernel.Float and
|
3
|
+
# Kernel.Integer. To use it, you should extend the database with the
|
4
|
+
# Sequel::LooserTypecasting module after loading the extension:
|
5
|
+
#
|
6
|
+
# Sequel.extension :looser_typecasting
|
7
|
+
# DB.extend(Sequel::LooserTypecasting)
|
8
|
+
|
9
|
+
module Sequel
|
10
|
+
module LooserTypecasting
|
11
|
+
# Typecast the value to a Float using to_f instead of Kernel.Float
|
12
|
+
def typecast_value_float(value)
|
13
|
+
value.to_f
|
14
|
+
end
|
15
|
+
|
16
|
+
# Typecast the value to an Integer using to_i instead of Kernel.Integer
|
17
|
+
def typecast_value_integer(value)
|
18
|
+
value.to_i
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
# Adds the Sequel::Migration and Sequel::Migrator classes, which allow
|
2
|
+
# the user to easily group schema changes and migrate the database
|
3
|
+
# to a newer version or revert to a previous version.
|
4
|
+
|
5
|
+
module Sequel
|
6
|
+
# The Migration class describes a database migration that can be reversed.
|
7
|
+
# The migration looks very similar to ActiveRecord (Rails) migrations, e.g.:
|
8
|
+
#
|
9
|
+
# class CreateSessions < Sequel::Migration
|
10
|
+
# def up
|
11
|
+
# create_table :sessions do
|
12
|
+
# primary_key :id
|
13
|
+
# String :session_id, :size => 32, :unique => true
|
14
|
+
# DateTime :created_at
|
15
|
+
# text :data
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def down
|
20
|
+
# # You can use raw SQL if you need to
|
21
|
+
# self << 'DROP TABLE sessions'
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class AlterItems < Sequel::Migration
|
26
|
+
# def up
|
27
|
+
# alter_table :items do
|
28
|
+
# add_column :category, String, :default => 'ruby'
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# def down
|
33
|
+
# alter_table :items do
|
34
|
+
# drop_column :category
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# To apply a migration to a database, you can invoke the #apply with
|
40
|
+
# the target database instance and the direction :up or :down, e.g.:
|
41
|
+
#
|
42
|
+
# DB = Sequel.connect('sqlite://mydb')
|
43
|
+
# CreateSessions.apply(DB, :up)
|
44
|
+
#
|
45
|
+
# See Sequel::Schema::Generator for the syntax to use for creating tables,
|
46
|
+
# and Sequel::Schema::AlterTableGenerator for the syntax to use when
|
47
|
+
# altering existing tables. Migrations act as a proxy for the database
|
48
|
+
# given in #apply, so inside #down and #up, you can act as though self
|
49
|
+
# refers to the database. So you can use any of the Sequel::Database
|
50
|
+
# instance methods directly.
|
51
|
+
class Migration
|
52
|
+
# Creates a new instance of the migration and sets the @db attribute.
|
53
|
+
def initialize(db)
|
54
|
+
@db = db
|
55
|
+
end
|
56
|
+
|
57
|
+
# Applies the migration to the supplied database in the specified
|
58
|
+
# direction.
|
59
|
+
def self.apply(db, direction)
|
60
|
+
obj = new(db)
|
61
|
+
case direction
|
62
|
+
when :up
|
63
|
+
obj.up
|
64
|
+
when :down
|
65
|
+
obj.down
|
66
|
+
else
|
67
|
+
raise ArgumentError, "Invalid migration direction specified (#{direction.inspect})"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the list of Migration descendants.
|
72
|
+
def self.descendants
|
73
|
+
@descendants ||= []
|
74
|
+
end
|
75
|
+
|
76
|
+
# Adds the new migration class to the list of Migration descendants.
|
77
|
+
def self.inherited(base)
|
78
|
+
descendants << base
|
79
|
+
end
|
80
|
+
|
81
|
+
# The default down action does nothing
|
82
|
+
def down
|
83
|
+
end
|
84
|
+
|
85
|
+
# Intercepts method calls intended for the database and sends them along.
|
86
|
+
def method_missing(method_sym, *args, &block)
|
87
|
+
@db.send(method_sym, *args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
# The default up action does nothing
|
91
|
+
def up
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# The Migrator module performs migrations based on migration files in a
|
96
|
+
# specified directory. The migration files should be named using the
|
97
|
+
# following pattern (in similar fashion to ActiveRecord migrations):
|
98
|
+
#
|
99
|
+
# <version>_<title>.rb
|
100
|
+
#
|
101
|
+
# For example, the following files are considered migration files:
|
102
|
+
#
|
103
|
+
# 001_create_sessions.rb
|
104
|
+
# 002_add_data_column.rb
|
105
|
+
# ...
|
106
|
+
#
|
107
|
+
# The migration files should contain one or more migration classes based
|
108
|
+
# on Sequel::Migration.
|
109
|
+
#
|
110
|
+
# Migrations are generally run via the sequel command line tool,
|
111
|
+
# using the -m and -M switches. The -m switch specifies the migration
|
112
|
+
# directory, and the -M switch specifies the version to which to migrate.
|
113
|
+
#
|
114
|
+
# You can apply migrations using the Migrator API, as well (this is necessary
|
115
|
+
# if you want to specify the version from which to migrate in addition to the version
|
116
|
+
# to which to migrate).
|
117
|
+
# To apply a migration, the #apply method must be invoked with the database
|
118
|
+
# instance, the directory of migration files and the target version. If
|
119
|
+
# no current version is supplied, it is read from the database. The migrator
|
120
|
+
# automatically creates a schema_info table in the database to keep track
|
121
|
+
# of the current migration version. If no migration version is stored in the
|
122
|
+
# database, the version is considered to be 0. If no target version is
|
123
|
+
# specified, the database is migrated to the latest version available in the
|
124
|
+
# migration directory.
|
125
|
+
#
|
126
|
+
# For example, to migrate the database to the latest version:
|
127
|
+
#
|
128
|
+
# Sequel::Migrator.apply(DB, '.')
|
129
|
+
#
|
130
|
+
# To migrate the database from version 1 to version 5:
|
131
|
+
#
|
132
|
+
# Sequel::Migrator.apply(DB, '.', 5, 1)
|
133
|
+
module Migrator
|
134
|
+
DEFAULT_SCHEMA_COLUMN = :version
|
135
|
+
DEFAULT_SCHEMA_TABLE = :schema_info
|
136
|
+
MIGRATION_FILE_PATTERN = /\A\d+_.+\.rb\z/.freeze
|
137
|
+
MIGRATION_SPLITTER = '_'.freeze
|
138
|
+
|
139
|
+
# Wrapper for run, maintaining backwards API compatibility
|
140
|
+
def self.apply(db, directory, target = nil, current = nil)
|
141
|
+
run(db, directory, :target => target, :current => current)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Migrates the supplied database using the migration files in the the specified directory. Options:
|
145
|
+
# * :column - The column in the :table argument storing the migration version (default: :version).
|
146
|
+
# * :current - The current version of the database. If not given, it is retrieved from the database
|
147
|
+
# using the :table and :column options.
|
148
|
+
# * :table - The table containing the schema version (default: :schema_info).
|
149
|
+
# * :target - The target version to which to migrate. If not given, migrates to the maximum version.
|
150
|
+
#
|
151
|
+
# Examples:
|
152
|
+
# Sequel::Migrator.run(DB, "migrations")
|
153
|
+
# Sequel::Migrator.run(DB, "migrations", :target=>15, :current=>10)
|
154
|
+
# Sequel::Migrator.run(DB, "app1/migrations", :column=> :app2_version)
|
155
|
+
# Sequel::Migrator.run(DB, "app2/migrations", :column => :app2_version, :table=>:schema_info2)
|
156
|
+
def self.run(db, directory, opts={})
|
157
|
+
raise(Error, "Must supply a valid migration path") unless directory and File.directory?(directory)
|
158
|
+
raise(Error, "No current version available") unless current = opts[:current] || get_current_migration_version(db, opts)
|
159
|
+
raise(Error, "No target version available") unless target = opts[:target] || latest_migration_version(directory)
|
160
|
+
|
161
|
+
direction = current < target ? :up : :down
|
162
|
+
|
163
|
+
classes = migration_classes(directory, target, current, direction)
|
164
|
+
|
165
|
+
db.transaction do
|
166
|
+
classes.each {|c| c.apply(db, direction)}
|
167
|
+
set_current_migration_version(db, target, opts)
|
168
|
+
end
|
169
|
+
|
170
|
+
target
|
171
|
+
end
|
172
|
+
|
173
|
+
# Gets the current migration version stored in the database. If no version
|
174
|
+
# number is stored, 0 is returned.
|
175
|
+
def self.get_current_migration_version(db, opts={})
|
176
|
+
(schema_info_dataset(db, opts).first || {})[opts[:column] || DEFAULT_SCHEMA_COLUMN] || 0
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns the latest version available in the specified directory.
|
180
|
+
def self.latest_migration_version(directory)
|
181
|
+
l = migration_files(directory).last
|
182
|
+
l ? migration_version_from_file(File.basename(l)) : nil
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns a list of migration classes filtered for the migration range and
|
186
|
+
# ordered according to the migration direction.
|
187
|
+
def self.migration_classes(directory, target, current, direction)
|
188
|
+
range = direction == :up ?
|
189
|
+
(current + 1)..target : (target + 1)..current
|
190
|
+
|
191
|
+
# Remove class definitions
|
192
|
+
Migration.descendants.each do |c|
|
193
|
+
Object.send(:remove_const, c.to_s) rescue nil
|
194
|
+
end
|
195
|
+
Migration.descendants.clear # remove any defined migration classes
|
196
|
+
|
197
|
+
# load migration files
|
198
|
+
migration_files(directory, range).each {|fn| load(fn)}
|
199
|
+
|
200
|
+
# get migration classes
|
201
|
+
classes = Migration.descendants
|
202
|
+
classes.reverse! if direction == :down
|
203
|
+
classes
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns any found migration files in the supplied directory.
|
207
|
+
def self.migration_files(directory, range = nil)
|
208
|
+
files = []
|
209
|
+
Dir.new(directory).each do |file|
|
210
|
+
files[migration_version_from_file(file)] = File.join(directory, file) if MIGRATION_FILE_PATTERN.match(file)
|
211
|
+
end
|
212
|
+
filtered = range ? files[range] : files
|
213
|
+
filtered ? filtered.compact : []
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns the dataset for the schema_info table. If no such table
|
217
|
+
# exists, it is automatically created.
|
218
|
+
def self.schema_info_dataset(db, opts={})
|
219
|
+
column = opts[:column] || DEFAULT_SCHEMA_COLUMN
|
220
|
+
table = opts[:table] || DEFAULT_SCHEMA_TABLE
|
221
|
+
db.create_table?(table){Integer column}
|
222
|
+
db.alter_table(table){add_column column, Integer} unless db.from(table).columns.include?(column)
|
223
|
+
db.from(table)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Sets the current migration version stored in the database.
|
227
|
+
def self.set_current_migration_version(db, version, opts={})
|
228
|
+
column = opts[:column] || DEFAULT_SCHEMA_COLUMN
|
229
|
+
dataset = schema_info_dataset(db, opts)
|
230
|
+
dataset.send(dataset.first ? :update : :insert, column => version)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Return the integer migration version based on the filename.
|
234
|
+
def self.migration_version_from_file(filename) # :nodoc:
|
235
|
+
filename.split(MIGRATION_SPLITTER, 2).first.to_i
|
236
|
+
end
|
237
|
+
private_class_method :migration_version_from_file
|
238
|
+
end
|
239
|
+
end
|