Add a “Rule” class, borrowing and extending the ActiveRecord-like design from data-explorer plugin
This commit is contained in:
parent
8b1a065e3d
commit
cd3bd8d807
|
@ -0,0 +1,107 @@
|
|||
|
||||
# Similar to an ActiveRecord class, but uses PluginStore for storage instead. Adapted from discourse-data-explorer
|
||||
# Using this means we can use a standard serializer for sending JSON to the client, and also have convenient save/update/delete methods
|
||||
# Since this is now being used in two plugins, maybe it should be built into core somehow
|
||||
class DiscourseChat::Rule
|
||||
attr_accessor :id, :provider, :channel, :category_id, :tags, :filter
|
||||
|
||||
def initialize(h={})
|
||||
h.each {|k,v| public_send("#{k}=",v)}
|
||||
end
|
||||
|
||||
# saving/loading functions
|
||||
def self.alloc_id
|
||||
DistributedMutex.synchronize('discourse-chat_rule-id') do
|
||||
max_id = DiscourseChat.pstore_get("rule:_id")
|
||||
max_id = 1 unless max_id
|
||||
DiscourseChat.pstore_set("rule:_id", max_id + 1)
|
||||
max_id
|
||||
end
|
||||
end
|
||||
|
||||
def self.from_hash(h)
|
||||
rule = DiscourseChat::Rule.new
|
||||
[:provider, :channel, :category_id, :tags, :filter].each do |sym|
|
||||
rule.send("#{sym}=", h[sym]) if h[sym]
|
||||
end
|
||||
if h[:id]
|
||||
rule.id = h[:id].to_i
|
||||
end
|
||||
rule
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
id: @id,
|
||||
provider: @provider,
|
||||
channel: @channel,
|
||||
category_id: @category_id,
|
||||
tags: @tags,
|
||||
filter: @filter,
|
||||
}
|
||||
end
|
||||
|
||||
def self.find(id, opts={})
|
||||
hash = DiscourseChat.pstore_get("rule:#{id}")
|
||||
unless hash
|
||||
return DiscourseChat::Rule.new if opts[:ignore_deleted]
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
from_hash hash
|
||||
end
|
||||
|
||||
def save
|
||||
unless @id && @id > 0
|
||||
@id = self.class.alloc_id
|
||||
end
|
||||
DiscourseChat.pstore_set "rule:#{id}", to_hash
|
||||
return self
|
||||
end
|
||||
|
||||
def destroy
|
||||
DiscourseChat.pstore_delete "rule:#{id}"
|
||||
end
|
||||
|
||||
def read_attribute_for_serialization(attr)
|
||||
self.send(attr)
|
||||
end
|
||||
|
||||
def self.all_for_provider(provider)
|
||||
self.where("value::json->>'provider'=?", provider)
|
||||
end
|
||||
|
||||
def self.all_for_category(category_id)
|
||||
if category_id.nil?
|
||||
self.where("json_typeof(value::json->'category_id')='null'")
|
||||
else
|
||||
self.where("value::json->>'category_id'=?", category_id.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Use JSON selectors like this:
|
||||
# Rule.where("value::json->>'provider'=?", "telegram")
|
||||
def self.where(*args)
|
||||
rows = self._all_raw.where(*args)
|
||||
self._from_psr_rows(rows)
|
||||
end
|
||||
|
||||
def self.all
|
||||
self._from_psr_rows(self._all_raw)
|
||||
end
|
||||
|
||||
def self._all_raw
|
||||
PluginStoreRow.where(plugin_name: DiscourseChat.plugin_name)
|
||||
.where("key LIKE 'rule:%'")
|
||||
.where("key != 'rule:_id'")
|
||||
end
|
||||
|
||||
def self._from_psr_rows(raw)
|
||||
raw.map do |psr|
|
||||
from_hash PluginStore.cast_value(psr.type_name, psr.value)
|
||||
end
|
||||
end
|
||||
|
||||
def self.destroy_all
|
||||
self._all_raw().destroy_all
|
||||
end
|
||||
end
|
17
plugin.rb
17
plugin.rb
|
@ -14,10 +14,27 @@ after_initialize do
|
|||
engine_name DiscourseChat::PLUGIN_NAME
|
||||
isolate_namespace DiscourseChat
|
||||
end
|
||||
|
||||
def self.plugin_name
|
||||
DiscourseChat::PLUGIN_NAME
|
||||
end
|
||||
|
||||
def self.pstore_get(key)
|
||||
PluginStore.get(self.plugin_name, key)
|
||||
end
|
||||
|
||||
def self.pstore_set(key, value)
|
||||
PluginStore.set(self.plugin_name, key, value)
|
||||
end
|
||||
|
||||
def self.pstore_delete(key)
|
||||
PluginStore.remove(self.plugin_name, key)
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "lib/provider"
|
||||
require_relative "lib/manager"
|
||||
require_relative "lib/rule"
|
||||
|
||||
DiscourseEvent.on(:post_created) do |post|
|
||||
if SiteSetting.chat_enabled?
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DiscourseChat::Rule do
|
||||
describe '.alloc_id' do
|
||||
it 'should return sequential numbers' do
|
||||
expect( DiscourseChat::Rule.alloc_id ).to eq(1)
|
||||
expect( DiscourseChat::Rule.alloc_id ).to eq(2)
|
||||
expect( DiscourseChat::Rule.alloc_id ).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should save and load successfully' do
|
||||
expect(DiscourseChat::Rule.all.length).to eq(0)
|
||||
|
||||
rule = DiscourseChat::Rule.new({
|
||||
provider:"slack",
|
||||
channel: "#general",
|
||||
category_id: 2,
|
||||
tags: ['hello', 'world'],
|
||||
filter: 'watch'
|
||||
}).save
|
||||
|
||||
expect(DiscourseChat::Rule.all.length).to eq(1)
|
||||
|
||||
loadedRule = DiscourseChat::Rule.find(rule.id)
|
||||
|
||||
expect(loadedRule.provider).to eq('slack')
|
||||
expect(loadedRule.channel).to eq('#general')
|
||||
expect(loadedRule.category_id).to eq(2)
|
||||
expect(loadedRule.tags).to contain_exactly('hello','world')
|
||||
expect(loadedRule.filter).to eq('watch')
|
||||
|
||||
end
|
||||
|
||||
describe 'general operations' do
|
||||
before do
|
||||
rule = DiscourseChat::Rule.new({
|
||||
provider:"slack",
|
||||
channel: "#general",
|
||||
category_id: 2,
|
||||
tags: ['hello', 'world']
|
||||
}).save
|
||||
end
|
||||
|
||||
it 'can be modified' do
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
rule.channel = "#random"
|
||||
|
||||
rule.save
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.channel).to eq('#random')
|
||||
end
|
||||
|
||||
it 'can be deleted' do
|
||||
DiscourseChat::Rule.new.save
|
||||
expect(DiscourseChat::Rule.all.length).to eq(2)
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
rule.destroy
|
||||
|
||||
expect(DiscourseChat::Rule.all.length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can delete all' do
|
||||
DiscourseChat::Rule.new.save
|
||||
DiscourseChat::Rule.new.save
|
||||
DiscourseChat::Rule.new.save
|
||||
DiscourseChat::Rule.new.save
|
||||
|
||||
expect(DiscourseChat::Rule.all.length).to eq(5)
|
||||
|
||||
DiscourseChat::Rule.destroy_all
|
||||
|
||||
expect(DiscourseChat::Rule.all.length).to eq(0)
|
||||
end
|
||||
|
||||
it 'can be filtered by provider' do
|
||||
rule2 = DiscourseChat::Rule.new({provider:'telegram'}).save
|
||||
rule3 = DiscourseChat::Rule.new({provider:'slack'}).save
|
||||
|
||||
expect(DiscourseChat::Rule.all.length).to eq(3)
|
||||
|
||||
expect(DiscourseChat::Rule.all_for_provider('slack').length).to eq(2)
|
||||
expect(DiscourseChat::Rule.all_for_provider('telegram').length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can be filtered by category' do
|
||||
rule2 = DiscourseChat::Rule.new({category_id: 1}).save
|
||||
rule3 = DiscourseChat::Rule.new({category_id: nil}).save
|
||||
|
||||
expect(DiscourseChat::Rule.all.length).to eq(3)
|
||||
|
||||
expect(DiscourseChat::Rule.all_for_category(2).length).to eq(1)
|
||||
expect(DiscourseChat::Rule.all_for_category(1).length).to eq(1)
|
||||
expect(DiscourseChat::Rule.all_for_category(nil).length).to eq(1)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue