Class: Trifle::Stats::Driver::Postgres

Inherits:
Object
  • Object
show all
Includes:
Mixins::Packer
Defined in:
lib/trifle/stats/driver/postgres.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixins::Packer

included

Constructor Details

#initialize(client = PG::Connection.new, table_name: 'trifle_stats') ⇒ Postgres

Returns a new instance of Postgres.



12
13
14
15
16
# File 'lib/trifle/stats/driver/postgres.rb', line 12

def initialize(client = PG::Connection.new, table_name: 'trifle_stats')
  @client = client
  @table_name = table_name
  @separator = '::'
end

Instance Attribute Details

#clientObject

Returns the value of attribute client.



10
11
12
# File 'lib/trifle/stats/driver/postgres.rb', line 10

def client
  @client
end

#separatorObject

Returns the value of attribute separator.



10
11
12
# File 'lib/trifle/stats/driver/postgres.rb', line 10

def separator
  @separator
end

#table_nameObject

Returns the value of attribute table_name.



10
11
12
# File 'lib/trifle/stats/driver/postgres.rb', line 10

def table_name
  @table_name
end

Class Method Details

.setup!(client = PG::Connection.new, table_name: 'trifle_stats') ⇒ Object



18
19
20
# File 'lib/trifle/stats/driver/postgres.rb', line 18

def self.setup!(client = PG::Connection.new, table_name: 'trifle_stats')
  client.exec("CREATE TABLE #{table_name} (key VARCHAR(255) PRIMARY KEY, data JSONB NOT NULL DEFAULT '{}'::jsonb)") # rubocop:disable Layout/LineLength
end

Instance Method Details

#get(keys:) ⇒ Object



58
59
60
61
62
63
64
# File 'lib/trifle/stats/driver/postgres.rb', line 58

def get(keys:)
  pkeys = keys.map { |key| key.join(separator) }
  data = get_all(keys: pkeys)
  map = data.inject({}) { |o, d| o.merge(d[:key] => d[:data]) }

  pkeys.map { |pkey| self.class.unpack(hash: map.fetch(pkey, {})) }
end

#get_all(keys:) ⇒ Object



66
67
68
69
70
71
72
73
74
# File 'lib/trifle/stats/driver/postgres.rb', line 66

def get_all(keys:)
  results = client.exec_params(get_query(keys: keys)).to_a

  results.map do |r|
    { key: r['key'], data: JSON.parse(r['data']) }
  rescue JSON::ParserError
    { key: r['key'], data: {} }
  end
end

#get_query(keys:) ⇒ Object



76
77
78
79
80
# File 'lib/trifle/stats/driver/postgres.rb', line 76

def get_query(keys:)
  <<-SQL
    SELECT * FROM #{table_name} WHERE key IN ('#{keys.join("', '")}');
  SQL
end

#inc(keys:, **values) ⇒ Object



22
23
24
25
26
27
28
29
30
# File 'lib/trifle/stats/driver/postgres.rb', line 22

def inc(keys:, **values)
  data = self.class.pack(hash: values)
  client.transaction do |c|
    keys.map do |key|
      pkey = key.join(separator)
      c.exec(inc_query(key: pkey, data: data))
    end
  end
end

#inc_query(key:, data:) ⇒ Object



32
33
34
35
36
37
38
# File 'lib/trifle/stats/driver/postgres.rb', line 32

def inc_query(key:, data:)
  <<-SQL
    INSERT INTO #{table_name} (key, data) VALUES ('#{key}', '#{data.to_json}')
    ON CONFLICT (key) DO UPDATE SET data =
    #{data.inject("to_jsonb(#{table_name}.data)") { |o, (k, v)| "jsonb_set(#{o}, '{#{k}}', (COALESCE(trifle_stats.data->>'#{k}', '0')::int + #{v})::text::jsonb)" }};
  SQL
end

#set(keys:, **values) ⇒ Object



40
41
42
43
44
45
46
47
48
# File 'lib/trifle/stats/driver/postgres.rb', line 40

def set(keys:, **values)
  data = self.class.pack(hash: values)
  client.transaction do |c|
    keys.map do |key|
      pkey = key.join(separator)
      c.exec(set_query(key: pkey, data: data))
    end
  end
end

#set_query(key:, data:) ⇒ Object



50
51
52
53
54
55
56
# File 'lib/trifle/stats/driver/postgres.rb', line 50

def set_query(key:, data:)
  <<-SQL
    INSERT INTO #{table_name} (key, data) VALUES ('#{key}', '#{data.to_json}')
    ON CONFLICT (key) DO UPDATE SET data =
    #{data.inject("to_jsonb(#{table_name}.data)") { |o, (k, v)| "jsonb_set(#{o}, '{#{k}}', (#{v})::text::jsonb)" }}
  SQL
end