mobility 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +19 -0
- data/Gemfile.lock +153 -0
- data/Guardfile +70 -0
- data/README.md +603 -13
- data/Rakefile +42 -0
- data/lib/generators/mobility/install_generator.rb +45 -0
- data/lib/generators/mobility/templates/create_string_translations.rb +15 -0
- data/lib/generators/mobility/templates/create_text_translations.rb +15 -0
- data/lib/mobility.rb +203 -2
- data/lib/mobility/active_model.rb +6 -0
- data/lib/mobility/active_model/attribute_methods.rb +27 -0
- data/lib/mobility/active_model/backend_resetter.rb +26 -0
- data/lib/mobility/active_record.rb +39 -0
- data/lib/mobility/active_record/backend_resetter.rb +26 -0
- data/lib/mobility/active_record/model_translation.rb +14 -0
- data/lib/mobility/active_record/string_translation.rb +7 -0
- data/lib/mobility/active_record/text_translation.rb +7 -0
- data/lib/mobility/active_record/translation.rb +14 -0
- data/lib/mobility/attributes.rb +210 -0
- data/lib/mobility/backend.rb +152 -0
- data/lib/mobility/backend/active_model.rb +7 -0
- data/lib/mobility/backend/active_model/dirty.rb +84 -0
- data/lib/mobility/backend/active_record.rb +13 -0
- data/lib/mobility/backend/active_record/column.rb +52 -0
- data/lib/mobility/backend/active_record/column/query_methods.rb +40 -0
- data/lib/mobility/backend/active_record/hash_valued.rb +58 -0
- data/lib/mobility/backend/active_record/hstore.rb +36 -0
- data/lib/mobility/backend/active_record/hstore/query_methods.rb +53 -0
- data/lib/mobility/backend/active_record/jsonb.rb +43 -0
- data/lib/mobility/backend/active_record/jsonb/query_methods.rb +53 -0
- data/lib/mobility/backend/active_record/key_value.rb +126 -0
- data/lib/mobility/backend/active_record/key_value/query_methods.rb +63 -0
- data/lib/mobility/backend/active_record/query_methods.rb +36 -0
- data/lib/mobility/backend/active_record/serialized.rb +93 -0
- data/lib/mobility/backend/active_record/serialized/query_methods.rb +32 -0
- data/lib/mobility/backend/active_record/table.rb +197 -0
- data/lib/mobility/backend/active_record/table/query_methods.rb +91 -0
- data/lib/mobility/backend/cache.rb +110 -0
- data/lib/mobility/backend/column.rb +52 -0
- data/lib/mobility/backend/dirty.rb +28 -0
- data/lib/mobility/backend/fallbacks.rb +89 -0
- data/lib/mobility/backend/hstore.rb +21 -0
- data/lib/mobility/backend/jsonb.rb +21 -0
- data/lib/mobility/backend/key_value.rb +71 -0
- data/lib/mobility/backend/null.rb +24 -0
- data/lib/mobility/backend/orm_delegator.rb +33 -0
- data/lib/mobility/backend/sequel.rb +14 -0
- data/lib/mobility/backend/sequel/column.rb +40 -0
- data/lib/mobility/backend/sequel/column/query_methods.rb +24 -0
- data/lib/mobility/backend/sequel/dirty.rb +54 -0
- data/lib/mobility/backend/sequel/hash_valued.rb +51 -0
- data/lib/mobility/backend/sequel/hstore.rb +36 -0
- data/lib/mobility/backend/sequel/hstore/query_methods.rb +42 -0
- data/lib/mobility/backend/sequel/jsonb.rb +43 -0
- data/lib/mobility/backend/sequel/jsonb/query_methods.rb +42 -0
- data/lib/mobility/backend/sequel/key_value.rb +139 -0
- data/lib/mobility/backend/sequel/key_value/query_methods.rb +48 -0
- data/lib/mobility/backend/sequel/query_methods.rb +22 -0
- data/lib/mobility/backend/sequel/serialized.rb +133 -0
- data/lib/mobility/backend/sequel/serialized/query_methods.rb +20 -0
- data/lib/mobility/backend/sequel/table.rb +149 -0
- data/lib/mobility/backend/sequel/table/query_methods.rb +48 -0
- data/lib/mobility/backend/serialized.rb +53 -0
- data/lib/mobility/backend/table.rb +93 -0
- data/lib/mobility/backend_resetter.rb +44 -0
- data/lib/mobility/configuration.rb +31 -0
- data/lib/mobility/core_ext/nil.rb +10 -0
- data/lib/mobility/core_ext/object.rb +19 -0
- data/lib/mobility/core_ext/string.rb +16 -0
- data/lib/mobility/instance_methods.rb +34 -0
- data/lib/mobility/orm.rb +4 -0
- data/lib/mobility/sequel.rb +26 -0
- data/lib/mobility/sequel/backend_resetter.rb +26 -0
- data/lib/mobility/sequel/column_changes.rb +29 -0
- data/lib/mobility/sequel/model_translation.rb +20 -0
- data/lib/mobility/sequel/string_translation.rb +7 -0
- data/lib/mobility/sequel/text_translation.rb +7 -0
- data/lib/mobility/sequel/translation.rb +53 -0
- data/lib/mobility/translates.rb +75 -0
- data/lib/mobility/wrapper.rb +31 -0
- metadata +152 -12
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/mobility.gemspec +0 -32
@@ -0,0 +1,110 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Caches values fetched from the backend so subsequent fetches can be performed
|
6
|
+
more quickly.
|
7
|
+
|
8
|
+
By default, the cache stores cached values in a simple hash, returned from the
|
9
|
+
{new_cache} method added to the including backend instance class. To use a
|
10
|
+
different cache class, simply define a +new_cache+ method in the backend and
|
11
|
+
return a new instance of the cache class (many backends do this, see
|
12
|
+
{Mobility::Backend::KeyValue} for one example.)
|
13
|
+
|
14
|
+
The cache is reset by the {clear_cache} method, which by default simply assigns
|
15
|
+
the result of {new_cache} to the +@cache+ instance variable. This behaviour can
|
16
|
+
also be customized by defining a +new_cache+ method on the backend class (see
|
17
|
+
{Mobility::Backend::ActiveRecord::Table#new_cache} for an example of a backend that does this).
|
18
|
+
|
19
|
+
The cache is reset when one of a set of events happens (saving, reloading,
|
20
|
+
etc.). See {BackendResetter} for details.
|
21
|
+
|
22
|
+
Values are added to the cache in two ways:
|
23
|
+
|
24
|
+
1. first read from backend
|
25
|
+
2. any write to backend
|
26
|
+
|
27
|
+
The latter can be customized by defining the {write_to_cache?} method, which by
|
28
|
+
default returns +false+. If set to +true+, then writes will only update the
|
29
|
+
cache and not hit the backend. This is a sensible setting in case the cache is
|
30
|
+
actually an object which directly stores the translation (see one of the
|
31
|
+
ORM-specific implementations of {Mobility::Backend::KeyValue} for examples of
|
32
|
+
this).
|
33
|
+
|
34
|
+
=end
|
35
|
+
module Cache
|
36
|
+
# @group Backend Accessors
|
37
|
+
# @!macro backend_reader
|
38
|
+
def read(locale, **options)
|
39
|
+
if write_to_cache? || cache.has_key?(locale)
|
40
|
+
cache[locale]
|
41
|
+
else
|
42
|
+
cache[locale] = super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @!macro backend_writer
|
47
|
+
def write(locale, value, **options)
|
48
|
+
cache[locale] = write_to_cache? ? value : super
|
49
|
+
end
|
50
|
+
# @!endgroup
|
51
|
+
|
52
|
+
# Adds hook to {Backend::Setup#setup_model} to include instance of
|
53
|
+
# model-specific {BackendResetter} subclass when setting up
|
54
|
+
# model class, to trigger cache resetting at specific events (saving,
|
55
|
+
# reloading, etc.)
|
56
|
+
module Setup
|
57
|
+
# @param model_class Model class
|
58
|
+
# @param [Array<String>] attributes Backend attributes
|
59
|
+
# @param [Hash] options Backend options
|
60
|
+
def setup_model(model_class, attributes, **options)
|
61
|
+
super
|
62
|
+
model_class.include BackendResetter.for(model_class).new(attributes) { clear_cache }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!group Cache Methods
|
67
|
+
# @!parse
|
68
|
+
# def new_cache
|
69
|
+
# {}
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @!parse
|
73
|
+
# def write_to_cache?
|
74
|
+
# false
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# @!parse
|
78
|
+
# def clear_cache
|
79
|
+
# @cache = new_cache
|
80
|
+
# end
|
81
|
+
# @!endgroup
|
82
|
+
|
83
|
+
# Includes cache methods to backend (unless they are already defined) and
|
84
|
+
# extends backend class with {Mobility::Cache::Setup} for backend resetting.
|
85
|
+
def self.included(backend_class)
|
86
|
+
backend_class.class_eval do
|
87
|
+
extend Setup
|
88
|
+
|
89
|
+
def new_cache
|
90
|
+
{}
|
91
|
+
end unless method_defined?(:new_cache)
|
92
|
+
|
93
|
+
def write_to_cache?
|
94
|
+
false
|
95
|
+
end unless method_defined?(:write_to_cache?)
|
96
|
+
|
97
|
+
def clear_cache
|
98
|
+
@cache = new_cache
|
99
|
+
end unless method_defined?(:clear_cache)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def cache
|
106
|
+
@cache ||= new_cache
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Stores translated attribute as a column on the model table.
|
6
|
+
|
7
|
+
To use this backend, ensure that the model table has columns named
|
8
|
+
+<attribute>_<locale>+ for every locale in +I18n.available_locales+.
|
9
|
+
|
10
|
+
==Backend Options
|
11
|
+
|
12
|
+
There are no options for this backend. Also, the +locale_accessors+ option will
|
13
|
+
be ignored if set, since it would cause a conflict with column accessors.
|
14
|
+
|
15
|
+
@see Mobility::Backend::ActiveRecord::Column
|
16
|
+
@see Mobility::Backend::Sequel::Column
|
17
|
+
|
18
|
+
=end
|
19
|
+
module Column
|
20
|
+
include OrmDelegator
|
21
|
+
|
22
|
+
# @!group Backend Accessors
|
23
|
+
#
|
24
|
+
# @!macro backend_reader
|
25
|
+
def read(locale, **options)
|
26
|
+
model.send(column(locale))
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!macro backend_writer
|
30
|
+
def write(locale, value, **options)
|
31
|
+
model.send("#{column(locale)}=", value)
|
32
|
+
end
|
33
|
+
# @!endgroup
|
34
|
+
|
35
|
+
# Returns name of column where translated attribute is stored
|
36
|
+
# @param [Symbol] locale
|
37
|
+
# @return [String]
|
38
|
+
def column(locale = Mobility.locale)
|
39
|
+
Column.column_name_for(attribute, locale)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns name of column where translated attribute is stored
|
43
|
+
# @param [String] attribute
|
44
|
+
# @param [Symbol] locale
|
45
|
+
# @return [String]
|
46
|
+
def self.column_name_for(attribute, locale = Mobility.locale)
|
47
|
+
normalized_locale = locale.to_s.downcase.sub("-", "_")
|
48
|
+
"#{attribute}_#{normalized_locale}".to_sym
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Dirty tracking for Mobility attributes. See class-specific implementations for
|
6
|
+
details.
|
7
|
+
|
8
|
+
@see Mobility::Backend::ActiveModel::Dirty
|
9
|
+
@see Mobility::Backend::Sequel::Dirty
|
10
|
+
|
11
|
+
=end
|
12
|
+
module Dirty
|
13
|
+
# @param model_class Class of model this backend is defined on.
|
14
|
+
# @return [Backend]
|
15
|
+
# @raise [ArgumentError] if model class does not support dirty tracking
|
16
|
+
def self.for(model_class)
|
17
|
+
model_class ||= Object
|
18
|
+
if Loaded::ActiveRecord && model_class.ancestors.include?(::ActiveModel::Dirty)
|
19
|
+
Backend::ActiveModel::Dirty
|
20
|
+
elsif Loaded::Sequel && model_class < ::Sequel::Model
|
21
|
+
Backend::Sequel::Dirty
|
22
|
+
else
|
23
|
+
raise ArgumentError, "#{model_class.to_s} does not support Dirty module."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Falls back to one or more alternative locales in case no value is defined for a
|
6
|
+
given locale.
|
7
|
+
|
8
|
+
For +fallbacks: true+, Mobility will use the value of
|
9
|
+
{Mobility::Configuration#default_fallbacks} for the fallbacks instance. This
|
10
|
+
defaults to an instance of +I18n::Locale::Fallbacks+, but can be configured
|
11
|
+
(see {Mobility::Configuration}).
|
12
|
+
|
13
|
+
If a hash is passed to the +fallbacks+ option, a new fallbacks instance will be
|
14
|
+
created for the model with the hash defining additional fallbacks.
|
15
|
+
|
16
|
+
In addition, fallbacks can be disabled when reading by passing `fallbacks:
|
17
|
+
false` to the reader method. This can be useful to determine the actual value
|
18
|
+
of the translated attribute, including a possible +nil+ value.
|
19
|
+
|
20
|
+
@see https://github.com/svenfuchs/i18n/wiki/Fallbacks I18n Fallbacks
|
21
|
+
|
22
|
+
@example With default fallbacks enabled (falls through to default locale)
|
23
|
+
class Post
|
24
|
+
translates :title, fallbacks: true
|
25
|
+
end
|
26
|
+
|
27
|
+
I18n.default_locale = :en
|
28
|
+
Mobility.locale = :en
|
29
|
+
post = Post.new(title: "foo")
|
30
|
+
|
31
|
+
Mobility.locale = :ja
|
32
|
+
post.title
|
33
|
+
#=> "foo"
|
34
|
+
|
35
|
+
post.title = "bar"
|
36
|
+
post.title
|
37
|
+
#=> "bar"
|
38
|
+
|
39
|
+
@example With additional fallbacks enabled
|
40
|
+
class Post
|
41
|
+
translates :title, fallbacks: { :'en-US' => 'de-DE', :pt => 'de-DE' }
|
42
|
+
end
|
43
|
+
|
44
|
+
Mobility.locale = :'de-DE'
|
45
|
+
post = Post.new(title: "foo")
|
46
|
+
|
47
|
+
Mobility.locale = :'en-US'
|
48
|
+
post.title
|
49
|
+
#=> "foo"
|
50
|
+
|
51
|
+
post.title = "bar"
|
52
|
+
post.title
|
53
|
+
#=> "bar"
|
54
|
+
|
55
|
+
@example Disabling fallbacks when reading value
|
56
|
+
class Post
|
57
|
+
translates :title, fallbacks: true
|
58
|
+
end
|
59
|
+
|
60
|
+
I18n.default_locale = :en
|
61
|
+
Mobility.locale = :en
|
62
|
+
post = Post.new(title: "foo")
|
63
|
+
|
64
|
+
Mobility.locale = :ja
|
65
|
+
post.title
|
66
|
+
#=> "foo"
|
67
|
+
post.title(fallbacks: false)
|
68
|
+
#=> nil
|
69
|
+
=end
|
70
|
+
module Fallbacks
|
71
|
+
# @!group Backend Accessors
|
72
|
+
# @!macro backend_reader
|
73
|
+
# @option options [Boolean] fallbacks +false+ to disable fallbacks on lookup
|
74
|
+
def read(locale, **options)
|
75
|
+
return super if options[:fallbacks] == false
|
76
|
+
fallbacks[locale].detect do |locale|
|
77
|
+
value = super(locale)
|
78
|
+
break value if value.present?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def fallbacks
|
85
|
+
@fallbacks ||= Mobility.default_fallbacks
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Stores translations as hash on Postgres hstore column.
|
7
|
+
|
8
|
+
==Backend Options
|
9
|
+
|
10
|
+
This backend has no options.
|
11
|
+
|
12
|
+
@see Mobility::Backend::ActiveRecord::Hstore
|
13
|
+
@see Mobility::Backend::Sequel::Hstore
|
14
|
+
@see https://www.postgresql.org/docs/current/static/hstore.html PostgreSQL Documentation for hstore
|
15
|
+
|
16
|
+
=end
|
17
|
+
module Hstore
|
18
|
+
include OrmDelegator
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Stores translations as hash on Postgres jsonb column.
|
7
|
+
|
8
|
+
==Backend Options
|
9
|
+
|
10
|
+
This backend has no options.
|
11
|
+
|
12
|
+
@see Mobility::Backend::ActiveRecord::Jsonb
|
13
|
+
@see Mobility::Backend::Sequel::Jsonb
|
14
|
+
@see https://www.postgresql.org/docs/current/static/datatype-json.html PostgreSQL Documentation for JSON Types
|
15
|
+
|
16
|
+
=end
|
17
|
+
module Jsonb
|
18
|
+
include OrmDelegator
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Stores attribute translation as attribute/value pair on a shared translations
|
6
|
+
table, using a polymorphic relationship between a translation class and models
|
7
|
+
using the backend. By default, two tables are assumed to be present supporting
|
8
|
+
string and text translations: a +mobility_text_translations+ table for text-valued translations and a
|
9
|
+
+mobility_string_translations+ table for string-valued translations (the only
|
10
|
+
difference being the column type of the +value+ column on the table).
|
11
|
+
|
12
|
+
==Backend Options
|
13
|
+
|
14
|
+
===+association_name+
|
15
|
+
|
16
|
+
Name of association on model. Defaults to +mobility_text_translations+ (if
|
17
|
+
+type+ is +:text+) or +mobility_string_translations+ (if +type+ is +:string+).
|
18
|
+
If specified, ensure name does not overlap with other methods on model or with
|
19
|
+
the association name used by other backends on model (otherwise one will
|
20
|
+
overwrite the other).
|
21
|
+
|
22
|
+
===+type+
|
23
|
+
|
24
|
+
Currently, either +:text+ or +:string+ is supported. Determines which class to
|
25
|
+
use for translations, which in turn determines which table to use to store
|
26
|
+
translations (by default +mobility_text_translations+ for text type,
|
27
|
+
+mobility_string_translations+ for string type).
|
28
|
+
|
29
|
+
===+class_name+
|
30
|
+
|
31
|
+
Class to use for translations when defining association. By default,
|
32
|
+
{Mobility::ActiveRecord::TextTranslation} or
|
33
|
+
{Mobility::ActiveRecord::StringTranslation} for ActiveRecord models (similar
|
34
|
+
for Sequel models). If string is passed in, it will be constantized to get the
|
35
|
+
class.
|
36
|
+
|
37
|
+
@see Mobility::Backend::ActiveRecord::KeyValue
|
38
|
+
@see Mobility::Backend::Sequel::KeyValue
|
39
|
+
|
40
|
+
=end
|
41
|
+
module KeyValue
|
42
|
+
include OrmDelegator
|
43
|
+
|
44
|
+
# Simple cache to memoize translations as a hash so they can be fetched
|
45
|
+
# quickly.
|
46
|
+
class TranslationsCache
|
47
|
+
# @param backend Instance of KeyValue backend to cache
|
48
|
+
# @return [TranslationsCache]
|
49
|
+
def initialize(backend)
|
50
|
+
@cache = Hash.new { |hash, locale| hash[locale] = backend.translation_for(locale) }
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param locale [Symbol] Locale to fetch
|
54
|
+
def [](locale)
|
55
|
+
@cache[locale].value
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param locale [Symbol] Locale to set
|
59
|
+
# @param value [String] Value to set
|
60
|
+
def []=(locale, value)
|
61
|
+
@cache[locale].value = value
|
62
|
+
end
|
63
|
+
|
64
|
+
# @yield [locale, translation]
|
65
|
+
def each_translation &block
|
66
|
+
@cache.each_value &block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Backend which does absolutely nothing. Mostly for testing purposes.
|
6
|
+
|
7
|
+
=end
|
8
|
+
class Null
|
9
|
+
include Backend
|
10
|
+
|
11
|
+
# @!group Backend Accessors
|
12
|
+
# @return [NilClass]
|
13
|
+
def read(*); end
|
14
|
+
|
15
|
+
# @return [NilClass]
|
16
|
+
def write(*); end
|
17
|
+
# @!endgroup
|
18
|
+
|
19
|
+
# @!group Backend Configuration
|
20
|
+
def self.configure!(*); end
|
21
|
+
# @!endgroup
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Adds {#for} method to backend to return ORM-specific backend.
|
6
|
+
|
7
|
+
@example KeyValue backend for AR model
|
8
|
+
class Post < ActiveRecord::Base
|
9
|
+
# ...
|
10
|
+
end
|
11
|
+
Mobility::Backend::KeyValue.for(Post)
|
12
|
+
#=> Mobility::Backend::ActiveRecord::KeyValue
|
13
|
+
|
14
|
+
=end
|
15
|
+
module OrmDelegator
|
16
|
+
# @param [Class] model_class Class of model
|
17
|
+
# @return [Class] Class of backend to use for model
|
18
|
+
def for(model_class)
|
19
|
+
if Loaded::ActiveRecord && model_class < ::ActiveRecord::Base
|
20
|
+
const_get(name.split("::").insert(-2, "ActiveRecord").join("::"))
|
21
|
+
elsif Loaded::Sequel && model_class < ::Sequel::Model
|
22
|
+
const_get(name.split("::").insert(-2, "Sequel").join("::"))
|
23
|
+
else
|
24
|
+
raise ArgumentError, "#{name.split('::').last} backend can only be used by ActiveRecord or Sequel models"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.included(base)
|
29
|
+
base.extend(self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|