mobility 0.1.20 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CHANGELOG.md +17 -0
  5. data/CONTRIBUTING.md +55 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +2 -56
  8. data/README.md +64 -15
  9. data/Rakefile +17 -17
  10. data/lib/mobility.rb +43 -40
  11. data/lib/mobility/active_model.rb +0 -1
  12. data/lib/mobility/active_model/backend_resetter.rb +1 -1
  13. data/lib/mobility/active_record.rb +8 -15
  14. data/lib/mobility/active_record/backend_resetter.rb +2 -0
  15. data/lib/mobility/active_record/model_translation.rb +1 -1
  16. data/lib/mobility/active_record/string_translation.rb +2 -0
  17. data/lib/mobility/active_record/text_translation.rb +2 -0
  18. data/lib/mobility/attributes.rb +74 -71
  19. data/lib/mobility/backend.rb +64 -25
  20. data/lib/mobility/backend/orm_delegator.rb +17 -7
  21. data/lib/mobility/backend/stringify_locale.rb +18 -0
  22. data/lib/mobility/backend_resetter.rb +8 -5
  23. data/lib/mobility/backends.rb +4 -0
  24. data/lib/mobility/backends/active_record.rb +20 -0
  25. data/lib/mobility/{backend → backends}/active_record/column.rb +25 -13
  26. data/lib/mobility/{backend → backends}/active_record/column/query_methods.rb +5 -3
  27. data/lib/mobility/backends/active_record/hstore.rb +29 -0
  28. data/lib/mobility/{backend → backends}/active_record/hstore/query_methods.rb +2 -2
  29. data/lib/mobility/{backend → backends}/active_record/jsonb.rb +6 -6
  30. data/lib/mobility/{backend → backends}/active_record/jsonb/query_methods.rb +4 -2
  31. data/lib/mobility/{backend → backends}/active_record/key_value.rb +10 -44
  32. data/lib/mobility/{backend → backends}/active_record/key_value/query_methods.rb +4 -2
  33. data/lib/mobility/{backend/active_record/hash_valued.rb → backends/active_record/pg_hash.rb} +13 -21
  34. data/lib/mobility/{backend → backends}/active_record/query_methods.rb +2 -2
  35. data/lib/mobility/{backend → backends}/active_record/serialized.rb +12 -28
  36. data/lib/mobility/{backend → backends}/active_record/serialized/query_methods.rb +5 -8
  37. data/lib/mobility/{backend → backends}/active_record/table.rb +11 -60
  38. data/lib/mobility/{backend → backends}/active_record/table/query_methods.rb +5 -3
  39. data/lib/mobility/{backend → backends}/column.rb +8 -4
  40. data/lib/mobility/backends/hash_valued.rb +29 -0
  41. data/lib/mobility/{backend → backends}/hstore.rb +4 -4
  42. data/lib/mobility/{backend → backends}/jsonb.rb +4 -4
  43. data/lib/mobility/backends/key_value.rb +111 -0
  44. data/lib/mobility/{backend → backends}/null.rb +4 -4
  45. data/lib/mobility/backends/sequel.rb +20 -0
  46. data/lib/mobility/backends/sequel/column.rb +52 -0
  47. data/lib/mobility/{backend → backends}/sequel/column/query_methods.rb +5 -3
  48. data/lib/mobility/backends/sequel/hstore.rb +29 -0
  49. data/lib/mobility/{backend → backends}/sequel/hstore/query_methods.rb +4 -3
  50. data/lib/mobility/{backend → backends}/sequel/jsonb.rb +6 -6
  51. data/lib/mobility/{backend → backends}/sequel/jsonb/query_methods.rb +4 -3
  52. data/lib/mobility/{backend → backends}/sequel/key_value.rb +28 -39
  53. data/lib/mobility/{backend → backends}/sequel/key_value/query_methods.rb +4 -5
  54. data/lib/mobility/backends/sequel/pg_hash.rb +46 -0
  55. data/lib/mobility/{backend → backends}/sequel/postgres_query_methods.rb +1 -2
  56. data/lib/mobility/{backend → backends}/sequel/query_methods.rb +6 -4
  57. data/lib/mobility/{backend → backends}/sequel/serialized.rb +17 -38
  58. data/lib/mobility/backends/sequel/serialized/query_methods.rb +17 -0
  59. data/lib/mobility/{backend → backends}/sequel/table.rb +29 -60
  60. data/lib/mobility/{backend → backends}/sequel/table/query_methods.rb +5 -3
  61. data/lib/mobility/{backend → backends}/serialized.rb +27 -5
  62. data/lib/mobility/{backend → backends}/table.rb +69 -29
  63. data/lib/mobility/configuration.rb +40 -0
  64. data/lib/mobility/{orm.rb → loaded.rb} +0 -0
  65. data/lib/mobility/plugins.rb +35 -0
  66. data/lib/mobility/plugins/active_model.rb +6 -0
  67. data/lib/mobility/plugins/active_model/dirty.rb +81 -0
  68. data/lib/mobility/plugins/active_record.rb +6 -0
  69. data/lib/mobility/plugins/active_record/dirty.rb +59 -0
  70. data/lib/mobility/plugins/cache.rb +54 -0
  71. data/lib/mobility/plugins/cache/translation_cacher.rb +40 -0
  72. data/lib/mobility/plugins/default.rb +73 -0
  73. data/lib/mobility/plugins/dirty.rb +61 -0
  74. data/lib/mobility/{backend → plugins}/fallbacks.rb +36 -31
  75. data/lib/mobility/plugins/fallthrough_accessors.rb +66 -0
  76. data/lib/mobility/plugins/locale_accessors.rb +84 -0
  77. data/lib/mobility/{backend → plugins}/presence.rb +15 -6
  78. data/lib/mobility/plugins/sequel.rb +6 -0
  79. data/lib/mobility/plugins/sequel/dirty.rb +59 -0
  80. data/lib/mobility/sequel.rb +5 -14
  81. data/lib/mobility/sequel/backend_resetter.rb +4 -6
  82. data/lib/mobility/sequel/column_changes.rb +4 -4
  83. data/lib/mobility/sequel/model_translation.rb +1 -1
  84. data/lib/mobility/sequel/string_translation.rb +2 -0
  85. data/lib/mobility/sequel/text_translation.rb +2 -0
  86. data/lib/mobility/translates.rb +1 -5
  87. data/lib/mobility/util.rb +126 -0
  88. data/lib/mobility/version.rb +1 -1
  89. data/lib/mobility/wrapper.rb +1 -1
  90. data/lib/rails/generators/mobility/translations_generator.rb +7 -3
  91. metadata +85 -55
  92. metadata.gz.sig +0 -0
  93. data/lib/mobility/backend/active_model.rb +0 -7
  94. data/lib/mobility/backend/active_model/dirty.rb +0 -95
  95. data/lib/mobility/backend/active_record.rb +0 -29
  96. data/lib/mobility/backend/active_record/dirty.rb +0 -54
  97. data/lib/mobility/backend/active_record/hstore.rb +0 -29
  98. data/lib/mobility/backend/cache.rb +0 -117
  99. data/lib/mobility/backend/dirty.rb +0 -38
  100. data/lib/mobility/backend/key_value.rb +0 -85
  101. data/lib/mobility/backend/sequel.rb +0 -29
  102. data/lib/mobility/backend/sequel/column.rb +0 -39
  103. data/lib/mobility/backend/sequel/dirty.rb +0 -57
  104. data/lib/mobility/backend/sequel/hash_valued.rb +0 -51
  105. data/lib/mobility/backend/sequel/hstore.rb +0 -29
  106. data/lib/mobility/backend/sequel/serialized/query_methods.rb +0 -20
  107. data/lib/mobility/core_ext/object.rb +0 -30
  108. data/lib/mobility/core_ext/string.rb +0 -16
  109. data/lib/mobility/fallthrough_accessors.rb +0 -57
  110. data/lib/mobility/locale_accessors.rb +0 -55
