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,45 @@
1
+ module Sequel
2
+ # Proc that is instance evaled to create the default inflections for both the
3
+ # model inflector and the inflector extension.
4
+ DEFAULT_INFLECTIONS_PROC = proc do
5
+ plural(/$/, 's')
6
+ plural(/s$/i, 's')
7
+ plural(/(alias|(?:stat|octop|vir|b)us)$/i, '\1es')
8
+ plural(/(buffal|tomat)o$/i, '\1oes')
9
+ plural(/([ti])um$/i, '\1a')
10
+ plural(/sis$/i, 'ses')
11
+ plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
12
+ plural(/(hive)$/i, '\1s')
13
+ plural(/([^aeiouy]|qu)y$/i, '\1ies')
14
+ plural(/(x|ch|ss|sh)$/i, '\1es')
15
+ plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
16
+ plural(/([m|l])ouse$/i, '\1ice')
17
+
18
+ singular(/s$/i, '')
19
+ singular(/([ti])a$/i, '\1um')
20
+ singular(/(analy|ba|cri|diagno|parenthe|progno|synop|the)ses$/i, '\1sis')
21
+ singular(/([^f])ves$/i, '\1fe')
22
+ singular(/([h|t]ive)s$/i, '\1')
23
+ singular(/([lr])ves$/i, '\1f')
24
+ singular(/([^aeiouy]|qu)ies$/i, '\1y')
25
+ singular(/(m)ovies$/i, '\1ovie')
26
+ singular(/(x|ch|ss|sh)es$/i, '\1')
27
+ singular(/([m|l])ice$/i, '\1ouse')
28
+ singular(/buses$/i, 'bus')
29
+ singular(/oes$/i, 'o')
30
+ singular(/shoes$/i, 'shoe')
31
+ singular(/(alias|(?:stat|octop|vir|b)us)es$/i, '\1')
32
+ singular(/(vert|ind)ices$/i, '\1ex')
33
+ singular(/matrices$/i, 'matrix')
34
+
35
+ irregular('person', 'people')
36
+ irregular('man', 'men')
37
+ irregular('child', 'children')
38
+ irregular('sex', 'sexes')
39
+ irregular('move', 'moves')
40
+ irregular('quiz', 'quizzes')
41
+ irregular('testis', 'testes')
42
+
43
+ uncountable(%w(equipment information rice money species series fish sheep news))
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module Sequel
2
+ class Model
3
+ # Errors represents validation errors, a simple hash subclass
4
+ # with a few convenience methods.
5
+ class Errors < ::Hash
6
+ ATTRIBUTE_JOINER = ' and '.freeze
7
+
8
+ # Assign an array of messages for each attribute on access
9
+ def [](k)
10
+ has_key?(k) ? super : (self[k] = [])
11
+ end
12
+
13
+ # Adds an error for the given attribute.
14
+ def add(att, msg)
15
+ self[att] << msg
16
+ end
17
+
18
+ # Return the total number of error messages.
19
+ def count
20
+ values.inject(0){|m, v| m + v.length}
21
+ end
22
+
23
+ # Returns an array of fully-formatted error messages.
24
+ def full_messages
25
+ inject([]) do |m, kv|
26
+ att, errors = *kv
27
+ errors.each {|e| m << "#{Array(att).join(ATTRIBUTE_JOINER)} #{e}"}
28
+ m
29
+ end
30
+ end
31
+
32
+ # Returns the array of errors for the given attribute, or nil
33
+ # if there are no errors for the attribute.
34
+ def on(att)
35
+ self[att] if has_key?(att)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ module Sequel
2
+ # Exception class raised when raise_on_save_failure is set and a before hook returns false
3
+ class BeforeHookFailed < Error; end
4
+
5
+ # Exception class raised when require_modification is set and an UPDATE or DELETE statement to modify the dataset doesn't
6
+ # modify a single row.
7
+ class NoExistingObject < Error; end
8
+
9
+ # Exception class raised when raise_on_save_failure is set and validation fails
10
+ class ValidationFailed < Error
11
+ def initialize(errors)
12
+ if errors.respond_to?(:full_messages)
13
+ @errors = errors
14
+ super(errors.full_messages.join(', '))
15
+ else
16
+ super
17
+ end
18
+ end
19
+ attr_reader :errors
20
+ end
21
+ end
@@ -0,0 +1,162 @@
1
+ module Sequel
2
+ # Yield the Inflections module if a block is given, and return
3
+ # the Inflections module.
4
+ def self.inflections
5
+ yield Inflections if block_given?
6
+ Inflections
7
+ end
8
+
9
+ # This module acts as a singleton returned/yielded by Sequel.inflections,
10
+ # which is used to override or specify additional inflection rules
11
+ # for Sequel. Examples:
12
+ #
13
+ # Sequel.inflections do |inflect|
14
+ # inflect.plural /^(ox)$/i, '\1\2en'
15
+ # inflect.singular /^(ox)en/i, '\1'
16
+ #
17
+ # inflect.irregular 'octopus', 'octopi'
18
+ #
19
+ # inflect.uncountable "equipment"
20
+ # end
21
+ #
22
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
23
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
24
+ # already have been loaded.
25
+ module Inflections
26
+ CAMELIZE_CONVERT_REGEXP = /(^|_)(.)/.freeze
27
+ CAMELIZE_MODULE_REGEXP = /\/(.?)/.freeze
28
+ DASH = '-'.freeze
29
+ DEMODULIZE_CONVERT_REGEXP = /^.*::/.freeze
30
+ EMPTY_STRING= ''.freeze
31
+ SLASH = '/'.freeze
32
+ VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
33
+ UNDERSCORE = '_'.freeze
34
+ UNDERSCORE_CONVERT_REGEXP1 = /([A-Z]+)([A-Z][a-z])/.freeze
35
+ UNDERSCORE_CONVERT_REGEXP2 = /([a-z\d])([A-Z])/.freeze
36
+ UNDERSCORE_CONVERT_REPLACE = '\1_\2'.freeze
37
+ UNDERSCORE_MODULE_REGEXP = /::/.freeze
38
+
39
+ @plurals, @singulars, @uncountables = [], [], []
40
+
41
+ class << self
42
+ # Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for plurization.
43
+ attr_reader :plurals
44
+
45
+ # Array of 2 element arrays, first containing a regex, and the second containing a substitution pattern, used for singularization.
46
+ attr_reader :singulars
47
+
48
+ # Array of strings for words were the singular form is the same as the plural form
49
+ attr_reader :uncountables
50
+ end
51
+
52
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
53
+ # the options are: :plurals, :singulars, :uncountables
54
+ #
55
+ # Examples:
56
+ # clear :all
57
+ # clear :plurals
58
+ def self.clear(scope = :all)
59
+ case scope
60
+ when :all
61
+ @plurals, @singulars, @uncountables = [], [], []
62
+ else
63
+ instance_variable_set("@#{scope}", [])
64
+ end
65
+ end
66
+
67
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
68
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
69
+ #
70
+ # Examples:
71
+ # irregular 'octopus', 'octopi'
72
+ # irregular 'person', 'people'
73
+ def self.irregular(singular, plural)
74
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
75
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
76
+ end
77
+
78
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
79
+ # The replacement should always be a string that may include references to the matched data from the rule.
80
+ #
81
+ # Example:
82
+ # plural(/(x|ch|ss|sh)$/i, '\1es')
83
+ def self.plural(rule, replacement)
84
+ @plurals.insert(0, [rule, replacement])
85
+ end
86
+
87
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
88
+ # The replacement should always be a string that may include references to the matched data from the rule.
89
+ #
90
+ # Example:
91
+ # singular(/([^aeiouy]|qu)ies$/i, '\1y')
92
+ def self.singular(rule, replacement)
93
+ @singulars.insert(0, [rule, replacement])
94
+ end
95
+
96
+ # Add uncountable words that shouldn't be attempted inflected.
97
+ #
98
+ # Examples:
99
+ # uncountable "money"
100
+ # uncountable "money", "information"
101
+ # uncountable %w( money information rice )
102
+ def self.uncountable(*words)
103
+ (@uncountables << words).flatten!
104
+ end
105
+
106
+ instance_eval(&DEFAULT_INFLECTIONS_PROC)
107
+
108
+ private
109
+
110
+ # Convert the given string to CamelCase. Will also convert '/' to '::' which is useful for converting paths to namespaces.
111
+ def camelize(s)
112
+ s = s.to_s
113
+ return s.camelize if s.respond_to?(:camelize)
114
+ s = s.gsub(CAMELIZE_MODULE_REGEXP){|x| "::#{x[-1..-1].upcase unless x == SLASH}"}.gsub(CAMELIZE_CONVERT_REGEXP){|x| x[-1..-1].upcase}
115
+ s
116
+ end
117
+
118
+ # Tries to find a declared constant with the name specified
119
+ # in the string. It raises a NameError when the name is not in CamelCase
120
+ # or is not initialized.
121
+ def constantize(s)
122
+ s = s.to_s
123
+ return s.constantize if s.respond_to?(:constantize)
124
+ raise(NameError, "#{inspect} is not a valid constant name!") unless m = VALID_CONSTANT_NAME_REGEXP.match(s.to_s)
125
+ Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
126
+ end
127
+
128
+ # Removes the module part from the expression in the string
129
+ def demodulize(s)
130
+ s = s.to_s
131
+ return s.demodulize if s.respond_to?(:demodulize)
132
+ s.gsub(DEMODULIZE_CONVERT_REGEXP, EMPTY_STRING)
133
+ end
134
+
135
+ # Returns the plural form of the word in the string.
136
+ def pluralize(s)
137
+ s = s.to_s
138
+ return s.pluralize if s.respond_to?(:pluralize)
139
+ result = s.dup
140
+ Inflections.plurals.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(s.downcase)
141
+ result
142
+ end
143
+
144
+ # The reverse of pluralize, returns the singular form of a word in a string.
145
+ def singularize(s)
146
+ s = s.to_s
147
+ return s.singularize if s.respond_to?(:singularize)
148
+ result = s.dup
149
+ Inflections.singulars.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(s.downcase)
150
+ result
151
+ end
152
+
153
+ # The reverse of camelize. Makes an underscored form from the expression in the string.
154
+ # Also changes '::' to '/' to convert namespaces to paths.
155
+ def underscore(s)
156
+ s = s.to_s
157
+ return s.underscore if s.respond_to?(:underscore)
158
+ s.gsub(UNDERSCORE_MODULE_REGEXP, SLASH).gsub(UNDERSCORE_CONVERT_REGEXP1, UNDERSCORE_CONVERT_REPLACE).
159
+ gsub(UNDERSCORE_CONVERT_REGEXP2, UNDERSCORE_CONVERT_REPLACE).tr(DASH, UNDERSCORE).downcase
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,70 @@
1
+ module Sequel
2
+ # Empty namespace that plugins should use to store themselves,
3
+ # so they can be loaded via Model.plugin.
4
+ #
5
+ # Plugins should be modules with one of the following conditions:
6
+ # * A singleton method named apply, which takes a model,
7
+ # additional arguments, and an optional block. This is called
8
+ # the first time the plugin is loaded for this model (unless it was
9
+ # already loaded by an ancestor class), with the arguments
10
+ # and block provided to the call to Model.plugin.
11
+ # * A module inside the plugin module named InstanceMethods,
12
+ # which will be included in the model class.
13
+ # * A module inside the plugin module named ClassMethods,
14
+ # which will extend the model class.
15
+ # * A module inside the plugin module named DatasetMethods,
16
+ # which will extend the model's dataset.
17
+ # * A singleton method named configure, which takes a model,
18
+ # additional arguments, and an optional block. This is called
19
+ # every time the Model.plugin method is called.
20
+ module Plugins
21
+ end
22
+
23
+ class Model
24
+ # Loads a plugin for use with the model class, passing optional arguments
25
+ # to the plugin. If the plugin is a module, load it directly. Otherwise,
26
+ # require the plugin from either sequel/plugins/#{plugin} or
27
+ # sequel_#{plugin}, and then attempt to load the module using a
28
+ # the camelized plugin name under Sequel::Plugins.
29
+ def self.plugin(plugin, *args, &blk)
30
+ arg = args.first
31
+ m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
32
+ unless @plugins.include?(m)
33
+ @plugins << m
34
+ m.apply(self, *args, &blk) if m.respond_to?(:apply)
35
+ include(m::InstanceMethods) if m.const_defined?("InstanceMethods")
36
+ extend(m::ClassMethods)if m.const_defined?("ClassMethods")
37
+ if m.const_defined?("DatasetMethods")
38
+ dataset.extend(m::DatasetMethods) if @dataset
39
+ dataset_method_modules << m::DatasetMethods
40
+ meths = m::DatasetMethods.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
41
+ def_dataset_method(*meths) unless meths.empty?
42
+ end
43
+ end
44
+ m.configure(self, *args, &blk) if m.respond_to?(:configure)
45
+ end
46
+
47
+ module ClassMethods
48
+ # Array of plugins loaded by this class
49
+ attr_reader :plugins
50
+
51
+ private
52
+
53
+ # Returns the module for the specified plugin. If the module is not
54
+ # defined, the corresponding plugin gem is automatically loaded.
55
+ def plugin_module(plugin)
56
+ module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
57
+ if !Sequel::Plugins.const_defined?(module_name) ||
58
+ (Sequel.const_defined?(module_name) &&
59
+ Sequel::Plugins.const_get(module_name) == Sequel.const_get(module_name))
60
+ begin
61
+ Sequel.tsk_require "sequel/plugins/#{plugin}"
62
+ rescue LoadError
63
+ Sequel.tsk_require "sequel_#{plugin}"
64
+ end
65
+ end
66
+ Sequel::Plugins.const_get(module_name)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,59 @@
1
+ require 'active_model'
2
+ module Sequel
3
+ module Plugins
4
+ # The ActiveModel plugin makes Sequel::Model objects the
5
+ # pass the ActiveModel::Lint tests, which should
6
+ # hopefully mean full ActiveModel compliance. This should
7
+ # allow the full support of Sequel::Model objects in Rails 3.
8
+ # This plugin requires active_model in order to use
9
+ # ActiveModel::Naming.
10
+ module ActiveModel
11
+ ClassMethods = ::ActiveModel::Naming
12
+
13
+ module InstanceMethods
14
+ # The default string to join composite primary keys with in to_param.
15
+ DEFAULT_TO_PARAM_JOINER = '-'.freeze
16
+
17
+ # Record that an object was destroyed, for later use by
18
+ # destroyed?
19
+ def after_destroy
20
+ super
21
+ @destroyed = true
22
+ end
23
+
24
+ # False if the object is new? or has been destroyed, true otherwise.
25
+ def persisted?
26
+ !new? && @destroyed != true
27
+ end
28
+
29
+ # An array of primary key values, or nil if the object is not persisted.
30
+ def to_key
31
+ if persisted?
32
+ primary_key.is_a?(Symbol) ? [pk] : pk
33
+ end
34
+ end
35
+
36
+ # With the ActiveModel plugin, Sequel model objects are already
37
+ # compliant, so this returns self.
38
+ def to_model
39
+ self
40
+ end
41
+
42
+ # An string representing the object's primary key. For composite
43
+ # primary keys, joins them with to_param_joiner.
44
+ def to_param
45
+ if k = to_key
46
+ k.join(to_param_joiner)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # The string to use to join composite primary key param strings.
53
+ def to_param_joiner
54
+ DEFAULT_TO_PARAM_JOINER
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,103 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The AssociationDependencies plugin allows you do easily set up before and/or after destroy hooks
4
+ # for destroying, deleting, or nullifying associated model objects. The following
5
+ # association types support the following dependency actions:
6
+ #
7
+ # * :many_to_many - :nullify (removes all related entries in join table)
8
+ # * :many_to_one - :delete, :destroy
9
+ # * :one_to_many, one_to_one - :delete, :destroy, :nullify (sets foreign key to NULL for all associated objects)
10
+ #
11
+ # This plugin works directly with the association datasets and does not use any cached association values.
12
+ # The :delete action will delete all associated objects from the database in a single SQL call.
13
+ # The :destroy action will load each associated object from the database and call the destroy method on it.
14
+ #
15
+ # To set up an association dependency, you must provide a hash with association name symbols
16
+ # and dependency action values. You can provide the hash to the plugin call itself or
17
+ # to the add_association_dependencies method:
18
+ #
19
+ # Business.plugin :association_dependencies, :address=>:delete
20
+ # # or:
21
+ # Artist.plugin :association_dependencies
22
+ # Artist.add_association_dependencies :albums=>:destroy, :reviews=>:delete, :tags=>:nullify
23
+ module AssociationDependencies
24
+ # Mapping of association types to when the dependency calls should be made (either
25
+ # :before for in before_destroy or :after for in after_destroy)
26
+ ASSOCIATION_MAPPING = {:one_to_many=>:before, :many_to_one=>:after, :many_to_many=>:before, :one_to_one=>:before}
27
+
28
+ # The valid dependence actions
29
+ DEPENDENCE_ACTIONS = [:delete, :destroy, :nullify]
30
+
31
+ # Initialize the association_dependencies hash for this model.
32
+ def self.apply(model, hash={})
33
+ model.instance_eval{@association_dependencies = {:before_delete=>[], :before_destroy=>[], :before_nullify=>[], :after_delete=>[], :after_destroy=>[]}}
34
+ end
35
+
36
+ # Call add_association_dependencies with any dependencies given in the plugin call.
37
+ def self.configure(model, hash={})
38
+ model.add_association_dependencies(hash) unless hash.empty?
39
+ end
40
+
41
+ module ClassMethods
42
+ # A hash specifying the association dependencies for each model. The keys
43
+ # are symbols indicating the type of action and when it should be executed
44
+ # (e.g. :before_delete). Values are an array of method symbols.
45
+ # For before_nullify, the symbols are remove_all_association methods. For other
46
+ # types, the symbols are association_dataset methods, on which delete or
47
+ # destroy is called.
48
+ attr_reader :association_dependencies
49
+
50
+ # Add association dependencies to this model. The hash should have association name
51
+ # symbol keys and dependency action symbol values (e.g. :albums=>:destroy).
52
+ def add_association_dependencies(hash)
53
+ hash.each do |association, action|
54
+ raise(Error, "Nonexistent association: #{association}") unless r = association_reflection(association)
55
+ type = r[:type]
56
+ raise(Error, "Invalid dependence action type: association: #{association}, dependence action: #{action}") unless DEPENDENCE_ACTIONS.include?(action)
57
+ raise(Error, "Invalid association type: association: #{association}, type: #{type}") unless time = ASSOCIATION_MAPPING[type]
58
+ association_dependencies[:"#{time}_#{action}"] << if action == :nullify
59
+ case type
60
+ when :one_to_many , :many_to_many
61
+ proc{send(r.remove_all_method)}
62
+ when :one_to_one
63
+ proc{send(r.setter_method, nil)}
64
+ else
65
+ raise(Error, "Can't nullify many_to_one associated objects: association: #{association}")
66
+ end
67
+ else
68
+ raise(Error, "Can only nullify many_to_many associations: association: #{association}") if type == :many_to_many
69
+ r.dataset_method
70
+ end
71
+ end
72
+ end
73
+
74
+ # Copy the current model object's association_dependencies into the subclass.
75
+ def inherited(subclass)
76
+ super
77
+ ad = association_dependencies.dup
78
+ ad.keys.each{|k| ad[k] = ad[k].dup}
79
+ subclass.instance_variable_set(:@association_dependencies, ad)
80
+ end
81
+ end
82
+
83
+ module InstanceMethods
84
+ # Run the delete and destroy association dependency actions for
85
+ # many_to_one associations.
86
+ def after_destroy
87
+ super
88
+ model.association_dependencies[:after_delete].each{|m| send(m).delete}
89
+ model.association_dependencies[:after_destroy].each{|m| send(m).destroy}
90
+ end
91
+
92
+ # Run the delete, destroy, and nullify association dependency actions for
93
+ # *_to_many associations.
94
+ def before_destroy
95
+ model.association_dependencies[:before_delete].each{|m| send(m).delete}
96
+ model.association_dependencies[:before_destroy].each{|m| send(m).destroy}
97
+ model.association_dependencies[:before_nullify].each{|p| instance_eval(&p)}
98
+ super
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end