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 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: []