@@ -0,0 +1,73 @@
1
+ module Mobility
2
+ module Plugins
3
+ =begin
4
+
5
+ Defines value or proc to fall through to if return value from getter would
6
+ otherwise be nil.
7
+
8
+ @example With default enabled (falls through to default value)
9
+ class Post
10
+ extend Mobility
11
+ translates :title, default: 'foo'
12
+ end
13
+
14
+ Mobility.locale = :en
15
+ post = Post.new(title: "English title")
16
+
17
+ Mobility.locale = :de
18
+ post.title
19
+ #=> 'foo'
20
+
21
+ @example Overriding default with reader option
22
+ class Post
23
+ extend Mobility
24
+ translates :title, default: 'foo'
25
+ end
26
+
27
+ Mobility.locale = :en
28
+ post = Post.new(title: "English title")
29
+
30
+ Mobility.locale = :de
31
+ post.title
32
+ #=> 'foo'
33
+
34
+ post.title(default: 'bar')
35
+ #=> 'bar'
36
+
37
+ post.title(default: nil)
38
+ #=> nil
39
+
40
+ @example Using Proc as default
41
+ class Post
42
+ extend Mobility
43
+ translates :title, default: lambda { |model:, attribute:| attribute.to_s }
44
+ end
45
+
46
+ post = Post.new(title: nil)
47
+ post.title
48
+ #=> "title"
49
+
50
+ post.title(default: lambda { |model:| model.class.name.to_s })
51
+ #=> "Post"
52
+ =end
53
+ class Default < Module
54
+ # Applies default plugin to attributes.
55
+ # @param [Attributes] attributes
56
+ # @param [Object] option
57
+ def self.apply(attributes, option)
58
+ attributes.backend_class.include(new(option))
59
+ end
60
+
61
+ def initialize(default_option)
62
+ define_method :read do |locale, options = {}|
63
+ default = options.has_key?(:default) ? options.delete(:default) : default_option
64
+ if (value = super(locale, options)).nil?
65
+ default.is_a?(Proc) ? default.call(model: model, attribute: attribute) : default
66
+ else
67
+ value
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,61 @@
1
+ require "mobility/backend_resetter"
2
+ require "mobility/plugins/fallthrough_accessors"
3
+
4
+ module Mobility
5
+ module Plugins
6
+ =begin
7
+
8
+ Dirty tracking for Mobility attributes. See class-specific implementations for
9
+ details.
10
+
11
+ @see Mobility::Plugins::ActiveModel::Dirty
12
+ @see Mobility::Plugins::Sequel::Dirty
13
+
14
+ @note Dirty tracking can have unexpected results when combined with fallbacks.
15
+ A change in the fallback locale value will not mark an attribute falling
16
+ through to that locale as changed, even though it may look like it has
17
+ changed. However, when the value for the current locale is changed from nil
18
+ or blank to a new value, the change will be recorded as a change from that
19
+ fallback value, rather than from the nil or blank value. The specs are the
20
+ most reliable source of information on the interaction between dirty tracking
21
+ and fallbacks.
22
+
23
+ =end
24
+ module Dirty
25
+ class << self
26
+ # Applies dirty plugin to attributes for a given option value.
27
+ # @param [Attributes] attributes
28
+ # @param [Boolean] option Value of option
29
+ # @raise [ArgumentError] if model class does not support dirty tracking
30
+ def apply(attributes, option)
31
+ if option
32
+ FallthroughAccessors.apply(attributes, true)
33
+ include_dirty_module(attributes.backend_class, attributes.model_class, *attributes.names)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def include_dirty_module(backend_class, model_class, *attribute_names)
40
+ dirty_module =
41
+ if Loaded::ActiveRecord && model_class.ancestors.include?(::ActiveModel::Dirty)
42
+ if (model_class < ::ActiveRecord::Base)
43
+ require "mobility/plugins/active_record/dirty"
44
+ Plugins::ActiveRecord::Dirty
45
+ else
46
+ require "mobility/plugins/active_model/dirty"
47
+ Plugins::ActiveModel::Dirty
48
+ end
49
+ elsif Loaded::Sequel && model_class < ::Sequel::Model
50
+ require "mobility/plugins/sequel/dirty"
51
+ Plugins::Sequel::Dirty
52
+ else
53
+ raise ArgumentError, "#{model_class} does not support Dirty module."
54
+ end
55
+ backend_class.include dirty_module
56
+ model_class.include dirty_module.const_get(:MethodsBuilder).new(*attribute_names)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,5 +1,7 @@
1
+ require "mobility/util"
2
+
1
3
  module Mobility
