scripter 0.0.1

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: 22716daa173876308969ce95a93267e6c84ca0f4
4
+ data.tar.gz: 791bde4324ef837160d794458bc168145e658d61
5
+ SHA512:
6
+ metadata.gz: b050f637350bc367bbe0f05e9b96d87b22b54689da5353e9a1ce367d27f1ffae1ee4a1d6202e2f94d3ce0caacbd23acf62eb3d2f9b3ae8e37b4d73740785aa72
7
+ data.tar.gz: 2e5876825ccc3d10ed0c9c4fb4014f06a84eeaeb8a99ea150cf4335bf8dd5babaa04d4b7e62753db1cbda78c61d4c9e8f5ab46b21a44b5ac6e592d1b0bebd5a1
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in scripter.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Artūrs Mekšs
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Scripter
2
+
3
+ Library for reducing of boilerplate in ruby scripts with possibility to run fault tolerant iterations, for example mass notifications, or support scripts
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'scripter'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install scripter
20
+
21
+ ## Usage
22
+
23
+ Usage example:
24
+ ```ruby
25
+ class MyScripterEnabledClass < Scripter::Base
26
+ #ENV variables which will be assigned to instance
27
+ env_variables :test_env
28
+
29
+ def execute
30
+ # your specific execution code goes here
31
+ # Note: use #perform_iteration helper with block in order to make fault tolerant iterations
32
+ end
33
+
34
+ def on_exit
35
+ # your reporting scripts goes here
36
+ # Note: #valid?, invalid?, #errors_grouped and #errors_count methods can be useful here
37
+ end
38
+ end
39
+ ```
40
+
41
+ Call example:
42
+ ```ruby
43
+ MyScripterEnabledClass.execute
44
+ ```
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it ( https://github.com/[my-github-username]/scripter/fork )
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,65 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Scripter
3
+ class Base
4
+ include Scripter::Errors
5
+ include Scripter::Logger
6
+ include Scripter::EnvVariables
7
+ include Scripter::IterationHistory
8
+
9
+ env_variables :use_cache, :dry_run, :force_run
10
+
11
+ def self.set_custom_executable_name(name)
12
+ class_eval %{
13
+ def self.#{name}
14
+ self.execute(:#{name})
15
+ end
16
+ }
17
+ end
18
+
19
+ def initialize
20
+ yield self if block_given?
21
+ self
22
+ end
23
+
24
+ def self.execute(iteration_function_name=:execute)
25
+ self.new do |scripter_instance|
26
+ # exits immediately without raising an exception on ctrl+c
27
+ trap("SIGINT") { exit! }
28
+
29
+ scripter_instance.with_fault_monitoring do
30
+ scripter_instance.instance_variable_set :@iteration_idx, 0
31
+ scripter_instance.public_send(iteration_function_name)
32
+ end
33
+
34
+ scripter_instance.on_exit if scripter_instance.respond_to?(:on_exit)
35
+ end
36
+ end
37
+
38
+ def with_fault_monitoring
39
+ log :info, "============================================== START =============================================="
40
+ begin
41
+ log :info, "Performing: #{self.inspect}"
42
+ yield
43
+ rescue Exception => error
44
+ add_error(error)
45
+ ensure
46
+ log :info, "=============================================== END ==============================================="
47
+ end
48
+ end
49
+
50
+ def perform_iteration(iteration_id, iteration_meta={}, verbose=true)
51
+ @iteration_idx += 1
52
+ begin
53
+ unless iteration_processed?(iteration_id)
54
+ yield
55
+ iteration_processed!(iteration_id)
56
+ end
57
+ rescue Exception => error
58
+ add_error(error, iteration_meta, verbose)
59
+ if @iteration_idx == 100 && errors_count > 50
60
+ raise "Iteration was interrupted as error rate was too big (#{errors_count.to_f / @iteration_idx.to_f * 100.0}%)"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'active_support'
3
+
4
+
5
+ module Scripter
6
+ class CacheStore
7
+ def initialize
8
+ @cache = defined?(Rails) ? Rails.cache : ActiveSupport::Cache.lookup_store(:file_store, 'tmp/cache')
9
+ end
10
+
11
+ def read(*args)
12
+ @cache.read(*args)
13
+ end
14
+
15
+ def write(*args)
16
+ @cache.write(*args)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Scripter
3
+ module EnvVariables
4
+
5
+ def self.included(receiver)
6
+ receiver.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def env_variables(*variables)
11
+ variables.each do |env_variable|
12
+ class_eval %{
13
+ def #{env_variable}
14
+ env_variables[:#{env_variable}]
15
+ end
16
+ }
17
+ end
18
+ end
19
+ end
20
+
21
+ def raw_env_variables
22
+ Hash[command_line_arguments.select{|arg| arg.include?('=')}.map{|arg| arg.split("=")}]
23
+ end
24
+
25
+ def env_variables
26
+ @env_variables ||= begin
27
+ env_variables = raw_env_variables.map do |key, value|
28
+ nomalized_key = key.downcase.to_sym
29
+ [nomalized_key, type_cast_env_variable(nomalized_key, value)]
30
+ end
31
+ Hash[env_variables]
32
+ end
33
+ end
34
+
35
+ # You can override this method in order to add additional typecasts for custom params,
36
+ # but don't forget to call super in else block of your switch statement
37
+ def type_cast_env_variable(name, value)
38
+ case name
39
+ when :date
40
+ ::Date.parse(value, false) unless value.to_s.empty?
41
+ when :dry_run, :force_run, :use_cache
42
+ !value.to_s.empty?
43
+ else
44
+ value
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def command_line_arguments
51
+ @command_line_arguments ||= ARGV
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Scripter
3
+ module Errors
4
+
5
+ def errors
6
+ @errors ||= []
7
+ end
8
+
9
+ def errors_grouped
10
+ @errors_grouped ||= errors.group_by{|err| err[:category] }
11
+ end
12
+
13
+ def errors_count
14
+ errors.count
15
+ end
16
+
17
+ def valid?
18
+ errors.empty?
19
+ end
20
+
21
+ def invalid?
22
+ !valid?
23
+ end
24
+
25
+ def add_error(error, meta_hash={}, error_to_log=true)
26
+ error_hash = normalize_error(error)
27
+ log_error(error_hash) if error_to_log
28
+ errors << error_hash.merge(meta_hash)
29
+ end
30
+
31
+ private
32
+
33
+ def normalize_error(error)
34
+ error.is_a?(Hash) ? error : { message: error.to_s, category: error.backtrace }
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Scripter
3
+ module IterationHistory
4
+
5
+ attr_writer :cache_store
6
+ def cache_store
7
+ @cache_store ||= Scripter::CacheStore.new
8
+ end
9
+
10
+ def iteration_processed!(iteration_item_id)
11
+ return if iteration_item_id.to_s.empty?
12
+ cache_store.write(calculate_cache_key(iteration_item_id), true, expires_in: 24 * 3600)
13
+ end
14
+
15
+ def iteration_processed?(iteration_item_id)
16
+ return false unless use_cache
17
+ !!cache_store.read(calculate_cache_key(iteration_item_id))
18
+ end
19
+
20
+ private
21
+
22
+ def calculate_cache_key(iteration_item_id)
23
+ "#{self.class}_#{iteration_item_id}"
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'logger'
3
+
4
+ module Scripter
5
+ module Logger
6
+
7
+ attr_writer :logger
8
+ def logger
9
+ @logger ||= ::Logger.new(STDOUT)
10
+ end
11
+
12
+ def log(type, message)
13
+ logger.send(type, message)
14
+ end
15
+
16
+ def log_title(message)
17
+ log :info, "--------------------------------------------------------------------"
18
+ log :info, "| #{message}"
19
+ log :info, "--------------------------------------------------------------------"
20
+ end
21
+
22
+ def log_error(error)
23
+ log :error, "\n ----------------------------- #{error.fetch(:message)} -----------------------------"
24
+ log :error, "#{error.fetch(:category)} \n"
25
+ log :error, "-------------------------------------------------------------------- \n"
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Scripter
3
+ VERSION = "0.0.1"
4
+ end
data/lib/scripter.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'scripter/version'
2
+ require 'scripter/cache_store'
3
+ require 'scripter/iteration_history'
4
+ require 'scripter/env_variables'
5
+ require 'scripter/logger'
6
+ require 'scripter/errors'
7
+ require 'scripter/base'
8
+
9
+ # Library for reducing of boilerplate in ruby scripts with possibility to run fault tolerant iterations, for example mass notifications, or support scripts
10
+ #
11
+ # Usage example:
12
+ # class MyScriptClass < Scripter::Base
13
+ # #ENV variables which will be assigned to instance
14
+ # env_variables :test_env
15
+ #
16
+ # def execute
17
+ # # your specific execution code goes here
18
+ # # Note: use #perform_iteration helper with block in order to make fault tolerant iterations
19
+ # end
20
+ #
21
+ # def on_exit
22
+ # # your reporting scripts goes here
23
+ # # Note: #valid?, invalid?, #errors_grouped and #errors_count methods can be useful here
24
+ # end
25
+ # end
26
+ #
27
+ # Call example:
28
+ # MyScriptClass.execute
29
+ #
30
+
31
+ module Scripter
32
+
33
+ end
data/scripter.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scripter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scripter"
8
+ spec.version = Scripter::VERSION
9
+ spec.authors = ["Artūrs Mekšs"]
10
+ spec.email = ["arturs.mekss@gmail.com"]
11
+ spec.summary = %q{Library for reducing of boilerplate in ruby scripts with possibility to run fault tolerant iterations}
12
+ spec.description = %q{Library for reducing of boilerplate in ruby scripts with possibility to run fault tolerant iterations, for example mass notifications, or support scripts}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec', '~> 3.0'
24
+ spec.add_development_dependency 'timecop', '~> 0.7.1'
25
+
26
+ spec.add_dependency 'activesupport', '>= 2'
27
+ end
@@ -0,0 +1,138 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ class Scripter::BaseTestable < Scripter::Base
5
+ env_variables :test_variable1, :test_variable2
6
+ attr_reader :was_executed, :iteration_count
7
+ def execute
8
+ @was_executed = true
9
+ @iteration_count = 0
10
+ end
11
+ end
12
+
13
+ class Scripter::BaseTestableWithCustomName < Scripter::BaseTestable
14
+ set_custom_executable_name :distribute
15
+ def distribute
16
+ @was_executed = true
17
+ end
18
+ end
19
+
20
+ describe Scripter::Base do
21
+ it "should have getters for to default environment variables" do
22
+ expect(subject).to respond_to(:use_cache)
23
+ expect(subject).to respond_to(:dry_run)
24
+ expect(subject).to respond_to(:force_run)
25
+ end
26
+
27
+ it "should have Errors, Logger, EnvVariables and IterationHistory modules included" do
28
+ expect(subject).to respond_to(:add_error)
29
+ expect(subject).to respond_to(:log)
30
+ expect(subject).to respond_to(:env_variables)
31
+ expect(subject).to respond_to(:cache_store)
32
+ end
33
+
34
+ it "should invoke instance method execute on execute class method call" do
35
+ scripter = Scripter::BaseTestable.execute
36
+ expect(scripter.was_executed).to be true
37
+ expect(scripter).to respond_to(:test_variable1)
38
+ expect(scripter).to respond_to(:test_variable2)
39
+ end
40
+
41
+ it "should be able to customize default iteration function name" do
42
+ scripter = Scripter::BaseTestableWithCustomName.distribute
43
+ expect(scripter.was_executed).to be true
44
+ end
45
+
46
+ context "errors handling" do
47
+ class Scripter::BaseTestableWithErrors < Scripter::BaseTestable
48
+ set_custom_executable_name :execute_with_global_error
49
+ set_custom_executable_name :execute_with_iteration_error
50
+
51
+ def execute_with_global_error
52
+ execute
53
+ raise "my global error"
54
+ end
55
+
56
+ def execute_with_iteration_error
57
+ execute
58
+
59
+ 1000.times do |idx|
60
+ perform_iteration(idx, {}, false) do
61
+ @iteration_count += 1
62
+ raise "my iteration error"
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ before(:each) do
69
+ @scripter_class = Scripter::BaseTestableWithErrors
70
+ end
71
+
72
+ it "should stop execution on global error" do
73
+ scripter = @scripter_class.execute_with_global_error
74
+ expect(scripter.was_executed).to be true
75
+ end
76
+
77
+ it "should continue execute only 100 times if there is an error in each execution loop" do
78
+ scripter = @scripter_class.execute_with_iteration_error
79
+ expect(scripter.was_executed).to be true
80
+ expect(scripter.iteration_count).to eq 100
81
+ end
82
+ end
83
+
84
+ context "EndToEnd test" do
85
+ class Scripter::BaseTestableEndToEnd < Scripter::BaseTestable
86
+ attr_reader :on_exit_was_called
87
+ set_custom_executable_name :execute_with_loop
88
+
89
+ def execute_with_loop
90
+ execute
91
+
92
+ 10.times do |idx|
93
+ perform_iteration(idx, {additional: 'info'}, false) do
94
+ @iteration_count += 1
95
+ raise "my iteration error nr #{idx}" if idx == 5
96
+ end
97
+ end
98
+ end
99
+
100
+ def on_exit
101
+ @on_exit_was_called = true
102
+ end
103
+ end
104
+
105
+ before(:each) do
106
+ @scripter_class = Scripter::BaseTestableEndToEnd
107
+ end
108
+
109
+ it "should execute all iterations every time called" do
110
+ scripter = @scripter_class.execute_with_loop
111
+ expect(scripter.iteration_count).to eq 10
112
+ scripter = @scripter_class.execute_with_loop
113
+ expect(scripter.iteration_count).to eq 10
114
+ end
115
+
116
+ it "should execute only failed iterations if cache enabled" do
117
+ allow_any_instance_of(@scripter_class).to receive(:use_cache).and_return(true)
118
+ scripter = @scripter_class.execute_with_loop
119
+ expect(scripter.iteration_count).to eq 10
120
+ scripter = @scripter_class.execute_with_loop
121
+ expect(scripter.iteration_count).to eq 1
122
+ end
123
+
124
+ it "should contain information about the failure including meta info" do
125
+ scripter = @scripter_class.execute_with_loop
126
+ error = scripter.errors.first
127
+ expect(error[:message]).to eq 'my iteration error nr 5'
128
+ expect(error[:additional]).to eq 'info'
129
+ expect(error[:category]).to_not be_nil
130
+ end
131
+
132
+ it "should call on_exit if defined" do
133
+ scripter = @scripter_class.execute_with_loop
134
+ expect(scripter.on_exit_was_called).to be true
135
+ end
136
+ end
137
+
138
+ end
@@ -0,0 +1,68 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ class Scripter::EnvVariablesTestable
5
+ include Scripter::EnvVariables
6
+
7
+ # custom type casts test
8
+ def type_cast_env_variable(name, value)
9
+ if name == :countries
10
+ value.split(/\s*,\s*/)
11
+ else
12
+ super
13
+ end
14
+ end
15
+ end
16
+
17
+ describe Scripter::EnvVariables do
18
+ before(:each) do
19
+ @it = Scripter::EnvVariablesTestable.new
20
+ end
21
+
22
+ describe "::env_variables" do
23
+ it "should be available" do
24
+ expect(@it.class.respond_to?(:env_variables)).to be true
25
+ end
26
+
27
+ it "should be able to add new Environment variable getters" do
28
+ expect(@it.respond_to?(:test_variable)).to be false
29
+ @it.class.send :env_variables, :test_variable
30
+ expect(@it).to receive(:env_variables).and_return({test_variable: 'something'})
31
+ expect(@it.test_variable).to eq 'something'
32
+ end
33
+ end
34
+
35
+ context "with stubbed arguments" do
36
+ before(:each) do
37
+ expect(@it).to receive(:command_line_arguments).and_return(['notifications', 'RAILS_ENV=test', 'COUNTRIES=LV,EE,FI', 'use_cache=1'])
38
+ end
39
+
40
+ it "#raw_env_variables should return only variables" do
41
+ expect(@it.raw_env_variables).to eq({'RAILS_ENV'=>'test', 'COUNTRIES'=>'LV,EE,FI', 'use_cache'=>'1'})
42
+ end
43
+
44
+ it "#env_variables should return normalized type casted variables" do
45
+ expect(@it.env_variables).to eq({rails_env: 'test', countries: ['LV', 'EE', 'FI'], use_cache: true})
46
+ end
47
+ end
48
+
49
+ describe "#type_cast_env_variable" do
50
+ it ":date should be date or nil" do
51
+ expect(@it.type_cast_env_variable(:date, nil)).to be_nil
52
+ expect(@it.type_cast_env_variable(:date, '2014-04-14')).to eq Date.new(2014, 4, 14)
53
+ end
54
+
55
+ [:dry_run, :force_run, :use_cache].each do |variable_name|
56
+ it ":#{variable_name} should be boolean" do
57
+ expect(@it.type_cast_env_variable(variable_name, nil)).to be false
58
+ expect(@it.type_cast_env_variable(variable_name, '')).to be false
59
+ expect(@it.type_cast_env_variable(variable_name, '1')).to be true
60
+ end
61
+ end
62
+ end
63
+
64
+ it "#command_line_arguments should return ARGV" do
65
+ expect(@it.send(:command_line_arguments)).to be ARGV
66
+ end
67
+
68
+ end
@@ -0,0 +1,96 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ class Scripter::ErrorsTestable
5
+ include Scripter::Errors
6
+ include Scripter::Logger
7
+
8
+ def raise_test_exception
9
+ begin
10
+ raise "test error"
11
+ rescue Exception => e
12
+ add_error(e, {}, false)
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ describe Scripter::Errors do
19
+ before(:each) do
20
+ @it = Scripter::ErrorsTestable.new
21
+ end
22
+
23
+ it "should provide errors array" do
24
+ expect(@it).to respond_to(:errors)
25
+ expect(@it.errors).to be_instance_of(Array)
26
+ end
27
+
28
+ it "should provide errors_grouped array groped by category" do
29
+ @it.errors << {category: 'test category', message: 'test message'}
30
+ @it.errors << {category: 'test category', message: 'test message1'}
31
+ @it.errors << {category: 'test category', message: 'test message2'}
32
+ @it.errors << {category: 'test category1', message: 'test message2'}
33
+
34
+ expect(@it.respond_to?(:errors_grouped)).to be true
35
+ expect(@it.errors_grouped).to be_instance_of(Hash)
36
+ expect(@it.errors_grouped['test category'].size).to eq 3
37
+ end
38
+
39
+ it "#errors_count should return count of stored errors" do
40
+ expect(@it.errors_count).to eq 0
41
+ @it.errors << {category: 'test category', message: 'test message'}
42
+ expect(@it.errors_count).to eq 1
43
+ @it.errors << {category: 'test category', message: 'test message1'}
44
+ expect(@it.errors_count).to eq 2
45
+ end
46
+
47
+ describe "#valid?" do
48
+ it "should be true if no errors stored" do
49
+ expect(@it.valid?).to be true
50
+ end
51
+
52
+ it "should be false if there are errors" do
53
+ @it.errors << {category: 'test category', message: 'test message'}
54
+ expect(@it.valid?).to be false
55
+ end
56
+ end
57
+
58
+ describe "#invalid?" do
59
+ it "should be false if no errors stored" do
60
+ expect(@it.invalid?).to be false
61
+ end
62
+
63
+ it "should be true if there are errors" do
64
+ @it.errors << {category: 'test category', message: 'test message'}
65
+ expect(@it.invalid?).to be true
66
+ end
67
+ end
68
+
69
+ describe "#add_error" do
70
+ before(:each) do
71
+ expect(@it.errors).to be_empty
72
+ @errors_hash = {category: 'error', message: 'some'}
73
+ end
74
+
75
+ it "should be able to log incoming error if error_to_log is true" do
76
+ expect(@it).to receive(:log_error).once.with(@errors_hash)
77
+ @it.add_error(@errors_hash, {}, true)
78
+ @it.add_error(@errors_hash, {}, false)
79
+ end
80
+
81
+ it "should add meta information to error if passed along" do
82
+ @it.add_error(@errors_hash, {add: 'info'}, false)
83
+ expect(@it.errors.first).to eq({category: 'error', message: 'some', add: 'info'})
84
+ end
85
+
86
+ it "should be able to add error from exception" do
87
+ @it.raise_test_exception
88
+
89
+ expect(@it.errors).not_to be_empty
90
+ error = @it.errors.first
91
+ expect(error[:message]).to eq 'test error'
92
+ expect(error[:category]).to be_a Array
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,71 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ class Scripter::IterationHistoryTestable
5
+ include Scripter::IterationHistory
6
+ attr_accessor :use_cache
7
+ end
8
+
9
+ class Scripter::IterationHistoryTestable2
10
+ include Scripter::IterationHistory
11
+ attr_accessor :use_cache
12
+ end
13
+
14
+ describe Scripter::IterationHistory do
15
+ before(:each) do
16
+ @it = Scripter::IterationHistoryTestable.new
17
+ @it.use_cache = true
18
+ end
19
+
20
+ it "should be able to set custom cache store" do
21
+ @it.cache_store = 'My custom cache_store'
22
+ expect(@it.cache_store).to eq 'My custom cache_store'
23
+ end
24
+
25
+ describe "#iteration_processed! and #iteration_processed?" do
26
+ it "should share cache between instances of the same class" do
27
+ expect(@it.iteration_processed?(1)).to be false
28
+
29
+ another_it = Scripter::IterationHistoryTestable.new
30
+ another_it.iteration_processed!(1)
31
+
32
+ expect(@it.iteration_processed?(1)).to be true
33
+ end
34
+
35
+ it "should not share cache between instances of different classes" do
36
+ expect(@it.iteration_processed?(1)).to be false
37
+
38
+ another_class_it = Scripter::IterationHistoryTestable2.new
39
+ another_class_it.use_cache = true
40
+ another_class_it.iteration_processed!(1)
41
+
42
+ expect(another_class_it.iteration_processed?(1)).to be true
43
+ expect(@it.iteration_processed?(1)).to be false
44
+ end
45
+
46
+ it "should remember results for 24 hours" do
47
+ @it.iteration_processed!(1)
48
+ expect(@it.iteration_processed?(1)).to be true
49
+
50
+ Timecop.travel(Time.now + 24.hours) do
51
+ expect(@it.iteration_processed?(1)).to be false
52
+ end
53
+ end
54
+
55
+ it "should be different for different iteration_id" do
56
+ expect(@it.iteration_processed?(1)).to be false
57
+ expect(@it.iteration_processed?(2)).to be false
58
+ @it.iteration_processed!(1)
59
+ expect(@it.iteration_processed?(1)).to be true
60
+ expect(@it.iteration_processed?(2)).to be false
61
+ end
62
+
63
+ it "should always be unprocessed if #use_cache is false" do
64
+ @it.use_cache = false
65
+ expect(@it.iteration_processed?(1)).to be false
66
+ @it.iteration_processed!(1)
67
+ expect(@it.iteration_processed?(1)).to be false
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,64 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ class Scripter::LoggerTestable
5
+ include Scripter::Logger
6
+ end
7
+
8
+ describe Scripter::Logger do
9
+ before(:each) do
10
+ @it = Scripter::LoggerTestable.new
11
+ end
12
+
13
+ it "should be able to set custom logger" do
14
+ @it.logger = 'My custom logger'
15
+ expect(@it.logger).to eq 'My custom logger'
16
+ end
17
+
18
+ describe "#log" do
19
+ it "should be available" do
20
+ expect(@it.respond_to?(:log)).to be true
21
+ end
22
+
23
+ [:info, :debug, :error].each do |type|
24
+ it "should be able to log #{type}" do
25
+ expect(@it.logger).to receive(type).with('log message')
26
+ @it.log type, 'log message'
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#log_title" do
32
+ it "should be available" do
33
+ expect(@it.respond_to?(:log_title)).to be true
34
+ end
35
+
36
+ it "should log title as info" do
37
+ messages = []
38
+ expect(@it).to receive(:log).exactly(3).times do |type, message|
39
+ messages << message
40
+ expect(type).to be :info
41
+ end
42
+ @it.log_title "test title"
43
+ expect(messages.join.include?("test title")).to be true
44
+ end
45
+ end
46
+
47
+ describe "#log_error" do
48
+ it "should be available" do
49
+ expect(@it.respond_to?(:log_error)).to be true
50
+ end
51
+
52
+ it "should log error message as error" do
53
+ messages = []
54
+ expect(@it).to receive(:log).exactly(3).times do |type, message|
55
+ messages << message
56
+ expect(type).to be :error
57
+ end
58
+ @it.log_error({category: "my test category", message: "my test message"})
59
+ expect(messages.join.include?("my test category")).to be true
60
+ expect(messages.join.include?("my test message")).to be true
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,96 @@
1
+ require 'scripter'
2
+ require 'timecop'
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
7
+ # file to always be loaded, without a need to explicitly require it in any files.
8
+ #
9
+ # Given that it is always loaded, you are encouraged to keep this file as
10
+ # light-weight as possible. Requiring heavyweight dependencies from this file
11
+ # will add to the boot time of your test suite on EVERY test run, even for an
12
+ # individual file that may not need all of that loaded. Instead, consider making
13
+ # a separate helper file that requires the additional dependencies and performs
14
+ # the additional setup, and require it from the spec files that actually need it.
15
+ #
16
+ # The `.rspec` file also contains a few flags that are not defaults but that
17
+ # users commonly want.
18
+ #
19
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
20
+ RSpec.configure do |config|
21
+ config.before(:each) do
22
+ FileUtils.rm_rf(Dir['tmp/cache'])
23
+ end
24
+
25
+ # rspec-expectations config goes here. You can use an alternate
26
+ # assertion/expectation library such as wrong or the stdlib/minitest
27
+ # assertions if you prefer.
28
+ config.expect_with :rspec do |expectations|
29
+ # This option will default to `true` in RSpec 4. It makes the `description`
30
+ # and `failure_message` of custom matchers include text for helper methods
31
+ # defined using `chain`, e.g.:
32
+ # be_bigger_than(2).and_smaller_than(4).description
33
+ # # => "be bigger than 2 and smaller than 4"
34
+ # ...rather than:
35
+ # # => "be bigger than 2"
36
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
37
+ end
38
+
39
+ # rspec-mocks config goes here. You can use an alternate test double
40
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
41
+ config.mock_with :rspec do |mocks|
42
+ # Prevents you from mocking or stubbing a method that does not exist on
43
+ # a real object. This is generally recommended, and will default to
44
+ # `true` in RSpec 4.
45
+ mocks.verify_partial_doubles = true
46
+ end
47
+
48
+ # The settings below are suggested to provide a good initial experience
49
+ # with RSpec, but feel free to customize to your heart's content.
50
+ =begin
51
+ # These two settings work together to allow you to limit a spec run
52
+ # to individual examples or groups you care about by tagging them with
53
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
54
+ # get run.
55
+ config.filter_run :focus
56
+ config.run_all_when_everything_filtered = true
57
+
58
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
59
+ # For more details, see:
60
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
61
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
62
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
63
+ config.disable_monkey_patching!
64
+
65
+ # This setting enables warnings. It's recommended, but in some cases may
66
+ # be too noisy due to issues in dependencies.
67
+ config.warnings = true
68
+
69
+ # Many RSpec users commonly either run the entire suite or an individual
70
+ # file, and it's useful to allow more verbose output when running an
71
+ # individual spec file.
72
+ if config.files_to_run.one?
73
+ # Use the documentation formatter for detailed output,
74
+ # unless a formatter has already been configured
75
+ # (e.g. via a command-line flag).
76
+ config.default_formatter = 'doc'
77
+ end
78
+
79
+ # Print the 10 slowest examples and example groups at the
80
+ # end of the spec run, to help surface which specs are running
81
+ # particularly slow.
82
+ config.profile_examples = 10
83
+
84
+ # Run specs in random order to surface order dependencies. If you find an
85
+ # order dependency and want to debug it, you can fix the order by providing
86
+ # the seed, which is printed after each run.
87
+ # --seed 1234
88
+ config.order = :random
89
+
90
+ # Seed global randomization in this process using the `--seed` CLI option.
91
+ # Setting this allows you to use `--seed` to deterministically reproduce
92
+ # test failures related to randomization by passing the same `--seed` value
93
+ # as the one that triggered the failure.
94
+ Kernel.srand config.seed
95
+ =end
96
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scripter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Artūrs Mekšs
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.7.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.7.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '2'
83
+ description: Library for reducing of boilerplate in ruby scripts with possibility
84
+ to run fault tolerant iterations, for example mass notifications, or support scripts
85
+ email:
86
+ - arturs.mekss@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/scripter.rb
98
+ - lib/scripter/base.rb
99
+ - lib/scripter/cache_store.rb
100
+ - lib/scripter/env_variables.rb
101
+ - lib/scripter/errors.rb
102
+ - lib/scripter/iteration_history.rb
103
+ - lib/scripter/logger.rb
104
+ - lib/scripter/version.rb
105
+ - scripter.gemspec
106
+ - spec/scripter/base_spec.rb
107
+ - spec/scripter/env_variables_spec.rb
108
+ - spec/scripter/errors_spec.rb
109
+ - spec/scripter/iteration_history_spec.rb
110
+ - spec/scripter/logger_spec.rb
111
+ - spec/spec_helper.rb
112
+ homepage: ''
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.2.2
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Library for reducing of boilerplate in ruby scripts with possibility to run
136
+ fault tolerant iterations
137
+ test_files:
138
+ - spec/scripter/base_spec.rb
139
+ - spec/scripter/env_variables_spec.rb
140
+ - spec/scripter/errors_spec.rb
141
+ - spec/scripter/iteration_history_spec.rb
142
+ - spec/scripter/logger_spec.rb
143
+ - spec/spec_helper.rb