mobility 1.0.0.alpha → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +54 -1
  5. data/Gemfile +5 -16
  6. data/Gemfile.lock +30 -82
  7. data/README.md +24 -29
  8. data/lib/mobility.rb +67 -9
  9. data/lib/mobility/backend.rb +8 -10
  10. data/lib/mobility/backends.rb +1 -1
  11. data/lib/mobility/backends/active_record.rb +1 -1
  12. data/lib/mobility/backends/active_record/column.rb +1 -1
  13. data/lib/mobility/backends/active_record/container.rb +6 -9
  14. data/lib/mobility/backends/active_record/hstore.rb +4 -4
  15. data/lib/mobility/backends/active_record/json.rb +3 -3
  16. data/lib/mobility/backends/active_record/jsonb.rb +3 -3
  17. data/lib/mobility/backends/active_record/key_value.rb +27 -11
  18. data/lib/mobility/backends/active_record/serialized.rb +4 -0
  19. data/lib/mobility/backends/active_record/table.rb +12 -7
  20. data/lib/mobility/backends/container.rb +10 -2
  21. data/lib/mobility/backends/hash_valued.rb +4 -0
  22. data/lib/mobility/backends/jsonb.rb +1 -1
  23. data/lib/mobility/backends/key_value.rb +12 -15
  24. data/lib/mobility/backends/sequel.rb +34 -2
  25. data/lib/mobility/backends/sequel/container.rb +8 -8
  26. data/lib/mobility/backends/sequel/hstore.rb +1 -1
  27. data/lib/mobility/backends/sequel/json.rb +1 -0
  28. data/lib/mobility/backends/sequel/key_value.rb +79 -12
  29. data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
  30. data/lib/mobility/backends/sequel/serialized.rb +4 -0
  31. data/lib/mobility/backends/sequel/table.rb +18 -8
  32. data/lib/mobility/backends/table.rb +29 -29
  33. data/lib/mobility/pluggable.rb +21 -1
  34. data/lib/mobility/plugin.rb +2 -2
  35. data/lib/mobility/plugins.rb +2 -0
  36. data/lib/mobility/plugins/active_model/dirty.rb +11 -5
  37. data/lib/mobility/plugins/active_record.rb +3 -0
  38. data/lib/mobility/plugins/active_record/backend.rb +2 -0
  39. data/lib/mobility/plugins/active_record/query.rb +7 -7
  40. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +5 -1
  41. data/lib/mobility/plugins/arel.rb +125 -0
  42. data/lib/mobility/plugins/arel/nodes.rb +15 -0
  43. data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
  44. data/lib/mobility/plugins/attribute_methods.rb +1 -0
  45. data/lib/mobility/plugins/attributes.rb +17 -15
  46. data/lib/mobility/plugins/backend.rb +45 -22
  47. data/lib/mobility/plugins/cache.rb +12 -5
  48. data/lib/mobility/plugins/default.rb +1 -1
  49. data/lib/mobility/plugins/fallbacks.rb +4 -4
  50. data/lib/mobility/plugins/fallthrough_accessors.rb +5 -6
  51. data/lib/mobility/plugins/locale_accessors.rb +2 -5
  52. data/lib/mobility/plugins/presence.rb +1 -1
  53. data/lib/mobility/plugins/reader.rb +2 -2
  54. data/lib/mobility/plugins/sequel/dirty.rb +2 -2
  55. data/lib/mobility/plugins/writer.rb +1 -1
  56. data/lib/mobility/version.rb +2 -2
  57. data/lib/rails/generators/mobility/templates/create_string_translations.rb +0 -1
  58. data/lib/rails/generators/mobility/templates/create_text_translations.rb +0 -1
  59. data/lib/rails/generators/mobility/templates/initializer.rb +11 -3
  60. metadata +14 -20
  61. metadata.gz.sig +0 -0
  62. data/lib/mobility/active_record/model_translation.rb +0 -14
  63. data/lib/mobility/active_record/string_translation.rb +0 -10
  64. data/lib/mobility/active_record/text_translation.rb +0 -10
  65. data/lib/mobility/active_record/translation.rb +0 -14
  66. data/lib/mobility/arel.rb +0 -49
  67. data/lib/mobility/arel/nodes.rb +0 -13
  68. data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
  69. data/lib/mobility/arel/visitor.rb +0 -61
  70. data/lib/mobility/sequel/column_changes.rb +0 -28
  71. data/lib/mobility/sequel/hash_initializer.rb +0 -21
  72. data/lib/mobility/sequel/model_translation.rb +0 -20
  73. data/lib/mobility/sequel/sql.rb +0 -16
  74. data/lib/mobility/sequel/string_translation.rb +0 -10
  75. data/lib/mobility/sequel/text_translation.rb +0 -10
  76. data/lib/mobility/sequel/translation.rb +0 -53
