sequel_timelined 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 +7 -0
- data/.gitignore +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +96 -0
- data/LICENSE +0 -0
- data/README.md +25 -0
- data/Rakefile +5 -0
- data/lib/sequel_timelined.rb +7 -0
- data/lib/sequel_timelined/plugin.rb +55 -0
- data/lib/sequel_timelined/transaction_manager.rb +57 -0
- data/lib/sequel_timelined/transaction_middleware.rb +36 -0
- data/lib/sequel_timelined/utilities.rb +64 -0
- data/lib/sequel_timelined/version.rb +9 -0
- data/sequel_timelined.gemspec +30 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7f84dadb8e4cd0f30d69de8086dfa22122b78c7e
|
4
|
+
data.tar.gz: edf27b253671bc6f3d92cfd191b083c7125cc5ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1a6466b268a9220ed1d46845974dabf20325ecaf4b41bb4de225f5e35c5d3f795fa8fe3c8780dfd7fae08a5fd86887f1a0de9819939f8fb127c4908a5aaaa3b2
|
7
|
+
data.tar.gz: e046a7d97fcd69c6d9f0888c90327c41d47ef3bb9b738e2170001a266b87498d395345a7a44637d5ceb375b8dbbb753b27a872ad0a1c25741da7e15f39fd50ee
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
sequel_timelined (0.1.0)
|
5
|
+
registry-rails (~> 0.0)
|
6
|
+
sequel (~> 4.10)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionmailer (3.2.18)
|
12
|
+
actionpack (= 3.2.18)
|
13
|
+
mail (~> 2.5.4)
|
14
|
+
actionpack (3.2.18)
|
15
|
+
activemodel (= 3.2.18)
|
16
|
+
activesupport (= 3.2.18)
|
17
|
+
builder (~> 3.0.0)
|
18
|
+
erubis (~> 2.7.0)
|
19
|
+
journey (~> 1.0.4)
|
20
|
+
rack (~> 1.4.5)
|
21
|
+
rack-cache (~> 1.2)
|
22
|
+
rack-test (~> 0.6.1)
|
23
|
+
sprockets (~> 2.2.1)
|
24
|
+
activemodel (3.2.18)
|
25
|
+
activesupport (= 3.2.18)
|
26
|
+
builder (~> 3.0.0)
|
27
|
+
activerecord (3.2.18)
|
28
|
+
activemodel (= 3.2.18)
|
29
|
+
activesupport (= 3.2.18)
|
30
|
+
arel (~> 3.0.2)
|
31
|
+
tzinfo (~> 0.3.29)
|
32
|
+
activeresource (3.2.18)
|
33
|
+
activemodel (= 3.2.18)
|
34
|
+
activesupport (= 3.2.18)
|
35
|
+
activesupport (3.2.18)
|
36
|
+
i18n (~> 0.6, >= 0.6.4)
|
37
|
+
multi_json (~> 1.0)
|
38
|
+
arel (3.0.3)
|
39
|
+
builder (3.0.4)
|
40
|
+
erubis (2.7.0)
|
41
|
+
hike (1.2.3)
|
42
|
+
i18n (0.6.9)
|
43
|
+
journey (1.0.4)
|
44
|
+
json (1.8.1)
|
45
|
+
mail (2.5.4)
|
46
|
+
mime-types (~> 1.16)
|
47
|
+
treetop (~> 1.4.8)
|
48
|
+
mime-types (1.25.1)
|
49
|
+
multi_json (1.10.0)
|
50
|
+
polyglot (0.3.4)
|
51
|
+
rack (1.4.5)
|
52
|
+
rack-cache (1.2)
|
53
|
+
rack (>= 0.4)
|
54
|
+
rack-ssl (1.3.4)
|
55
|
+
rack
|
56
|
+
rack-test (0.6.2)
|
57
|
+
rack (>= 1.0)
|
58
|
+
rails (3.2.18)
|
59
|
+
actionmailer (= 3.2.18)
|
60
|
+
actionpack (= 3.2.18)
|
61
|
+
activerecord (= 3.2.18)
|
62
|
+
activeresource (= 3.2.18)
|
63
|
+
activesupport (= 3.2.18)
|
64
|
+
bundler (~> 1.0)
|
65
|
+
railties (= 3.2.18)
|
66
|
+
railties (3.2.18)
|
67
|
+
actionpack (= 3.2.18)
|
68
|
+
activesupport (= 3.2.18)
|
69
|
+
rack-ssl (~> 1.3.2)
|
70
|
+
rake (>= 0.8.7)
|
71
|
+
rdoc (~> 3.4)
|
72
|
+
thor (>= 0.14.6, < 2.0)
|
73
|
+
rake (10.3.1)
|
74
|
+
rdoc (3.12.2)
|
75
|
+
json (~> 1.4)
|
76
|
+
registry-rails (0.0.3)
|
77
|
+
rails (~> 3.1)
|
78
|
+
sequel (4.10.0)
|
79
|
+
sprockets (2.2.2)
|
80
|
+
hike (~> 1.2)
|
81
|
+
multi_json (~> 1.0)
|
82
|
+
rack (~> 1.0)
|
83
|
+
tilt (~> 1.1, != 1.3.0)
|
84
|
+
thor (0.19.1)
|
85
|
+
tilt (1.4.1)
|
86
|
+
treetop (1.4.15)
|
87
|
+
polyglot
|
88
|
+
polyglot (>= 0.3.1)
|
89
|
+
tzinfo (0.3.39)
|
90
|
+
|
91
|
+
PLATFORMS
|
92
|
+
ruby
|
93
|
+
|
94
|
+
DEPENDENCIES
|
95
|
+
rake (~> 10.3)
|
96
|
+
sequel_timelined!
|
data/LICENSE
ADDED
File without changes
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
dm-is-timelined
|
2
|
+
===============
|
3
|
+
|
4
|
+
This [sequel][1] plugin adds parallel copy-on-write to all models, copying all properties, changing the 'id' field
|
5
|
+
to 'timeline_id' and adding the properties 'timeline_action', 'timeline_timestamp'.
|
6
|
+
|
7
|
+
On create, delete and update, the model will be automatically copied to '''model_name_timeline''' to provide full
|
8
|
+
history and auditing capabilities without having to fall back to complex UNION statements.
|
9
|
+
|
10
|
+
Warning
|
11
|
+
-------
|
12
|
+
|
13
|
+
sequel-timelined will NOT provide helpers for creating views (yet).
|
14
|
+
|
15
|
+
sequel-timelined will ONLY work for models using an artificial key called 'id'.
|
16
|
+
|
17
|
+
TODO
|
18
|
+
----
|
19
|
+
|
20
|
+
- Add helpers for views.
|
21
|
+
- Handle composite keys gracefully.
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
[1]: http://http://sequel.rubyforge.org/
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
module Timelined
|
4
|
+
|
5
|
+
def self.apply(model)
|
6
|
+
self.add_timeline(model)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.add_timeline(model)
|
10
|
+
timeline_table_name = "#{model.name}Timelines".underscore.to_sym
|
11
|
+
timeline_model = Class.new(Sequel::Model(timeline_table_name))
|
12
|
+
timeline_model.class_eval do
|
13
|
+
def self.at_timestamp(timestamp)
|
14
|
+
self.timestamp_partition(timestamp).from_self.where(t1__row_number: 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.timestamp_partition(timestamp)
|
18
|
+
self.select_all(self.table_name).select_append do
|
19
|
+
row_number(:over, partition: :timeline_id, order: Sequel.desc(:timeline_timestamp)) {}
|
20
|
+
end.where {timeline_timestamp <= timestamp}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.join_at(timestamp, model, conditions={})
|
24
|
+
conditions[:row_number] = 1
|
25
|
+
self.at_timestamp(timestamp).join(model.timestamp_partition(timestamp), conditions)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
model.const_set(:Timeline, timeline_model)
|
29
|
+
end
|
30
|
+
|
31
|
+
module InstanceMethods
|
32
|
+
def transaction_manager
|
33
|
+
register = Registry::Rails::Register.instance
|
34
|
+
register.get(:timeline_transaction_manager, TransactionManager.new)
|
35
|
+
end
|
36
|
+
|
37
|
+
def write_timeline_object(additional_data = {})
|
38
|
+
values = self.values.dup.extract!(*self.columns)
|
39
|
+
values.merge!(additional_data)
|
40
|
+
self.transaction_manager.append(self.class, self.id, values)
|
41
|
+
end
|
42
|
+
|
43
|
+
def after_save
|
44
|
+
super
|
45
|
+
write_timeline_object
|
46
|
+
end
|
47
|
+
|
48
|
+
def after_destroy
|
49
|
+
super
|
50
|
+
write_timeline_object(timeline_action: 'deleted')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
module Timelined
|
4
|
+
|
5
|
+
class TransactionManager
|
6
|
+
attr_accessor :timeline_changes, :timestamp_generator
|
7
|
+
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.timeline_changes = {}
|
11
|
+
self.timestamp_generator = Utilities::TimestampGenerator.new.method(:timestamp)
|
12
|
+
end
|
13
|
+
|
14
|
+
def timeline_timestamp
|
15
|
+
self.timestamp_generator.()
|
16
|
+
end
|
17
|
+
|
18
|
+
def append(model, id, data)
|
19
|
+
self.timeline_changes[model] ||= {}
|
20
|
+
self.timeline_changes[model][id] = data
|
21
|
+
end
|
22
|
+
|
23
|
+
def write_timelined_objects
|
24
|
+
timestamp = self.timeline_timestamp
|
25
|
+
self.timeline_changes.each do |model, objects|
|
26
|
+
data = objects.values.map do |values|
|
27
|
+
values[:timeline_id] = values.delete(:id)
|
28
|
+
values[:timeline_timestamp] = timestamp
|
29
|
+
values
|
30
|
+
end
|
31
|
+
model::Timeline.multi_insert(data)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.db
|
36
|
+
Sequel::Model.db
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.timelined_transaction
|
40
|
+
if block_given?
|
41
|
+
self.db.transaction do
|
42
|
+
register = Registry::Rails::Register.instance
|
43
|
+
ttm = self.new
|
44
|
+
register.set(:timeline_transaction_manager, ttm)
|
45
|
+
result = yield(ttm)
|
46
|
+
ttm.write_timelined_objects
|
47
|
+
result
|
48
|
+
end
|
49
|
+
else
|
50
|
+
raise "No block given"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
|
4
|
+
module Sequel::Plugins::Timelined
|
5
|
+
|
6
|
+
class TransactionMiddleware
|
7
|
+
|
8
|
+
def initialize(application)
|
9
|
+
@app = application
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
method = env['REQUEST_METHOD']
|
14
|
+
if %w[PUT POST].include?(method)
|
15
|
+
self.call_with_transaction(env)
|
16
|
+
else
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def call_with_transaction(env)
|
22
|
+
status, headers, body = [nil, nil, nil]
|
23
|
+
TransactionManager.timelined_transaction do
|
24
|
+
status, headers, body = @app.call(env)
|
25
|
+
unless (200...400).include?(status)
|
26
|
+
# rollback database changes on any status that is not success (2xx)
|
27
|
+
# or redirect (3xx)
|
28
|
+
raise Sequel::Rollback
|
29
|
+
end
|
30
|
+
end
|
31
|
+
[status, headers, body]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
module Timelined
|
4
|
+
|
5
|
+
class Utilities
|
6
|
+
|
7
|
+
class TimestampGenerator
|
8
|
+
|
9
|
+
def timestamp
|
10
|
+
@timestamp ||= DateTime.now
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class SchemaGenerator < Sequel::Schema::CreateTableGenerator
|
16
|
+
|
17
|
+
def column(name, type, opts = {})
|
18
|
+
super(name, type)
|
19
|
+
end
|
20
|
+
|
21
|
+
def foreign_key(name, table=nil, options={})
|
22
|
+
self.column(name, :integer)
|
23
|
+
end
|
24
|
+
|
25
|
+
def constraint(name, *args, &block)
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.db
|
31
|
+
Sequel::Model.db
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.create_table(name, options={}, &block)
|
35
|
+
generator, timeline_generator = self.create_table_generator(&block)
|
36
|
+
timelined_name = "#{name.to_s.singularize}_timelines"
|
37
|
+
self.db.transaction do
|
38
|
+
self.db.create_table(name, generator)
|
39
|
+
self.db.create_table(timelined_name, timeline_generator)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.create_table_generator(&block)
|
44
|
+
generator = Sequel::Schema::CreateTableGenerator.new(self.db, &block)
|
45
|
+
timeline_generator = self::SchemaGenerator.new(self.db, &block)
|
46
|
+
self.timeline_fields.each do |field_desc|
|
47
|
+
timeline_generator.columns << field_desc
|
48
|
+
end
|
49
|
+
|
50
|
+
[generator, timeline_generator]
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.timeline_fields
|
54
|
+
[
|
55
|
+
{ name: :timeline_timestamp, type: 'timestamp with time zone' },
|
56
|
+
{ name: :timeline_id, type: :integer },
|
57
|
+
{ name: :timeline_action, type: 'character varying(255)' }
|
58
|
+
]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'lib', 'sequel_timelined', 'version')
|
4
|
+
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "sequel_timelined"
|
9
|
+
s.version = Sequel::Plugins::Timelined::VERSION
|
10
|
+
|
11
|
+
|
12
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
13
|
+
s.authors = ["Andreas Kopecky <andreas.kopecky@radarservices.com>", "Martin Natano <martin.natano@radarservices.com"]
|
14
|
+
s.date = Date.today.strftime
|
15
|
+
s.description = "Sequel plugin enabling extended timeline storage as parallel copy-on-write of models"
|
16
|
+
s.email = "gems [a] radarservices [d] com"
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
19
|
+
s.extra_rdoc_files = %w[LICENSE README.md]
|
20
|
+
|
21
|
+
s.homepage = "http://github.com/rs-dev/sequel_timelined"
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
s.rubygems_version = "2.2.2"
|
24
|
+
s.summary = "Sequel plugin enabling extended timeline storage as parallel copy-on-write of models"
|
25
|
+
|
26
|
+
|
27
|
+
s.add_runtime_dependency(%q<sequel>, ["~> 4.10"])
|
28
|
+
s.add_runtime_dependency(%q<registry-rails>, ["~> 0.0"])
|
29
|
+
s.add_development_dependency(%q<rake>, ["~> 10.3"])
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sequel_timelined
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andreas Kopecky <andreas.kopecky@radarservices.com>
|
8
|
+
- Martin Natano <martin.natano@radarservices.com
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-06-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '4.10'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '4.10'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: registry-rails
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0.0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.3'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '10.3'
|
56
|
+
description: Sequel plugin enabling extended timeline storage as parallel copy-on-write
|
57
|
+
of models
|
58
|
+
email: gems [a] radarservices [d] com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files:
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
files:
|
65
|
+
- ".gitignore"
|
66
|
+
- Gemfile
|
67
|
+
- Gemfile.lock
|
68
|
+
- LICENSE
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- lib/sequel_timelined.rb
|
72
|
+
- lib/sequel_timelined/plugin.rb
|
73
|
+
- lib/sequel_timelined/transaction_manager.rb
|
74
|
+
- lib/sequel_timelined/transaction_middleware.rb
|
75
|
+
- lib/sequel_timelined/utilities.rb
|
76
|
+
- lib/sequel_timelined/version.rb
|
77
|
+
- sequel_timelined.gemspec
|
78
|
+
homepage: http://github.com/rs-dev/sequel_timelined
|
79
|
+
licenses: []
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.2.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Sequel plugin enabling extended timeline storage as parallel copy-on-write
|
101
|
+
of models
|
102
|
+
test_files: []
|