rom-sql 2.0.0.beta2 → 2.0.0.beta3
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/CHANGELOG.md +66 -0
- data/lib/rom/plugins/relation/sql/postgres/explain.rb +54 -0
- data/lib/rom/sql.rb +1 -1
- data/lib/rom/sql/attribute.rb +17 -18
- data/lib/rom/sql/errors.rb +3 -0
- data/lib/rom/sql/extensions/mysql.rb +1 -1
- data/lib/rom/sql/extensions/mysql/type_builder.rb +28 -0
- data/lib/rom/sql/extensions/postgres.rb +3 -1
- data/lib/rom/sql/extensions/postgres/commands.rb +30 -13
- data/lib/rom/sql/extensions/postgres/{attributes_inferrer.rb → type_builder.rb} +24 -28
- data/lib/rom/sql/extensions/postgres/type_serializer.rb +39 -0
- data/lib/rom/sql/extensions/postgres/types.rb +24 -477
- data/lib/rom/sql/extensions/postgres/types/array.rb +163 -0
- data/lib/rom/sql/extensions/postgres/types/geometric.rb +135 -0
- data/lib/rom/sql/extensions/postgres/types/json.rb +235 -0
- data/lib/rom/sql/extensions/postgres/types/network.rb +15 -0
- data/lib/rom/sql/extensions/sqlite.rb +1 -1
- data/lib/rom/sql/extensions/sqlite/{attributes_inferrer.rb → type_builder.rb} +5 -5
- data/lib/rom/sql/extensions/sqlite/types.rb +8 -3
- data/lib/rom/sql/foreign_key.rb +17 -0
- data/lib/rom/sql/function.rb +86 -8
- data/lib/rom/sql/gateway.rb +26 -26
- data/lib/rom/sql/index.rb +4 -0
- data/lib/rom/sql/migration.rb +3 -3
- data/lib/rom/sql/migration/inline_runner.rb +9 -83
- data/lib/rom/sql/migration/migrator.rb +35 -12
- data/lib/rom/sql/migration/recorder.rb +21 -0
- data/lib/rom/sql/migration/runner.rb +115 -0
- data/lib/rom/sql/migration/schema_diff.rb +108 -53
- data/lib/rom/sql/migration/writer.rb +61 -0
- data/lib/rom/sql/relation.rb +2 -1
- data/lib/rom/sql/relation/reading.rb +63 -3
- data/lib/rom/sql/relation/writing.rb +38 -0
- data/lib/rom/sql/schema.rb +9 -3
- data/lib/rom/sql/schema/attributes_inferrer.rb +3 -119
- data/lib/rom/sql/schema/inferrer.rb +99 -18
- data/lib/rom/sql/schema/type_builder.rb +94 -0
- data/lib/rom/sql/type_dsl.rb +30 -0
- data/lib/rom/sql/type_extensions.rb +11 -6
- data/lib/rom/sql/type_serializer.rb +46 -0
- data/lib/rom/sql/types.rb +12 -0
- data/lib/rom/sql/version.rb +1 -1
- metadata +26 -244
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -17
- data/.rspec +0 -3
- data/.travis.yml +0 -39
- data/.yardopts +0 -2
- data/Gemfile +0 -33
- data/Guardfile +0 -24
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -19
- data/circle.yml +0 -10
- data/lib/rom/sql/extensions/mysql/attributes_inferrer.rb +0 -10
- data/lib/rom/sql/relation/sequel_api.rb +0 -133
- data/log/.gitkeep +0 -0
- data/rom-sql.gemspec +0 -29
- data/spec/extensions/postgres/attribute_spec.rb +0 -217
- data/spec/extensions/postgres/integration_spec.rb +0 -59
- data/spec/extensions/postgres/types_spec.rb +0 -252
- data/spec/extensions/sqlite/types_spec.rb +0 -11
- data/spec/fixtures/migrations/20150403090603_create_carrots.rb +0 -8
- data/spec/integration/associations/many_to_many/custom_fks_spec.rb +0 -76
- data/spec/integration/associations/many_to_many/from_view_spec.rb +0 -88
- data/spec/integration/associations/many_to_many_spec.rb +0 -162
- data/spec/integration/associations/many_to_one/custom_fks_spec.rb +0 -64
- data/spec/integration/associations/many_to_one/from_view_spec.rb +0 -84
- data/spec/integration/associations/many_to_one/self_ref_spec.rb +0 -53
- data/spec/integration/associations/many_to_one_spec.rb +0 -117
- data/spec/integration/associations/one_to_many/custom_fks_spec.rb +0 -54
- data/spec/integration/associations/one_to_many/from_view_spec.rb +0 -57
- data/spec/integration/associations/one_to_many/self_ref_spec.rb +0 -54
- data/spec/integration/associations/one_to_many_spec.rb +0 -86
- data/spec/integration/associations/one_to_one_spec.rb +0 -69
- data/spec/integration/associations/one_to_one_through_spec.rb +0 -92
- data/spec/integration/auto_migrations/errors_spec.rb +0 -31
- data/spec/integration/auto_migrations/indexes_spec.rb +0 -253
- data/spec/integration/auto_migrations/managing_columns_spec.rb +0 -156
- data/spec/integration/auto_migrations/postgres/column_types_spec.rb +0 -63
- data/spec/integration/combine_with_spec.rb +0 -43
- data/spec/integration/commands/create_spec.rb +0 -304
- data/spec/integration/commands/delete_spec.rb +0 -84
- data/spec/integration/commands/update_spec.rb +0 -90
- data/spec/integration/commands/upsert_spec.rb +0 -83
- data/spec/integration/gateway_spec.rb +0 -107
- data/spec/integration/migration_spec.rb +0 -55
- data/spec/integration/plugins/associates/many_to_many_spec.rb +0 -69
- data/spec/integration/plugins/associates_spec.rb +0 -250
- data/spec/integration/plugins/auto_restrictions_spec.rb +0 -74
- data/spec/integration/relation_schema_spec.rb +0 -271
- data/spec/integration/schema/call_spec.rb +0 -24
- data/spec/integration/schema/inferrer/mysql_spec.rb +0 -45
- data/spec/integration/schema/inferrer/postgres_spec.rb +0 -203
- data/spec/integration/schema/inferrer/sqlite_spec.rb +0 -37
- data/spec/integration/schema/inferrer_spec.rb +0 -390
- data/spec/integration/schema/prefix_spec.rb +0 -16
- data/spec/integration/schema/qualified_spec.rb +0 -16
- data/spec/integration/schema/rename_spec.rb +0 -21
- data/spec/integration/schema/view_spec.rb +0 -29
- data/spec/integration/sequel_api_spec.rb +0 -36
- data/spec/integration/setup_spec.rb +0 -26
- data/spec/integration/support/active_support_notifications_spec.rb +0 -24
- data/spec/integration/support/rails_log_subscriber_spec.rb +0 -30
- data/spec/integration/wrap_spec.rb +0 -91
- data/spec/shared/accounts.rb +0 -48
- data/spec/shared/database_setup.rb +0 -70
- data/spec/shared/notes.rb +0 -23
- data/spec/shared/posts.rb +0 -34
- data/spec/shared/puppies.rb +0 -15
- data/spec/shared/relations.rb +0 -8
- data/spec/shared/users.rb +0 -32
- data/spec/shared/users_and_tasks.rb +0 -50
- data/spec/spec_helper.rb +0 -122
- data/spec/support/env_helper.rb +0 -25
- data/spec/support/helpers.rb +0 -24
- data/spec/support/oracle/create_users.sql +0 -7
- data/spec/support/oracle/set_sys_passwords.sql +0 -2
- data/spec/support/test_configuration.rb +0 -16
- data/spec/unit/attribute_spec.rb +0 -104
- data/spec/unit/function_spec.rb +0 -48
- data/spec/unit/gateway_spec.rb +0 -70
- data/spec/unit/logger_spec.rb +0 -14
- data/spec/unit/migration_tasks_spec.rb +0 -111
- data/spec/unit/migrator_spec.rb +0 -25
- data/spec/unit/order_dsl_spec.rb +0 -43
- data/spec/unit/plugin/associates_spec.rb +0 -94
- data/spec/unit/plugin/pagination_spec.rb +0 -91
- data/spec/unit/plugin/timestamp_spec.rb +0 -117
- data/spec/unit/projection_dsl_spec.rb +0 -110
- data/spec/unit/relation/assoc_spec.rb +0 -87
- data/spec/unit/relation/associations_spec.rb +0 -27
- data/spec/unit/relation/avg_spec.rb +0 -11
- data/spec/unit/relation/by_pk_spec.rb +0 -62
- data/spec/unit/relation/dataset_spec.rb +0 -50
- data/spec/unit/relation/distinct_spec.rb +0 -15
- data/spec/unit/relation/exclude_spec.rb +0 -11
- data/spec/unit/relation/exist_predicate_spec.rb +0 -25
- data/spec/unit/relation/exists_spec.rb +0 -18
- data/spec/unit/relation/fetch_spec.rb +0 -21
- data/spec/unit/relation/group_spec.rb +0 -61
- data/spec/unit/relation/having_spec.rb +0 -22
- data/spec/unit/relation/inner_join_spec.rb +0 -158
- data/spec/unit/relation/inspect_spec.rb +0 -11
- data/spec/unit/relation/instrument_spec.rb +0 -45
- data/spec/unit/relation/invert_spec.rb +0 -11
- data/spec/unit/relation/left_join_spec.rb +0 -55
- data/spec/unit/relation/lock_spec.rb +0 -93
- data/spec/unit/relation/map_spec.rb +0 -16
- data/spec/unit/relation/max_spec.rb +0 -11
- data/spec/unit/relation/min_spec.rb +0 -11
- data/spec/unit/relation/order_spec.rb +0 -51
- data/spec/unit/relation/pluck_spec.rb +0 -11
- data/spec/unit/relation/prefix_spec.rb +0 -29
- data/spec/unit/relation/primary_key_spec.rb +0 -27
- data/spec/unit/relation/project_spec.rb +0 -24
- data/spec/unit/relation/qualified_columns_spec.rb +0 -30
- data/spec/unit/relation/qualified_spec.rb +0 -25
- data/spec/unit/relation/read_spec.rb +0 -25
- data/spec/unit/relation/rename_spec.rb +0 -23
- data/spec/unit/relation/right_join_spec.rb +0 -57
- data/spec/unit/relation/select_append_spec.rb +0 -21
- data/spec/unit/relation/select_spec.rb +0 -40
- data/spec/unit/relation/sum_spec.rb +0 -11
- data/spec/unit/relation/union_spec.rb +0 -19
- data/spec/unit/relation/unique_predicate_spec.rb +0 -18
- data/spec/unit/relation/where_spec.rb +0 -133
- data/spec/unit/restriction_dsl_spec.rb +0 -34
- data/spec/unit/schema_spec.rb +0 -25
- data/spec/unit/types_spec.rb +0 -65
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
require 'sequel/core'
|
|
2
|
+
|
|
3
|
+
Sequel.extension(*%i(pg_array pg_array_ops))
|
|
4
|
+
|
|
5
|
+
module ROM
|
|
6
|
+
module SQL
|
|
7
|
+
module Postgres
|
|
8
|
+
module Types
|
|
9
|
+
Array = SQL::Types::Array
|
|
10
|
+
|
|
11
|
+
ArrayRead = Array.constructor { |v| v.respond_to?(:to_ary) ? v.to_ary : v }
|
|
12
|
+
|
|
13
|
+
constructor = -> type { -> value { Sequel.pg_array(value, type) } }
|
|
14
|
+
|
|
15
|
+
@array_types = ::Hash.new do |hash, type|
|
|
16
|
+
name = "#{ type }[]"
|
|
17
|
+
array_type = Type(name, Array.constructor(constructor.(type))).
|
|
18
|
+
meta(type: type, read: ArrayRead)
|
|
19
|
+
TypeExtensions.register(array_type) { include ArrayMethods }
|
|
20
|
+
hash[type] = array_type
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.Array(db_type)
|
|
24
|
+
@array_types[db_type]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @!parse
|
|
28
|
+
# class SQL::Attribute
|
|
29
|
+
# # @!method contain(other)
|
|
30
|
+
# # Check whether the array includes another array
|
|
31
|
+
# # Translates to the @> operator
|
|
32
|
+
# #
|
|
33
|
+
# # @param [Array] other
|
|
34
|
+
# #
|
|
35
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
36
|
+
# #
|
|
37
|
+
# # @api public
|
|
38
|
+
#
|
|
39
|
+
# # @!method get(idx)
|
|
40
|
+
# # Get element by index (PG uses 1-based indexing)
|
|
41
|
+
# #
|
|
42
|
+
# # @param [Integer] idx
|
|
43
|
+
# #
|
|
44
|
+
# # @return [SQL::Attribute]
|
|
45
|
+
# #
|
|
46
|
+
# # @api public
|
|
47
|
+
#
|
|
48
|
+
# # @!method any(value)
|
|
49
|
+
# # Check whether the array includes a value
|
|
50
|
+
# # Translates to the ANY operator
|
|
51
|
+
# #
|
|
52
|
+
# # @param [Object] value
|
|
53
|
+
# #
|
|
54
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
55
|
+
# #
|
|
56
|
+
# # @api public
|
|
57
|
+
#
|
|
58
|
+
# # @!method contained_by(other)
|
|
59
|
+
# # Check whether the array is contained by another array
|
|
60
|
+
# # Translates to the <@ operator
|
|
61
|
+
# #
|
|
62
|
+
# # @param [Array] other
|
|
63
|
+
# #
|
|
64
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
65
|
+
# #
|
|
66
|
+
# # @api public
|
|
67
|
+
#
|
|
68
|
+
# # @!method length
|
|
69
|
+
# # Return array size
|
|
70
|
+
# #
|
|
71
|
+
# # @return [SQL::Attribute<Types::Int>]
|
|
72
|
+
# #
|
|
73
|
+
# # @api public
|
|
74
|
+
#
|
|
75
|
+
# # @!method overlaps(other)
|
|
76
|
+
# # Check whether the arrays have common values
|
|
77
|
+
# # Translates to &&
|
|
78
|
+
# #
|
|
79
|
+
# # @param [Array] other
|
|
80
|
+
# #
|
|
81
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
82
|
+
# #
|
|
83
|
+
# # @api public
|
|
84
|
+
#
|
|
85
|
+
# # @!method remove_value(value)
|
|
86
|
+
# # Remove elements by value
|
|
87
|
+
# #
|
|
88
|
+
# # @param [Object] value
|
|
89
|
+
# #
|
|
90
|
+
# # @return [SQL::Attribute<Types::PG::Array>]
|
|
91
|
+
# #
|
|
92
|
+
# # @api public
|
|
93
|
+
#
|
|
94
|
+
# # @!method join(delimiter, null_repr)
|
|
95
|
+
# # Convert the array to a string by joining
|
|
96
|
+
# # values with a delimiter (empty stirng by default)
|
|
97
|
+
# # and optional filler for NULL values
|
|
98
|
+
# # Translates to an `array_to_string` call
|
|
99
|
+
# #
|
|
100
|
+
# # @param [Object] delimiter
|
|
101
|
+
# # @param [Object] null
|
|
102
|
+
# #
|
|
103
|
+
# # @return [SQL::Attribute<Types::String>]
|
|
104
|
+
# #
|
|
105
|
+
# # @api public
|
|
106
|
+
#
|
|
107
|
+
# # @!method +(other)
|
|
108
|
+
# # Concatenate two arrays
|
|
109
|
+
# #
|
|
110
|
+
# # @param [Array] other
|
|
111
|
+
# #
|
|
112
|
+
# # @return [SQL::Attribute<Types::PG::Array>]
|
|
113
|
+
# #
|
|
114
|
+
# # @api public
|
|
115
|
+
# end
|
|
116
|
+
module ArrayMethods
|
|
117
|
+
def contain(type, expr, other)
|
|
118
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: expr.pg_array.contains(type[other]))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def get(type, expr, idx)
|
|
122
|
+
Attribute[type].meta(sql_expr: expr.pg_array[idx])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def any(type, expr, value)
|
|
126
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: { value => expr.pg_array.any })
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def contained_by(type, expr, other)
|
|
130
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: expr.pg_array.contained_by(type[other]))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def length(type, expr)
|
|
134
|
+
Attribute[SQL::Types::Int].meta(sql_expr: expr.pg_array.length)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def overlaps(type, expr, other_array)
|
|
138
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: expr.pg_array.overlaps(type[other_array]))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def remove_value(type, expr, value)
|
|
142
|
+
Attribute[type].meta(sql_expr: expr.pg_array.remove(cast(type, value)))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def join(type, expr, delimiter = '', null = nil)
|
|
146
|
+
Attribute[SQL::Types::String].meta(sql_expr: expr.pg_array.join(delimiter, null))
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def +(type, expr, other)
|
|
150
|
+
Attribute[type].meta(sql_expr: expr.pg_array.concat(other))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
def cast(type, value)
|
|
156
|
+
db_type = type.optional? ? type.right.meta[:type] : type.meta[:type]
|
|
157
|
+
Sequel.cast(value, db_type)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
module ROM
|
|
2
|
+
module SQL
|
|
3
|
+
module Postgres
|
|
4
|
+
module Values
|
|
5
|
+
Point = ::Struct.new(:x, :y)
|
|
6
|
+
|
|
7
|
+
Line = ::Struct.new(:a, :b, :c)
|
|
8
|
+
|
|
9
|
+
Circle = ::Struct.new(:center, :radius)
|
|
10
|
+
|
|
11
|
+
Box = ::Struct.new(:upper_right, :lower_left)
|
|
12
|
+
|
|
13
|
+
LineSegment = ::Struct.new(:begin, :end)
|
|
14
|
+
|
|
15
|
+
Path = ::Struct.new(:points, :type) do
|
|
16
|
+
def open?
|
|
17
|
+
type == :open
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def closed?
|
|
21
|
+
type == :closed
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_a
|
|
25
|
+
points
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @api public
|
|
31
|
+
module Types
|
|
32
|
+
# The list of geometric data types supported by PostgreSQL
|
|
33
|
+
# @see https://www.postgresql.org/docs/current/static/datatype-geometric.html
|
|
34
|
+
|
|
35
|
+
Point = SQL::Types.define(Values::Point) do
|
|
36
|
+
input do |point|
|
|
37
|
+
"(#{ point.x },#{ point.y })"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
output do |point|
|
|
41
|
+
x, y = point.to_s[1...-1].split(',', 2)
|
|
42
|
+
Values::Point.new(Float(x), Float(y))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Line = SQL::Types.define(Values::Line) do
|
|
47
|
+
input do |line|
|
|
48
|
+
"{#{ line.a },#{ line.b },#{line.c}}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
output do |line|
|
|
52
|
+
a, b, c = line.to_s[1..-2].split(',', 3)
|
|
53
|
+
Values::Line.new(Float(a), Float(b), Float(c))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Circle = SQL::Types.define(Values::Circle) do
|
|
58
|
+
input do |circle|
|
|
59
|
+
"<(#{ circle.center.x },#{ circle.center.y }),#{ circle.radius }>"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
output do |circle|
|
|
63
|
+
x, y, r = circle.to_s.tr('()<>', '').split(',', 3)
|
|
64
|
+
center = Values::Point.new(Float(x), Float(y))
|
|
65
|
+
Values::Circle.new(center, Float(r))
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
Box = SQL::Types.define(Values::Box) do
|
|
70
|
+
input do |box|
|
|
71
|
+
"((#{ box.upper_right.x },#{ box.upper_right.y }),"\
|
|
72
|
+
"(#{ box.lower_left.x },#{ box.lower_left.y }))"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
output do |box|
|
|
76
|
+
x_right, y_right, x_left, y_left = box.to_s.tr('()', '').split(',', 4)
|
|
77
|
+
upper_right = Values::Point.new(Float(x_right), Float(y_right))
|
|
78
|
+
lower_left = Values::Point.new(Float(x_left), Float(y_left))
|
|
79
|
+
Values::Box.new(upper_right, lower_left)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
LineSegment = SQL::Types.define(Values::LineSegment) do
|
|
84
|
+
input do |segment|
|
|
85
|
+
"[(#{ segment.begin.x },#{ segment.begin.y }),"\
|
|
86
|
+
"(#{ segment.end.x },#{ segment.end.y })]"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
output do |segment|
|
|
90
|
+
x_begin, y_begin, x_end, y_end = segment.to_s.tr('()[]', '').split(',', 4)
|
|
91
|
+
point_begin = Values::Point.new(Float(x_begin), Float(y_begin))
|
|
92
|
+
point_end = Values::Point.new(Float(x_end), Float(y_end))
|
|
93
|
+
Values::LineSegment.new(point_begin, point_end)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Polygon = SQL::Types.define(::Array) do
|
|
98
|
+
input do |points|
|
|
99
|
+
points_joined = points.map { |p| "(#{ p.x },#{ p.y })" }.join(',')
|
|
100
|
+
"(#{ points_joined })"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
output do |polygon|
|
|
104
|
+
coordinates = polygon.to_s.tr('()', '').split(',').each_slice(2)
|
|
105
|
+
coordinates.map { |x, y| Values::Point.new(Float(x), Float(y)) }
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
Path = SQL::Types.define(Values::Path) do
|
|
110
|
+
input do |path|
|
|
111
|
+
points_joined = path.to_a.map { |p| "(#{ p.x },#{ p.y })" }.join(',')
|
|
112
|
+
|
|
113
|
+
if path.open?
|
|
114
|
+
"[#{ points_joined }]"
|
|
115
|
+
else
|
|
116
|
+
"(#{ points_joined })"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
output do |path|
|
|
121
|
+
open = path.to_s.start_with?('[') && path.to_s.end_with?(']')
|
|
122
|
+
coordinates = path.to_s.tr('()[]', '').split(',').each_slice(2)
|
|
123
|
+
points = coordinates.map { |x, y| Values::Point.new(Float(x), Float(y)) }
|
|
124
|
+
|
|
125
|
+
if open
|
|
126
|
+
Values::Path.new(points, :open)
|
|
127
|
+
else
|
|
128
|
+
Values::Path.new(points, :closed)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
require 'sequel/core'
|
|
2
|
+
|
|
3
|
+
Sequel.extension(*%i(pg_json pg_json_ops))
|
|
4
|
+
|
|
5
|
+
module ROM
|
|
6
|
+
module SQL
|
|
7
|
+
module Postgres
|
|
8
|
+
module Types
|
|
9
|
+
JSONRead = (SQL::Types::Array | SQL::Types::Hash).constructor do |value|
|
|
10
|
+
if value.respond_to?(:to_hash)
|
|
11
|
+
value.to_hash
|
|
12
|
+
elsif value.respond_to?(:to_ary)
|
|
13
|
+
value.to_ary
|
|
14
|
+
else
|
|
15
|
+
value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
JSON = Type('json') do
|
|
20
|
+
(SQL::Types::Array | SQL::Types::Hash)
|
|
21
|
+
.constructor(Sequel.method(:pg_json))
|
|
22
|
+
.meta(read: JSONRead)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
JSONB = Type('jsonb') do
|
|
26
|
+
(SQL::Types::Array | SQL::Types::Hash)
|
|
27
|
+
.constructor(Sequel.method(:pg_jsonb))
|
|
28
|
+
.meta(read: JSONRead)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @!parse
|
|
32
|
+
# class SQL::Attribute
|
|
33
|
+
# # @!method contain(value)
|
|
34
|
+
# # Check whether the JSON value includes a json value
|
|
35
|
+
# # Translates to the @> operator
|
|
36
|
+
# #
|
|
37
|
+
# # @example
|
|
38
|
+
# # people.where { fields.contain(gender: 'Female') }
|
|
39
|
+
# # people.where(people[:fields].contain([name: 'age']))
|
|
40
|
+
# # people.select { fields.contain(gender: 'Female').as(:is_female) }
|
|
41
|
+
# #
|
|
42
|
+
# # @param [Hash,Array,Object] value
|
|
43
|
+
# #
|
|
44
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
45
|
+
# #
|
|
46
|
+
# # @api public
|
|
47
|
+
#
|
|
48
|
+
# # @!method contained_by(value)
|
|
49
|
+
# # Check whether the JSON value is contained by other value
|
|
50
|
+
# # Translates to the <@ operator
|
|
51
|
+
# #
|
|
52
|
+
# # @example
|
|
53
|
+
# # people.where { custom_values.contained_by(age: 25, foo: 'bar') }
|
|
54
|
+
# #
|
|
55
|
+
# # @param [Hash,Array] value
|
|
56
|
+
# #
|
|
57
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
58
|
+
# #
|
|
59
|
+
# # @api public
|
|
60
|
+
#
|
|
61
|
+
# # @!method get(*path)
|
|
62
|
+
# # Extract the JSON value using at the specified path
|
|
63
|
+
# # Translates to -> or #> depending on the number of arguments
|
|
64
|
+
# #
|
|
65
|
+
# # @example
|
|
66
|
+
# # people.select { data.get('age').as(:person_age) }
|
|
67
|
+
# # people.select { fields.get(0).as(:first_field) }
|
|
68
|
+
# # people.select { fields.get('0', 'value').as(:first_field_value) }
|
|
69
|
+
# #
|
|
70
|
+
# # @param [Array<Integer>,Array<String>] path Path to extract
|
|
71
|
+
# #
|
|
72
|
+
# # @return [SQL::Attribute<Types::PG::JSON>,SQL::Attribute<Types::PG::JSONB>]
|
|
73
|
+
# #
|
|
74
|
+
# # @api public
|
|
75
|
+
#
|
|
76
|
+
# # @!method get_text(*path)
|
|
77
|
+
# # Extract the JSON value as text using at the specified path
|
|
78
|
+
# # Translates to ->> or #>> depending on the number of arguments
|
|
79
|
+
# #
|
|
80
|
+
# # @example
|
|
81
|
+
# # people.select { data.get('age').as(:person_age) }
|
|
82
|
+
# # people.select { fields.get(0).as(:first_field) }
|
|
83
|
+
# # people.select { fields.get('0', 'value').as(:first_field_value) }
|
|
84
|
+
# #
|
|
85
|
+
# # @param [Array<Integer>,Array<String>] path Path to extract
|
|
86
|
+
# #
|
|
87
|
+
# # @return [SQL::Attribute<Types::String>]
|
|
88
|
+
# #
|
|
89
|
+
# # @api public
|
|
90
|
+
#
|
|
91
|
+
# # @!method has_key(key)
|
|
92
|
+
# # Does the JSON value have the specified top-level key
|
|
93
|
+
# # Translates to ?
|
|
94
|
+
# #
|
|
95
|
+
# # @example
|
|
96
|
+
# # people.where { data.has_key('age') }
|
|
97
|
+
# #
|
|
98
|
+
# # @param [String] key
|
|
99
|
+
# #
|
|
100
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
101
|
+
# #
|
|
102
|
+
# # @api public
|
|
103
|
+
#
|
|
104
|
+
# # @!method has_any_key(*keys)
|
|
105
|
+
# # Does the JSON value have any of the specified top-level keys
|
|
106
|
+
# # Translates to ?|
|
|
107
|
+
# #
|
|
108
|
+
# # @example
|
|
109
|
+
# # people.where { data.has_any_key('age', 'height') }
|
|
110
|
+
# #
|
|
111
|
+
# # @param [Array<String>] keys
|
|
112
|
+
# #
|
|
113
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
114
|
+
# #
|
|
115
|
+
# # @api public
|
|
116
|
+
#
|
|
117
|
+
# # @!method has_all_keys(*keys)
|
|
118
|
+
# # Does the JSON value have all the specified top-level keys
|
|
119
|
+
# # Translates to ?&
|
|
120
|
+
# #
|
|
121
|
+
# # @example
|
|
122
|
+
# # people.where { data.has_all_keys('age', 'height') }
|
|
123
|
+
# #
|
|
124
|
+
# # @param [Array<String>] keys
|
|
125
|
+
# #
|
|
126
|
+
# # @return [SQL::Attribute<Types::Bool>]
|
|
127
|
+
# #
|
|
128
|
+
# # @api public
|
|
129
|
+
#
|
|
130
|
+
# # @!method merge(value)
|
|
131
|
+
# # Concatenate two JSON values
|
|
132
|
+
# # Translates to ||
|
|
133
|
+
# #
|
|
134
|
+
# # @example
|
|
135
|
+
# # people.select { data.merge(fetched_at: Time.now).as(:data) }
|
|
136
|
+
# # people.select { (fields + [name: 'height', value: 165]).as(:fields) }
|
|
137
|
+
# #
|
|
138
|
+
# # @param [Hash,Array] value
|
|
139
|
+
# #
|
|
140
|
+
# # @return [SQL::Attribute<Types::PG::JSONB>]
|
|
141
|
+
# #
|
|
142
|
+
# # @api public
|
|
143
|
+
#
|
|
144
|
+
# # @!method +(value)
|
|
145
|
+
# # An alias for SQL::Attribute<JSONB>#merge
|
|
146
|
+
# #
|
|
147
|
+
# # @api public
|
|
148
|
+
#
|
|
149
|
+
# # @!method delete(*path)
|
|
150
|
+
# # Deletes the specified value by key, index, or path
|
|
151
|
+
# # Translates to - or #- depending on the number of arguments
|
|
152
|
+
# #
|
|
153
|
+
# # @example
|
|
154
|
+
# # people.select { data.delete('age').as(:data_without_age) }
|
|
155
|
+
# # people.select { fields.delete(0).as(:fields_without_first) }
|
|
156
|
+
# # people.select { fields.delete(-1).as(:fields_without_last) }
|
|
157
|
+
# # people.select { data.delete('deeply', 'nested', 'value').as(:data) }
|
|
158
|
+
# # people.select { fields.delete('0', 'name').as(:data) }
|
|
159
|
+
# #
|
|
160
|
+
# # @param [Array<String>] path
|
|
161
|
+
# #
|
|
162
|
+
# # @return [SQL::Attribute<Types::PG::JSONB>]
|
|
163
|
+
# #
|
|
164
|
+
# # @api public
|
|
165
|
+
# end
|
|
166
|
+
module JSONMethods
|
|
167
|
+
def self.[](type, wrap)
|
|
168
|
+
parent = self
|
|
169
|
+
Module.new do
|
|
170
|
+
include parent
|
|
171
|
+
define_method(:json_type) { type }
|
|
172
|
+
define_method(:wrap, wrap)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def get(type, expr, *path)
|
|
177
|
+
Attribute[json_type].meta(sql_expr: wrap(expr)[path_args(path)])
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def get_text(type, expr, *path)
|
|
181
|
+
Attribute[SQL::Types::String].meta(sql_expr: wrap(expr).get_text(path_args(path)))
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
def path_args(path)
|
|
187
|
+
case path.size
|
|
188
|
+
when 0 then raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
|
|
189
|
+
when 1 then path[0]
|
|
190
|
+
else path
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
TypeExtensions.register(JSON) do
|
|
196
|
+
include JSONMethods[JSON, :pg_json.to_proc]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
TypeExtensions.register(JSONB) do
|
|
200
|
+
include JSONMethods[JSONB, :pg_jsonb.to_proc]
|
|
201
|
+
|
|
202
|
+
def contain(type, expr, value)
|
|
203
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: wrap(expr).contains(value))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def contained_by(type, expr, value)
|
|
207
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: wrap(expr).contained_by(value))
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def has_key(type, expr, key)
|
|
211
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: wrap(expr).has_key?(key))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def has_any_key(type, expr, *keys)
|
|
215
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: wrap(expr).contain_any(keys))
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def has_all_keys(type, expr, *keys)
|
|
219
|
+
Attribute[SQL::Types::Bool].meta(sql_expr: wrap(expr).contain_all(keys))
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def merge(type, expr, value)
|
|
223
|
+
Attribute[JSONB].meta(sql_expr: wrap(expr).concat(value))
|
|
224
|
+
end
|
|
225
|
+
alias_method :+, :merge
|
|
226
|
+
|
|
227
|
+
def delete(type, expr, *path)
|
|
228
|
+
sql_expr = path.size == 1 ? wrap(expr) - path : wrap(expr).delete_path(path)
|
|
229
|
+
Attribute[JSONB].meta(sql_expr: sql_expr)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|