metadata.gz.sig CHANGED
Binary file
@@ -1,14 +0,0 @@
1
- module Mobility
2
- module ActiveRecord
3
- =begin
4
-
5
- Subclassed dynamically to generate translation class in
6
- {Backends::ActiveRecord::Table} backend.
7
-
8
- =end
9
- class ModelTranslation < ::ActiveRecord::Base
10
- self.abstract_class = true
11
- validates :locale, presence: true
12
- end
13
- end
14
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
- require "mobility/active_record/translation"
3
-
4
- module Mobility
5
- module ActiveRecord
6
- class StringTranslation < Translation
7
- self.table_name = "mobility_string_translations"
8
- end
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
- require "mobility/active_record/translation"
3
-
4
- module Mobility
5
- module ActiveRecord
6
- class TextTranslation < Translation
7
- self.table_name = "mobility_text_translations"
8
- end
9
- end
10
- end
@@ -1,14 +0,0 @@
1
- module Mobility
2
- module ActiveRecord
3
- # @abstract Subclass and set +table_name+ to implement for a particular column type.
4
- class Translation < ::ActiveRecord::Base
5
- self.abstract_class = true
6
-
7
- belongs_to :translatable, polymorphic: true, touch: true
8
-
9
- validates :key, presence: true, uniqueness: { scope: [:translatable_id, :translatable_type, :locale], case_sensitive: true }
10
- validates :translatable, presence: true
11
- validates :locale, presence: true
12
- end
13
- end
14
- end
@@ -1,49 +0,0 @@
1
- # frozen-string-literal: true
2
- require "mobility/arel/nodes"
3
- require "mobility/arel/visitor"
4
-
5
- module Mobility
6
- module Arel
7
- module MobilityExpressions
8
- include ::Arel::Expressions
9
-
10
- # @note This is necessary in order to ensure that when a translated
11
- # attribute is selected with an alias using +AS+, the resulting
12
- # expression can still be counted without blowing up.
13
- #
14
- # Extending +::Arel::Expressions+ is necessary to convince ActiveRecord
15
- # that this node should not be stringified, which otherwise would
16
- # result in garbage SQL.
17
- #
18
- # @see https://github.com/rails/rails/blob/847342c25c61acaea988430dc3ab66a82e3ed486/activerecord/lib/active_record/relation/calculations.rb#L261
19
- def as(*)
20
- super
21
- .extend(::Arel::Expressions)
22
- .extend(Countable)
23
- end
24
-
25
- module Countable
26
- # @note This allows expressions with selected translated attributes to
27
- # be counted.
28
- def count(*args)
29
- left.count(*args)
30
- end
31
- end
32
- end
33
-
34
- class Attribute < ::Arel::Attributes::Attribute
35
- include MobilityExpressions
36
-
37
- attr_reader :backend_class
38
- attr_reader :locale
39
- attr_reader :attribute_name
40
-
41
- def initialize(relation, column_name, locale, backend_class, attribute_name: nil)
42
- @backend_class = backend_class
43
- @locale = locale
44
- @attribute_name = attribute_name || column_name
45
- super(relation, column_name)
46
- end
47
- end
48
- end
49
- end
@@ -1,13 +0,0 @@
1
- # frozen-string-literal: true
2
- module Mobility
3
- module Arel
4
- module Nodes
5
- class Binary < ::Arel::Nodes::Binary; end
6
- class Grouping < ::Arel::Nodes::Grouping; end
7
-
8
- ::Arel::Visitors::ToSql.class_eval do
9
- alias :visit_Mobility_Arel_Nodes_Grouping :visit_Arel_Nodes_Grouping
10
- end
11
- end
12
- end
13
- end
@@ -1,132 +0,0 @@
1
- # frozen-string-literal: true
2
- require "mobility/arel"
3
-
4
- module Mobility
5
- module Arel
6
- module Nodes
7
- %w[
8
- JsonDashArrow
9
- JsonDashDoubleArrow
10
- JsonbDashArrow
11
- JsonbDashDoubleArrow
12
- JsonbQuestion
13
- HstoreDashArrow
14
- HstoreQuestion
15
- ].each do |name|
16
- const_set name, (Class.new(Binary) do
17
- include ::Arel::Predications
18
- include ::Arel::OrderPredications
19
- include ::Arel::AliasPredication
20
- include ::Mobility::Arel::MobilityExpressions
21
-
22
- def lower
23
- super self
24
- end
25
- end)
26
- end
27
-
28
- # Needed for AR 4.2, can be removed when support is deprecated
29
- if ::ActiveRecord::VERSION::STRING < '5.0'
30
- [JsonbDashDoubleArrow, HstoreDashArrow].each do |klass|
31
- klass.class_eval do
32
- def quoted_node other
33
- other && super
34
- end
35
- end
36
- end
37
- end
38
-
39
- class Jsonb < JsonbDashDoubleArrow
40
- def to_dash_arrow
41
- JsonbDashArrow.new left, right
42
- end
43
-
44
- def to_question
45
- JsonbQuestion.new left, right
46
- end
47
-
48
- def eq other
49
- case other
50
- when NilClass
51
- to_question.not
52
- when Integer, Array, ::Hash
53
- to_dash_arrow.eq other.to_json
54
- when Jsonb
55
- to_dash_arrow.eq other.to_dash_arrow
56
- when JsonbDashArrow
57
- to_dash_arrow.eq other
58
- else
59
- super
60
- end
61
- end
62
- end
63
-
64
- class Hstore < HstoreDashArrow
65
- def to_question
66
- HstoreQuestion.new left, right
67
- end
68
-
69
- def eq other
70
- other.nil? ? to_question.not : super
71
- end
72
- end
73
-
74
- class Json < JsonDashDoubleArrow; end
75
-
76
- class JsonContainer < Json
77
- def initialize column, locale, attr
78
- super(Arel::Nodes::JsonDashArrow.new(column, locale), attr)
79
- end
80
- end
81
-
82
- class JsonbContainer < Jsonb
83
- def initialize column, locale, attr
84
- @column, @locale = column, locale
85
- super(JsonbDashArrow.new(column, locale), attr)
86
- end
87
-
88
- def eq other
89
- other.nil? ? super.or(JsonbQuestion.new(@column, @locale).not) : super
90
- end
91
- end
92
- end
93
-
94
- module Visitors
95
- def visit_Mobility_Arel_Nodes_JsonDashArrow o, a
96
- json_infix o, a, '->'
97
- end
98
-
99
- def visit_Mobility_Arel_Nodes_JsonDashDoubleArrow o, a
100
- json_infix o, a, '->>'
101
- end
102
-
103
- def visit_Mobility_Arel_Nodes_JsonbDashArrow o, a
104
- json_infix o, a, '->'
105
- end
106
-
107
- def visit_Mobility_Arel_Nodes_JsonbDashDoubleArrow o, a
108
- json_infix o, a, '->>'
109
- end
110
-
111
- def visit_Mobility_Arel_Nodes_JsonbQuestion o, a
112
- json_infix o, a, '?'
113
- end
114
-
115
- def visit_Mobility_Arel_Nodes_HstoreDashArrow o, a
116
- json_infix o, a, '->'
117
- end
118
-
119
- def visit_Mobility_Arel_Nodes_HstoreQuestion o, a
120
- json_infix o, a, '?'
121
- end
122
-
123
- private
124
-
125
- def json_infix o, a, opr
126
- visit(Nodes::Grouping.new(::Arel::Nodes::InfixOperation.new(opr, o.left, o.right)), a)
127
- end
128
- end
129
-
130
- ::Arel::Visitors::PostgreSQL.include Visitors
131
- end
132
- end
@@ -1,61 +0,0 @@
1
- # frozen-string-literal: true
2
- module Mobility
3
- module Arel
4
- class Visitor < ::Arel::Visitors::Visitor
5
- INNER_JOIN = ::Arel::Nodes::InnerJoin
6
- OUTER_JOIN = ::Arel::Nodes::OuterJoin
7
-
8
- attr_reader :backend_class, :locale
9
-
10
- def initialize(backend_class, locale)
11
- super()
12
- @backend_class, @locale = backend_class, locale
13
- end
14
-
15
- private
16
-
17
- def visit(*args)
18
- super
19
- rescue TypeError
20
- visit_default(*args)
21
- end
22
-
23
- def visit_collection(_objects)
24
- raise NotImplementedError
25
- end
26
- alias :visit_Array :visit_collection
27
-
28
- def visit_Arel_Nodes_Unary(object)
29
- visit(object.expr)
30
- end
31
-
32
- def visit_Arel_Nodes_Binary(object)
33
- visit_collection([object.left, object.right])
34
- end
35
-
36
- def visit_Arel_Nodes_Function(object)
37
- visit_collection(object.expressions)
38
- end
39
-
40
- def visit_Arel_Nodes_Case(object)
41
- visit_collection([object.case, object.conditions, object.default])
42
- end
43
-
44
- def visit_Arel_Nodes_And(object)
45
- visit_Array(object.children)
46
- end
47
-
48
- def visit_Arel_Nodes_Node(object)
49
- visit_default(object)
50
- end
51
-
52
- def visit_Arel_Attributes_Attribute(object)
53
- visit_default(object)
54
- end
55
-
56
- def visit_default(_object)
57
- nil
58
- end
59
- end
60
- end
61
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mobility
4
- module Sequel
5
- =begin
6
-
7
- Internal class used to force Sequel model to notice changes when mobility
8
- setter method is called.
9
-
10
- =end
11
- class ColumnChanges < Module
12
- # @param [Array<String>] attributes Backend attributes
13
- def initialize(attributes, column_affix: "%s")
14
- attributes.each do |attribute|
15
- define_method "#{attribute}=" do |value, **options|
16
- if !options[:super] && send(attribute) != value
17
- locale = options[:locale] || Mobility.locale
18
- column = (column_affix % attribute).to_sym
19
- attribute_with_locale = :"#{attribute}_#{Mobility.normalize_locale(locale)}"
20
- @changed_columns = changed_columns | [column, attribute.to_sym, attribute_with_locale]
21
- end
22
- super(value, **options)
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mobility
4
- module Sequel
5
- =begin
6
-
7
- Internal class used to initialize column value(s) by default to a hash.
8
-
9
- =end
10
- class HashInitializer < Module
11
- def initialize(*columns)
12
- class_eval <<-EOM, __FILE__, __LINE__ + 1
13
- def initialize_set(values)
14
- #{columns.map { |c| "self[:#{c}] = {}" }.join(';')}
15
- super
16
- end
17
- EOM
18
- end
19
- end
20
- end
21
- end
@@ -1,20 +0,0 @@
1
- module Mobility
2
- module Sequel
3
- =begin
4
-
5
- Module included in translation class dynamically generated by
6
- {Backends::Sequel::Table} backend.
7
-
8
- =end
9
- module ModelTranslation
10
- def self.included(base)
11
- base.plugin :validation_helpers
12
- end
13
-
14
- def validate
15
- super
16
- validates_presence [:locale]
17
- end
18
- end
19
- end
20
- end
@@ -1,16 +0,0 @@
1
- module Mobility
2
- module Sequel
3
- module SQL
4
- class QualifiedIdentifier < ::Sequel::SQL::QualifiedIdentifier
5
- attr_reader :backend_class, :locale, :attribute_name
6
-
7
- def initialize(table, column, locale, backend_class, attribute_name: nil)
8
- @backend_class = backend_class
9
- @locale = locale
10
- @attribute_name = attribute_name || column
11
- super(table, column)
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
- require "mobility/sequel/translation"
3
-
4
- module Mobility
5
- module Sequel
6
- class StringTranslation < ::Sequel::Model(:mobility_string_translations)
7
- include Translation
8
- end
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
- require "mobility/sequel/translation"
3
-
4
- module Mobility
5
- module Sequel
6
- class TextTranslation < ::Sequel::Model(:mobility_text_translations)
7
- include Translation
8
- end
9
- end
10
- end
@@ -1,53 +0,0 @@
1
- module Mobility
2
- module Sequel
3
- module Translation
4
- def self.included(base)
5
- base.class_eval do
6
- plugin :validation_helpers
7
-
8
- # Paraphased from sequel_polymorphic gem
9
- #
10
- model = underscore(self.to_s)
11
- plural_model = pluralize(model)
12
- many_to_one :translatable,
13
- reciprocal: plural_model.to_sym,
14
- reciprocal_type: :many_to_one,
15
- setter: (proc do |able_instance|
16
- self[:translatable_id] = (able_instance.pk if able_instance)
17
- self[:translatable_type] = (able_instance.class.name if able_instance)
18
- end),
19
- dataset: (proc do
20
- translatable_type = send :translatable_type
21
- translatable_id = send :translatable_id
22
- return if translatable_type.nil? || translatable_id.nil?
23
- klass = self.class.send(:constantize, translatable_type)
24
- klass.where(klass.primary_key => translatable_id)
25
- end),
26
- eager_loader: (proc do |eo|
27
- id_map = {}
28
- eo[:rows].each do |model|
29
- model_able_type = model.send :translatable_type
30
- model_able_id = model.send :translatable_id
31
- model.associations[:translatable] = nil
32
- ((id_map[model_able_type] ||= {})[model_able_id] ||= []) << model if !model_able_type.nil? && !model_able_id.nil?
33
- end
34
- id_map.each do |klass_name, id_map|
35
- klass = constantize(camelize(klass_name))
36
- klass.where(klass.primary_key=>id_map.keys).all do |related_obj|
37
- id_map[related_obj.pk].each do |model|
38
- model.associations[:translatable] = related_obj
39
- end
40
- end
41
- end
42
- end)
43
-
44
- def validate
45
- super
46
- validates_presence [:locale, :key, :translatable_id, :translatable_type]
47
- validates_unique [:locale, :key, :translatable_id, :translatable_type]
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end