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.
- checksums.yaml +4 -4
- data/README.md +81 -1
- data/lib/associate_jsonb.rb +111 -1
- data/lib/associate_jsonb/arel_extensions/nodes/binary.rb +14 -0
- data/lib/associate_jsonb/arel_extensions/nodes/table_alias.rb +38 -0
- data/lib/associate_jsonb/arel_extensions/table.rb +40 -0
- data/lib/associate_jsonb/arel_extensions/visitors/postgresql.rb +113 -0
- data/lib/associate_jsonb/arel_extensions/visitors/visitor.rb +19 -0
- data/lib/associate_jsonb/arel_nodes/jsonb/attribute.rb +38 -0
- data/lib/associate_jsonb/arel_nodes/sql_casted_binary.rb +20 -0
- data/lib/associate_jsonb/arel_nodes/sql_casted_equality.rb +26 -12
- data/lib/associate_jsonb/associations/alias_tracker.rb +13 -0
- data/lib/associate_jsonb/associations/association_scope.rb +18 -45
- data/lib/associate_jsonb/associations/belongs_to_association.rb +8 -8
- data/lib/associate_jsonb/associations/builder/belongs_to.rb +5 -3
- data/lib/associate_jsonb/associations/join_dependency.rb +21 -0
- data/lib/associate_jsonb/attribute_methods.rb +19 -0
- data/lib/associate_jsonb/attribute_methods/read.rb +15 -0
- data/lib/associate_jsonb/connection_adapters/schema_creation.rb +162 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/add_jsonb_foreign_key_function.rb +9 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/add_jsonb_nested_set_function.rb +9 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/alter_table.rb +40 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/constraint_definition.rb +60 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/reference_definition.rb +88 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/table.rb +12 -0
- data/lib/associate_jsonb/connection_adapters/schema_definitions/table_definition.rb +25 -0
- data/lib/associate_jsonb/connection_adapters/schema_statements.rb +116 -0
- data/lib/associate_jsonb/persistence.rb +14 -0
- data/lib/associate_jsonb/predicate_builder.rb +15 -0
- data/lib/associate_jsonb/reflection.rb +2 -2
- data/lib/associate_jsonb/relation/where_clause.rb +19 -0
- data/lib/associate_jsonb/supported_rails_version.rb +6 -0
- data/lib/associate_jsonb/version.rb +1 -1
- data/lib/associate_jsonb/with_store_attribute.rb +59 -23
- metadata +39 -14
- data/lib/associate_jsonb/arel_node_extensions/binary.rb +0 -12
- data/lib/associate_jsonb/connection_adapters/reference_definition.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0832e64c43e22ba206a98d0603c0e231c1606b17315f715a4050e19a1fdb21b
|
4
|
+
data.tar.gz: ff37b2777adf131f20ad9a95076d8797ecf30dc2ddeaa86913de5a457cf9fed4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00fbd32bc9e66fa8bd85fcf9cab51850bc65443a99f66dbc3bcff5ca5d05e855624c62ac9a0aa7bcbed330a2650dbe149806c49bc3bb40ecd3aa6cdf002fde19
|
7
|
+
data.tar.gz: b134266f93576482f3558be28840081b803982ce369bd75497af2d9d2d2db30e3c27364247485ef2a4712705fb01183d80b812f6fa28f18d8f7db37db555bb88
|
data/README.md
CHANGED
@@ -2,7 +2,87 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/associate_jsonb)
|
4
4
|
|
5
|
-
|
5
|
+
#### PostgreSQL JSONB extensions including:
|
6
|
+
- Basic ActiveRecord Associations using PostgreSQL JSONB columns, with built-in accessors and column indexes
|
7
|
+
- Thread-Safe JSONB updates (well, as safe as they can be) using a custom nested version of `jsonb_set` (`jsonb_nested_set`)
|
8
|
+
|
9
|
+
**Requirements:**
|
10
|
+
|
11
|
+
- PostgreSQL (>= 12)
|
12
|
+
- Rails 6.0.3.2
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
### Jsonb Associations
|
17
|
+
|
18
|
+
|
19
|
+
### jsonb_set based hash updates
|
20
|
+
|
21
|
+
When enabled, *only* keys present in the updated hash and with values changed in memory will be updated.
|
22
|
+
To completely delete a key, value pair from an enabled attribute, set the key's value to `nil`.
|
23
|
+
|
24
|
+
e.g.
|
25
|
+
```ruby
|
26
|
+
# given: instance#data == { "key_1"=>1,
|
27
|
+
# "key_2"=>2,
|
28
|
+
# "key_3"=> { "key_4"=>7,
|
29
|
+
# "key_5"=>8,
|
30
|
+
# "key_6"=>9 } }
|
31
|
+
|
32
|
+
instance.update({ key_1: "asdf", a: 1, key_2: nil, key_3: { key_5: nil }})
|
33
|
+
# instance#data => { "key_1"=>"asdf",
|
34
|
+
# "a"=>"asdf",
|
35
|
+
# "key_3"=> { "key_4"=>7,
|
36
|
+
# "key_6"=>9 } }
|
37
|
+
|
38
|
+
```
|
39
|
+
|
40
|
+
#### enabling/adding attribute types
|
41
|
+
first, create the sql function
|
42
|
+
```bash
|
43
|
+
rails g migration add_jsonb_nested_set_function
|
44
|
+
```
|
45
|
+
```ruby
|
46
|
+
class CreateState < ActiveRecord::Migration[6.0]
|
47
|
+
def up
|
48
|
+
add_jsonb_nested_set_function
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
then in an initializer, enable key based updates:
|
54
|
+
```ruby
|
55
|
+
# config/initializers/associate_jsonb.rb
|
56
|
+
AssociateJsonb.enable_jsonb_set
|
57
|
+
```
|
58
|
+
|
59
|
+
Key based updates rely on inheritance for allowed attribute types. Any attributes that respond true to `attr_type.is_a?(GivenClass)` for any enabled type classes will use `jsonb_nested_set`
|
60
|
+
|
61
|
+
To add classes to the enabled list, pass them as arguments to `AssociateJsonb.add_hash_type(*klasses)`. Any arguments passed to `AssociateJsonb.enable_jsonb_set` are forwarded to `AssociateJsonb.add_hash_type`
|
62
|
+
|
63
|
+
By default, calling `AssociateJsonb.enable_jsonb_set(*klasses)` without arguments, and no classes previously added, adds `ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb` to the allowed classes list
|
64
|
+
|
65
|
+
#### disabling/removing attribute types
|
66
|
+
by default `jsonb_nested_set` updates are disabled.
|
67
|
+
|
68
|
+
if you've enabled them and need to disable, use: `AssociateJsonb.disable_jsonb_set`
|
69
|
+
|
70
|
+
To remove a class from the allowed list while leaving nested set updates enabled, use `AssociateJsonb.remove_hash_type(*klasses)`.
|
71
|
+
Any arguments passed to `AssociateJsonb.disable_jsonb_set` are forwarded to `AssociateJsonb.remove_hash_type`
|
72
|
+
|
73
|
+
### Automatically delete nil value hash keys
|
74
|
+
|
75
|
+
When jsonb_set updates are disabled, jsonb columns are replaced with the current document (i.e. default rails behavior)
|
76
|
+
|
77
|
+
You are also given the option to automatically clear nil/null values from the hash automatically
|
78
|
+
|
79
|
+
in an initializer, enable stripping nil values:
|
80
|
+
```ruby
|
81
|
+
# config/initializers/associate_jsonb.rb
|
82
|
+
AssociateJsonb.jsonb_delete_nil = true
|
83
|
+
```
|
84
|
+
|
85
|
+
Rules for classes to with this applies are the same as for `jsonb_nested_set`; add and remove classes through `AssociateJsonb.(add|remove)_hash_type(*klasses)`
|
6
86
|
|
7
87
|
<!-- This gem was created as a solution to this [task](http://cultofmartians.com/tasks/active-record-jsonb-associations.html) from [EvilMartians](http://evilmartians.com).
|
8
88
|
|
data/lib/associate_jsonb.rb
CHANGED
@@ -12,21 +12,106 @@ require "mutex_m"
|
|
12
12
|
|
13
13
|
require "zeitwerk"
|
14
14
|
loader = Zeitwerk::Loader.for_gem
|
15
|
+
loader.inflector.inflect(
|
16
|
+
"postgresql" => "PostgreSQL",
|
17
|
+
"supported_rails_version" => "SUPPORTED_RAILS_VERSION"
|
18
|
+
)
|
19
|
+
loader.collapse("#{__dir__}/associate_jsonb/connection_adapters/schema_definitions")
|
15
20
|
loader.setup # ready!
|
16
21
|
|
17
22
|
module AssociateJsonb
|
23
|
+
mattr_accessor :jsonb_hash_types, default: []
|
24
|
+
mattr_accessor :jsonb_set_removed, default: []
|
25
|
+
mattr_accessor :jsonb_set_enabled, default: false
|
26
|
+
mattr_accessor :jsonb_delete_nil, default: false
|
27
|
+
private_class_method :jsonb_hash_types=
|
28
|
+
private_class_method :jsonb_set_enabled=
|
29
|
+
|
30
|
+
def self.enable_jsonb_set(klass = nil, *classes)
|
31
|
+
add_hash_type(*Array(klass), *classes) unless klass.nil?
|
32
|
+
self.jsonb_set_enabled = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.disable_jsonb_set(klass = nil, *classes)
|
36
|
+
remove_hash_type(*Array(klass), *classes) unless klass.nil?
|
37
|
+
self.jsonb_set_enabled = false
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.add_hash_type(*classes)
|
41
|
+
self.jsonb_hash_types |= classes.flatten
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.remove_hash_type(*classes)
|
45
|
+
self.jsonb_set_removed |= classes.flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.merge_hash?(v)
|
49
|
+
return false unless jsonb_set_enabled && v
|
50
|
+
self.jsonb_hash_types.any? { |type| v.is_a?(type) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.is_hash?(v)
|
54
|
+
self.jsonb_hash_types.any? { |type| v.is_a?(type) }
|
55
|
+
end
|
18
56
|
end
|
19
57
|
|
20
58
|
|
21
59
|
# rubocop:disable Metrics/BlockLength
|
22
60
|
ActiveSupport.on_load :active_record do
|
23
61
|
loader.eager_load
|
62
|
+
AssociateJsonb.class_eval do
|
63
|
+
def self.enable_jsonb_set(klass = nil, *classes)
|
64
|
+
if klass.nil?
|
65
|
+
add_hash_type ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb if jsonb_hash_types.empty?
|
66
|
+
else
|
67
|
+
add_hash_type |= [*Array(klass), *classes].flatten
|
68
|
+
end
|
69
|
+
self.jsonb_set_enabled = true
|
70
|
+
end
|
71
|
+
|
72
|
+
self.enable_jsonb_set if jsonb_set_enabled
|
73
|
+
|
74
|
+
def self.remove_hash_type(*classes)
|
75
|
+
self.jsonb_hash_types -= classes.flatten
|
76
|
+
end
|
77
|
+
removed = jsonb_set_removed
|
78
|
+
self.remove_hash_type removed
|
79
|
+
self.send :remove_method, :jsonb_set_removed
|
80
|
+
self.send :remove_method, :jsonb_set_removed=
|
81
|
+
end
|
82
|
+
|
24
83
|
|
25
84
|
ActiveRecord::Base.include AssociateJsonb::WithStoreAttribute
|
26
85
|
ActiveRecord::Base.include AssociateJsonb::Associations
|
86
|
+
ActiveRecord::Base.include AssociateJsonb::AttributeMethods
|
87
|
+
ActiveRecord::Base.include AssociateJsonb::Persistence
|
27
88
|
|
28
89
|
Arel::Nodes.include AssociateJsonb::ArelNodes
|
29
|
-
|
90
|
+
|
91
|
+
Arel::Nodes::Binary.prepend(
|
92
|
+
AssociateJsonb::ArelExtensions::Nodes::Binary
|
93
|
+
)
|
94
|
+
|
95
|
+
Arel::Nodes::TableAlias.prepend(
|
96
|
+
AssociateJsonb::ArelExtensions::Nodes::TableAlias
|
97
|
+
)
|
98
|
+
|
99
|
+
Arel::Table.prepend(
|
100
|
+
AssociateJsonb::ArelExtensions::Table
|
101
|
+
)
|
102
|
+
|
103
|
+
Arel::Visitors::PostgreSQL.prepend(
|
104
|
+
AssociateJsonb::ArelExtensions::Visitors::PostgreSQL
|
105
|
+
)
|
106
|
+
|
107
|
+
Arel::Visitors::Visitor.singleton_class.prepend(
|
108
|
+
AssociateJsonb::ArelExtensions::Visitors::Visitor
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
ActiveRecord::Associations::AliasTracker.prepend(
|
113
|
+
AssociateJsonb::Associations::AliasTracker
|
114
|
+
)
|
30
115
|
|
31
116
|
ActiveRecord::Associations::Builder::BelongsTo.extend(
|
32
117
|
AssociateJsonb::Associations::Builder::BelongsTo
|
@@ -63,8 +148,33 @@ ActiveSupport.on_load :active_record do
|
|
63
148
|
# ActiveRecord::Associations::Preloader::HasMany.prepend(
|
64
149
|
# AssociateJsonb::Associations::Preloader::HasMany
|
65
150
|
# )
|
151
|
+
%i[
|
152
|
+
AlterTable
|
153
|
+
ConstraintDefinition
|
154
|
+
ReferenceDefinition
|
155
|
+
SchemaCreation
|
156
|
+
Table
|
157
|
+
TableDefinition
|
158
|
+
].each do |m|
|
159
|
+
includable = AssociateJsonb::ConnectionAdapters.const_get(m)
|
160
|
+
including =
|
161
|
+
begin
|
162
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL.const_get(m)
|
163
|
+
rescue NameError
|
164
|
+
ActiveRecord::ConnectionAdapters.const_get(m)
|
165
|
+
end
|
166
|
+
including.prepend includable
|
167
|
+
rescue NameError
|
168
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL.const_set(m, includable)
|
169
|
+
end
|
170
|
+
|
171
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.include(
|
172
|
+
AssociateJsonb::ConnectionAdapters::SchemaStatements
|
173
|
+
)
|
174
|
+
|
66
175
|
|
67
176
|
ActiveRecord::Reflection::AbstractReflection.prepend AssociateJsonb::Reflection
|
177
|
+
ActiveRecord::PredicateBuilder.prepend AssociateJsonb::PredicateBuilder
|
68
178
|
ActiveRecord::Relation::WhereClause.prepend AssociateJsonb::Relation::WhereClause
|
69
179
|
|
70
180
|
ActiveRecord::ConnectionAdapters::ReferenceDefinition.prepend(
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module AssociateJsonb
|
5
|
+
module ArelExtensions
|
6
|
+
module Nodes
|
7
|
+
module TableAlias
|
8
|
+
attr_reader :store_tracker
|
9
|
+
|
10
|
+
def initialize(*args, store_columns: nil)
|
11
|
+
@store_columns = store_columns
|
12
|
+
super(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_store_tracker(tracker)
|
16
|
+
@store_tracker = tracker
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](name)
|
21
|
+
return super unless store_col = store_tracker&.get(name)
|
22
|
+
|
23
|
+
attr = ::Arel::Nodes::Jsonb::DashArrow.
|
24
|
+
new(self, self[store_col[:store]], store_col[:key])
|
25
|
+
|
26
|
+
if cast_as = (store_col[:cast] && store_col[:cast][:sql_type])
|
27
|
+
attr = ::Arel::Nodes::NamedFunction.new(
|
28
|
+
"CAST",
|
29
|
+
[ attr.as(cast_as) ]
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
Arel::Nodes::Jsonb::Attribute.new(self, name, attr)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module AssociateJsonb
|
5
|
+
module ArelExtensions
|
6
|
+
module Table
|
7
|
+
attr_reader :store_tracker
|
8
|
+
|
9
|
+
def initialize(*args, store_tracker: nil, **opts)
|
10
|
+
@store_tracker = store_tracker
|
11
|
+
super(*args, **opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def alias(...)
|
15
|
+
super(...).with_store_tracker(store_tracker)
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_store_tracker(tracker)
|
19
|
+
@store_tracker = tracker
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](name)
|
24
|
+
return super unless store_col = store_tracker&.get(name)
|
25
|
+
|
26
|
+
attr = ::Arel::Nodes::Jsonb::DashDoubleArrow.
|
27
|
+
new(self, self[store_col[:store]], store_col[:key])
|
28
|
+
|
29
|
+
if cast_as = (store_col[:cast] && store_col[:cast][:sql_type])
|
30
|
+
attr = ::Arel::Nodes::NamedFunction.new(
|
31
|
+
"CAST",
|
32
|
+
[ attr.as(cast_as) ]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
Arel::Nodes::Jsonb::Attribute.new(self, name, attr)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module AssociateJsonb
|
2
|
+
module ArelExtensions
|
3
|
+
module Visitors
|
4
|
+
module PostgreSQL
|
5
|
+
private
|
6
|
+
def collect_hash_changes(original, updated, nesting = nil)
|
7
|
+
keys = original.keys.map(&:to_s)
|
8
|
+
updated_keys = updated.keys.map(&:to_s)
|
9
|
+
keys |= updated_keys
|
10
|
+
original = original.with_indifferent_access
|
11
|
+
updated = updated.with_indifferent_access
|
12
|
+
added = []
|
13
|
+
deleted = []
|
14
|
+
finished = {}
|
15
|
+
keys.each do |k|
|
16
|
+
if updated[k].is_a?(Hash)
|
17
|
+
finished[k], a, d = collect_hash_changes(original[k].is_a?(Hash) ? original[k] : {}, updated[k], nesting ? "#{nesting},#{k}" : k)
|
18
|
+
a = [[(nesting ? "{#{nesting},#{k}}" : "{#{k}}"), {}]] if original[k].nil? && a.blank?
|
19
|
+
added |= a
|
20
|
+
deleted |= d
|
21
|
+
elsif updated[k].nil?
|
22
|
+
deleted << (nesting ? "{#{nesting},#{k}}" : "{#{k}}") if updated_keys.include?(k)
|
23
|
+
elsif original[k] != updated[k]
|
24
|
+
finished[k] = updated[k]
|
25
|
+
added << [(nesting ? "{#{nesting},#{k}}" : "{#{k}}"), updated[k]]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
[ finished, added, deleted ]
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_hash?(type)
|
32
|
+
AssociateJsonb.is_hash? type
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_update?(collector)
|
36
|
+
collector &&
|
37
|
+
Array(collector.value).any? {|v| v.is_a?(String) && (v =~ /UPDATE/) }
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_insert?(collector)
|
43
|
+
collector &&
|
44
|
+
Array(collector.value).any? {|v| v.is_a?(String) && (v =~ /INSERT INTO/) }
|
45
|
+
rescue
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def visit_BindHashChanges(t, collector)
|
50
|
+
changes, additions, deletions =
|
51
|
+
collect_hash_changes(
|
52
|
+
t.original_value.presence || {},
|
53
|
+
t.value.presence || {}
|
54
|
+
)
|
55
|
+
|
56
|
+
base_json = +"COALESCE(#{quote_column_name(t.name)}, '{}'::jsonb)"
|
57
|
+
json = base_json
|
58
|
+
|
59
|
+
deletions.each do |del|
|
60
|
+
json = +"(#{json} #- '#{del}')"
|
61
|
+
end
|
62
|
+
|
63
|
+
coalesced_paths = []
|
64
|
+
additions.sort.each do |add, value|
|
65
|
+
collector.add_bind(t.with_value_from_user(value)) do |i|
|
66
|
+
json = +"jsonb_nested_set(#{json},'#{add}', COALESCE($#{i}, '{}'::jsonb))"
|
67
|
+
''
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
collector << json
|
72
|
+
end
|
73
|
+
|
74
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
75
|
+
catch(:nodes_bound) do
|
76
|
+
if AssociateJsonb.jsonb_set_enabled
|
77
|
+
catch(:not_hashable) do
|
78
|
+
if is_hash?(o.value.type)
|
79
|
+
if is_update?(collector)
|
80
|
+
visit_BindHashChanges(o.value, collector)
|
81
|
+
|
82
|
+
throw :nodes_bound, collector
|
83
|
+
elsif is_insert?(collector)
|
84
|
+
value = o.value
|
85
|
+
|
86
|
+
value, _, _ =
|
87
|
+
collect_hash_changes(
|
88
|
+
{},
|
89
|
+
value.value.presence || {}
|
90
|
+
)
|
91
|
+
throw :nodes_bound, collector.add_bind(o.value.with_cast_value(value)) { |i| "$#{i}"}
|
92
|
+
else
|
93
|
+
throw :not_hashable
|
94
|
+
end
|
95
|
+
else
|
96
|
+
throw :not_hashable
|
97
|
+
end
|
98
|
+
end
|
99
|
+
elsif AssociateJsonb.jsonb_delete_nil && is_hash?(o.value.type)
|
100
|
+
value, _, _ =
|
101
|
+
collect_hash_changes(
|
102
|
+
{},
|
103
|
+
o.value.value.presence || {}
|
104
|
+
)
|
105
|
+
throw :nodes_bound, collector.add_bind(o.value.with_cast_value(value)) { |i| "$#{i}" }
|
106
|
+
end
|
107
|
+
throw :nodes_bound, collector.add_bind(o.value) { |i| "$#{i}" }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|