bin_script 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|