wowsql-sdk 3.0.2 → 3.9.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 +4 -4
- data/CHANGELOG.md +20 -0
- data/LICENSE +81 -17
- data/README.md +587 -322
- data/lib/wowsql/auth.rb +2 -2
- data/lib/wowsql/client.rb +62 -58
- data/lib/wowsql/query_builder.rb +197 -226
- data/lib/wowsql/realtime.rb +201 -0
- data/lib/wowsql/schema.rb +2 -2
- data/lib/wowsql/storage.rb +2 -2
- data/lib/wowsql/table.rb +123 -110
- data/lib/wowsql/version.rb +1 -1
- data/lib/wowsql.rb +1 -0
- metadata +17 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'thread'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module WOWSQL
|
|
6
|
+
def self.build_realtime_websocket_url(project_url, api_key)
|
|
7
|
+
origin = project_url.to_s.sub(%r{/$}, '')
|
|
8
|
+
ws = origin.sub(/\Ahttp/, 'ws')
|
|
9
|
+
"#{ws}/realtime/v1/websocket?apikey=#{URI.encode_www_form_component(api_key)}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class RealtimeChannel
|
|
13
|
+
attr_reader :name
|
|
14
|
+
|
|
15
|
+
def initialize(rt, name)
|
|
16
|
+
@rt = rt
|
|
17
|
+
@name = name
|
|
18
|
+
@joined = false
|
|
19
|
+
@state = {}
|
|
20
|
+
@broadcast = []
|
|
21
|
+
@presence = []
|
|
22
|
+
@tracked = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def on(kind, filter = {}, &cb)
|
|
26
|
+
if kind.to_s == 'broadcast'
|
|
27
|
+
ev = (filter[:event] || filter['event'] || '*').to_s
|
|
28
|
+
@broadcast << ->(msg) { cb.call(msg) if ev == '*' || ev == msg['event'].to_s }
|
|
29
|
+
else
|
|
30
|
+
@presence << cb
|
|
31
|
+
end
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def subscribe(&on_status)
|
|
36
|
+
@rt.ensure_connected
|
|
37
|
+
@rt.send_json(type: 'join', channel: @name)
|
|
38
|
+
@joined = true
|
|
39
|
+
on_status&.call('SUBSCRIBED')
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def send(event:, payload: {})
|
|
44
|
+
@rt.ensure_connected
|
|
45
|
+
unless @joined
|
|
46
|
+
@rt.send_json(type: 'join', channel: @name)
|
|
47
|
+
@joined = true
|
|
48
|
+
end
|
|
49
|
+
@rt.send_json(type: 'broadcast', channel: @name, event: event, payload: payload || {})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def track(payload)
|
|
53
|
+
@tracked = payload
|
|
54
|
+
@rt.ensure_connected
|
|
55
|
+
unless @joined
|
|
56
|
+
@rt.send_json(type: 'join', channel: @name)
|
|
57
|
+
@joined = true
|
|
58
|
+
end
|
|
59
|
+
@rt.send_json(type: 'presence', event: 'track', channel: @name, payload: payload)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def presence_state
|
|
63
|
+
@state.dup
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def unsubscribe
|
|
67
|
+
@rt.send_json(type: 'leave', channel: @name)
|
|
68
|
+
@joined = false
|
|
69
|
+
@rt.drop_channel(@name)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def rejoin
|
|
73
|
+
@rt.send_json(type: 'join', channel: @name)
|
|
74
|
+
@rt.send_json(type: 'presence', event: 'track', channel: @name, payload: @tracked) if @tracked
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def handle_server(message)
|
|
78
|
+
case message['type']
|
|
79
|
+
when 'joined' then @joined = true
|
|
80
|
+
when 'presence'
|
|
81
|
+
ev = message['event']
|
|
82
|
+
if ev == 'sync' && message['state'].is_a?(Hash)
|
|
83
|
+
@state = message['state']
|
|
84
|
+
elsif ev == 'join' && message['key']
|
|
85
|
+
@state[message['key']] = message['payload']
|
|
86
|
+
elsif ev == 'leave' && message['key']
|
|
87
|
+
@state.delete(message['key'])
|
|
88
|
+
end
|
|
89
|
+
@presence.each { |cb| cb.call(message) }
|
|
90
|
+
when 'broadcast'
|
|
91
|
+
wrapped = { 'event' => message['event'], 'payload' => message['payload'] || {}, 'channel' => @name }
|
|
92
|
+
@broadcast.each { |cb| cb.call(wrapped) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class WowSQLRealtime
|
|
98
|
+
def initialize(project_url, api_key)
|
|
99
|
+
@project_url = project_url.to_s.sub(%r{/$}, '')
|
|
100
|
+
@api_key = api_key
|
|
101
|
+
@ws = nil
|
|
102
|
+
@manual = false
|
|
103
|
+
@subs = []
|
|
104
|
+
@channels = {}
|
|
105
|
+
@mutex = Mutex.new
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def url
|
|
109
|
+
WOWSQL.build_realtime_websocket_url(@project_url, @api_key)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def channel(name)
|
|
113
|
+
@channels[name] ||= RealtimeChannel.new(self, name)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def subscribe(table, schema: 'public', event: '*', &callback)
|
|
117
|
+
sub = { schema: schema, table: table, event: event, callback: callback }
|
|
118
|
+
@subs << sub
|
|
119
|
+
Thread.new do
|
|
120
|
+
ensure_connected
|
|
121
|
+
send_json(type: 'subscribe', schema: schema, table: table, event: event)
|
|
122
|
+
end
|
|
123
|
+
-> { unsubscribe(sub) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def disconnect
|
|
127
|
+
@manual = true
|
|
128
|
+
@ws&.close
|
|
129
|
+
@ws = nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def drop_channel(name)
|
|
133
|
+
@channels.delete(name)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def send_json(obj)
|
|
137
|
+
@ws&.send(JSON.dump(obj))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def ensure_connected
|
|
141
|
+
return if @ws
|
|
142
|
+
begin
|
|
143
|
+
require 'websocket-client-simple'
|
|
144
|
+
rescue LoadError
|
|
145
|
+
raise LoadError, 'Realtime requires the websocket-client-simple gem'
|
|
146
|
+
end
|
|
147
|
+
@manual = false
|
|
148
|
+
rt = self
|
|
149
|
+
@ws = WebSocket::Client::Simple.connect(url) do |ws|
|
|
150
|
+
ws.on :message do |msg|
|
|
151
|
+
rt.send(:handle, msg.data)
|
|
152
|
+
end
|
|
153
|
+
ws.on :open do
|
|
154
|
+
rt.send(:on_open)
|
|
155
|
+
end
|
|
156
|
+
ws.on :close do |_e|
|
|
157
|
+
rt.send(:on_close)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
sleep 0.2
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def on_open
|
|
166
|
+
@subs.each { |s| send_json(type: 'subscribe', schema: s[:schema], table: s[:table], event: s[:event]) }
|
|
167
|
+
@channels.each_value(&:rejoin)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def on_close
|
|
171
|
+
@ws = nil
|
|
172
|
+
ensure_connected if !@manual && (!@subs.empty? || !@channels.empty?)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def unsubscribe(sub)
|
|
176
|
+
@subs.delete(sub)
|
|
177
|
+
send_json(type: 'unsubscribe', schema: sub[:schema], table: sub[:table])
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def handle(raw)
|
|
181
|
+
message = JSON.parse(raw)
|
|
182
|
+
name = message['channel']
|
|
183
|
+
@channels[name]&.handle_server(message) if name
|
|
184
|
+
return unless message['type'] == 'broadcast'
|
|
185
|
+
return if message['channel'] && !message['table']
|
|
186
|
+
nested = message['payload'].is_a?(Hash) ? message['payload'] : {}
|
|
187
|
+
event = (message['event'] || nested['type'] || '').to_s.upcase
|
|
188
|
+
schema = (message['schema'] || nested['schema'] || 'public').to_s
|
|
189
|
+
table = (message['table'] || nested['table'] || '').to_s
|
|
190
|
+
return if table.empty? || !%w[INSERT UPDATE DELETE].include?(event)
|
|
191
|
+
change = { 'event' => event, 'schema' => schema, 'table' => table, 'payload' => nested }
|
|
192
|
+
change['new'] = nested['new'] if nested.key?('new')
|
|
193
|
+
change['old'] = nested['old'] if nested.key?('old')
|
|
194
|
+
@subs.each do |s|
|
|
195
|
+
next unless s[:schema] == schema && s[:table] == table
|
|
196
|
+
next unless s[:event] == '*' || s[:event].to_s.upcase == event
|
|
197
|
+
s[:callback].call(change)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
data/lib/wowsql/schema.rb
CHANGED
|
@@ -24,11 +24,11 @@ module WOWSQL
|
|
|
24
24
|
|
|
25
25
|
# @param project_url [String] Project subdomain or full URL
|
|
26
26
|
# @param service_key [String] Service role key (wowsql_service_...)
|
|
27
|
-
# @param base_domain [String] Base domain (default: "
|
|
27
|
+
# @param base_domain [String] Base domain (default: "wowsqlconnect.com")
|
|
28
28
|
# @param secure [Boolean] Use HTTPS (default: true)
|
|
29
29
|
# @param timeout [Integer] Request timeout in seconds (default: 30)
|
|
30
30
|
# @param verify_ssl [Boolean] Verify SSL certificates (default: true)
|
|
31
|
-
def initialize(project_url, service_key, base_domain: '
|
|
31
|
+
def initialize(project_url, service_key, base_domain: 'wowsqlconnect.com', secure: true,
|
|
32
32
|
timeout: 30, verify_ssl: true)
|
|
33
33
|
if project_url.start_with?('http://') || project_url.start_with?('https://')
|
|
34
34
|
base = project_url.chomp('/')
|
data/lib/wowsql/storage.rb
CHANGED
|
@@ -98,12 +98,12 @@ module WOWSQL
|
|
|
98
98
|
# @param api_key [String] API key for authentication
|
|
99
99
|
# @param project_slug [String] Explicit slug (used with base_url)
|
|
100
100
|
# @param base_url [String] Explicit base URL (used with project_slug)
|
|
101
|
-
# @param base_domain [String] Base domain (default: "
|
|
101
|
+
# @param base_domain [String] Base domain (default: "wowsqlconnect.com")
|
|
102
102
|
# @param secure [Boolean] Use HTTPS (default: true)
|
|
103
103
|
# @param timeout [Integer] Request timeout in seconds (default: 60)
|
|
104
104
|
# @param verify_ssl [Boolean] Verify SSL certificates (default: true)
|
|
105
105
|
def initialize(project_url = '', api_key = '', project_slug: '', base_url: '',
|
|
106
|
-
base_domain: '
|
|
106
|
+
base_domain: 'wowsqlconnect.com', secure: true, timeout: 60, verify_ssl: true)
|
|
107
107
|
if !project_slug.empty? && !base_url.empty?
|
|
108
108
|
@base_url = base_url.chomp('/')
|
|
109
109
|
@project_slug = project_slug
|
data/lib/wowsql/table.rb
CHANGED
|
@@ -1,169 +1,182 @@
|
|
|
1
1
|
require_relative 'query_builder'
|
|
2
2
|
|
|
3
3
|
module WOWSQL
|
|
4
|
-
# Table interface for
|
|
4
|
+
# Table interface for direct PostgREST operations.
|
|
5
|
+
#
|
|
6
|
+
# All mutations use PostgREST native query parameters (?id=eq.val) and
|
|
7
|
+
# Prefer headers (return=representation, resolution=merge-duplicates).
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# user = client.table("users").get_by_id("uuid-here")
|
|
11
|
+
# result = client.table("users").create({ email: "a@b.com", name: "Alice" })
|
|
5
12
|
class Table
|
|
6
13
|
def initialize(client, table_name)
|
|
7
|
-
@client
|
|
14
|
+
@client = client
|
|
8
15
|
@table_name = table_name
|
|
9
16
|
end
|
|
10
17
|
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
# @param columns [Array<String>] Column(s) to select
|
|
14
|
-
# @return [QueryBuilder] QueryBuilder for chaining
|
|
18
|
+
# ── Query builder shortcuts ───────────────────────────────────
|
|
19
|
+
|
|
15
20
|
def select(*columns)
|
|
16
21
|
QueryBuilder.new(@client, @table_name).select(*columns)
|
|
17
22
|
end
|
|
18
23
|
|
|
19
|
-
# Start a query with a filter.
|
|
20
|
-
#
|
|
21
|
-
# @param column [String, Hash] Column name or filter hash
|
|
22
|
-
# @param operator [String, nil] Operator
|
|
23
|
-
# @param value [Object] Filter value
|
|
24
|
-
# @param logical_op [String] "AND" or "OR"
|
|
25
|
-
# @return [QueryBuilder] QueryBuilder for chaining
|
|
26
24
|
def filter(column, operator = nil, value = nil, logical_op: 'AND')
|
|
27
25
|
QueryBuilder.new(@client, @table_name).filter(column, operator, value, logical_op: logical_op)
|
|
28
26
|
end
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
def eq(column, value)
|
|
29
|
+
QueryBuilder.new(@client, @table_name).eq(column, value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def neq(column, value)
|
|
33
|
+
QueryBuilder.new(@client, @table_name).neq(column, value)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def gt(column, value)
|
|
37
|
+
QueryBuilder.new(@client, @table_name).gt(column, value)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def gte(column, value)
|
|
41
|
+
QueryBuilder.new(@client, @table_name).gte(column, value)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def lt(column, value)
|
|
45
|
+
QueryBuilder.new(@client, @table_name).lt(column, value)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def lte(column, value)
|
|
49
|
+
QueryBuilder.new(@client, @table_name).lte(column, value)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def order_by(column, direction = 'asc')
|
|
53
|
+
QueryBuilder.new(@client, @table_name).order_by(column, direction)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def limit(n)
|
|
57
|
+
QueryBuilder.new(@client, @table_name).limit(n)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def offset(n)
|
|
61
|
+
QueryBuilder.new(@client, @table_name).offset(n)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# ── Read ─────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
# Retrieve all rows (with optional filter options forwarded to QueryBuilder#get).
|
|
31
67
|
#
|
|
32
|
-
# @param options [Hash, nil]
|
|
33
|
-
# @return [Hash]
|
|
68
|
+
# @param options [Hash, nil] Reserved for compatibility; use chained methods instead
|
|
69
|
+
# @return [Hash] { data: Array, count: Integer, total: Integer, limit: Integer, offset: Integer }
|
|
34
70
|
def get(options = nil)
|
|
35
71
|
QueryBuilder.new(@client, @table_name).get(options)
|
|
36
72
|
end
|
|
37
73
|
|
|
38
|
-
#
|
|
74
|
+
# Retrieve a single row by primary key.
|
|
39
75
|
#
|
|
40
|
-
# @param record_id [
|
|
41
|
-
# @return [Hash]
|
|
76
|
+
# @param record_id [String] Row UUID
|
|
77
|
+
# @return [Hash] Row data
|
|
42
78
|
def get_by_id(record_id)
|
|
43
|
-
@client.request(
|
|
79
|
+
result = @client.request(
|
|
80
|
+
'GET', "/#{@table_name}",
|
|
81
|
+
{ 'id' => "eq.#{record_id}" },
|
|
82
|
+
nil,
|
|
83
|
+
'Prefer' => 'return=representation'
|
|
84
|
+
)
|
|
85
|
+
data = normalise_data(result)
|
|
86
|
+
data.first
|
|
44
87
|
end
|
|
45
88
|
|
|
46
|
-
#
|
|
89
|
+
# ── Write ────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
# Insert a single row and return the created record.
|
|
47
92
|
#
|
|
48
|
-
# @param data [Hash]
|
|
49
|
-
# @return [Hash]
|
|
93
|
+
# @param data [Hash] Column→value map
|
|
94
|
+
# @return [Hash] Created row
|
|
50
95
|
def create(data)
|
|
51
|
-
@client.request(
|
|
96
|
+
result = @client.request(
|
|
97
|
+
'POST', "/#{@table_name}", nil, data,
|
|
98
|
+
'Prefer' => 'return=representation'
|
|
99
|
+
)
|
|
100
|
+
normalise_data(result).first || {}
|
|
52
101
|
end
|
|
53
102
|
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
# @param data [Hash] Record data
|
|
57
|
-
# @return [Hash] Create response with new record ID
|
|
58
|
-
def insert(data)
|
|
59
|
-
create(data)
|
|
60
|
-
end
|
|
103
|
+
alias insert create
|
|
61
104
|
|
|
62
|
-
# Insert multiple
|
|
105
|
+
# Insert multiple rows.
|
|
63
106
|
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# @param records [Array<Hash>] List of record hashes
|
|
68
|
-
# @return [Array<Hash>] List of create responses
|
|
107
|
+
# @param records [Array<Hash>] List of row hashes
|
|
108
|
+
# @return [Array<Hash>] Created rows
|
|
69
109
|
def bulk_insert(records)
|
|
70
110
|
return [] if records.nil? || records.empty?
|
|
71
111
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
end
|
|
112
|
+
result = @client.request(
|
|
113
|
+
'POST', "/#{@table_name}", nil, records,
|
|
114
|
+
'Prefer' => 'return=representation'
|
|
115
|
+
)
|
|
116
|
+
normalise_data(result)
|
|
78
117
|
end
|
|
79
118
|
|
|
80
119
|
# Insert or update based on conflict column.
|
|
81
120
|
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
# @param data [Hash] Record data (must include the conflict column)
|
|
87
|
-
# @param on_conflict [String] Column to check for conflicts (default: "id")
|
|
88
|
-
# @return [Hash] Create or update response
|
|
121
|
+
# @param data [Hash] Row data (must include the conflict column value)
|
|
122
|
+
# @param on_conflict [String] Column to detect conflicts on (default: "id")
|
|
123
|
+
# @return [Hash] Upserted row
|
|
89
124
|
def upsert(data, on_conflict: 'id')
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if update_data.empty?
|
|
97
|
-
{ 'message' => 'No changes', 'affected_rows' => 0 }
|
|
98
|
-
else
|
|
99
|
-
update(conflict_value, update_data)
|
|
100
|
-
end
|
|
101
|
-
else
|
|
102
|
-
create(data)
|
|
103
|
-
end
|
|
125
|
+
result = @client.request(
|
|
126
|
+
'POST', "/#{@table_name}", nil, data,
|
|
127
|
+
'Prefer' => 'return=representation,resolution=merge-duplicates',
|
|
128
|
+
'on-conflict-column' => on_conflict
|
|
129
|
+
)
|
|
130
|
+
normalise_data(result).first || {}
|
|
104
131
|
end
|
|
105
132
|
|
|
106
|
-
# Update a
|
|
133
|
+
# Update a row by ID.
|
|
107
134
|
#
|
|
108
|
-
# @param record_id [
|
|
109
|
-
# @param data [Hash]
|
|
110
|
-
# @return [Hash]
|
|
135
|
+
# @param record_id [String] Row UUID
|
|
136
|
+
# @param data [Hash] Columns to update
|
|
137
|
+
# @return [Hash] Updated row
|
|
111
138
|
def update(record_id, data)
|
|
112
|
-
@client.request(
|
|
139
|
+
result = @client.request(
|
|
140
|
+
'PATCH', "/#{@table_name}",
|
|
141
|
+
{ 'id' => "eq.#{record_id}" },
|
|
142
|
+
data,
|
|
143
|
+
'Prefer' => 'return=representation'
|
|
144
|
+
)
|
|
145
|
+
normalise_data(result).first || {}
|
|
113
146
|
end
|
|
114
147
|
|
|
115
|
-
# Delete a
|
|
148
|
+
# Delete a row by ID.
|
|
116
149
|
#
|
|
117
|
-
# @param record_id [
|
|
118
|
-
# @return [Hash]
|
|
150
|
+
# @param record_id [String] Row UUID
|
|
151
|
+
# @return [Hash] Deleted row
|
|
119
152
|
def delete(record_id)
|
|
120
|
-
@client.request(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def neq(column, value)
|
|
130
|
-
QueryBuilder.new(@client, @table_name).neq(column, value)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def gt(column, value)
|
|
134
|
-
QueryBuilder.new(@client, @table_name).gt(column, value)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def gte(column, value)
|
|
138
|
-
QueryBuilder.new(@client, @table_name).gte(column, value)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def lt(column, value)
|
|
142
|
-
QueryBuilder.new(@client, @table_name).lt(column, value)
|
|
153
|
+
result = @client.request(
|
|
154
|
+
'DELETE', "/#{@table_name}",
|
|
155
|
+
{ 'id' => "eq.#{record_id}" },
|
|
156
|
+
nil,
|
|
157
|
+
'Prefer' => 'return=representation'
|
|
158
|
+
)
|
|
159
|
+
normalise_data(result).first || {}
|
|
143
160
|
end
|
|
144
161
|
|
|
145
|
-
|
|
146
|
-
QueryBuilder.new(@client, @table_name).lte(column, value)
|
|
147
|
-
end
|
|
162
|
+
# ── Aggregates / pagination ───────────────────────────────────
|
|
148
163
|
|
|
149
|
-
def order_by(column, direction = 'asc')
|
|
150
|
-
QueryBuilder.new(@client, @table_name).order_by(column, direction)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Get total record count for this table.
|
|
154
|
-
#
|
|
155
|
-
# @return [Integer]
|
|
156
164
|
def count
|
|
157
165
|
QueryBuilder.new(@client, @table_name).count
|
|
158
166
|
end
|
|
159
167
|
|
|
160
|
-
# Paginate all records in this table.
|
|
161
|
-
#
|
|
162
|
-
# @param page [Integer] Page number (1-indexed)
|
|
163
|
-
# @param per_page [Integer] Records per page
|
|
164
|
-
# @return [Hash]
|
|
165
168
|
def paginate(page: 1, per_page: 20)
|
|
166
169
|
QueryBuilder.new(@client, @table_name).paginate(page: page, per_page: per_page)
|
|
167
170
|
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
def normalise_data(result)
|
|
175
|
+
case result
|
|
176
|
+
when Array then result
|
|
177
|
+
when Hash then result['data'] || result['rows'] || (result.key?('id') ? [result] : [])
|
|
178
|
+
else []
|
|
179
|
+
end
|
|
180
|
+
end
|
|
168
181
|
end
|
|
169
182
|
end
|
data/lib/wowsql/version.rb
CHANGED
data/lib/wowsql.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wowsql-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0
|
|
4
|
+
version: 3.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- WOWSQL Team
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '2.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: websocket-client-simple
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.3'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.3'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: rspec
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -87,6 +101,7 @@ executables: []
|
|
|
87
101
|
extensions: []
|
|
88
102
|
extra_rdoc_files: []
|
|
89
103
|
files:
|
|
104
|
+
- CHANGELOG.md
|
|
90
105
|
- LICENSE
|
|
91
106
|
- README.md
|
|
92
107
|
- lib/wowmysql.rb
|
|
@@ -95,6 +110,7 @@ files:
|
|
|
95
110
|
- lib/wowsql/client.rb
|
|
96
111
|
- lib/wowsql/exceptions.rb
|
|
97
112
|
- lib/wowsql/query_builder.rb
|
|
113
|
+
- lib/wowsql/realtime.rb
|
|
98
114
|
- lib/wowsql/schema.rb
|
|
99
115
|
- lib/wowsql/storage.rb
|
|
100
116
|
- lib/wowsql/table.rb
|