bin_script 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +104 -0
- data/LICENSE +20 -0
- data/README.markdown +68 -0
- data/Rakefile +17 -0
- data/bin/bin_helper +77 -0
- data/bin_script.gemspec +27 -0
- data/init.rb +1 -0
- data/lib/bin_script.rb +2 -0
- data/lib/bin_script/bin_script.rb +389 -0
- data/lib/bin_script/class_inheritable_attributes.rb +25 -0
- data/lib/bin_script/lock_file.rb +45 -0
- data/lib/bin_script/version.rb +4 -0
- data/lib/bin_script/xlogger.rb +60 -0
- data/lib/generators/bin/bin_generator.rb +33 -0
- data/lib/generators/bin/templates/script.rb +12 -0
- data/lib/generators/bin/templates/script_class.rb +42 -0
- data/lib/generators/bin/templates/spec.rb +13 -0
- data/spec/bin_script_spec.rb +184 -0
- data/spec/spec_helper.rb +7 -0
- metadata +116 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Will automatically pull in this gem and all its
|
4
|
+
# dependencies specified in the gemspec
|
5
|
+
gem "bin_script", :path => File.expand_path("..", __FILE__)
|
6
|
+
|
7
|
+
gem 'rails'
|
8
|
+
gem 'activesupport'
|
9
|
+
# These are development dependencies
|
10
|
+
gem "rspec"
|
11
|
+
gem 'rake'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
bin_script (0.1)
|
5
|
+
activesupport (>= 2.3.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionmailer (3.2.3)
|
11
|
+
actionpack (= 3.2.3)
|
12
|
+
mail (~> 2.4.4)
|
13
|
+
actionpack (3.2.3)
|
14
|
+
activemodel (= 3.2.3)
|
15
|
+
activesupport (= 3.2.3)
|
16
|
+
builder (~> 3.0.0)
|
17
|
+
erubis (~> 2.7.0)
|
18
|
+
journey (~> 1.0.1)
|
19
|
+
rack (~> 1.4.0)
|
20
|
+
rack-cache (~> 1.2)
|
21
|
+
rack-test (~> 0.6.1)
|
22
|
+
sprockets (~> 2.1.2)
|
23
|
+
activemodel (3.2.3)
|
24
|
+
activesupport (= 3.2.3)
|
25
|
+
builder (~> 3.0.0)
|
26
|
+
activerecord (3.2.3)
|
27
|
+
activemodel (= 3.2.3)
|
28
|
+
activesupport (= 3.2.3)
|
29
|
+
arel (~> 3.0.2)
|
30
|
+
tzinfo (~> 0.3.29)
|
31
|
+
activeresource (3.2.3)
|
32
|
+
activemodel (= 3.2.3)
|
33
|
+
activesupport (= 3.2.3)
|
34
|
+
activesupport (3.2.3)
|
35
|
+
i18n (~> 0.6)
|
36
|
+
multi_json (~> 1.0)
|
37
|
+
arel (3.0.2)
|
38
|
+
builder (3.0.0)
|
39
|
+
diff-lcs (1.1.3)
|
40
|
+
erubis (2.7.0)
|
41
|
+
hike (1.2.1)
|
42
|
+
i18n (0.6.0)
|
43
|
+
journey (1.0.3)
|
44
|
+
json (1.6.6)
|
45
|
+
mail (2.4.4)
|
46
|
+
i18n (>= 0.4.0)
|
47
|
+
mime-types (~> 1.16)
|
48
|
+
treetop (~> 1.4.8)
|
49
|
+
mime-types (1.18)
|
50
|
+
multi_json (1.2.0)
|
51
|
+
polyglot (0.3.3)
|
52
|
+
rack (1.4.1)
|
53
|
+
rack-cache (1.2)
|
54
|
+
rack (>= 0.4)
|
55
|
+
rack-ssl (1.3.2)
|
56
|
+
rack
|
57
|
+
rack-test (0.6.1)
|
58
|
+
rack (>= 1.0)
|
59
|
+
rails (3.2.3)
|
60
|
+
actionmailer (= 3.2.3)
|
61
|
+
actionpack (= 3.2.3)
|
62
|
+
activerecord (= 3.2.3)
|
63
|
+
activeresource (= 3.2.3)
|
64
|
+
activesupport (= 3.2.3)
|
65
|
+
bundler (~> 1.0)
|
66
|
+
railties (= 3.2.3)
|
67
|
+
railties (3.2.3)
|
68
|
+
actionpack (= 3.2.3)
|
69
|
+
activesupport (= 3.2.3)
|
70
|
+
rack-ssl (~> 1.3.2)
|
71
|
+
rake (>= 0.8.7)
|
72
|
+
rdoc (~> 3.4)
|
73
|
+
thor (~> 0.14.6)
|
74
|
+
rake (0.9.2.2)
|
75
|
+
rdoc (3.12)
|
76
|
+
json (~> 1.4)
|
77
|
+
rspec (2.9.0)
|
78
|
+
rspec-core (~> 2.9.0)
|
79
|
+
rspec-expectations (~> 2.9.0)
|
80
|
+
rspec-mocks (~> 2.9.0)
|
81
|
+
rspec-core (2.9.0)
|
82
|
+
rspec-expectations (2.9.1)
|
83
|
+
diff-lcs (~> 1.1.3)
|
84
|
+
rspec-mocks (2.9.0)
|
85
|
+
sprockets (2.1.2)
|
86
|
+
hike (~> 1.2)
|
87
|
+
rack (~> 1.0)
|
88
|
+
tilt (~> 1.1, != 1.3.0)
|
89
|
+
thor (0.14.6)
|
90
|
+
tilt (1.3.3)
|
91
|
+
treetop (1.4.10)
|
92
|
+
polyglot
|
93
|
+
polyglot (>= 0.3.1)
|
94
|
+
tzinfo (0.3.32)
|
95
|
+
|
96
|
+
PLATFORMS
|
97
|
+
ruby
|
98
|
+
|
99
|
+
DEPENDENCIES
|
100
|
+
activesupport
|
101
|
+
bin_script!
|
102
|
+
rails
|
103
|
+
rake
|
104
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Makarchev K, Lifshits D
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
Rails BinScript
|
2
|
+
===============
|
3
|
+
|
4
|
+
Easy writing and executing bins (espesually for crontab or god) in Rails project.
|
5
|
+
For my purposes much better than Rake, Thor and Rails Runner.
|
6
|
+
|
7
|
+
Features:
|
8
|
+
|
9
|
+
1. Each bin is a class
|
10
|
+
2. Easy writing tests
|
11
|
+
3. Bin use lock file and logger with formatter when executing
|
12
|
+
|
13
|
+
Rails 2.3 and 3 compatible
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
gem 'bin_script'
|
17
|
+
```
|
18
|
+
|
19
|
+
rails g bin:bin bla
|
20
|
+
(for 2.3 copy generator into lib/generators and run: ./script/generate bin bla)
|
21
|
+
|
22
|
+
Call like:
|
23
|
+
|
24
|
+
$ cd project && ./bin/bla.rb -e production -a -b -c -d "asdf"
|
25
|
+
|
26
|
+
Examples (default features):
|
27
|
+
|
28
|
+
$ ./bin/bla.rb -e production
|
29
|
+
$ ./bin/bla.rb -e production -L ./locks/bla.lock
|
30
|
+
$ ./bin/bla.rb -e production -l ./log/bla.log
|
31
|
+
$ ./bin/bla.rb -e production --daemonize --pidfile=./tmp/bla.pid
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
Example Bin
|
36
|
+
-----------
|
37
|
+
app/models/bin/stuff_script.rb
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
class StuffScript < BinScript
|
41
|
+
optional :u, "Update string"
|
42
|
+
required :d, "Date in format YYYY-MM-DD or YYYY-MM"
|
43
|
+
noarg :t, "Test run"
|
44
|
+
|
45
|
+
def test?
|
46
|
+
params(:t)
|
47
|
+
end
|
48
|
+
|
49
|
+
def do!
|
50
|
+
if test?
|
51
|
+
logger.info "update string #{params(:u)}"
|
52
|
+
else
|
53
|
+
logger.info "data #{Time.parse(params(:d))}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### Custom exception notifier (create initializer with:)
|
60
|
+
|
61
|
+
``` ruby
|
62
|
+
class BinScript
|
63
|
+
def notify_about_error(exception)
|
64
|
+
Mailter.some_notification(exception)...
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
|
+
|
10
|
+
require 'rubygems/package_task'
|
11
|
+
require 'rubygems/specification'
|
12
|
+
|
13
|
+
spec = eval(File.read('bin_script.gemspec'))
|
14
|
+
|
15
|
+
Gem::PackageTask.new(spec) do |pkg|
|
16
|
+
pkg.gem_spec = spec
|
17
|
+
end
|
data/bin/bin_helper
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
# This helper is useful for requiring from bin scripts.
|
4
|
+
# Detect RAILS_ENV from '-e' parameter in ARGV (or 'development' as default) and load rails environment if need.
|
5
|
+
#
|
6
|
+
# Sample ./bin/script_name.rb
|
7
|
+
# #!/usr/bin/env ruby
|
8
|
+
#
|
9
|
+
# #NO_RAILS = true # Uncomment this line if you don't need rails environment in your script
|
10
|
+
# require File.join(File.dirname(__FILE__),'_bin_helper')
|
11
|
+
#
|
12
|
+
# Helper will detect class name for this script automatically. So for example above helper call something like this: ScriptName.new.run!
|
13
|
+
|
14
|
+
APP_ROOT ||= File.expand_path(File.dirname($0)) + "/../"
|
15
|
+
|
16
|
+
if defined?(DAEMONIZE) || ARGV.include?('--daemonize') # для демонизации бина
|
17
|
+
ARGV.delete('--daemonize')
|
18
|
+
|
19
|
+
# Stolen from activesupport
|
20
|
+
module Process
|
21
|
+
def self.daemon
|
22
|
+
exit if fork # Parent exits, child continues.
|
23
|
+
Process.setsid # Become session leader.
|
24
|
+
exit if fork # Zap session leader. See [1].
|
25
|
+
return 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Process.daemon
|
30
|
+
|
31
|
+
if ARGV.include?('--pidfile')
|
32
|
+
pidfile = nil
|
33
|
+
|
34
|
+
ARGV.each_index do |i|
|
35
|
+
if ARGV[i] == '--pidfile' && ARGV.length > i
|
36
|
+
pidfile = ARGV[i+1]
|
37
|
+
|
38
|
+
ARGV.delete('--pidfile')
|
39
|
+
ARGV.delete(pidfile) if pidfile
|
40
|
+
break
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if pidfile
|
45
|
+
File.open(pidfile, 'w'){|f| f.write($$) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
require 'rubygems'
|
51
|
+
|
52
|
+
# Define RAILS_ENV
|
53
|
+
ARGV.each_index do |i|
|
54
|
+
if ARGV[i] == '-e' && ARGV.length > i
|
55
|
+
RAILS_ENV = ARGV[i+1]
|
56
|
+
break
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Если задан ключ -h, то нам нужно только показать хэлп и environment грузить не обязательно
|
61
|
+
NO_RAILS = true if ARGV.include?('-h') && !defined?(NO_RAILS)
|
62
|
+
|
63
|
+
# Set defaults
|
64
|
+
RAILS_ENV = 'development' if defined?(RAILS_ENV).nil?
|
65
|
+
|
66
|
+
if !defined?(RAILS_ROOT) && !defined?(NO_RAILS)
|
67
|
+
# Load rails envoronment if not yet and we need it
|
68
|
+
require File.join(APP_ROOT, %w{config environment})
|
69
|
+
else
|
70
|
+
require 'active_support'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Load BinScript class source
|
74
|
+
require 'bin_script' unless defined?(BinScript) == 'constant'
|
75
|
+
|
76
|
+
# Call script runner
|
77
|
+
BinScript.run_script
|
data/bin_script.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.dirname(__FILE__) + "/lib/bin_script/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = %q{bin_script}
|
6
|
+
s.version = BinScript::VERSION
|
7
|
+
|
8
|
+
s.authors = ["Lifshits Dmitry", "Makarchev Konstantin"]
|
9
|
+
s.autorequire = %q{init}
|
10
|
+
|
11
|
+
s.description = %q{Easy writing and executing bins (espesually for crontab or god) in Rails project}
|
12
|
+
|
13
|
+
s.summary = %q{Easy writing and executing bins (espesually for crontab or god) in Rails project
|
14
|
+
For my purposes much better than Rake, Thor and Rails Runner}
|
15
|
+
|
16
|
+
s.email = %q{kostya27@gmail.com}
|
17
|
+
s.homepage = %q{http://github.com/kostya/bin_script}
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
s.add_dependency 'activesupport', ">=2.3.2"
|
25
|
+
s.add_development_dependency "rspec"
|
26
|
+
|
27
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/lib/bin_script'
|
data/lib/bin_script.rb
ADDED
@@ -0,0 +1,389 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'getoptlong'
|
3
|
+
require 'pathname'
|
4
|
+
require 'rails' unless defined?(Rails)
|
5
|
+
require File.dirname(__FILE__) + '/lock_file'
|
6
|
+
require File.dirname(__FILE__) + '/xlogger'
|
7
|
+
require File.dirname(__FILE__) + '/class_inheritable_attributes'
|
8
|
+
#require 'active_support/core_ext/class/attribute.rb'
|
9
|
+
|
10
|
+
class BinScript
|
11
|
+
include ClassLevelInheritableAttributes
|
12
|
+
class_inheritable_attributes :parameters, :log_level, :enable_locking, :enable_logging, :date_log_postfix, :disable_puts_for_tests
|
13
|
+
|
14
|
+
# Default parameters
|
15
|
+
@parameters = [
|
16
|
+
{:key => :e, :type => :optional, :description => "Rails environment ID (default - development)"},
|
17
|
+
{:key => :h, :type => :noarg, :description => "Print usage message", :alias => [:H, :help]},
|
18
|
+
{:key => :l, :type => :optional, :description => "Path to log file (default \#{Rails.root}/log/[script_name].log"},
|
19
|
+
{:key => :L, :type => :optional, :description => "Path to lock file (default \#{Rails.root}/lock/[script_name].lock"}
|
20
|
+
]
|
21
|
+
|
22
|
+
# Default log level INFO or DEBUG for test env
|
23
|
+
@log_level = (Rails.env.development? ? XLogger::DEBUG : XLogger::INFO)
|
24
|
+
|
25
|
+
# Enable locking by default
|
26
|
+
@enable_locking = true
|
27
|
+
|
28
|
+
# Enable logging by default
|
29
|
+
@enable_logging = true
|
30
|
+
|
31
|
+
# По умолчанию мы при каждом запуске скрипта продолжаем писать в основной лог. Но можно записать сюда формат, который
|
32
|
+
# можно скормить time.strftime(format) и результат подставится в конец имени файла лога. Таким образом можно делать
|
33
|
+
# отдельные лог-файлы для каждого запуска, для каждого дня/месяца/года/часа и т.д...
|
34
|
+
# Например "_%Y-%m-%d_%H-%M-%S" - каждый запуск новый лог
|
35
|
+
# "_%Y-%m-%d" - каждый день новый лог
|
36
|
+
@date_log_postfix = ''
|
37
|
+
|
38
|
+
# По умолчанию puts работает как обычно, но BinScript может ничего не выводить если RAILS_ENV=test
|
39
|
+
# Если скрипт что-то выводит, это попадает в лог спеков, что не красиво. Так что для таких скриптов лучше включать эту опцию
|
40
|
+
@disable_puts_for_tests = false
|
41
|
+
|
42
|
+
# Allowed parameter types. Equivalence aliases with GetoptLong constants.
|
43
|
+
PARAMETER_TYPES = {
|
44
|
+
:noarg => GetoptLong::NO_ARGUMENT,
|
45
|
+
:optional => GetoptLong::OPTIONAL_ARGUMENT,
|
46
|
+
:required => GetoptLong::REQUIRED_ARGUMENT
|
47
|
+
}
|
48
|
+
|
49
|
+
# Place for logger object
|
50
|
+
attr_accessor :logger
|
51
|
+
|
52
|
+
# Place for store exit status.
|
53
|
+
attr_accessor :exit_status
|
54
|
+
|
55
|
+
# Create shortcuts to simplify logging from scripts
|
56
|
+
singleton = (class << self; self end)
|
57
|
+
Logger::Severity.constants.each do |level|
|
58
|
+
method = level.to_s.downcase
|
59
|
+
|
60
|
+
# Define class level helper method
|
61
|
+
singleton.class_eval do
|
62
|
+
define_method :info do |message|
|
63
|
+
return unless Rails.logger
|
64
|
+
Rails.logger.send(method,message)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Define instance level helper method
|
69
|
+
define_method method do |message|
|
70
|
+
return unless @logger
|
71
|
+
@logger.send(method,message)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
# Get parameter by key
|
77
|
+
def get_parameter(key)
|
78
|
+
param = @parameters.find{|p| p[:key] == key || (p[:alias].present? && p[:alias].include?(key))}
|
79
|
+
raise "Can't find parameter with key #{key.inspect} for class #{self.inspect}!" if param.nil?
|
80
|
+
param
|
81
|
+
end
|
82
|
+
|
83
|
+
# Prepare readable script name
|
84
|
+
def script_name
|
85
|
+
self.to_s.underscore.gsub('/','__')
|
86
|
+
end
|
87
|
+
|
88
|
+
# Parse script filename. Extract important path parts
|
89
|
+
def parse_script_file_name(filename)
|
90
|
+
result = {}
|
91
|
+
# Prepare important parts of source script filename
|
92
|
+
parts = filename.split(File::SEPARATOR)
|
93
|
+
parts = parts[parts.index('bin')+1..-1]
|
94
|
+
parts.map!{|p| File.basename(p).split('.').first}
|
95
|
+
|
96
|
+
result[:parts] = parts
|
97
|
+
|
98
|
+
result[:class] = calculate_script_class_name(parts)
|
99
|
+
result[:files] = calculate_script_class_filename(parts)
|
100
|
+
|
101
|
+
result
|
102
|
+
end
|
103
|
+
|
104
|
+
# Prepare class name from filename parts
|
105
|
+
def calculate_script_class_name(parts)
|
106
|
+
# Calculate class name and paths from source script filename parts
|
107
|
+
if(parts.length > 1)
|
108
|
+
class_name = parts.map{|p| p.camelize}.join('::') + parts.first.camelize
|
109
|
+
else
|
110
|
+
class_name = parts.first.camelize
|
111
|
+
end
|
112
|
+
class_name += "Script"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Prepare class name from filename parts
|
116
|
+
def calculate_script_class_filename(parts)
|
117
|
+
files = []
|
118
|
+
|
119
|
+
# Calculate and add to list file with script class itself
|
120
|
+
class_file = File.join(%w{app models bin}, parts)
|
121
|
+
class_file += '_nagios' if(parts.length > 1)
|
122
|
+
class_file += '_script.rb'
|
123
|
+
files << class_file
|
124
|
+
|
125
|
+
# Add intermediate classes
|
126
|
+
parts[0..-2].each { |p| files << "app/models/#{p}_script.rb" }
|
127
|
+
|
128
|
+
files.reverse
|
129
|
+
end
|
130
|
+
|
131
|
+
# Run script detected by the filename of source script file
|
132
|
+
def run_script(filename = $0)
|
133
|
+
cfg = parse_script_file_name(Pathname.new(filename).realpath.to_s)
|
134
|
+
cfg[:files].each { |f| require File.join(rails_root, f) }
|
135
|
+
|
136
|
+
# Create instance and call run! for script class
|
137
|
+
klass = cfg[:class].constantize
|
138
|
+
script = klass.new
|
139
|
+
script.run!
|
140
|
+
|
141
|
+
# Exit with specified exit status
|
142
|
+
exit script.exit_status || 0
|
143
|
+
end
|
144
|
+
|
145
|
+
# Prepare aliases for adding parameters in child classes
|
146
|
+
PARAMETER_TYPES.keys.each do |type|
|
147
|
+
define_method type do |key, opts|
|
148
|
+
param = {:key => key, :type => type}
|
149
|
+
if opts.is_a?(String)
|
150
|
+
param[:description] = opts
|
151
|
+
else
|
152
|
+
param = param.merge(opts)
|
153
|
+
end
|
154
|
+
|
155
|
+
# We want aliases always to be an array
|
156
|
+
param[:alias] = [param[:alias]].flatten.compact
|
157
|
+
|
158
|
+
@parameters = @parameters + [param]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Remove parameter
|
163
|
+
def remove_parameter(key)
|
164
|
+
new = []
|
165
|
+
@parameters.each { |p| new << p if p[:key] != key }
|
166
|
+
@parameters = new
|
167
|
+
end
|
168
|
+
|
169
|
+
# RAILS_ROOT is not availible if rails environment was not loaded. So detect application root in this case from current file path
|
170
|
+
def rails_root
|
171
|
+
Pathname.new(Rails.root ? Rails.root : File.join(File.dirname(__FILE__), %w{.. ..})).realpath.to_s
|
172
|
+
end
|
173
|
+
|
174
|
+
# Prepare ARGV parameters as hash
|
175
|
+
def get_argv_values
|
176
|
+
values = {}
|
177
|
+
o = GetoptLong.new(*get_getoptlong_params)
|
178
|
+
o.quiet = true # Don't write arg error to STDERR
|
179
|
+
o.each { |opt, arg| values[opt[1..-1].to_sym] = arg }
|
180
|
+
values
|
181
|
+
end
|
182
|
+
|
183
|
+
# Prepare usage message
|
184
|
+
def usage(message = nil)
|
185
|
+
usage_msg = ''
|
186
|
+
usage_msg += "Error: #{message}\n\n" unless message.nil?
|
187
|
+
usage_msg += "Use: ./bin/#{script_name}.rb [OPTIONS]\n\nAvailible options:\n\n"
|
188
|
+
@parameters.each do |param|
|
189
|
+
usage_msg += " #{prefix_key param[:key]} #{param[:description]}\n"
|
190
|
+
end
|
191
|
+
usage_msg += "\n"
|
192
|
+
usage_msg
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
# Prepare parameters in Getoptlong lib format
|
197
|
+
def get_getoptlong_params
|
198
|
+
result = []
|
199
|
+
@parameters.each do |param|
|
200
|
+
cfg = [prefix_key(param[:key])]
|
201
|
+
param[:alias].each{|als| cfg << prefix_key(als) } unless param[:alias].blank?
|
202
|
+
cfg << PARAMETER_TYPES[param[:type]]
|
203
|
+
result << cfg
|
204
|
+
end
|
205
|
+
result
|
206
|
+
end
|
207
|
+
|
208
|
+
# Prepare argument name with short or long prefix
|
209
|
+
def prefix_key(key)
|
210
|
+
key = key.to_s
|
211
|
+
(key.length > 1 ? "--" : "-") + key
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def puts(*arg)
|
216
|
+
return if self.class.disable_puts_for_tests && Rails.env.test?
|
217
|
+
Kernel.puts(*arg)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Initialize script
|
221
|
+
def initialize
|
222
|
+
begin
|
223
|
+
@source_argv = ARGV.dup
|
224
|
+
@overrided_parameters = {}
|
225
|
+
@params_values = (Rails.env.test? ? {} : self.class.get_argv_values)
|
226
|
+
|
227
|
+
# Create logger if logging enabled
|
228
|
+
if self.class.enable_logging
|
229
|
+
@logger = XLogger.new(:file => log_filename, :dont_touch_rails_logger => (Rails.env.test?))
|
230
|
+
@logger.level = self.class.log_level
|
231
|
+
end
|
232
|
+
|
233
|
+
rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument, GetoptLong::NeedlessArgument => e
|
234
|
+
usage_exit e.message
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Create lock file, call script code and unlock file even if error happend.
|
239
|
+
def run!
|
240
|
+
|
241
|
+
# Print usage and exit if asked
|
242
|
+
usage_exit if params(:h)
|
243
|
+
|
244
|
+
# Create and check lock file if enabled
|
245
|
+
if self.class.enable_locking
|
246
|
+
@lock = LockFile.new(lock_filename)
|
247
|
+
@lock.quiet = true # Don't write errors to STDERR
|
248
|
+
info "Use lock file: #{@lock.path}"
|
249
|
+
if(@lock.lock)
|
250
|
+
warn "Lock file '#{@lock.path}' already open in exclusive mode. Exit!"
|
251
|
+
exit
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
begin
|
256
|
+
# Log important info and call script job
|
257
|
+
info "Log level = #{@logger.level}" if self.class.enable_logging
|
258
|
+
info "Parameters: #{@source_argv.join(', ')}"
|
259
|
+
info "Starting script #{self.class.script_name}..."
|
260
|
+
start = Time.now
|
261
|
+
|
262
|
+
# Инкрементируем счетчик запусков этого скрипта
|
263
|
+
inc_counter("#{self.class.script_name}_times")
|
264
|
+
|
265
|
+
do!
|
266
|
+
duration = Time.now - start
|
267
|
+
info "Script #{self.class.script_name} finished!"
|
268
|
+
info "Script job duration: #{duration}"
|
269
|
+
info "Exit status: #{@exit_status}" if @exit_status
|
270
|
+
|
271
|
+
# Инкрементируем время работы э
|
272
|
+
inc_counter("#{self.class.script_name}_long", duration)
|
273
|
+
|
274
|
+
# Log benchmarker info if it's not empty
|
275
|
+
log_benchmarker_data
|
276
|
+
rescue Exception => e
|
277
|
+
# Print error info if it's not test env or exit
|
278
|
+
if !Rails.env.test? && e.class != SystemExit && e.class != Interrupt
|
279
|
+
msg = self.class.prepare_exception_message(e)
|
280
|
+
puts "\n" + msg
|
281
|
+
fatal msg
|
282
|
+
notify_about_error(e)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Инкрементируем счетчик ошибок этого скрипта
|
286
|
+
inc_counter("#{self.class.script_name}_raised")
|
287
|
+
ensure
|
288
|
+
# Unlock lock file
|
289
|
+
@lock.unlock if self.class.enable_locking && @lock
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Print usage message and exit
|
294
|
+
def usage_exit(message = nil)
|
295
|
+
error "Exit with error message: #{message}"
|
296
|
+
Kernel.puts(self.class.usage(message))
|
297
|
+
exit
|
298
|
+
end
|
299
|
+
|
300
|
+
# Dummy for do! method
|
301
|
+
def do!; end
|
302
|
+
|
303
|
+
# Override one or more parameters for testing purposes
|
304
|
+
def override_parameters(args)
|
305
|
+
if args.is_a?(Symbol)
|
306
|
+
override_parameter(self.class.get_parameter(args))
|
307
|
+
elsif args.is_a?(Hash)
|
308
|
+
args.each{|key, value| override_parameter(self.class.get_parameter(key), value)}
|
309
|
+
else
|
310
|
+
raise "Parameter should be Symbol or Hash"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Return parameter value by key
|
315
|
+
def params(key)
|
316
|
+
param = self.class.get_parameter(key)
|
317
|
+
|
318
|
+
# Use dafault key (if call by alias)
|
319
|
+
key = param[:key]
|
320
|
+
|
321
|
+
case param[:type]
|
322
|
+
when :noarg
|
323
|
+
return (@overrided_parameters.has_key?(key) && @overrided_parameters[key]) || !@params_values[key].nil?
|
324
|
+
when :optional
|
325
|
+
#return nil unless @overrided_parameters.has_key?(key) || @params_values.has_key?(key)
|
326
|
+
return @overrided_parameters[key] || @params_values[key] || param[:default]
|
327
|
+
when :required
|
328
|
+
value = @overrided_parameters[key] || @params_values[key] || param[:default]
|
329
|
+
return value
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# Prepare filename of log file
|
334
|
+
def lock_filename
|
335
|
+
params(:L).blank? ? File.join(self.class.rails_root, 'locks', "#{self.class.script_name}.lock") : params(:L)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Prepare filename of log file
|
339
|
+
def log_filename
|
340
|
+
params(:l).blank? ? File.join(self.class.rails_root, 'log', "#{self.class.script_name}#{log_filename_time_part}.log") : params(:l)
|
341
|
+
end
|
342
|
+
|
343
|
+
private
|
344
|
+
|
345
|
+
# Current time logname part.
|
346
|
+
def log_filename_time_part
|
347
|
+
Time.now.strftime(self.class.date_log_postfix)
|
348
|
+
end
|
349
|
+
|
350
|
+
# Override value for one parameter
|
351
|
+
def override_parameter(param, value = nil)
|
352
|
+
value = case param[:type]
|
353
|
+
when :noarg
|
354
|
+
true
|
355
|
+
when :optional
|
356
|
+
value.to_s
|
357
|
+
when :required
|
358
|
+
value
|
359
|
+
end
|
360
|
+
@overrided_parameters[param[:key]] = value
|
361
|
+
end
|
362
|
+
|
363
|
+
# Print benchmarker statistic to log if its not empty
|
364
|
+
def log_benchmarker_data
|
365
|
+
benchmark_data = {} #benchmark_get_data
|
366
|
+
return if benchmark_data.empty?
|
367
|
+
info "Benchmarker data:"
|
368
|
+
info benchmark_data.to_yaml
|
369
|
+
end
|
370
|
+
|
371
|
+
# Prepare string with exception details
|
372
|
+
def self.prepare_exception_message(e)
|
373
|
+
<<-EXCEPTION
|
374
|
+
Exception happend
|
375
|
+
Type: #{e.class.inspect}
|
376
|
+
Error occurs: #{e.message}
|
377
|
+
Backtrace: #{e.backtrace.join("\n")}
|
378
|
+
EXCEPTION
|
379
|
+
end
|
380
|
+
|
381
|
+
def inc_counter(id, counter = 1)
|
382
|
+
# stub
|
383
|
+
end
|
384
|
+
|
385
|
+
def notify_about_error(ex)
|
386
|
+
# stub
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ClassLevelInheritableAttributes
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def class_inheritable_attributes(*args)
|
8
|
+
@class_inheritable_attributes ||= [:class_inheritable_attributes]
|
9
|
+
@class_inheritable_attributes += args
|
10
|
+
args.each do |arg|
|
11
|
+
class_eval %(
|
12
|
+
class << self; attr_accessor :#{arg} end
|
13
|
+
)
|
14
|
+
end
|
15
|
+
@class_inheritable_attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
def inherited(subclass)
|
19
|
+
@class_inheritable_attributes.each do |class_inheritable_attribute|
|
20
|
+
instance_var = "@#{class_inheritable_attribute}"
|
21
|
+
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
class LockFile
|
3
|
+
attr_accessor :logger, :path, :quiet
|
4
|
+
|
5
|
+
# By default print errors
|
6
|
+
@quiet = false
|
7
|
+
|
8
|
+
def initialize(path, logger = nil)
|
9
|
+
if path == nil
|
10
|
+
raise "file path cannot be nil"
|
11
|
+
end
|
12
|
+
@path = path
|
13
|
+
@logger = logger || $logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def lock(waiting_unlock = false)
|
17
|
+
flags = File::WRONLY | File::TRUNC | File::CREAT
|
18
|
+
|
19
|
+
@f = File.open(@path, flags)
|
20
|
+
flags = File::LOCK_EX
|
21
|
+
flags |= File::LOCK_NB if waiting_unlock == false
|
22
|
+
|
23
|
+
unless @f.flock flags
|
24
|
+
log "File '#{@path}' already open in exclusive mode.\n"
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
|
30
|
+
def unlock
|
31
|
+
@f.close
|
32
|
+
File.delete( @f.path)
|
33
|
+
rescue Errno::ENOENT
|
34
|
+
#do nothing
|
35
|
+
end
|
36
|
+
|
37
|
+
def log(str)
|
38
|
+
return if @quiet
|
39
|
+
if @logger
|
40
|
+
@logger.warn str
|
41
|
+
else
|
42
|
+
print str
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
class XLogger < Logger
|
6
|
+
def initialize(hint = {})
|
7
|
+
|
8
|
+
if(hint[:rotate])
|
9
|
+
super(hint[:file] || STDOUT, hint[:rotate])
|
10
|
+
else
|
11
|
+
super(hint[:file] || STDOUT)
|
12
|
+
end
|
13
|
+
|
14
|
+
STDOUT.sync = true
|
15
|
+
self.formatter = Formatter.new
|
16
|
+
self.datetime_format = hint[:date_format] || "%d.%m %H:%M:%S"
|
17
|
+
|
18
|
+
# Don't change default logger if asked
|
19
|
+
if hint[:dont_touch_rails_logger].blank? && defined?(ActiveRecord::Base)
|
20
|
+
ActiveRecord::Base.logger = self
|
21
|
+
def Rails.logger; ActiveRecord::Base.logger; end
|
22
|
+
|
23
|
+
# This raise warning to STDOUT
|
24
|
+
#Object.const_set "RAILS_DEFAULT_LOGGER", self
|
25
|
+
end
|
26
|
+
|
27
|
+
self.level = hint[:log_level] || rails_env_log_level
|
28
|
+
log_sql if hint[:log_sql] || !Rails.env.production?
|
29
|
+
end
|
30
|
+
|
31
|
+
class Formatter < Logger::Formatter
|
32
|
+
def call(severity, time, progname, msg)
|
33
|
+
if severity == 'INFO' && msg.nil?
|
34
|
+
# use this if you want a simple blank line without date in your logs:
|
35
|
+
# just call a logger.info without any params
|
36
|
+
"\n"
|
37
|
+
else
|
38
|
+
format_datetime(time) << " " << msg2str(msg) << "\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_sql
|
44
|
+
ActiveRecord::Base.connection.logger = self if defined?(ActiveRecord::Base)
|
45
|
+
end
|
46
|
+
|
47
|
+
def rails_env_log_level
|
48
|
+
Rails.env.production? ? Logger::INFO : Logger::DEBUG
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module ActiveRecord
|
53
|
+
module ConnectionAdapters
|
54
|
+
class AbstractAdapter
|
55
|
+
def logger=(val)
|
56
|
+
@logger = val
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# for Rails 3
|
2
|
+
if Rails::VERSION::MAJOR >= 3
|
3
|
+
|
4
|
+
module Bin
|
5
|
+
class BinGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def add_files
|
9
|
+
template "script.rb", "bin/#{file_path}.rb"
|
10
|
+
template "script_class.rb", "app/models/bin/#{file_path}_script.rb"
|
11
|
+
template "spec.rb", "spec/models/bin/#{file_path}_script_spec.rb"
|
12
|
+
chmod "bin/#{file_path}.rb", 0755
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
# for Rails 2.3
|
20
|
+
if Rails::VERSION::MAJOR == 2
|
21
|
+
|
22
|
+
class BinGenerator < Rails::Generator::NamedBase
|
23
|
+
def manifest
|
24
|
+
record do |m|
|
25
|
+
m.template "script.rb", "bin/#{file_path}.rb", :chmod => 0755
|
26
|
+
m.template "script_class.rb", "app/models/bin/#{file_path}_script.rb"
|
27
|
+
m.directory "spec/models/bin"
|
28
|
+
m.template "spec.rb", "spec/models/bin/#{file_path}_script_spec.rb"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
path = File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath)
|
5
|
+
|
6
|
+
APP_ROOT = File.dirname(path)
|
7
|
+
ENV['BUNDLE_GEMFILE'] ||= path
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'bundler/setup'
|
11
|
+
|
12
|
+
load Gem.bin_path('bin_script', 'bin_helper')
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class <%= class_name %>Script < BinScript
|
2
|
+
# You can define script parameters this way:
|
3
|
+
# {type} {key}, {options}
|
4
|
+
#
|
5
|
+
# Availible types: noarg, optional and required
|
6
|
+
# Key is the Symbol with parameter key
|
7
|
+
# Options is a hash that may contain this keys:
|
8
|
+
# :description - parameters descriptions to automatic prepare usage message
|
9
|
+
# :alias - symbol or array of symbols with aliases for this parameters
|
10
|
+
# :default - default value for this parameter
|
11
|
+
#
|
12
|
+
# Instead of options hash you can use just string with description.
|
13
|
+
#
|
14
|
+
# Some examples:
|
15
|
+
# noarg :n, "This is a parameter without argument. 'params(:n)' in script will return true or false."
|
16
|
+
# optional :o, :description => "Optional parameter that can has argument. 'params()'"
|
17
|
+
# optional :oo, :description => "Optional parameter with default value and aliases. 'params(:o)' and 'params(:oo)' will return argument value or default key value", :default => "some default value", :alias => :oo
|
18
|
+
# required :r, :description => "Required argemtn. Script will exit if you use this parameter without argument value. Also you can use multiple aliases.", :alias => [:rr, :rrr]
|
19
|
+
#
|
20
|
+
# You may override default log level (Logger::INFO) for this script log this way:
|
21
|
+
# self.log_level = Logger::INFO
|
22
|
+
#
|
23
|
+
# Availible log levels:
|
24
|
+
# DEBUG = 0
|
25
|
+
# INFO = 1
|
26
|
+
# WARN = 2
|
27
|
+
# ERROR = 3
|
28
|
+
# FATAL = 4
|
29
|
+
# UNKNOWN = 5
|
30
|
+
#
|
31
|
+
# По умолчанию мы при каждом запуске скрипта продолжаем писать в основной лог. Но можно записать сюда формат, который
|
32
|
+
# можно скормить time.strftime(format) и результат подставится в конец имени файла лога. Таким образом можно делать
|
33
|
+
# отдельные лог-файлы для каждого запуска, для каждого дня/месяца/года/часа и т.д...
|
34
|
+
# Например "_%Y-%m-%d_%H-%M-%S" - каждый запуск новый лог
|
35
|
+
# "_%Y-%m-%d" - каждый день новый лог
|
36
|
+
# self.date_log_postfix = "_%Y-%m-%d"
|
37
|
+
|
38
|
+
# Do script job!
|
39
|
+
def do!
|
40
|
+
logger.info "Script <%= class_name %> works!"
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe <%= class_name %>Script do
|
4
|
+
before :each do
|
5
|
+
@bin = <%= class_name %>Script.new
|
6
|
+
|
7
|
+
# if want to point some params into bin => @bin.override_parameters({:i => 10, :a => '20'})
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should do! and not raise' do
|
11
|
+
@bin.do!
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe BinScript do
|
4
|
+
before :each do
|
5
|
+
root = Pathname.new(File.dirname(__FILE__) + '/../').realpath.to_s
|
6
|
+
Rails.stub!(:root).and_return(root)
|
7
|
+
end
|
8
|
+
|
9
|
+
class TestScript < BinScript
|
10
|
+
noarg :n, "Test parameter that can't have argument"
|
11
|
+
optional :o, :description => "Test parameter that can have argument", :alias => :oo
|
12
|
+
required :r, :description => "Test parameter that should have argument", :alias => [:rr, :rrr]
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "class name detection" do
|
16
|
+
before(:all) do
|
17
|
+
@test_date = [
|
18
|
+
{:filename => "/prj/bin/nagios/some.rb", :parts => ['nagios','some'], :class => "Nagios::SomeNagiosScript", :files => ["app/models/nagios_script.rb", "app/models/bin/nagios/some_nagios_script.rb"]},
|
19
|
+
{:filename => "/prj/bin/another.rb", :parts => ['another'], :class => "AnotherScript", :files => ["app/models/bin/another_script.rb"]}
|
20
|
+
]
|
21
|
+
@test_keys = [:parts, :class, :files]
|
22
|
+
end
|
23
|
+
it 'should detect class name and filenames from source script filename' do
|
24
|
+
@test_date.each do |test|
|
25
|
+
result = BinScript.parse_script_file_name(test[:filename])
|
26
|
+
@test_keys.each { |key| result[key].should == test[key] }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "parameters" do
|
32
|
+
it 'should accept string as description' do
|
33
|
+
TestScript.get_parameter(:n)[:description].should == "Test parameter that can't have argument"
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should accept hash as second parameter' do
|
37
|
+
TestScript.get_parameter(:o)[:description].should == "Test parameter that can have argument"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should accept parameters of all types" do
|
41
|
+
{:n => :noarg, :o => :optional, :r => :required}.each do |key,type|
|
42
|
+
TestScript.get_parameter(key)[:type].should == type
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "required parameters" do
|
47
|
+
it "should return correct requred argument value" do
|
48
|
+
@script.override_parameters(:r => 'value')
|
49
|
+
@script.params(:r).should == 'value'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "overrided parameters for testing" do
|
54
|
+
it "should return false or true for noarg parameters even without value" do
|
55
|
+
@script.params(:n).should be_false
|
56
|
+
@script.override_parameters(:n)
|
57
|
+
@script.params(:n).should be_true
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "optional parameters" do
|
61
|
+
it "should return nil if parameter not set or empty string if no argument defined" do
|
62
|
+
@script.params(:o).should be_nil
|
63
|
+
@script.override_parameters(:o)
|
64
|
+
@script.params(:o).should == ''
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return value of argument" do
|
68
|
+
@script.params(:o).should be_nil
|
69
|
+
@script.override_parameters(:o => 'value')
|
70
|
+
@script.params(:o).should == 'value'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "aliases" do
|
75
|
+
it 'should accept and handle one alias' do
|
76
|
+
@script.override_parameters(:o => 'value')
|
77
|
+
@script.params(:o).should == 'value'
|
78
|
+
@script.params(:o).should == @script.params(:oo)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should accept and handle some alias' do
|
82
|
+
@script.override_parameters(:r => 'value')
|
83
|
+
@script.params(:r).should == 'value'
|
84
|
+
@script.params(:r).should == @script.params(:rr)
|
85
|
+
@script.params(:rr).should == @script.params(:rrr)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should override parameters by alias' do
|
89
|
+
@script.override_parameters(:oo => 'value')
|
90
|
+
@script.params(:o).should == 'value'
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "default values" do
|
94
|
+
class DevValTestScript < BinScript
|
95
|
+
optional :o, :default => "def opt value"
|
96
|
+
required :r, :default => "def req value"
|
97
|
+
end
|
98
|
+
it 'should return default value if value for optional argument was not defiend' do
|
99
|
+
DevValTestScript.new.params(:o).should == 'def opt value'
|
100
|
+
DevValTestScript.new.params(:r).should == 'def req value'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "usage" do
|
108
|
+
it "should generate usage message" do
|
109
|
+
USAGE = <<USAGE
|
110
|
+
Use: ./bin/test_script.rb [OPTIONS]
|
111
|
+
|
112
|
+
Availible options:
|
113
|
+
|
114
|
+
-e Rails environment ID (default - development)
|
115
|
+
-h Print usage message
|
116
|
+
-l Path to log file (default \#{Rails.root}/log/[script_name].log
|
117
|
+
-L Path to lock file (default \#{Rails.root}/lock/[script_name].lock
|
118
|
+
-n Test parameter that can't have argument
|
119
|
+
-o Test parameter that can have argument
|
120
|
+
-r Test parameter that should have argument
|
121
|
+
|
122
|
+
USAGE
|
123
|
+
TestScript.usage.should == USAGE
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
before(:each) do
|
128
|
+
@script = TestScript.new
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "locking" do
|
132
|
+
it "should set default lock file" do
|
133
|
+
@script.lock_filename.should == File.join(Rails.root, %w{locks test_script.lock})
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should overwrite lock filename with option -L" do
|
137
|
+
@script.override_parameters(:L => '/tmp/test_script.lock')
|
138
|
+
@script.lock_filename.should == '/tmp/test_script.lock'
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should create lock file while execute do! and delete it after even when exceptions occurs" do
|
142
|
+
# Extend test script
|
143
|
+
class TestScript
|
144
|
+
attr_accessor :lock_file_has_been_created
|
145
|
+
attr_accessor :raise_before_finish
|
146
|
+
def do!
|
147
|
+
self.lock_file_has_been_created = File.exist?(lock_filename)
|
148
|
+
raise "Test exception" if self.raise_before_finish
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@script.raise_before_finish = false
|
152
|
+
@script.lock_file_has_been_created.should_not be_true
|
153
|
+
@script.run!
|
154
|
+
@script.lock_file_has_been_created.should be_true
|
155
|
+
File.exist?(@script.lock_filename).should be_false
|
156
|
+
|
157
|
+
# And now test case when execptions occurs while script executed
|
158
|
+
@script.lock_file_has_been_created = false
|
159
|
+
@script.raise_before_finish = true
|
160
|
+
@script.run!
|
161
|
+
@script.lock_file_has_been_created.should be_true
|
162
|
+
@script.raise_before_finish = true
|
163
|
+
File.exist?(@script.lock_filename).should be_false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "logging" do
|
168
|
+
it "should set default log file correctly" do
|
169
|
+
@script.log_filename.should == File.join(Rails.root, %w{log test_script.log})
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should overwrite log filename with option -l" do
|
173
|
+
@script.override_parameters(:l => '/tmp/test_script.log')
|
174
|
+
@script.log_filename.should == '/tmp/test_script.log'
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should create default logger" do
|
178
|
+
default_log = File.join(Rails.root, %w{tmp test_script.log})
|
179
|
+
File.delete(default_log) if File.exist?(default_log)
|
180
|
+
@script.logger.should_not be_nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bin_script
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Lifshits Dmitry
|
13
|
+
- Makarchev Konstantin
|
14
|
+
autorequire: init
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-04-07 00:00:00 +04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 3
|
33
|
+
- 2
|
34
|
+
version: 2.3.2
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
description: Easy writing and executing bins (espesually for crontab or god) in Rails project
|
52
|
+
email: kostya27@gmail.com
|
53
|
+
executables:
|
54
|
+
- bin_helper
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- .gitignore
|
61
|
+
- Gemfile
|
62
|
+
- Gemfile.lock
|
63
|
+
- LICENSE
|
64
|
+
- README.markdown
|
65
|
+
- Rakefile
|
66
|
+
- bin/bin_helper
|
67
|
+
- bin_script.gemspec
|
68
|
+
- init.rb
|
69
|
+
- lib/bin_script.rb
|
70
|
+
- lib/bin_script/bin_script.rb
|
71
|
+
- lib/bin_script/class_inheritable_attributes.rb
|
72
|
+
- lib/bin_script/lock_file.rb
|
73
|
+
- lib/bin_script/version.rb
|
74
|
+
- lib/bin_script/xlogger.rb
|
75
|
+
- lib/generators/bin/bin_generator.rb
|
76
|
+
- lib/generators/bin/templates/script.rb
|
77
|
+
- lib/generators/bin/templates/script_class.rb
|
78
|
+
- lib/generators/bin/templates/spec.rb
|
79
|
+
- spec/bin_script_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://github.com/kostya/bin_script
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.3.7
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Easy writing and executing bins (espesually for crontab or god) in Rails project For my purposes much better than Rake, Thor and Rails Runner
|
115
|
+
test_files: []
|
116
|
+
|