2
- module Backend
4
+ module Plugins
3
5
  =begin
4
6
 
5
7
  Falls back to one or more alternative locales in case no value is defined for a
@@ -75,41 +77,44 @@ locale was +nil+.
75
77
  post.title(fallback: :fr)
76
78
  #=> "Mobilité"
77
79
  =end
78
- module Fallbacks
79
- # @!macro [new] backend_constructor
80
- # @param model Model on which backend is defined
81
- # @param [String] attribute Backend attribute
82
- # @option backend_options [Hash] fallbacks Fallbacks hash
83
- def initialize(model, attributes, **backend_options)
84
- super
85
- @fallbacks =
86
- if (fallbacks = backend_options[:fallbacks]).is_a?(Hash)
87
- Mobility.default_fallbacks(fallbacks)
88
- elsif fallbacks == true
89
- Mobility.default_fallbacks
90
- end
80
+ class Fallbacks < Module
81
+ # Applies fallbacks plugin to attributes.
82
+ # @param [Attributes] attributes
83
+ # @param [Boolean] option
84
+ def self.apply(attributes, option)
85
+ attributes.backend_class.include(new(option)) unless option == false
91
86
  end
92
87
 
93
- # @!group Backend Accessors
94
- # @!macro backend_reader
95
- # @param [Boolean,Symbol,Array] fallback
96
- # +false+ to disable fallbacks on lookup, or a locale or array of
97
- # locales to set fallback(s) for this lookup.
98
- def read(locale, **options)
99
- if !options[:fallbacks].nil?
100
- warn "You passed an option with key 'fallbacks', which will be
101
- ignored. Did you mean 'fallback'?"
102
- end
103
- fallback = options.delete(:fallback)
104
- return super if fallback == false || (fallback.nil? && fallbacks.nil?)
105
- (fallback ? [locale, *fallback] : fallbacks[locale]).detect do |fallback_locale|
106
- value = super(fallback_locale, **options)
107
- break value if value.present?
108
- end
88
+ def initialize(fallbacks_option)
89
+ define_read(convert_option_to_fallbacks(fallbacks_option))
109
90
  end
