query_dam 0.0.1 → 0.1.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/.gitignore +1 -0
- data/.rspec +0 -1
- data/README.md +44 -8
- data/lib/query_dam.rb +42 -3
- data/lib/query_dam/concerns/trackable.rb +39 -13
- data/lib/query_dam/version.rb +1 -1
- data/query_dam.gemspec +2 -2
- data/spec/models/item.rb +3 -0
- data/spec/query_dam_spec.rb +53 -0
- data/spec/spec_helper.rb +18 -0
- metadata +7 -4
- data/lib/query_dam/concerns/cacheable.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0612b6f926d6c396e8a7be1e4a8c5c6ab2a888d7
|
4
|
+
data.tar.gz: a8973faabf07ffeb5afeb3540b1def06187ca51a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44d012c14b7e738279bd545156efd54a93d65dcc965031223c4fe73b4ff9cd8da66d7aa19ddc311901a67950a544f8e951b2b0af6eb2fd4c15ac526f97655a5c
|
7
|
+
data.tar.gz: a294b14782c0b07cb595061b511407aada190ee135e065a5dd6a54c4d6c097bcec10d18fa440a4be906cbf82ed28e92ca6a5c9625e9f1d2f93669bd7f7f73d18
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/README.md
CHANGED
@@ -1,24 +1,60 @@
|
|
1
1
|
# QueryDam
|
2
2
|
|
3
|
-
|
3
|
+
QueryDam is a tool for observing changes in ActiveRecord relation results.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
Add
|
7
|
+
Add it to your gemfile Gemfile:
|
8
8
|
|
9
9
|
gem 'query_dam'
|
10
10
|
|
11
|
-
|
11
|
+
## Usage
|
12
12
|
|
13
|
-
|
13
|
+
First, include `QueryDam::Trackable` in every model you with to track.
|
14
14
|
|
15
|
-
|
15
|
+
~~~ruby
|
16
|
+
class Item < ActiveRecord::Base
|
17
|
+
include QueryDam::Trackable
|
18
|
+
end
|
19
|
+
~~~
|
16
20
|
|
17
|
-
|
21
|
+
Then start watching updates of relation:
|
18
22
|
|
19
|
-
|
23
|
+
~~~ruby
|
24
|
+
relation = Item.where(name: 'pencil')
|
25
|
+
tracking_key = QueryDam.watch_relation(relation)
|
26
|
+
~~~
|
27
|
+
|
28
|
+
To get updates, call `QueryDam.get_updates(tracking_key)`.
|
29
|
+
The result is a hash with 3 keys:
|
30
|
+
`:model` - the model of the relation,
|
31
|
+
`:updates` - relation containing records that were updated or entered the scope of the relation
|
32
|
+
`:exclusions` - array of ids of records that left the scope of the relation
|
33
|
+
|
34
|
+
The call to `get_updates` clears updates/exclusions.
|
35
|
+
|
36
|
+
~~~ruby
|
37
|
+
Item.create(name: 'pencil')
|
38
|
+
QueryDam.get_updates(tracking_key)
|
39
|
+
# =>
|
40
|
+
# {:model=>Item,
|
41
|
+
# :updates=>#<ActiveRecord::Relation [#<Item id: 1, name: "pencil">]>,
|
42
|
+
# :exclusions=>[]}
|
43
|
+
|
44
|
+
QueryDam.get_updates(tracking_key)
|
45
|
+
# =>
|
46
|
+
# {:model=>Item,
|
47
|
+
# :updates=>#<ActiveRecord::Relation []>,
|
48
|
+
# :exclusions=>[]}
|
49
|
+
|
50
|
+
Item.first.destory
|
20
51
|
|
21
|
-
|
52
|
+
QueryDam.get_updates(tracking_key)
|
53
|
+
# =>
|
54
|
+
# {:model=>Item,
|
55
|
+
# :updates=>#<ActiveRecord::Relation []>,
|
56
|
+
# :exclusions=>[1]}
|
57
|
+
~~~
|
22
58
|
|
23
59
|
## Contributing
|
24
60
|
|
data/lib/query_dam.rb
CHANGED
@@ -1,9 +1,48 @@
|
|
1
1
|
require 'securerandom'
|
2
|
-
require 'active_support/
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'redis-objects'
|
3
4
|
|
4
5
|
require 'query_dam/version'
|
6
|
+
require 'query_dam/concerns/trackable'
|
5
7
|
|
6
8
|
module QueryDam
|
7
|
-
|
8
|
-
|
9
|
+
|
10
|
+
QUERY_EXPIRE = 5.minutes
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def watch_relation(relation)
|
14
|
+
key = SecureRandom.uuid
|
15
|
+
query = relation.where("`#{relation.model.table_name}`.`id` = #{key}").to_sql
|
16
|
+
query_hash = Redis::HashKey.new(key)
|
17
|
+
query_hash.bulk_set(model: relation.model.name, query: query)
|
18
|
+
query_hash.expire(QUERY_EXPIRE)
|
19
|
+
Redis::Set.new(relation.model.name) << key
|
20
|
+
key
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_updates(key)
|
24
|
+
query_hash = Redis::HashKey.new(key)
|
25
|
+
return nil unless query_hash.exists?
|
26
|
+
|
27
|
+
model = query_hash[:model].constantize
|
28
|
+
|
29
|
+
result = {
|
30
|
+
model: model,
|
31
|
+
updates: model.where(id: updates_set(key).to_a),
|
32
|
+
exclusions: exclusions_set(key).to_a.map(&:to_i) }
|
33
|
+
|
34
|
+
updates_set(key).clear
|
35
|
+
exclusions_set(key).clear
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def updates_set(key)
|
41
|
+
Redis::Set.new("#{key}-updates")
|
42
|
+
end
|
43
|
+
|
44
|
+
def exclusions_set(key)
|
45
|
+
Redis::Set.new("#{key}-exclusions")
|
46
|
+
end
|
47
|
+
end
|
9
48
|
end
|
@@ -3,23 +3,49 @@ module QueryDam
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
7
|
-
|
6
|
+
before_save :save_previous_query_dam_status
|
7
|
+
before_destroy :save_previous_query_dam_status
|
8
|
+
after_commit :update_query_dam
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
|
12
|
-
sql = Redis::HashKey.new(query_id)[:sql].gsub(query_id, id.to_s)
|
11
|
+
def save_previous_query_dam_status
|
12
|
+
return unless persisted?
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
@_query_dam_previous_status = {}
|
15
|
+
queries_set = Redis::Set.new(self.class.name)
|
16
|
+
queries_set.members.each do |key|
|
17
|
+
query_hash = Redis::HashKey.new(key)
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
unless query_hash.exists?
|
20
|
+
queries_set.delete(key)
|
21
|
+
next
|
22
|
+
end
|
23
|
+
|
24
|
+
query = query_hash[:query].gsub(key, id.to_s)
|
25
|
+
|
26
|
+
@_query_dam_previous_status[key] =
|
27
|
+
self.class.find_by_sql(query).any?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_query_dam
|
32
|
+
queries_set = Redis::Set.new(self.class.name)
|
33
|
+
queries_set.members.each do |key|
|
34
|
+
query_hash = Redis::HashKey.new(key)
|
35
|
+
|
36
|
+
unless query_hash.exists?
|
37
|
+
queries_set.delete(key)
|
38
|
+
next
|
39
|
+
end
|
40
|
+
|
41
|
+
query = query_hash[:query].gsub(key, id.to_s)
|
42
|
+
|
43
|
+
if self.class.find_by_sql(query).any?
|
44
|
+
QueryDam.updates_set(key) << id
|
45
|
+
QueryDam.exclusions_set(key).delete id
|
46
|
+
elsif @_query_dam_previous_status[key]
|
47
|
+
QueryDam.updates_set(key).delete id
|
48
|
+
QueryDam.exclusions_set(key) << id
|
23
49
|
end
|
24
50
|
end
|
25
51
|
end
|
data/lib/query_dam/version.rb
CHANGED
data/query_dam.gemspec
CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency 'redis', '>= 3.1.0'
|
22
22
|
spec.add_dependency 'redis-objects', '>= 0.8.0'
|
23
|
-
spec.add_dependency '
|
23
|
+
spec.add_dependency 'activerecord', '>= 4.0.0'
|
24
24
|
|
25
|
-
spec.add_development_dependency 'bundler',
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
26
26
|
spec.add_development_dependency 'rake'
|
27
27
|
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
28
28
|
spec.add_development_dependency 'sqlite3'
|
data/spec/models/item.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
describe QueryDam do
|
2
|
+
let!(:relation) { Item.where(name: 'pencil') }
|
3
|
+
let!(:key) { QueryDam.watch_relation(relation) }
|
4
|
+
|
5
|
+
specify 'item enters scope on creation' do
|
6
|
+
item = Item.create(name: 'pencil')
|
7
|
+
|
8
|
+
expect(QueryDam.get_updates(key)).to eq(
|
9
|
+
{model: Item, updates: [item], exclusions: []})
|
10
|
+
|
11
|
+
expect(QueryDam.get_updates(key)).to eq(
|
12
|
+
{model: Item, updates: [], exclusions: []})
|
13
|
+
end
|
14
|
+
|
15
|
+
specify 'item enters scope on update' do
|
16
|
+
item = Item.create
|
17
|
+
|
18
|
+
expect(QueryDam.get_updates(key)).to eq(
|
19
|
+
{model: Item, updates: [], exclusions: []})
|
20
|
+
|
21
|
+
item.update_attributes!(name: 'pencil')
|
22
|
+
|
23
|
+
expect(QueryDam.get_updates(key)).to eq(
|
24
|
+
{model: Item, updates: [item], exclusions: []})
|
25
|
+
end
|
26
|
+
|
27
|
+
specify 'item leaves scope on update' do
|
28
|
+
item = Item.create(name: 'pencil')
|
29
|
+
QueryDam.get_updates(key)
|
30
|
+
|
31
|
+
item.update_attributes!(name: 'pen')
|
32
|
+
expect(QueryDam.get_updates(key)).to eq(
|
33
|
+
{model: Item, updates: [], exclusions: [item.id]})
|
34
|
+
end
|
35
|
+
|
36
|
+
specify 'item leaves scope on deletion' do
|
37
|
+
item = Item.create(name: 'pencil')
|
38
|
+
QueryDam.get_updates(key)
|
39
|
+
|
40
|
+
item.destroy
|
41
|
+
expect(QueryDam.get_updates(key)).to eq(
|
42
|
+
{model: Item, updates: [], exclusions: [item.id]})
|
43
|
+
end
|
44
|
+
|
45
|
+
specify 'items gets updated' do
|
46
|
+
item = Item.create(name: 'pencil')
|
47
|
+
QueryDam.get_updates(key)
|
48
|
+
|
49
|
+
item.update_attributes!(quantity: 1)
|
50
|
+
expect(QueryDam.get_updates(key)).to eq(
|
51
|
+
{model: Item, updates: [item], exclusions: []})
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'query_dam'
|
2
|
+
require 'active_record'
|
3
|
+
require 'models/item'
|
4
|
+
|
1
5
|
RSpec.configure do |config|
|
2
6
|
config.order = :random
|
3
7
|
|
@@ -10,3 +14,17 @@ RSpec.configure do |config|
|
|
10
14
|
mocks.verify_partial_doubles = true
|
11
15
|
end
|
12
16
|
end
|
17
|
+
|
18
|
+
ActiveRecord::Base.establish_connection(
|
19
|
+
adapter: 'sqlite3',
|
20
|
+
database: 'spec/testdb.sqlite3')
|
21
|
+
|
22
|
+
ActiveRecord::Base.connection.execute 'drop table if exists items'
|
23
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
24
|
+
create table items (
|
25
|
+
id integer primary key,
|
26
|
+
name varchar(255),
|
27
|
+
quantity integer)
|
28
|
+
SQL
|
29
|
+
|
30
|
+
Redis.current = Redis.new(host: '127.0.0.1', port: 6379)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: query_dam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Kuznietsov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.8.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: activerecord
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - '>='
|
@@ -122,10 +122,11 @@ files:
|
|
122
122
|
- README.md
|
123
123
|
- Rakefile
|
124
124
|
- lib/query_dam.rb
|
125
|
-
- lib/query_dam/concerns/cacheable.rb
|
126
125
|
- lib/query_dam/concerns/trackable.rb
|
127
126
|
- lib/query_dam/version.rb
|
128
127
|
- query_dam.gemspec
|
128
|
+
- spec/models/item.rb
|
129
|
+
- spec/query_dam_spec.rb
|
129
130
|
- spec/spec_helper.rb
|
130
131
|
homepage: https://github.com/romankuznietsov/query_dam
|
131
132
|
licenses:
|
@@ -152,4 +153,6 @@ signing_key:
|
|
152
153
|
specification_version: 4
|
153
154
|
summary: Detecting query result changes
|
154
155
|
test_files:
|
156
|
+
- spec/models/item.rb
|
157
|
+
- spec/query_dam_spec.rb
|
155
158
|
- spec/spec_helper.rb
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module QueryDam
|
2
|
-
module Cacheable
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
def cache_query(relation)
|
6
|
-
query_id = SecureRandom.uuid
|
7
|
-
sql = relation.where("`#{relation.model.table_name}`.`id` = #{query_id}").to_sql
|
8
|
-
Redis::HashKey.new(query_id).bulk_set(sql: sql, model: relation.model.name)
|
9
|
-
Redis::Set.new(relation.model.name) << query_id
|
10
|
-
query_id
|
11
|
-
end
|
12
|
-
|
13
|
-
def query_updates(query_ids)
|
14
|
-
query_ids.map do |query_id|
|
15
|
-
model = Redis::HashKey.new(query_id)[:model].constantize
|
16
|
-
updated_set = Redis::Set.new("#{query_id}_updated")
|
17
|
-
excluded_set = Redis::Set.new("#{query_id}_excluded")
|
18
|
-
|
19
|
-
updated_ids = updated_set.to_a
|
20
|
-
excluded_ids = excluded_set.to_a
|
21
|
-
|
22
|
-
updated_set.clear
|
23
|
-
excluded_set.clear
|
24
|
-
|
25
|
-
updated_records = model.where(id: updated_ids)
|
26
|
-
updated_records = yield(updated_records) if block_given?
|
27
|
-
|
28
|
-
{query_id: query_id,
|
29
|
-
updated: updated_records,
|
30
|
-
excluded: excluded_ids}
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|