saseo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.saseo.standalone_migrations +6 -0
- data/.saseo_source.standalone_migrations +6 -0
- data/.travis.yml +38 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +38 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/db/saseo/migrate/20151028181502_initial_schema.rb +18 -0
- data/db/saseo/schema.rb +32 -0
- data/db/saseo_source/migrate/20151028181502_initial_schema.rb +16 -0
- data/db/saseo_source/schema.rb +30 -0
- data/exe/saseo_consumer +6 -0
- data/exe/saseo_publisher +6 -0
- data/lib/generators/saseo/install_generator.rb +35 -0
- data/lib/generators/saseo/templates/add_saseo_trigger.rb.erb +14 -0
- data/lib/generators/saseo/templates/add_saseo_trigger_function.rb +105 -0
- data/lib/generators/saseo/trigger_generator.rb +42 -0
- data/lib/saseo/config/defaults.rb +41 -0
- data/lib/saseo/config/saseo_database.yml +14 -0
- data/lib/saseo/config/saseo_source_database.yml +14 -0
- data/lib/saseo/config.rb +52 -0
- data/lib/saseo/extensions/active_record/detector.rb +17 -0
- data/lib/saseo/extensions/active_record/patcher.rb +32 -0
- data/lib/saseo/extensions/active_record/v_3/connection_adapters/postgresql_adapter.rb +26 -0
- data/lib/saseo/extensions/active_record/v_3.rb +11 -0
- data/lib/saseo/extensions/active_record/v_4/connection_adapters/postgresql/database_statements.rb +29 -0
- data/lib/saseo/extensions/active_record/v_4.rb +11 -0
- data/lib/saseo/extensions/active_record.rb +9 -0
- data/lib/saseo/extensions.rb +7 -0
- data/lib/saseo/models/base.rb +51 -0
- data/lib/saseo/models/source/base.rb +22 -0
- data/lib/saseo/models/source/version.rb +20 -0
- data/lib/saseo/models/version.rb +19 -0
- data/lib/saseo/persistence/consumer.rb +25 -0
- data/lib/saseo/persistence/persistor.rb +37 -0
- data/lib/saseo/persistence.rb +8 -0
- data/lib/saseo/publishing/data_change_message.rb +43 -0
- data/lib/saseo/publishing/publisher.rb +95 -0
- data/lib/saseo/publishing.rb +8 -0
- data/lib/saseo/version.rb +3 -0
- data/lib/saseo/whodunnit.rb +41 -0
- data/lib/saseo.rb +10 -0
- data/saseo.example.yml +12 -0
- data/saseo.gemspec +34 -0
- data/tasks/bump.rake +30 -0
- metadata +264 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'saseo/extensions/active_record/detector'
|
2
|
+
|
3
|
+
module Saseo
|
4
|
+
module Extensions
|
5
|
+
module ActiveRecord
|
6
|
+
module Patcher
|
7
|
+
extend self
|
8
|
+
|
9
|
+
UnsupportedActiveRecordVersion = Class.new(RuntimeError)
|
10
|
+
|
11
|
+
EXTENSIONS = {
|
12
|
+
3 => ['saseo/extensions/active_record/v_3'],
|
13
|
+
4 => ['saseo/extensions/active_record/v_4'],
|
14
|
+
}
|
15
|
+
|
16
|
+
def patch_active_record!
|
17
|
+
return false unless Saseo::Extensions::ActiveRecord::Detector.active_record_detected?
|
18
|
+
version = Saseo::Extensions::ActiveRecord::Detector.active_record_version
|
19
|
+
|
20
|
+
raise UnsupportedActiveRecordVersion.new unless EXTENSIONS.keys.include? version
|
21
|
+
EXTENSIONS[version].each do |version_extension|
|
22
|
+
require version_extension
|
23
|
+
end
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
patch_active_record!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'saseo/extensions/active_record/detector'
|
2
|
+
|
3
|
+
require 'saseo/whodunnit'
|
4
|
+
|
5
|
+
module Saseo
|
6
|
+
module Extensions
|
7
|
+
module ActiveRecord
|
8
|
+
module V3
|
9
|
+
module ConnectionAdapters
|
10
|
+
module PostgreSQLAdapter
|
11
|
+
extend self
|
12
|
+
def begin_db_transaction
|
13
|
+
execute "BEGIN"
|
14
|
+
Saseo::Whodunnit.set_db_whodunnit
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if Saseo::Extensions::ActiveRecord::Detector.active_record_version == 3
|
24
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
25
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend ::Saseo::Extensions::ActiveRecord::V3::ConnectionAdapters::PostgreSQLAdapter
|
26
|
+
end
|
data/lib/saseo/extensions/active_record/v_4/connection_adapters/postgresql/database_statements.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'saseo/extensions/active_record/detector'
|
2
|
+
|
3
|
+
require 'saseo/whodunnit'
|
4
|
+
|
5
|
+
module Saseo
|
6
|
+
module Extensions
|
7
|
+
module ActiveRecord
|
8
|
+
module V4
|
9
|
+
module ConnectionAdapters
|
10
|
+
module PostgreSQL
|
11
|
+
module DatabaseStatements
|
12
|
+
extend self
|
13
|
+
|
14
|
+
def begin_db_transaction
|
15
|
+
execute "BEGIN"
|
16
|
+
Saseo::Whodunnit.set_db_whodunnit
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if Saseo::Extensions::ActiveRecord::Detector.active_record_version == 3
|
27
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
28
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements.prepend ::Saseo::Extensions::ActiveRecord::V4::ConnectionAdapters::PostgreSQL::DatabaseStatements
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'yaml'
|
3
|
+
require 'saseo/config'
|
4
|
+
|
5
|
+
module Saseo
|
6
|
+
module Models
|
7
|
+
class Base < ActiveRecord::Base
|
8
|
+
self.abstract_class = true
|
9
|
+
class << self
|
10
|
+
def database_config_path
|
11
|
+
Saseo.config.database_config_path
|
12
|
+
end
|
13
|
+
|
14
|
+
def database_config_url
|
15
|
+
Saseo.config.database_url
|
16
|
+
end
|
17
|
+
|
18
|
+
def database_config
|
19
|
+
database_config_url || database_config_from_file
|
20
|
+
end
|
21
|
+
|
22
|
+
def database_config_from_file
|
23
|
+
begin
|
24
|
+
database_config_from_relative_path
|
25
|
+
rescue
|
26
|
+
database_config_from_load_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def database_config_from_relative_path
|
31
|
+
YAML::load(File.open(File.expand_path(database_config_path)))[Saseo.config.env]
|
32
|
+
end
|
33
|
+
|
34
|
+
def database_config_from_load_path
|
35
|
+
config = nil
|
36
|
+
config_path = database_config_path
|
37
|
+
$:.each do |load_path|
|
38
|
+
begin
|
39
|
+
config = YAML::load(File.open(File.expand_path File.join(load_path, config_path)))[Saseo.config.env]
|
40
|
+
break
|
41
|
+
rescue
|
42
|
+
end
|
43
|
+
end
|
44
|
+
config
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
establish_connection database_config
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'saseo/models/base'
|
2
|
+
|
3
|
+
module Saseo
|
4
|
+
module Models
|
5
|
+
module Source
|
6
|
+
class Base < Saseo::Models::Base
|
7
|
+
self.abstract_class = true
|
8
|
+
class << self
|
9
|
+
def database_config_path
|
10
|
+
Saseo.config.source_database_config_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def database_config_url
|
14
|
+
Saseo.config.source_database_url
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
establish_connection database_config
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'saseo/models/source/base'
|
2
|
+
require 'saseo/config'
|
3
|
+
|
4
|
+
module Saseo
|
5
|
+
module Models
|
6
|
+
module Source
|
7
|
+
class Version < Saseo::Models::Source::Base
|
8
|
+
self.table_name = Saseo.config.source_table_name
|
9
|
+
|
10
|
+
validates :transaction_id, presence: true
|
11
|
+
validates :table_name, presence: true
|
12
|
+
validates :action_timestamp, presence: true
|
13
|
+
validates :action, presence: true, inclusion: {in: %w[INSERT UPDATE DELETE]}
|
14
|
+
validates :whodunnit, presence: true
|
15
|
+
validates :old_data, presence: true, unless: ->(version) { version.new_data.present? }
|
16
|
+
validates :new_data, presence: true, unless: ->(version) { version.old_data.present? }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'saseo/models/base'
|
2
|
+
require 'saseo/config'
|
3
|
+
|
4
|
+
module Saseo
|
5
|
+
module Models
|
6
|
+
class Version < Saseo::Models::Base
|
7
|
+
self.table_name = Saseo.config.table_name
|
8
|
+
|
9
|
+
validates :transaction_id, presence: true
|
10
|
+
validates :table_name, presence: true
|
11
|
+
validates :item_id, presence: true
|
12
|
+
validates :action_timestamp, presence: true
|
13
|
+
validates :action, presence: true, inclusion: {in: %w[INSERT UPDATE DELETE]}
|
14
|
+
validates :whodunnit, presence: true
|
15
|
+
validates :old_data, presence: true, unless: ->(version) { version.new_data.present? }
|
16
|
+
validates :new_data, presence: true, unless: ->(version) { version.old_data.present? }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'philotic/consumer'
|
2
|
+
require 'saseo/config'
|
3
|
+
require 'saseo/persistence/persistor'
|
4
|
+
|
5
|
+
module Saseo
|
6
|
+
module Persistence
|
7
|
+
class Consumer < Philotic::Consumer
|
8
|
+
# subscribe_to philotic_message_type: :'saseo.record_audit'
|
9
|
+
subscribe_to Saseo.config.consumer_philotic_subscription
|
10
|
+
|
11
|
+
manually_acknowledge
|
12
|
+
|
13
|
+
def consume(message)
|
14
|
+
begin
|
15
|
+
Saseo::Persistence::Persistor.persist! message
|
16
|
+
|
17
|
+
acknowledge message
|
18
|
+
rescue => e
|
19
|
+
# be sure to create a queue that monitors for saseo_consumer_error: true
|
20
|
+
Philotic::Message.publish({saseo_consumer_error: true, message: e.message, error_class: e.class.name, trace: e.backtrace})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'saseo/models/version'
|
3
|
+
|
4
|
+
module Saseo
|
5
|
+
module Persistence
|
6
|
+
module Persistor
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def version_attributes
|
10
|
+
%i[
|
11
|
+
id
|
12
|
+
transaction_id
|
13
|
+
table_name
|
14
|
+
item_id
|
15
|
+
item_uuid
|
16
|
+
action_timestamp
|
17
|
+
action
|
18
|
+
whodunnit
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
def persist!(message)
|
23
|
+
version_record = Saseo::Models::Version.new
|
24
|
+
|
25
|
+
version_attributes.each do |attr|
|
26
|
+
version_record.send("#{attr}=", message.send(attr))
|
27
|
+
end
|
28
|
+
|
29
|
+
# ActiveRecord 3 doesn't handle jsonb columns properly
|
30
|
+
version_record.old_data = message.old_data && Oj.dump(message.old_data)
|
31
|
+
version_record.new_data = message.new_data && Oj.dump(message.new_data)
|
32
|
+
|
33
|
+
version_record.save!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'philotic/message'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
module Saseo
|
5
|
+
module Publishing
|
6
|
+
class DataChangeMessage < Philotic::Message
|
7
|
+
|
8
|
+
COMPONENT = :saseo
|
9
|
+
MESSAGE_TYPE = :'saseo.record_audit'
|
10
|
+
|
11
|
+
attr_routable :table_name, :action, :whodunnit, :item_id, :item_uuid, :transaction_id
|
12
|
+
attr_payload :id, :old_data, :new_data, :action_timestamp
|
13
|
+
|
14
|
+
def initialize(version)
|
15
|
+
|
16
|
+
super({})
|
17
|
+
@id = version.id
|
18
|
+
@transaction_id = version.transaction_id
|
19
|
+
@table_name = version.table_name
|
20
|
+
@action = version.action
|
21
|
+
@whodunnit = version.whodunnit
|
22
|
+
@action_timestamp = version.action_timestamp
|
23
|
+
|
24
|
+
# ActiveRecord 3 doesn't handle jsonb columns properly
|
25
|
+
@old_data = ensure_json_load version.old_data
|
26
|
+
@new_data = ensure_json_load version.new_data
|
27
|
+
|
28
|
+
@item_id = @new_data ? @new_data['id'] : @old_data['id']
|
29
|
+
@item_uuid = @new_data ? @new_data['uuid'] : @old_data['uuid']
|
30
|
+
|
31
|
+
@philotic_component = COMPONENT
|
32
|
+
@philotic_message_type = MESSAGE_TYPE
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def ensure_json_load(val)
|
38
|
+
return val unless val.is_a? String
|
39
|
+
Oj.load val
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'saseo/config'
|
3
|
+
require 'saseo/publishing/data_change_message'
|
4
|
+
require 'saseo/models/source/version'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
module Saseo
|
8
|
+
module Publishing
|
9
|
+
class Publisher
|
10
|
+
|
11
|
+
attr_reader :channels
|
12
|
+
attr_writer :logger
|
13
|
+
|
14
|
+
def initialize(channels: nil)
|
15
|
+
self.channels = channels
|
16
|
+
end
|
17
|
+
|
18
|
+
def logger
|
19
|
+
@logger ||= Logger.new(STDOUT)
|
20
|
+
end
|
21
|
+
|
22
|
+
def channels=(val)
|
23
|
+
if val == :all
|
24
|
+
tables = Saseo::Models::Source::Base.connection.tables
|
25
|
+
|
26
|
+
@channels = tables.reduce([]) do |channels, table|
|
27
|
+
%w[insert update delete].each do |operation|
|
28
|
+
channels << "SASEO_#{table}_#{operation}_AUDIT".upcase
|
29
|
+
end
|
30
|
+
channels
|
31
|
+
end
|
32
|
+
else
|
33
|
+
@channels = Array(val).flatten
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def run(channels: :all, loop: true, timeout: nil)
|
38
|
+
self.channels = channels
|
39
|
+
logger.info "listening on #{self.channels}..."
|
40
|
+
|
41
|
+
listen channels: self.channels, loop: loop, timeout: timeout do |channel, pid, payload|
|
42
|
+
uuid = payload
|
43
|
+
Saseo::Models::Source::Base.transaction do
|
44
|
+
|
45
|
+
record = Saseo::Models::Source::Version.find(uuid)
|
46
|
+
Saseo::Publishing::DataChangeMessage.publish(record)
|
47
|
+
record.delete
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def notify(channel, payload)
|
53
|
+
conn = Saseo::Models::Source::Base.connection.instance_variable_get(:@connection)
|
54
|
+
conn.async_exec "SELECT pg_notify('#{channel}', '#{payload.to_s}')"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Heavily cribbed from Sequel:
|
58
|
+
# https://github.com/jeremyevans/sequel/blob/c6678741ce34aac52cff966ff6a6288ccb8d5b75/lib/sequel/adapters/postgres.rb#L440
|
59
|
+
def listen(channels:, after_listen: nil, timeout: nil, loop: false, &block)
|
60
|
+
if loop && !block
|
61
|
+
raise ArgumentError, 'calling #listen with :loop requires a block'
|
62
|
+
end
|
63
|
+
channels = Array(channels)
|
64
|
+
|
65
|
+
timeout_block = timeout.respond_to?(:call) ? timeout : proc { timeout }
|
66
|
+
loop_callable = loop.respond_to?(:call)
|
67
|
+
Saseo::Models::Source::Base.connection_pool.with_connection do |connection|
|
68
|
+
begin
|
69
|
+
conn = connection.instance_variable_get(:@connection)
|
70
|
+
channels.each do |channel|
|
71
|
+
conn.async_exec "LISTEN \"#{channel}\""
|
72
|
+
logger.debug { "listening to #{channel}..." }
|
73
|
+
|
74
|
+
end
|
75
|
+
after_listen.call(conn) if after_listen
|
76
|
+
|
77
|
+
if loop
|
78
|
+
|
79
|
+
catch(:stop) do
|
80
|
+
loop do
|
81
|
+
conn.wait_for_notify(timeout_block.call, &block)
|
82
|
+
loop.call(conn) if loop_callable
|
83
|
+
end
|
84
|
+
end
|
85
|
+
else
|
86
|
+
conn.wait_for_notify(timeout_block.call, &block)
|
87
|
+
end
|
88
|
+
ensure
|
89
|
+
conn.async_exec 'UNLISTEN *'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'saseo/extensions/active_record/detector'
|
2
|
+
|
3
|
+
module Saseo
|
4
|
+
|
5
|
+
module Whodunnit
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def whodunnit
|
9
|
+
@@whodunnit ||= nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def whodunnit=(val)
|
13
|
+
@@whodunnit = val
|
14
|
+
if Saseo::Extensions::ActiveRecord::Detector.active_record_detected? && ActiveRecord::Base.connected?
|
15
|
+
set_db_whodunnit(whodunnit)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def impersonate(who)
|
20
|
+
whodidit = whodunnit
|
21
|
+
@@whodunnit = who
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
@@whodunnit = whodidit
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_db_whodunnit(who = nil)
|
28
|
+
who ||= whodunnit
|
29
|
+
connection.execute "SET saseo.whodunnit TO '#{connection.quote_string who.to_s}'"
|
30
|
+
end
|
31
|
+
|
32
|
+
def connection
|
33
|
+
return ActiveRecord::Base.connection if Saseo::Extensions::ActiveRecord::Detector.active_record_detected? && ActiveRecord::Base.connected?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
extend Forwardable
|
39
|
+
def_delegators Saseo::Whodunnit, :whodunnit, :whodunnit=, :impersonate
|
40
|
+
end
|
41
|
+
end
|
data/lib/saseo.rb
ADDED
data/saseo.example.yml
ADDED
data/saseo.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'saseo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'saseo'
|
8
|
+
spec.version = Saseo::VERSION
|
9
|
+
spec.authors = ['Nathan Keyes']
|
10
|
+
spec.email = ['nathan.keyes@avantcredit.com']
|
11
|
+
|
12
|
+
spec.summary = %q{RabbitMQ based replacement for PaperTrail}
|
13
|
+
spec.description = %q{RabbitMQ based replacement for PaperTrail}
|
14
|
+
spec.homepage = 'https://github.com/avantcredit/saseo'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($\).reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'awesome_print'
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
23
|
+
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4'
|
24
|
+
spec.add_development_dependency 'database_cleaner'
|
25
|
+
spec.add_development_dependency 'generator_spec'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'rspec'
|
28
|
+
|
29
|
+
spec.add_dependency 'oj'
|
30
|
+
spec.add_dependency 'pg'
|
31
|
+
spec.add_dependency 'philotic', '~> 1.2'
|
32
|
+
spec.add_dependency 'rails', '>= 3.2'
|
33
|
+
spec.add_dependency 'standalone_migrations', '>= 2.1.5'
|
34
|
+
end
|
data/tasks/bump.rake
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
desc 'shortcut for bump:patch'
|
2
|
+
task bump: 'bump:patch'
|
3
|
+
|
4
|
+
namespace :bump do
|
5
|
+
desc 'Bump x.y.Z'
|
6
|
+
task :patch
|
7
|
+
|
8
|
+
desc 'Bump x.Y.z'
|
9
|
+
task :minor
|
10
|
+
|
11
|
+
desc 'Bump X.y.z'
|
12
|
+
task :major
|
13
|
+
end
|
14
|
+
|
15
|
+
# extracted and modified from https://github.com/grosser/project_template
|
16
|
+
rule /^bump:.*/ do |t|
|
17
|
+
sh "git status | grep 'nothing to commit'" # ensure we are not dirty
|
18
|
+
index = ['major', 'minor', 'patch'].index(t.name.split(':').last)
|
19
|
+
file = 'lib/saseo/version.rb'
|
20
|
+
|
21
|
+
version_file = File.read(file)
|
22
|
+
old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
|
23
|
+
version_parts[index] = version_parts[index].to_i + 1
|
24
|
+
version_parts[2] = 0 if index < 2
|
25
|
+
version_parts[1] = 0 if index < 1
|
26
|
+
new_version = version_parts * '.'
|
27
|
+
File.open(file, 'w') { |f| f.write(version_file.sub(old_version, new_version)) }
|
28
|
+
|
29
|
+
sh "bundle && git add #{file} && git commit -m 'bump version to #{new_version}'"
|
30
|
+
end
|