rooq 1.0.0
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 +7 -0
- data/.tool-versions +1 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +33 -0
- data/CLAUDE.md +54 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +116 -0
- data/LICENSE +661 -0
- data/README.md +98 -0
- data/Rakefile +130 -0
- data/USAGE.md +850 -0
- data/exe/rooq +7 -0
- data/lib/rooq/adapters/postgresql.rb +117 -0
- data/lib/rooq/adapters.rb +3 -0
- data/lib/rooq/cli.rb +230 -0
- data/lib/rooq/condition.rb +104 -0
- data/lib/rooq/configuration.rb +56 -0
- data/lib/rooq/connection.rb +131 -0
- data/lib/rooq/context.rb +141 -0
- data/lib/rooq/dialect/base.rb +27 -0
- data/lib/rooq/dialect/postgresql.rb +531 -0
- data/lib/rooq/dialect.rb +9 -0
- data/lib/rooq/dsl/delete_query.rb +37 -0
- data/lib/rooq/dsl/insert_query.rb +43 -0
- data/lib/rooq/dsl/select_query.rb +301 -0
- data/lib/rooq/dsl/update_query.rb +44 -0
- data/lib/rooq/dsl.rb +28 -0
- data/lib/rooq/executor.rb +65 -0
- data/lib/rooq/expression.rb +494 -0
- data/lib/rooq/field.rb +71 -0
- data/lib/rooq/generator/code_generator.rb +91 -0
- data/lib/rooq/generator/introspector.rb +265 -0
- data/lib/rooq/generator.rb +9 -0
- data/lib/rooq/parameter_converter.rb +98 -0
- data/lib/rooq/query_validator.rb +176 -0
- data/lib/rooq/result.rb +248 -0
- data/lib/rooq/schema_validator.rb +56 -0
- data/lib/rooq/table.rb +69 -0
- data/lib/rooq/version.rb +5 -0
- data/lib/rooq.rb +25 -0
- data/rooq.gemspec +35 -0
- data/sorbet/config +4 -0
- metadata +115 -0
data/lib/rooq/result.rb
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "time"
|
|
5
|
+
require "date"
|
|
6
|
+
|
|
7
|
+
module Rooq
|
|
8
|
+
# Result wraps a database result set and provides:
|
|
9
|
+
# - Symbol keys instead of string keys
|
|
10
|
+
# - Automatic type coercion for JSON, JSONB, ARRAY, timestamps, dates
|
|
11
|
+
# - Enumerable interface
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# result = ctx.fetch_all(query)
|
|
15
|
+
# result.each do |row|
|
|
16
|
+
# puts row[:title] # Symbol key access
|
|
17
|
+
# puts row[:tags].first # Array is parsed
|
|
18
|
+
# puts row[:metadata] # JSON is parsed to Hash
|
|
19
|
+
# end
|
|
20
|
+
class Result
|
|
21
|
+
include Enumerable
|
|
22
|
+
|
|
23
|
+
attr_reader :raw_result
|
|
24
|
+
|
|
25
|
+
# @param raw_result [PG::Result] the raw database result
|
|
26
|
+
# @param coercer [TypeCoercer] optional custom type coercer
|
|
27
|
+
def initialize(raw_result, coercer: TypeCoercer.new)
|
|
28
|
+
@raw_result = raw_result
|
|
29
|
+
@coercer = coercer
|
|
30
|
+
@field_info = build_field_info
|
|
31
|
+
@rows = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @yield [Hash] each row with symbol keys
|
|
35
|
+
def each(&block)
|
|
36
|
+
rows.each(&block)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Hash, nil] the first row or nil
|
|
40
|
+
def first
|
|
41
|
+
rows.first
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Array<Hash>] all rows
|
|
45
|
+
def to_a
|
|
46
|
+
rows.dup
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Boolean] true if no rows
|
|
50
|
+
def empty?
|
|
51
|
+
size.zero?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [Integer] number of rows
|
|
55
|
+
def size
|
|
56
|
+
@raw_result.ntuples
|
|
57
|
+
end
|
|
58
|
+
alias length size
|
|
59
|
+
alias count size
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def rows
|
|
64
|
+
@rows ||= build_rows
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def build_rows
|
|
68
|
+
result = []
|
|
69
|
+
@raw_result.ntuples.times do |row_index|
|
|
70
|
+
result << build_row(row_index)
|
|
71
|
+
end
|
|
72
|
+
result
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def build_row(row_index)
|
|
76
|
+
row = {}
|
|
77
|
+
@field_info.each_with_index do |(name, oid), col_index|
|
|
78
|
+
value = @raw_result.getvalue(row_index, col_index)
|
|
79
|
+
row[name] = @coercer.coerce(value, oid)
|
|
80
|
+
end
|
|
81
|
+
row
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def build_field_info
|
|
85
|
+
info = []
|
|
86
|
+
@raw_result.nfields.times do |i|
|
|
87
|
+
name = @raw_result.fname(i).to_sym
|
|
88
|
+
oid = @raw_result.ftype(i)
|
|
89
|
+
info << [name, oid]
|
|
90
|
+
end
|
|
91
|
+
info
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# TypeCoercer converts PostgreSQL values to Ruby types based on OID.
|
|
96
|
+
class TypeCoercer
|
|
97
|
+
# PostgreSQL type OIDs
|
|
98
|
+
OID_BOOL = 16
|
|
99
|
+
OID_INT2 = 21
|
|
100
|
+
OID_INT4 = 23
|
|
101
|
+
OID_INT8 = 20
|
|
102
|
+
OID_FLOAT4 = 700
|
|
103
|
+
OID_FLOAT8 = 701
|
|
104
|
+
OID_NUMERIC = 1700
|
|
105
|
+
OID_TEXT = 25
|
|
106
|
+
OID_VARCHAR = 1043
|
|
107
|
+
OID_DATE = 1082
|
|
108
|
+
OID_TIMESTAMP = 1114
|
|
109
|
+
OID_TIMESTAMPTZ = 1184
|
|
110
|
+
OID_JSON = 114
|
|
111
|
+
OID_JSONB = 3802
|
|
112
|
+
OID_INT4_ARRAY = 1007
|
|
113
|
+
OID_INT8_ARRAY = 1016
|
|
114
|
+
OID_TEXT_ARRAY = 1009
|
|
115
|
+
OID_VARCHAR_ARRAY = 1015
|
|
116
|
+
OID_FLOAT4_ARRAY = 1021
|
|
117
|
+
OID_FLOAT8_ARRAY = 1022
|
|
118
|
+
OID_BOOL_ARRAY = 1000
|
|
119
|
+
OID_UUID = 2950
|
|
120
|
+
|
|
121
|
+
# Coerce a value based on its PostgreSQL OID.
|
|
122
|
+
# @param value [String, nil] the raw value from the database
|
|
123
|
+
# @param oid [Integer] the PostgreSQL type OID
|
|
124
|
+
# @return [Object] the coerced value
|
|
125
|
+
def coerce(value, oid)
|
|
126
|
+
return nil if value.nil?
|
|
127
|
+
|
|
128
|
+
case oid
|
|
129
|
+
when OID_JSON, OID_JSONB
|
|
130
|
+
coerce_json(value)
|
|
131
|
+
when OID_INT4_ARRAY, OID_INT8_ARRAY
|
|
132
|
+
coerce_int_array(value)
|
|
133
|
+
when OID_TEXT_ARRAY, OID_VARCHAR_ARRAY
|
|
134
|
+
coerce_text_array(value)
|
|
135
|
+
when OID_FLOAT4_ARRAY, OID_FLOAT8_ARRAY
|
|
136
|
+
coerce_float_array(value)
|
|
137
|
+
when OID_BOOL_ARRAY
|
|
138
|
+
coerce_bool_array(value)
|
|
139
|
+
when OID_TIMESTAMP, OID_TIMESTAMPTZ
|
|
140
|
+
coerce_timestamp(value)
|
|
141
|
+
when OID_DATE
|
|
142
|
+
coerce_date(value)
|
|
143
|
+
when OID_BOOL
|
|
144
|
+
coerce_bool(value)
|
|
145
|
+
when OID_INT2, OID_INT4, OID_INT8
|
|
146
|
+
coerce_integer(value)
|
|
147
|
+
when OID_FLOAT4, OID_FLOAT8, OID_NUMERIC
|
|
148
|
+
coerce_float(value)
|
|
149
|
+
else
|
|
150
|
+
value
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def coerce_json(value)
|
|
157
|
+
JSON.parse(value)
|
|
158
|
+
rescue JSON::ParserError
|
|
159
|
+
value
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def coerce_int_array(value)
|
|
163
|
+
parse_pg_array(value).map { |v| v&.to_i }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def coerce_text_array(value)
|
|
167
|
+
parse_pg_array(value)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def coerce_float_array(value)
|
|
171
|
+
parse_pg_array(value).map { |v| v&.to_f }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def coerce_bool_array(value)
|
|
175
|
+
parse_pg_array(value).map { |v| coerce_bool(v) }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def coerce_timestamp(value)
|
|
179
|
+
Time.parse(value)
|
|
180
|
+
rescue ArgumentError
|
|
181
|
+
value
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def coerce_date(value)
|
|
185
|
+
Date.parse(value)
|
|
186
|
+
rescue ArgumentError
|
|
187
|
+
value
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def coerce_bool(value)
|
|
191
|
+
return nil if value.nil?
|
|
192
|
+
return value if value == true || value == false
|
|
193
|
+
|
|
194
|
+
value == "t" || value == "true" || value == "1"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def coerce_integer(value)
|
|
198
|
+
return value if value.is_a?(Integer)
|
|
199
|
+
|
|
200
|
+
value.to_i
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def coerce_float(value)
|
|
204
|
+
return value if value.is_a?(Float)
|
|
205
|
+
|
|
206
|
+
value.to_f
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Parse PostgreSQL array literal format: {val1,val2,val3}
|
|
210
|
+
def parse_pg_array(value)
|
|
211
|
+
return [] if value == "{}"
|
|
212
|
+
|
|
213
|
+
# Remove outer braces
|
|
214
|
+
inner = value[1..-2]
|
|
215
|
+
return [] if inner.nil? || inner.empty?
|
|
216
|
+
|
|
217
|
+
elements = []
|
|
218
|
+
current = ""
|
|
219
|
+
in_quotes = false
|
|
220
|
+
escape_next = false
|
|
221
|
+
|
|
222
|
+
inner.each_char do |char|
|
|
223
|
+
if escape_next
|
|
224
|
+
current += char
|
|
225
|
+
escape_next = false
|
|
226
|
+
elsif char == '\\'
|
|
227
|
+
escape_next = true
|
|
228
|
+
elsif char == '"'
|
|
229
|
+
in_quotes = !in_quotes
|
|
230
|
+
elsif char == ',' && !in_quotes
|
|
231
|
+
elements << parse_array_element(current)
|
|
232
|
+
current = ""
|
|
233
|
+
else
|
|
234
|
+
current += char
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
elements << parse_array_element(current) unless current.empty?
|
|
239
|
+
elements
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def parse_array_element(str)
|
|
243
|
+
return nil if str == "NULL"
|
|
244
|
+
|
|
245
|
+
str
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rooq
|
|
4
|
+
class SchemaValidator
|
|
5
|
+
def initialize(connection, schema: "public")
|
|
6
|
+
@introspector = Generator::Introspector.new(connection)
|
|
7
|
+
@schema = schema
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def validate(tables)
|
|
11
|
+
errors = []
|
|
12
|
+
|
|
13
|
+
tables.each do |table|
|
|
14
|
+
table_errors = validate_table(table)
|
|
15
|
+
errors.concat(table_errors)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
raise SchemaValidationError.new(errors) unless errors.empty?
|
|
19
|
+
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def validate_table(table)
|
|
26
|
+
errors = []
|
|
27
|
+
|
|
28
|
+
db_tables = @introspector.introspect_tables(schema: @schema)
|
|
29
|
+
|
|
30
|
+
unless db_tables.include?(table.name.to_s)
|
|
31
|
+
errors << "Table '#{table.name}' does not exist in database"
|
|
32
|
+
return errors
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
db_columns = @introspector.introspect_columns(table.name.to_s, schema: @schema)
|
|
36
|
+
db_column_names = db_columns.map(&:name)
|
|
37
|
+
|
|
38
|
+
table.fields.each_key do |field_name|
|
|
39
|
+
unless db_column_names.include?(field_name.to_s)
|
|
40
|
+
errors << "Column '#{field_name}' on table '#{table.name}' does not exist in database"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
errors
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class SchemaValidationError < Error
|
|
49
|
+
attr_reader :validation_errors
|
|
50
|
+
|
|
51
|
+
def initialize(errors)
|
|
52
|
+
@validation_errors = errors
|
|
53
|
+
super("Schema validation failed:\n - #{errors.join("\n - ")}")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/rooq/table.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
module Rooq
|
|
7
|
+
class Table
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { returns(Symbol) }
|
|
11
|
+
attr_reader :name
|
|
12
|
+
|
|
13
|
+
sig { returns(T::Hash[Symbol, Field]) }
|
|
14
|
+
attr_reader :fields
|
|
15
|
+
|
|
16
|
+
sig { params(name: Symbol, block: T.nilable(T.proc.params(builder: TableBuilder).void)).void }
|
|
17
|
+
def initialize(name, &block)
|
|
18
|
+
@name = name
|
|
19
|
+
@fields = T.let({}, T::Hash[Symbol, Field])
|
|
20
|
+
@field_accessors = T.let({}, T::Hash[Symbol, Field])
|
|
21
|
+
|
|
22
|
+
if block_given?
|
|
23
|
+
builder = TableBuilder.new(self)
|
|
24
|
+
block.call(builder)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@fields.freeze
|
|
28
|
+
freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
sig { returns(T::Array[Field]) }
|
|
32
|
+
def asterisk
|
|
33
|
+
@fields.values
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
sig { params(method_name: Symbol, args: T.untyped).returns(Field) }
|
|
37
|
+
def method_missing(method_name, *args)
|
|
38
|
+
field_name = method_name.to_s.downcase.to_sym
|
|
39
|
+
|
|
40
|
+
if @fields.key?(field_name)
|
|
41
|
+
T.must(@fields[field_name])
|
|
42
|
+
else
|
|
43
|
+
available = @fields.keys.join(", ")
|
|
44
|
+
raise ValidationError, "Unknown field '#{field_name}' on table '#{@name}'. Available fields: #{available}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sig { params(method_name: Symbol, include_private: T::Boolean).returns(T::Boolean) }
|
|
49
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
50
|
+
field_name = method_name.to_s.downcase.to_sym
|
|
51
|
+
@fields.key?(field_name) || super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class TableBuilder
|
|
55
|
+
extend T::Sig
|
|
56
|
+
|
|
57
|
+
sig { params(table: Table).void }
|
|
58
|
+
def initialize(table)
|
|
59
|
+
@table = table
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
sig { params(name: Symbol, type: Symbol).returns(Field) }
|
|
63
|
+
def field(name, type)
|
|
64
|
+
field = Field.new(name, @table.name, type)
|
|
65
|
+
@table.instance_variable_get(:@fields)[name] = field
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/rooq/version.rb
ADDED
data/lib/rooq.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rooq
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
class SchemaError < Error; end
|
|
6
|
+
class ValidationError < Error; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "rooq/version"
|
|
10
|
+
require_relative "rooq/connection"
|
|
11
|
+
require_relative "rooq/configuration"
|
|
12
|
+
require_relative "rooq/expression"
|
|
13
|
+
require_relative "rooq/field"
|
|
14
|
+
require_relative "rooq/table"
|
|
15
|
+
require_relative "rooq/condition"
|
|
16
|
+
require_relative "rooq/dsl"
|
|
17
|
+
require_relative "rooq/dialect"
|
|
18
|
+
require_relative "rooq/generator"
|
|
19
|
+
require_relative "rooq/result"
|
|
20
|
+
require_relative "rooq/parameter_converter"
|
|
21
|
+
require_relative "rooq/executor"
|
|
22
|
+
require_relative "rooq/context"
|
|
23
|
+
require_relative "rooq/schema_validator"
|
|
24
|
+
require_relative "rooq/query_validator"
|
|
25
|
+
require_relative "rooq/adapters"
|
data/rooq.gemspec
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/rooq/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "rooq"
|
|
7
|
+
spec.version = Rooq::VERSION
|
|
8
|
+
spec.authors = ["Guillermo G. Almazor"]
|
|
9
|
+
spec.email = ["guille@galmazor.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "A jOOQ-inspired query builder for Ruby"
|
|
12
|
+
spec.description = "Build type-safe SQL queries using a fluent, chainable API. Generate Ruby code from database schemas with optional Sorbet type annotations."
|
|
13
|
+
spec.homepage = "https://github.com/ggalmazor/rooq"
|
|
14
|
+
spec.license = "AGPL-3.0-only"
|
|
15
|
+
spec.required_ruby_version = ">= 3.4"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
21
|
+
|
|
22
|
+
spec.files = Dir.chdir(__dir__) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
24
|
+
(File.expand_path(f) == __FILE__) ||
|
|
25
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github .idea docs/])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = ["rooq"]
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
spec.add_dependency "pg", "~> 1.5"
|
|
34
|
+
spec.add_dependency "sorbet-runtime", "~> 0.5"
|
|
35
|
+
end
|
data/sorbet/config
ADDED
metadata
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rooq
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Guillermo G. Almazor
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: pg
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.5'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.5'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: sorbet-runtime
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.5'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.5'
|
|
40
|
+
description: Build type-safe SQL queries using a fluent, chainable API. Generate Ruby
|
|
41
|
+
code from database schemas with optional Sorbet type annotations.
|
|
42
|
+
email:
|
|
43
|
+
- guille@galmazor.com
|
|
44
|
+
executables:
|
|
45
|
+
- rooq
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- ".tool-versions"
|
|
50
|
+
- ".yardopts"
|
|
51
|
+
- CHANGELOG.md
|
|
52
|
+
- CLAUDE.md
|
|
53
|
+
- Gemfile
|
|
54
|
+
- Gemfile.lock
|
|
55
|
+
- LICENSE
|
|
56
|
+
- README.md
|
|
57
|
+
- Rakefile
|
|
58
|
+
- USAGE.md
|
|
59
|
+
- exe/rooq
|
|
60
|
+
- lib/rooq.rb
|
|
61
|
+
- lib/rooq/adapters.rb
|
|
62
|
+
- lib/rooq/adapters/postgresql.rb
|
|
63
|
+
- lib/rooq/cli.rb
|
|
64
|
+
- lib/rooq/condition.rb
|
|
65
|
+
- lib/rooq/configuration.rb
|
|
66
|
+
- lib/rooq/connection.rb
|
|
67
|
+
- lib/rooq/context.rb
|
|
68
|
+
- lib/rooq/dialect.rb
|
|
69
|
+
- lib/rooq/dialect/base.rb
|
|
70
|
+
- lib/rooq/dialect/postgresql.rb
|
|
71
|
+
- lib/rooq/dsl.rb
|
|
72
|
+
- lib/rooq/dsl/delete_query.rb
|
|
73
|
+
- lib/rooq/dsl/insert_query.rb
|
|
74
|
+
- lib/rooq/dsl/select_query.rb
|
|
75
|
+
- lib/rooq/dsl/update_query.rb
|
|
76
|
+
- lib/rooq/executor.rb
|
|
77
|
+
- lib/rooq/expression.rb
|
|
78
|
+
- lib/rooq/field.rb
|
|
79
|
+
- lib/rooq/generator.rb
|
|
80
|
+
- lib/rooq/generator/code_generator.rb
|
|
81
|
+
- lib/rooq/generator/introspector.rb
|
|
82
|
+
- lib/rooq/parameter_converter.rb
|
|
83
|
+
- lib/rooq/query_validator.rb
|
|
84
|
+
- lib/rooq/result.rb
|
|
85
|
+
- lib/rooq/schema_validator.rb
|
|
86
|
+
- lib/rooq/table.rb
|
|
87
|
+
- lib/rooq/version.rb
|
|
88
|
+
- rooq.gemspec
|
|
89
|
+
- sorbet/config
|
|
90
|
+
homepage: https://github.com/ggalmazor/rooq
|
|
91
|
+
licenses:
|
|
92
|
+
- AGPL-3.0-only
|
|
93
|
+
metadata:
|
|
94
|
+
homepage_uri: https://github.com/ggalmazor/rooq
|
|
95
|
+
source_code_uri: https://github.com/ggalmazor/rooq
|
|
96
|
+
changelog_uri: https://github.com/ggalmazor/rooq/blob/main/CHANGELOG.md
|
|
97
|
+
rubygems_mfa_required: 'true'
|
|
98
|
+
rdoc_options: []
|
|
99
|
+
require_paths:
|
|
100
|
+
- lib
|
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '3.4'
|
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
requirements: []
|
|
112
|
+
rubygems_version: 4.0.3
|
|
113
|
+
specification_version: 4
|
|
114
|
+
summary: A jOOQ-inspired query builder for Ruby
|
|
115
|
+
test_files: []
|