associate_jsonb 0.0.1 → 0.0.7

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -1
  3. data/lib/associate_jsonb.rb +111 -1
  4. data/lib/associate_jsonb/arel_extensions/nodes/binary.rb +14 -0
  5. data/lib/associate_jsonb/arel_extensions/nodes/table_alias.rb +38 -0
  6. data/lib/associate_jsonb/arel_extensions/table.rb +40 -0
  7. data/lib/associate_jsonb/arel_extensions/visitors/postgresql.rb +113 -0
  8. data/lib/associate_jsonb/arel_extensions/visitors/visitor.rb +19 -0
  9. data/lib/associate_jsonb/arel_nodes/jsonb/attribute.rb +38 -0
  10. data/lib/associate_jsonb/arel_nodes/sql_casted_binary.rb +20 -0
  11. data/lib/associate_jsonb/arel_nodes/sql_casted_equality.rb +26 -12
  12. data/lib/associate_jsonb/associations/alias_tracker.rb +13 -0
  13. data/lib/associate_jsonb/associations/association_scope.rb +18 -45
  14. data/lib/associate_jsonb/associations/belongs_to_association.rb +8 -8
  15. data/lib/associate_jsonb/associations/builder/belongs_to.rb +5 -3
  16. data/lib/associate_jsonb/associations/join_dependency.rb +21 -0
  17. data/lib/associate_jsonb/attribute_methods.rb +19 -0
  18. data/lib/associate_jsonb/attribute_methods/read.rb +15 -0
  19. data/lib/associate_jsonb/connection_adapters/schema_creation.rb +162 -0
  20. data/lib/associate_jsonb/connection_adapters/schema_definitions/add_jsonb_foreign_key_function.rb +9 -0
  21. data/lib/associate_jsonb/connection_adapters/schema_definitions/add_jsonb_nested_set_function.rb +9 -0
  22. data/lib/associate_jsonb/connection_adapters/schema_definitions/alter_table.rb +40 -0
  23. data/lib/associate_jsonb/connection_adapters/schema_definitions/constraint_definition.rb +60 -0
  24. data/lib/associate_jsonb/connection_adapters/schema_definitions/reference_definition.rb +88 -0
  25. data/lib/associate_jsonb/connection_adapters/schema_definitions/table.rb +12 -0
  26. data/lib/associate_jsonb/connection_adapters/schema_definitions/table_definition.rb +25 -0
  27. data/lib/associate_jsonb/connection_adapters/schema_statements.rb +116 -0
  28. data/lib/associate_jsonb/persistence.rb +14 -0
  29. data/lib/associate_jsonb/predicate_builder.rb +15 -0
  30. data/lib/associate_jsonb/reflection.rb +2 -2
  31. data/lib/associate_jsonb/relation/where_clause.rb +19 -0
  32. data/lib/associate_jsonb/supported_rails_version.rb +6 -0
  33. data/lib/associate_jsonb/version.rb +1 -1
  34. data/lib/associate_jsonb/with_store_attribute.rb +59 -23
  35. metadata +39 -14
  36. data/lib/associate_jsonb/arel_node_extensions/binary.rb +0 -12
  37. data/lib/associate_jsonb/connection_adapters/reference_definition.rb +0 -64
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module AssociateJsonb
5
+ SUPPORTED_RAILS_VERSION = "6.0.3.2"
6
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module AssociateJsonb
5
- VERSION = "0.0.1"
5
+ VERSION = "0.0.7"
6
6
  end
@@ -12,18 +12,29 @@ module AssociateJsonb
12
12
  @names_list ||= {}
13
13
  end
14
14
 
15
- def add_name(name, store, key)
16
- @names_list ||= {}
17
- @names_list[name.to_s.freeze] = { store: store, key: key }
15
+ def add_name(name, store, key, cast)
16
+ names_list[name.to_s.freeze] = { store: store, key: key, cast: cast }
18
17
  end
19
18
 
20
19
  def has_name?(name)
21
20
  names_list.key? name.to_s
22
21
  end
22
+
23
+ def get(name)
24
+ names_list[name.to_s]
25
+ end
26
+
27
+ def store_for(name)
28
+ (get(name) || {})[:store]
29
+ end
30
+
31
+ def key_for(name)
32
+ (get(name) || {})[:key]
33
+ end
23
34
  end
24
35
 
25
36
  included do
