sequel_timelined 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,13 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
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,5 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+
5
+ FileList['tasks/**/*.rake'].each { |task| import task }
@@ -0,0 +1,7 @@
1
+ require 'sequel'
2
+ require 'registry-rails'
3
+
4
+ require 'sequel_timelined/plugin'
5
+ require 'sequel_timelined/transaction_manager'
6
+ require 'sequel_timelined/transaction_middleware'
7
+ require 'sequel_timelined/utilities'
@@ -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,9 @@
1
+ module Sequel
2
+ module Plugins
3
+ module Timelined
4
+
5
+ VERSION = '0.1.0'
6
+
7
+ end
8
+ end
9
+ 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: []