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.
Files changed (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. 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