26
- instance_eval <<-CODE, __FILE__, __LINE__ + 1
37
+ instance_eval <<~CODE, __FILE__, __LINE__ + 1
27
38
  initialize_store_column_attribute_tracker
28
39
 
29
40
  after_initialize &set_store_column_attribute_values_on_init
@@ -37,6 +48,10 @@ module AssociateJsonb
37
48
  super
38
49
  end
39
50
 
51
+ def arel_table
52
+ super.with_store_tracker(store_column_attribute_tracker)
53
+ end
54
+
40
55
  def initialize_store_column_attribute_tracker
41
56
  @store_column_attribute_tracker = const_set(:StoreColumnAttributeTracker, StoreColumnAttributeTracker.new)
42
57
  private_constant :StoreColumnAttributeTracker
@@ -56,9 +71,9 @@ module AssociateJsonb
56
71
  end
57
72
  end
58
73
 
59
- def add_store_column_attribute_name(name, store, key)
74
+ def add_store_column_attribute_name(name, store, key, cast_opts)
60
75
  store_column_attribute_tracker.synchronize do
61
- store_column_attribute_tracker.add_name(name, store, key)
76
+ store_column_attribute_tracker.add_name(name, store, key, cast_opts)
62
77
  end
63
78
  end
64
79
 
@@ -83,16 +98,16 @@ module AssociateJsonb
83
98
  store_column_attribute :data, *args, **opts
84
99
  end
85
100
 
86
- def store_column_attribute(store, attr, *opts, key: nil, **attribute_opts)
101
+ def store_column_attribute(store, attr, cast_type = ActiveRecord::Type::Value.new, sql_type: nil, key: nil, **attribute_opts)
87
102
  store = store.to_sym
88
103
  attr = attr.to_sym
89
104
  key ||= attr
90
105
  key = key.to_s
91
106
  array = attribute_opts[:array]
92
- attribute attr, *opts, **attribute_opts
107
+ attribute attr, cast_type, **attribute_opts
93
108
 