110
91
 
111
92
  private
112
- attr_reader :fallbacks
93
+
94
+ def define_read(fallbacks)
95
+ define_method :read do |locale, **options|
96
+ fallback = options.delete(:fallback)
97
+
98
+ if fallback == false || (fallback.nil? && fallbacks.nil?)
99
+ super(locale, options)
100
+ else
101
+ (fallback ? [locale, *fallback] : fallbacks[locale]).detect do |fallback_locale|
102
+ value = super(fallback_locale, options)
103
+ break value if Util.present?(value)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def convert_option_to_fallbacks(option)
110
+ if option.is_a?(Hash)
111
+ Mobility.default_fallbacks(option)
112
+ elsif option == true
113
+ Mobility.default_fallbacks
114
+ else
115
+ option
116
+ end
117
+ end
113
118
  end
114
119
  end
115
120
  end
@@ -0,0 +1,66 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Mobility
4
+ module Plugins
5
+ =begin
6
+
7
+ Defines +method_missing+ and +respond_to_missing?+ methods for a set of
8
+ attributes such that a method call using a locale accessor, like:
9
+
10
+ article.title_pt_br
11
+
12
+ will return the value of +article.title+ with the locale set to +pt-BR+ around
13
+ the method call. The class is called "FallthroughAccessors" because when
14
+ included in a model class, locale-specific methods will be available even if
15
+ not explicitly defined with the +locale_accessors+ option.
16
+
17
+ This is a less efficient (but more open-ended) implementation of locale
18
+ accessors, for use in cases where the locales to be used are not known when the
19
+ model class is generated.
20
+
21
+ @example Using fallthrough locales on a plain old ruby class
22
+ class Post
23
+ def title
24
+ "title in #{Mobility.locale}"
25
+ end
26
+ include Mobility::FallthroughAccessors.new("title")
27
+ end
28
+
29
+ Mobility.locale = :en
30
+ post = Post.new
31
+ post.title
32
+ #=> "title in en"
33
+ post.title_fr
34
+ #=> "title in fr"
35
+
36
+ =end
37
+ class FallthroughAccessors < Module
38
+ # Apply fallthrough accessors plugin to attributes.
39
+ # @param [Attributes] attributes
40
+ # @param [Boolean] option
41
+ def self.apply(attributes, option)
42
+ attributes.model_class.include new(*attributes.names) if option
43
+ end
44
+
45
+ # @param [String] One or more attributes
46
+ def initialize(*attributes)
47
+ method_name_regex = /\A(#{attributes.join('|'.freeze)})_([a-z]{2}(_[a-z]{2})?)(=?|\??)\z/.freeze
48
+
49
+ define_method :method_missing do |method_name, *arguments, &block|
50
+ if method_name =~ method_name_regex
51
+ attribute = $1.to_sym
52
+ locale, suffix = $2.split('_'.freeze)
53
+ locale = "#{locale}-#{suffix.upcase}".freeze if suffix
54
+ Mobility.with_locale(locale) { public_send("#{attribute}#{$4}".freeze, *arguments) }
55
+ else
56
+ super(method_name, *arguments, &block)
57
+ end
58
+ end
59
+
60
+ define_method :respond_to_missing? do |method_name, include_private = false|
61
+ (method_name =~ method_name_regex) || super(method_name, include_private)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,84 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Mobility
4
+ module Plugins
5
+ =begin
6
+
7
+ Defines methods for a set of locales to access translated attributes in those
8
+ locales directly with a method call, using a suffix including the locale:
9
+
10
+ article.title_pt_br
11
+
12
+ If no locales are passed as an option to the initializer,
13
+ +I18n.available_locales+ will be used by default.
14
+
15
+ @example
16
+ class Post
17
+ def title
18
+ "title in #{Mobility.locale}"
19
+ end
20
+ include Mobility::Plugins::LocaleAccessors.new("title", locales: [:en, :fr])
21
+ end
22
+
23
+ Mobility.locale = :en
24
+ post = Post.new
25
+ post.title
26
+ #=> "title in en"
27
+ post.title_fr
28
+ #=> "title in fr"
29
+
30
+ =end
31
+ class LocaleAccessors < Module
32
+ # Apply locale accessors plugin to attributes.
33
+ # @param [Attributes] attributes
34
+ # @param [Boolean] option
35
+ def self.apply(attributes, option)
36
+ if accessor_locales = option
37
+ accessor_locales = Mobility.config.default_accessor_locales if accessor_locales == true
38
+ attributes.model_class.include new(*attributes.names, locales: accessor_locales)
39
+ end
40
+ end
41
+
42
+ # @param [String] One or more attribute names
43
+ # @param [Array<Symbol>] Locales
44
+ def initialize(*attribute_names, locales: I18n.available_locales)
45
+ attribute_names.each do |name|
46
+ locales.each do |locale|
47
+ define_reader(name, locale)
48
+ define_writer(name, locale)
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def define_reader(name, locale)
56
+ warning_message = "locale passed as option to locale accessor will be ignored".freeze
57
+ normalized_locale = Mobility.normalize_locale(locale)
58
+
59
+ define_method "#{name}_#{normalized_locale}" do |**options|
60
+ return super() if options.delete(:super)
61
+ warn warning_message if options.delete(:locale)
62
+ Mobility.with_locale(locale) { send(name, options) }
63
+ end
64
+
65
+ define_method "#{name}_#{normalized_locale}?" do |**options|
66
+ return super() if options.delete(:super)
67
+ warn warning_message if options.delete(:locale)
68
+ Mobility.with_locale(locale) { send("#{name}?", options) }
69
+ end
70
+ end
71
+
72
+ def define_writer(name, locale)
73
+ warning_message = "locale passed as option to locale accessor will be ignored".freeze
74
+ normalized_locale = Mobility.normalize_locale(locale)
75
+
76
+ define_method "#{name}_#{normalized_locale}=" do |value, **options|
77
+ return super(value) if options.delete(:super)
78
+ warn warning_message if options.delete(:locale)
79
+ Mobility.with_locale(locale) { send("#{name}=", value, options) }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,29 +1,38 @@
1
+ require "mobility/util"
2
+
1
3
  module Mobility
2
- module Backend
4
+ module Plugins
3
5
  =begin
4
6
 
5
7
  Applies presence filter to values fetched from backend and to values set on
6
- backend. Included by default, but can be disabled with presence: false option.
8
+ backend. Included by default, but can be disabled with +presence: false+ option.
7
9
 
8
10
  =end
9
11
  module Presence
12
+ # Applies presence plugin to attributes.
13
+ # @param [Attributes] attributes
14
+ # @param [Boolean] option
15
+ def self.apply(attributes, option)
16
+ attributes.backend_class.include(self) if option
17
+ end
18
+
10
19
  # @group Backend Accessors
11
20
  # @!macro backend_reader
12
- # @param [Boolean] presence
21
+ # @option options [Boolean] presence
13
22
  # *false* to disable presence filter.
14
23
  def read(locale, **options)
15
24
  return super if options.delete(:presence) == false
16
25
  value = super
17
- value == false ? value : value.presence
26
+ value == false ? value : Util.presence(value)
18
27
  end
19
28
 
20
29
  # @group Backend Accessors
21
30
  # @!macro backend_writer
22
- # @param [Boolean] presence
31
+ # @option options [Boolean] presence
23
32
  # *false* to disable presence filter.
24
33
  def write(locale, value, **options)
25
34
  return super if options.delete(:presence) == false
26
- super(locale, value == false ? value : value.presence, **options)
35
+ super(locale, value == false ? value : Util.presence(value), options)
27
36
  end
28
37
  end
29
38
  end
@@ -0,0 +1,6 @@
1
+ module Mobility
2
+ module Plugins
3
+ module Sequel
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,59 @@
1
+ # frozen-string-literal: true
2
+ require "sequel/plugins/dirty"
3
+
4
+ module Mobility
5
+ module Plugins
6
+ =begin
7
+
8
+ Dirty tracking for Sequel models which use the +Sequel::Plugins::Dirty+ plugin.
9
+ Automatically includes dirty plugin in model class when enabled.
10
+
11
+ @see http://sequel.jeremyevans.net/rdoc-plugins/index.html Sequel dirty plugin
12
+
13
+ =end
14
+ module Sequel
15
+ module Dirty
16
+ # @!group Backend Accessors
17
+ # @!macro backend_writer
18
+ # @param [Hash] options
19
+ def write(locale, value, options = {})
20
+ locale_accessor = Mobility.normalize_locale_accessor(attribute, locale).to_sym
21
+ if model.column_changes.has_key?(locale_accessor) && model.initial_values[locale_accessor] == value
22
+ super
23
+ [model.changed_columns, model.initial_values].each { |h| h.delete(locale_accessor) }
24
+ elsif read(locale, options.merge(fallback: false)) != value
25
+ model.will_change_column(locale_accessor)
26
+ super
27
+ end
28
+ end
29
+ # @!endgroup
30
+
31
+ # Builds module which overrides dirty methods to handle translated as
32
+ # well as normal (untranslated) attributes.
33
+ class MethodsBuilder < Module
34
+ def initialize(*attribute_names)
35
+ # Although we load the plugin in the included callback method, we
36
+ # need to include this module here in advance to ensure that its
37
+ # instance methods are included *before* the ones defined here.
38
+ include ::Sequel::Plugins::Dirty::InstanceMethods
39
+
40
+ %w[initial_value column_change column_changed? reset_column].each do |method_name|
41
+ define_method method_name do |column|
42
+ if attribute_names.map(&:to_sym).include?(column)
43
+ super(Mobility.normalize_locale_accessor(column).to_sym)
44
+ else
45
+ super(column)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def included(model_class)
52
+ # this just adds Sequel::Plugins::Dirty to @plugins
53
+ model_class.plugin :dirty
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end