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,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
|