94
- instance_eval <<-CODE, __FILE__, __LINE__ + 1
95
- add_store_column_attribute_name("#{attr}", :#{store}, "#{key}")
109
+ instance_eval <<~CODE, __FILE__, __LINE__ + 1
110
+ add_store_column_attribute_name("#{attr}", :#{store}, "#{key}", { sql_type: sql_type, type: cast_type, opts: attribute_opts })
96
111
  CODE
97
112
 
98
113
  include WithStoreAttribute::InstanceMethodsOnActivation.new(self, store, attr, key, array)
@@ -102,31 +117,52 @@ module AssociateJsonb
102
117
  class InstanceMethodsOnActivation < Module
103
118
  def initialize(mixin, store, attribute, key, is_array)
104
119
  is_array = !!(is_array && attribute.to_s =~ /_ids$/)
105
- on_attr_change =
106
- is_array \
107
- ? "write_attribute(:#{attribute}, Array(given))" \
108
- : "super(given)"
120
+
121
+ array_or_attr = ->(value) {
122
+ is_array \
123
+ ? %Q(Array(#{value})) \
124
+ : %Q(#{value})
125
+ }
126
+
127
+ on_store_change = "_write_attribute(:#{attribute}, #{array_or_attr.call %Q(#{store}["#{key}"])})"
128
+ on_attr_change = "super(#{array_or_attr.call %Q(given)})"
129
+
109
130
  if is_array
110
- mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
131
+ mixin.class_eval <<~CODE, __FILE__, __LINE__ + 1
111
132
  def #{attribute}
112
133
  _read_attribute(:#{attribute}) || []
113
134
  end
114
135
  CODE
115
136
  end
116
137
 
117
- mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
138
+ mixin.class_eval <<~CODE, __FILE__, __LINE__ + 1
118
139
  def #{store}=(given)
119
- given = super((given || {}).with_indifferent_access)
120
- write_attribute(:#{attribute}, given["#{key}"])
121
- given["#{key}"] = #{attribute} unless #{attribute}.nil?
122
- super(given)
140
+ if given.is_a?(::String)
141
+ given = ActiveSupport::JSON.decode(given) rescue nil
142
+ end
143
+
144
+ if AssociateJsonb.merge_hash?(self.class.attribute_types["#{store}"])
145
+ if !given
146
+ given = {}
147
+ #{store}.keys.each do |k|
148
+ given[k] = nil
149
+ end
150
+ end
151
+ super(#{store}.deep_merge(given.deep_stringify_keys))
152
+
153
+ self.#{attribute}= #{store}["#{key}"] if #{store}.key?("#{key}")
154
+ else
155
+ super given || {}
156
+ self.#{attribute}= #{store}["#{key}"]
157
+ end
158
+
159
+ #{store}
123
160
  end
124
161
 
125
162
  def #{attribute}=(given)
126
163
  #{on_attr_change}
127
- value = #{store}["#{key}"] = #{attribute}
128
- #{store}.delete("#{key}") if value.nil?
129
- _write_attribute(:#{store}, #{store})
164
+ value = #{store}["#{key}"] = #{attribute}.presence
165
+ #{store}.delete("#{key}") unless !value.nil? || AssociateJsonb.merge_hash?(self.class.attribute_types["#{store}"])
130
166
  value
131
167
  end
132
168
  CODE
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: associate_jsonb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sampson Crowley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-14 00:00:00.000000000 Z
11
+ date: 2020-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '6'
19
+ version: 6.0.3
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 6.0.3.1
22
+ version: 6.0.3.2
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '6'
29
+ version: 6.0.3
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 6.0.3.1
32
+ version: 6.0.3.2
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: pg
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -104,14 +104,18 @@ dependencies:
104
104
  - - "~>"
105
105
  - !ruby/object:Gem::Version
106
106
  version: 3.7.0
107
- description: |2
108
- This gem extends ActiveRecord to let you use PostgreSQL JSONB data for associations
107
+ description: |
108
+ This gem extends ActiveRecord to add additional functionality to JSONB
109
109
 
110
- Inspired by activerecord-jsonb-associations, but for use in Rails 6+ and
111
- ruby 2.7+ and with some unnecessary options and features (HABTM) removed
110
+ - use PostgreSQL JSONB data for associations
111
+ - thread-safe single-key updates to JSONB columns using `jsonb_set`
112
+ - extended `table#references` for easy migrations and indexes
113
+ - virtual JSONB foreign keys using check constraints
114
+ (NOTE: real foreign key constraints are not possible with PostgreSQL JSONB)
112
115
 
113
- BONUS: extended `table#references` for easy migrations and indexes
114
- (NOTE: real foreign key constraints are not possible with PostgreSQL JSONB)
116
+ Inspired by activerecord-jsonb-associations, but for use in Rails 6+ and
117
+ ruby 2.7+ and with some unnecessary options and features (HABTM) removed
118
+ and some additional features added
115
119
  email:
116
120
  - sampsonsprojects@gmail.com
117
121
  executables: []
@@ -122,15 +126,22 @@ files:
122
126
  - README.md
123
127
  - Rakefile
124
128
  - lib/associate_jsonb.rb
125
- - lib/associate_jsonb/arel_node_extensions/binary.rb
129
+ - lib/associate_jsonb/arel_extensions/nodes/binary.rb
130
+ - lib/associate_jsonb/arel_extensions/nodes/table_alias.rb
131
+ - lib/associate_jsonb/arel_extensions/table.rb
132
+ - lib/associate_jsonb/arel_extensions/visitors/postgresql.rb
133
+ - lib/associate_jsonb/arel_extensions/visitors/visitor.rb
126
134
  - lib/associate_jsonb/arel_nodes/jsonb/at_arrow.rb
135
+ - lib/associate_jsonb/arel_nodes/jsonb/attribute.rb
127
136
  - lib/associate_jsonb/arel_nodes/jsonb/bindable_operator.rb
128
137
  - lib/associate_jsonb/arel_nodes/jsonb/dash_arrow.rb
129
138
  - lib/associate_jsonb/arel_nodes/jsonb/dash_double_arrow.rb
130
139
  - lib/associate_jsonb/arel_nodes/jsonb/double_pipe.rb
131
140
  - lib/associate_jsonb/arel_nodes/jsonb/hash_arrow.rb
132
141
  - lib/associate_jsonb/arel_nodes/jsonb/operator.rb
142
+ - lib/associate_jsonb/arel_nodes/sql_casted_binary.rb
133
143
  - lib/associate_jsonb/arel_nodes/sql_casted_equality.rb
144
+ - lib/associate_jsonb/associations/alias_tracker.rb
134
145
  - lib/associate_jsonb/associations/association.rb
135
146
  - lib/associate_jsonb/associations/association_scope.rb
136
147
  - lib/associate_jsonb/associations/belongs_to_association.rb
@@ -139,11 +150,25 @@ files:
139
150
  - lib/associate_jsonb/associations/builder/has_one.rb
140
151
  - lib/associate_jsonb/associations/conflicting_association.rb
141
152
  - lib/associate_jsonb/associations/has_many_association.rb
153
+ - lib/associate_jsonb/associations/join_dependency.rb
142
154
  - lib/associate_jsonb/associations/preloader/association.rb
155
+ - lib/associate_jsonb/attribute_methods.rb
156
+ - lib/associate_jsonb/attribute_methods/read.rb
143
157
  - lib/associate_jsonb/connection_adapters.rb
144
- - lib/associate_jsonb/connection_adapters/reference_definition.rb
158
+ - lib/associate_jsonb/connection_adapters/schema_creation.rb
159
+ - lib/associate_jsonb/connection_adapters/schema_definitions/add_jsonb_foreign_key_function.rb
160
+ - lib/associate_jsonb/connection_adapters/schema_definitions/add_jsonb_nested_set_function.rb
161
+ - lib/associate_jsonb/connection_adapters/schema_definitions/alter_table.rb
162
+ - lib/associate_jsonb/connection_adapters/schema_definitions/constraint_definition.rb
163
+ - lib/associate_jsonb/connection_adapters/schema_definitions/reference_definition.rb
164
+ - lib/associate_jsonb/connection_adapters/schema_definitions/table.rb
165
+ - lib/associate_jsonb/connection_adapters/schema_definitions/table_definition.rb
166
+ - lib/associate_jsonb/connection_adapters/schema_statements.rb
167
+ - lib/associate_jsonb/persistence.rb
168
+ - lib/associate_jsonb/predicate_builder.rb
145
169
  - lib/associate_jsonb/reflection.rb
146
170
  - lib/associate_jsonb/relation/where_clause.rb
171
+ - lib/associate_jsonb/supported_rails_version.rb
147
172
  - lib/associate_jsonb/version.rb
148
173
  - lib/associate_jsonb/with_store_attribute.rb
149
174
  homepage: https://github.com/SampsonCrowley/associate_jsonb
@@ -1,12 +0,0 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: true
3
-
4
- module AssociateJsonb
5
- module ArelNodeExtensions
6
- module Binary
7
- def original_left
8
- left
9
- end
10
- end
11
- end
12
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AssociateJsonb
4
- module ConnectionAdapters
5
- module ReferenceDefinition #:nodoc:
6
- # rubocop:disable Metrics/ParameterLists
7
- def initialize(
8
- name,
9
- store: false,
10
- **options
11
- )
12
- @store = store && store.to_sym
13
-
14
- super(name, **options)
15
- end
16
- # rubocop:enable Metrics/ParameterLists
17
-
18
- def add_to(table)
19
- return super unless store
20
-
21
- should_add_col = false
22
- if table.respond_to? :column_exists?
23
- should_add_col = !table.column_exists?(store)
24
- elsif table.respond_to? :columns
25
- should_add_col = table.columns.none? {|col| col.name.to_sym == store}
26
- end
27
-
28
- table.column(store, :jsonb, null: false, default: {}) if should_add_col
29
-
30
- return unless index
31
-
32
- # should_add_idx = false
33
- # if table.respond_to? :index_exists?
34
- # should_add_idx = !table.index_exists?([ store ], using: :gin)
35
- # elsif table.respond_to? :indexes
36
- # should_add_idx = table.indexes.none? do |idx, opts|
37
- # (idx == [ store ]) \
38
- # && (opts == { using: :gin })
39
- # end
40
- # end
41
- #
42
- # table.index([ store ], using: :gin) if should_add_idx
43
-
44
- column_names.each do |column_name|
45
- table.index(
46
- "CAST (\"#{store}\"->'#{column_name}' AS #{@type || :bigint})",
47
- using: :btree,
48
- name: "index_#{table.name}_on_#{store}_#{column_name}"
49
- )
50
-
51
- table.index(
52
- "(#{store}->>'#{column_name}')",
53
- using: :btree,
54
- name: "index_#{table.name}_on_#{store}_#{column_name}_text"
55
- )
56
- end
57
- end
58
-
59
- protected
60
-
61
- attr_reader :store
62
- end
63
- end
64
- end