console_runner 0.1.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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +12 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +58 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/console_runner.gemspec +44 -0
- data/exe/c_run +86 -0
- data/exe/c_run.rb +7 -0
- data/lib/cmd_parser.rb +223 -0
- data/lib/console_runner.rb +79 -0
- data/lib/console_runner/version.rb +3 -0
- data/lib/console_runner_error.rb +1 -0
- data/lib/file_parser.rb +88 -0
- data/lib/runner.rb +21 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f8075476480adc8aea5a9508dca384fa66508a22
|
4
|
+
data.tar.gz: f58b9bedc8621e0ff85b83e918b3f64c2d8cb852
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a717dc7f2387a0549d984d571a9cedaa1eb2f45f1e4575ff639d3d7e61ca55ee8cc3aeca2e55478d69ad4d308d54c94bded243bae7d781bc6a5baa2270d0fa29
|
7
|
+
data.tar.gz: 162a8c1b729c4eb5c833cdcc1efee701d597ec8879875adc0f05f105f01d52faf6188aad67d739c4a491718e415262f64e73d118e39084f8782866a4067b3c58
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
repo_token: Zt5KZXhBj3awn9OS5Hy6pDuVEGSrWWSjT
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Yury Karpovich
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# ConsoleRunner
|
2
|
+
[![Gem Version][GV img]][Gem Version]
|
3
|
+
[![Build Status][BS img]][Build Status]
|
4
|
+
[![Dependency Status][DS img]][Dependency Status]
|
5
|
+
[![Code Climate][CC img]][Code Climate]
|
6
|
+
[![Coverage Status][CS img]][Coverage Status]
|
7
|
+
|
8
|
+
This gem provides you an ability to run any Ruby method from command-line (no any code modifications required!!!).
|
9
|
+
One thing you need to do is to add an [YARD](http://yardoc.org/) tag annotation `@runnable`.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'console_runner'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install console_runner
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
TODO: Write usage instructions here
|
30
|
+
|
31
|
+
## Development
|
32
|
+
|
33
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
34
|
+
|
35
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/yuri-karpovich/console_runner.
|
40
|
+
|
41
|
+
|
42
|
+
## License
|
43
|
+
|
44
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
45
|
+
|
46
|
+
|
47
|
+
[Gem Version]: https://rubygems.org/gems/console_runner
|
48
|
+
[Build Status]: https://travis-ci.org/yuri-karpovich/console_runner
|
49
|
+
[travis pull requests]: https://travis-ci.org/yuri-karpovich/console_runner/pull_requests
|
50
|
+
[Dependency Status]: https://gemnasium.com/github.com/yuri-karpovich/console_runner
|
51
|
+
[Code Climate]: https://codeclimate.com/github/yuri-karpovich/console_runner
|
52
|
+
[Coverage Status]: https://coveralls.io/github/yuri-karpovich/console_runner
|
53
|
+
|
54
|
+
[GV img]: https://badge.fury.io/rb/console_runner.png
|
55
|
+
[BS img]: https://travis-ci.org/yuri-karpovich/console_runner.svg?branch=master
|
56
|
+
[DS img]: https://gemnasium.com/badges/github.com/yuri-karpovich/console_runner.svg
|
57
|
+
[CC img]: https://codeclimate.com/github/yuri-karpovich/console_runner.png
|
58
|
+
[CS img]: https://coveralls.io/repos/github/yuri-karpovich/console_runner/badge.svg
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "console_runner"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'console_runner/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'console_runner'
|
9
|
+
spec.version = ConsoleRunner::VERSION
|
10
|
+
spec.authors = ['Yury Karpovich']
|
11
|
+
spec.email = %w(spoonest@gmail.com yuri.karpovich@gmail.com)
|
12
|
+
|
13
|
+
spec.summary = 'Command-line runner for ruby code.'
|
14
|
+
spec.description = 'This gem provides you an ability to run any Ruby method ' \
|
15
|
+
'from command-line (no any code modifications required!!!)'
|
16
|
+
spec.homepage = 'https://github.com/yuri-karpovich/console_runner'
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
21
|
+
if spec.respond_to?(:metadata)
|
22
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
23
|
+
else
|
24
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
25
|
+
'public gem pushes.'
|
26
|
+
end
|
27
|
+
|
28
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
f.match(%r{^(test|spec|features)/})
|
30
|
+
end
|
31
|
+
spec.bindir = 'exe'
|
32
|
+
spec.executables = ['c_run']
|
33
|
+
spec.require_paths = ['lib']
|
34
|
+
|
35
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
36
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
37
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
38
|
+
spec.add_development_dependency 'pry'
|
39
|
+
spec.add_development_dependency 'simplecov'
|
40
|
+
spec.add_development_dependency 'coveralls', '~> 0.8'
|
41
|
+
spec.add_dependency 'trollop', '~> 2.1'
|
42
|
+
spec.add_dependency 'yard', '~> 0.9'
|
43
|
+
spec.add_dependency 'colorize', '~> 0.8'
|
44
|
+
end
|
data/exe/c_run
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# #!/usr/bin/env ruby
|
2
|
+
# require 'trollop'
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
6
|
+
# $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
7
|
+
# require '../lib/file_parser'
|
8
|
+
# require '../lib/cmd_parser'
|
9
|
+
# require '../lib/runner'
|
10
|
+
#
|
11
|
+
# file_path = ENV['DEBUG_FILE']
|
12
|
+
# file_path ||= File.realpath ARGV.shift
|
13
|
+
# file_parser = FileParser.new(file_path)
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# runnable_classes = file_parser.list_classes(:runnable)
|
17
|
+
# raise "One runnable Class should be specified in file.
|
18
|
+
# Runnable class should be marked with @#{FileParser::RUNNABLE_TAG.to_s} tag" if runnable_classes.count != 1
|
19
|
+
#
|
20
|
+
# clazz = runnable_classes.first
|
21
|
+
# all_methods = file_parser.list_methods(:all, clazz)
|
22
|
+
# runnable_methods = file_parser.list_methods(:runnable, clazz)
|
23
|
+
# initialize_method = all_methods.find{|m| m.name == :initialize}
|
24
|
+
# run_method = all_methods.find{|m| m.name == :run}
|
25
|
+
#
|
26
|
+
#
|
27
|
+
# cmd_parser = CmdParser.new(runnable_methods, initialize_method)
|
28
|
+
# cmd = ARGV[0]
|
29
|
+
# action_method = runnable_methods.find { |m| m.name.to_s == cmd } # get the subcommand
|
30
|
+
# action_method ||= run_method
|
31
|
+
# cmd_parser.parse_method action_method
|
32
|
+
# raise "Cannot run #{cmd} command because method is nil" unless action_method
|
33
|
+
#
|
34
|
+
#
|
35
|
+
# puts "======================================================="
|
36
|
+
# puts "Global options:"
|
37
|
+
# puts cmd_parser.global_opts.map{|k,v| " #{k} = #{v}" }.join("\n")
|
38
|
+
# if initialize_method
|
39
|
+
# puts "INIT: #{initialize_method.name}"
|
40
|
+
# puts "INIT options:"
|
41
|
+
# puts cmd_parser.init_method.cmd_opts.map{|k,v| " #{k} = #{v}" }.join("\n")
|
42
|
+
# end
|
43
|
+
# puts "Subcommand: #{action_method.name}"
|
44
|
+
# puts "Subcommand options:"
|
45
|
+
# puts cmd_parser.method.cmd_opts.map{|k,v| " #{k} = #{v}" }.join("\n")
|
46
|
+
# puts "Remaining arguments: #{ARGV.inspect}" if ARGV != []
|
47
|
+
# puts "======================================================="
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# Runner.run{
|
51
|
+
# require file_path
|
52
|
+
# class_full_name = clazz.title
|
53
|
+
# raise "#{class_full_name} is not defined" unless Module.const_defined?(class_full_name)
|
54
|
+
# klass_obj = Module.const_get(class_full_name)
|
55
|
+
# method_type = action_method.scope
|
56
|
+
# method_params = cmd_parser.method.params_array
|
57
|
+
#
|
58
|
+
# case method_type
|
59
|
+
# when :class
|
60
|
+
# klass_obj.send(action_method.name, *method_params)
|
61
|
+
# when :instance
|
62
|
+
# init_params = cmd_parser.init_method.params_array
|
63
|
+
# # TODO catch errors
|
64
|
+
# obj = klass_obj.new(*init_params)
|
65
|
+
# obj.send(action_method.name, *method_params)
|
66
|
+
# else
|
67
|
+
# raise "Unknown method type: #{method_type}"
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# }
|
71
|
+
#
|
72
|
+
#
|
73
|
+
#
|
74
|
+
#
|
75
|
+
#
|
76
|
+
#
|
77
|
+
#
|
78
|
+
#
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# # raise "#{clazz.name}#initialize method should be specified" unless initialize_method
|
82
|
+
# #
|
83
|
+
# # raise "At least one method should be marked with @#{FileParser::RUNNABLE_TAG.to_s} tag.
|
84
|
+
# # Also you may specify #run method and it will be executed by default.
|
85
|
+
# # #run method don't need any code annotations as well." if runnable_methods.count == 0 unless run_method
|
86
|
+
#
|
data/exe/c_run.rb
ADDED
data/lib/cmd_parser.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
|
3
|
+
# Parses command line
|
4
|
+
class CmdParser
|
5
|
+
|
6
|
+
# Parses method code
|
7
|
+
class ParsedMethod
|
8
|
+
attr_reader :method, :name, :param_tags, :option_tags
|
9
|
+
attr_accessor :cmd_opts
|
10
|
+
|
11
|
+
def initialize(method)
|
12
|
+
@method = method
|
13
|
+
@name = @method.name
|
14
|
+
@parameters = @method.parameters
|
15
|
+
@param_tags = FileParser.select_param_tags @method
|
16
|
+
@option_tags = FileParser.select_option_tags @method
|
17
|
+
@cmd_opts = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def params_array
|
21
|
+
expected_params = @parameters.map(&:first).map.with_index { |p, i| [i, p] }.to_h
|
22
|
+
options_groups = {}
|
23
|
+
get_params = {}
|
24
|
+
|
25
|
+
expected_params.each do |index, name|
|
26
|
+
if options_group?(name)
|
27
|
+
options_groups[index] = name
|
28
|
+
get_params[index] = option_as_hash(name)
|
29
|
+
else
|
30
|
+
get_params[index] = @cmd_opts[name.to_sym]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
get_params = get_params.to_a.sort_by { |a| a[0] }.reverse
|
34
|
+
|
35
|
+
stop_delete = false
|
36
|
+
get_params.delete_if do |a|
|
37
|
+
index = a[0]
|
38
|
+
value = a[1]
|
39
|
+
result = false
|
40
|
+
|
41
|
+
if options_groups[index]
|
42
|
+
result = value == {}
|
43
|
+
else
|
44
|
+
result = value == nil
|
45
|
+
end
|
46
|
+
stop_delete = true unless result
|
47
|
+
next if stop_delete
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
get_params.sort_by { |a| a[0] }.map { |a| a[1] }
|
52
|
+
end
|
53
|
+
|
54
|
+
def param_tags_names
|
55
|
+
param_tags.map { |t| t.name }
|
56
|
+
end
|
57
|
+
|
58
|
+
def option_tags_names
|
59
|
+
option_tags.map { |t| t.pair.name.gsub(':', '') }
|
60
|
+
end
|
61
|
+
|
62
|
+
def options_group?(param_name)
|
63
|
+
option_tags.any? { |t| t.name == param_name }
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_params
|
67
|
+
@parameters.map do |array|
|
68
|
+
array.map do |a|
|
69
|
+
if a
|
70
|
+
['"', "'"].include?(a[0]) && ['"', "'"].include?(a[-1]) ? a[1..-2] : a
|
71
|
+
else
|
72
|
+
a
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end.to_h
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def option_as_hash(options_group_name)
|
81
|
+
result = {}
|
82
|
+
option_tags.select { |t| t.name == options_group_name }.each do |t|
|
83
|
+
option_name = t.pair.name.gsub(':', '')
|
84
|
+
option_value = @cmd_opts[option_name.to_sym]
|
85
|
+
result[option_name] = option_value if option_value
|
86
|
+
end
|
87
|
+
result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_reader :global_opts, :method, :init_method
|
92
|
+
|
93
|
+
# Should be executed before ARGV.shift
|
94
|
+
def initialize(runnable_methods, init_method= nil)
|
95
|
+
clazz = runnable_methods.first.parent
|
96
|
+
sub_commands = runnable_methods.map { |m| m.name.to_s }
|
97
|
+
@parser = Trollop::Parser.new
|
98
|
+
@parser.banner(FileParser.select_runnable_tags(clazz).map { |t| (t.text + "\n") }.join("\n"))
|
99
|
+
@parser.stop_on sub_commands
|
100
|
+
|
101
|
+
if init_method
|
102
|
+
@init_method = ParsedMethod.new init_method
|
103
|
+
same_methods = @init_method.param_tags_names & @init_method.option_tags_names
|
104
|
+
raise "You have the same name for @param and @option attribute(s): #{same_methods.join(', ')}.
|
105
|
+
Use different names to `console_runner` be able to run #{@init_method.name} method." if same_methods.count > 0
|
106
|
+
|
107
|
+
|
108
|
+
@init_method.param_tags.each do |tag|
|
109
|
+
tag_name = tag.name
|
110
|
+
tag_text = tag.text
|
111
|
+
tag_type = tag.type
|
112
|
+
if tag_type == "Hash"
|
113
|
+
options = option_tags.select { |t| t.name == tag.name }
|
114
|
+
if options.count > 0
|
115
|
+
options.each do |option|
|
116
|
+
option_name = option.pair.name.gsub(':', '')
|
117
|
+
option_text = option.pair.text
|
118
|
+
option_type = option.pair.type
|
119
|
+
@parser.opt(option_name.to_sym, "(Ruby class: #{option_type}) " + option_text.to_s, type: CmdParser.parse_type(option_type))
|
120
|
+
end
|
121
|
+
else
|
122
|
+
@parser.opt(tag_name.to_sym, "(Ruby class: #{tag_type}) " + tag_text.to_s, type: CmdParser.parse_type(tag_type))
|
123
|
+
end
|
124
|
+
else
|
125
|
+
@parser.opt(tag_name.to_sym, "(Ruby class: #{tag_type}) " + tag_text.to_s, type: CmdParser.parse_type(tag_type))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
@global_opts = Trollop::with_standard_exception_handling(@parser) do
|
131
|
+
begin
|
132
|
+
@parser.parse ARGV
|
133
|
+
rescue Trollop::CommandlineError => e
|
134
|
+
raise "You must provide one of available actions: #{sub_commands.join ', '}" unless sub_commands.include?(ARGV[0])
|
135
|
+
raise e
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
if @init_method
|
140
|
+
given_attrs = @global_opts.keys.select { |k| k.to_s.include? '_given' }.map { |k| k.to_s.gsub('_given', '').to_sym }
|
141
|
+
@init_method.cmd_opts = @global_opts.select { |k, v| given_attrs.include? k }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def parse_method(method)
|
147
|
+
ARGV.shift
|
148
|
+
@method = ParsedMethod.new method
|
149
|
+
same_methods = @method.param_tags_names & @method.option_tags_names
|
150
|
+
raise "You have the same name for @param and @option attribute(s): #{same_methods.join(', ')}.
|
151
|
+
Use different names to `console_runner` be able to run #{@method.name} method." if same_methods.count > 0
|
152
|
+
method_params_tags = @method.param_tags
|
153
|
+
|
154
|
+
method_params_tags.each do |tag|
|
155
|
+
tag_name = tag.name
|
156
|
+
tag_text = tag.text
|
157
|
+
tag_type = tag.type
|
158
|
+
if tag_type == "Hash"
|
159
|
+
options = @method.option_tags.select { |t| t.name == tag.name }
|
160
|
+
if options.count > 0
|
161
|
+
options.each do |option|
|
162
|
+
option_name = option.pair.name.gsub(':', '')
|
163
|
+
option_text = option.pair.text
|
164
|
+
option_type = option.pair.type
|
165
|
+
@parser.opt(option_name.to_sym, "(Ruby class: #{option_type}) " + option_text.to_s, type: CmdParser.parse_type(option_type))
|
166
|
+
end
|
167
|
+
else
|
168
|
+
@parser.opt(tag_name.to_sym, "(Ruby class: #{tag_type}) " + tag_text.to_s, type: CmdParser.parse_type(tag_type))
|
169
|
+
end
|
170
|
+
else
|
171
|
+
@parser.opt(tag_name.to_sym, "(Ruby class: #{tag_type}) " + tag_text.to_s, type: CmdParser.parse_type(tag_type))
|
172
|
+
end
|
173
|
+
end
|
174
|
+
cmd_opts = Trollop::with_standard_exception_handling @parser do
|
175
|
+
unless method_params_tags.count.zero?
|
176
|
+
raise Trollop::HelpNeeded if ARGV.empty?
|
177
|
+
end
|
178
|
+
# show help screen
|
179
|
+
@parser.parse ARGV
|
180
|
+
end
|
181
|
+
given_attrs = cmd_opts.keys.select { |k| k.to_s.include? '_given' }.map { |k| k.to_s.gsub('_given', '').to_sym }
|
182
|
+
@method.cmd_opts = cmd_opts.select { |k, v| given_attrs.include? k }
|
183
|
+
@method.default_params.each do |k, v|
|
184
|
+
@method.cmd_opts[k.to_sym] ||= v
|
185
|
+
end
|
186
|
+
|
187
|
+
method_params_tags.select { |t| t.tag_name == 'param' }.map(&:name).each do |required_param|
|
188
|
+
next if @method.options_group? required_param
|
189
|
+
unless @method.cmd_opts[required_param.to_sym]
|
190
|
+
raise "You must specify required parameter: #{required_param}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.parse_type(yard_type)
|
196
|
+
case yard_type
|
197
|
+
when 'String'
|
198
|
+
:string
|
199
|
+
when 'Integer'
|
200
|
+
:int
|
201
|
+
when 'Fixnum'
|
202
|
+
:int
|
203
|
+
when 'Float'
|
204
|
+
:float
|
205
|
+
when 'Boolean'
|
206
|
+
:boolean
|
207
|
+
when 'Array(String)'
|
208
|
+
:strings
|
209
|
+
when 'Array(Integer)'
|
210
|
+
:ints
|
211
|
+
when 'Array(Fixnum)'
|
212
|
+
:ints
|
213
|
+
when 'Array(Float)'
|
214
|
+
:floats
|
215
|
+
when 'Array(Boolean)'
|
216
|
+
:booleans
|
217
|
+
else
|
218
|
+
raise "Unsupported YARD type: #{yard_type}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ConsoleRunner
|
2
|
+
|
3
|
+
require 'file_parser'
|
4
|
+
require 'cmd_parser'
|
5
|
+
require 'runner'
|
6
|
+
|
7
|
+
file_from_arg = ARGV.shift
|
8
|
+
raise ConsoleRunnerError, 'Specify file to be executed' unless file_from_arg
|
9
|
+
file_path ||= File.realpath file_from_arg
|
10
|
+
raise ConsoleRunnerError, "File doesn't exist: #{file_path}" unless File.exist? file_path
|
11
|
+
file_parser = FileParser.new(file_path)
|
12
|
+
|
13
|
+
|
14
|
+
runnable_classes = file_parser.list_classes(:runnable)
|
15
|
+
if runnable_classes.count != 1
|
16
|
+
raise ConsoleRunnerError, "One runnable Class should be specified in file.
|
17
|
+
Runnable class should be marked with @#{FileParser::RUNNABLE_TAG} tag"
|
18
|
+
end
|
19
|
+
|
20
|
+
clazz = runnable_classes.first
|
21
|
+
all_methods = file_parser.list_methods(:all, clazz)
|
22
|
+
runnable_methods = file_parser.list_methods(:runnable, clazz)
|
23
|
+
initialize_method = all_methods.find { |m| m.name == :initialize }
|
24
|
+
run_method = all_methods.find { |m| m.name == :run }
|
25
|
+
|
26
|
+
|
27
|
+
cmd = ARGV[0]
|
28
|
+
action_method = runnable_methods.find { |m| m.name.to_s == cmd } # get sub-command
|
29
|
+
action_method ||= run_method
|
30
|
+
cmd_parser = CmdParser.new(runnable_methods, initialize_method)
|
31
|
+
raise ConsoleRunnerError, "Cannot run! You haven't specify any method to run." unless action_method
|
32
|
+
cmd_parser.parse_method action_method
|
33
|
+
|
34
|
+
|
35
|
+
puts '======================================================='
|
36
|
+
puts 'Global options:'
|
37
|
+
puts cmd_parser.global_opts.map { |k, v| " #{k} = #{v}" }.join("\n")
|
38
|
+
if initialize_method
|
39
|
+
puts "INIT: #{initialize_method.name}"
|
40
|
+
puts 'INIT options:'
|
41
|
+
puts cmd_parser.init_method.cmd_opts.map { |k, v| " #{k} = #{v}" }.join("\n")
|
42
|
+
end
|
43
|
+
puts "Subcommand: #{action_method.name}"
|
44
|
+
puts 'Subcommand options:'
|
45
|
+
puts cmd_parser.method.cmd_opts.map { |k, v| " #{k} = #{v}" }.join("\n")
|
46
|
+
puts "Remaining arguments: #{ARGV.inspect}" if ARGV != []
|
47
|
+
puts '======================================================='
|
48
|
+
|
49
|
+
|
50
|
+
Runner.run {
|
51
|
+
require file_path
|
52
|
+
class_full_name = clazz.title
|
53
|
+
raise ConsoleRunnerError, "#{class_full_name} is not defined" unless Module.const_defined?(class_full_name)
|
54
|
+
klass_obj = Module.const_get(class_full_name)
|
55
|
+
method_type = action_method.scope
|
56
|
+
method_params = cmd_parser.method.params_array
|
57
|
+
|
58
|
+
case method_type
|
59
|
+
when :class
|
60
|
+
klass_obj.send(action_method.name, *method_params)
|
61
|
+
when :instance
|
62
|
+
init_params = cmd_parser.init_method.params_array
|
63
|
+
# TODO catch errors
|
64
|
+
obj = klass_obj.new(*init_params)
|
65
|
+
obj.send(action_method.name, *method_params)
|
66
|
+
else
|
67
|
+
raise ConsoleRunnerError, "Unknown method type: #{method_type}"
|
68
|
+
end
|
69
|
+
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
# raise ConsoleRunnerError, "#{clazz.name}#initialize method should be specified" unless initialize_method
|
74
|
+
#
|
75
|
+
# raise ConsoleRunnerError, "At least one method should be marked with @#{FileParser::RUNNABLE_TAG.to_s} tag.
|
76
|
+
# Also you may specify #run method and it will be executed by default.
|
77
|
+
# #run method don't need any code annotations as well." if runnable_methods.count == 0 unless run_method
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class ConsoleRunnerError < Exception; end
|
data/lib/file_parser.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'yard'
|
2
|
+
require 'console_runner/version'
|
3
|
+
require 'console_runner_error'
|
4
|
+
|
5
|
+
# Parses code in a file
|
6
|
+
class FileParser
|
7
|
+
attr_reader :all_objects
|
8
|
+
|
9
|
+
RUNNABLE_TAG = :runnable
|
10
|
+
|
11
|
+
# Parse file with #YARD::CLI::Stats
|
12
|
+
#
|
13
|
+
# @param [String] file_path path to the file to be parsed,
|
14
|
+
# @return[Array]
|
15
|
+
def initialize(file_path)
|
16
|
+
raise ConsoleRunnerError "Cannot find file #{file_path}" unless File.exist?(file_path)
|
17
|
+
code = YARD::CLI::Stats.new
|
18
|
+
code.run(file_path)
|
19
|
+
@all_objects = code.all_objects
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# List methods
|
24
|
+
# @param [Symbol] scope :all - list all methods, :runnable - list only runnable methods
|
25
|
+
# @param [YARD::CodeObjects::ClassObject, nil] clazz list methods of specified class only
|
26
|
+
def list_methods(scope= :all, clazz = nil)
|
27
|
+
all_methods = @all_objects.select { |o| o.type == :method }
|
28
|
+
all_class_methods = []
|
29
|
+
all_class_methods = clazz.children.select { |m| m.class == YARD::CodeObjects::MethodObject } if clazz
|
30
|
+
|
31
|
+
case scope
|
32
|
+
when :all
|
33
|
+
if clazz
|
34
|
+
all_class_methods
|
35
|
+
else
|
36
|
+
all_methods
|
37
|
+
end
|
38
|
+
when RUNNABLE_TAG
|
39
|
+
if clazz
|
40
|
+
all_class_methods.select { |m| m.has_tag? RUNNABLE_TAG }
|
41
|
+
else
|
42
|
+
all_methods.select { |m| m.has_tag? RUNNABLE_TAG }
|
43
|
+
end
|
44
|
+
else
|
45
|
+
raise ":key can be :all or #{RUNNABLE_TAG}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# List classes
|
50
|
+
# @param [Symbol] scope :all - list all classes, :runnable - list only runnable classes
|
51
|
+
def list_classes(scope= :all)
|
52
|
+
all_classes = @all_objects.select { |o| o.type == :class }
|
53
|
+
case scope
|
54
|
+
when :all
|
55
|
+
all_classes
|
56
|
+
when RUNNABLE_TAG
|
57
|
+
all_classes.select { |m| m.has_tag? RUNNABLE_TAG }
|
58
|
+
else
|
59
|
+
raise ':key can be :all or :runnable'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.select_runnable_tags(yard_object)
|
64
|
+
yard_object.tags.select { |t| t.tag_name == RUNNABLE_TAG.to_s }
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.select_option_tags(yard_object)
|
68
|
+
yard_object.tags.select { |t| t.tag_name == 'option' }
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.select_param_tags(yard_object)
|
72
|
+
yard_object.tags.select { |t| t.tag_name == 'param' }
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
# YARD::Tags::Library.define_tag "Run in Console", :runnable, :with_types_and_name
|
78
|
+
YARD::Tags::Library.define_tag 'Console Tool Description', FileParser::RUNNABLE_TAG
|
79
|
+
|
80
|
+
module YARD
|
81
|
+
module CLI
|
82
|
+
class Stats < Yardoc
|
83
|
+
def print_statistics; end
|
84
|
+
|
85
|
+
def print_undocumented_objects; end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/runner.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Runner
|
2
|
+
|
3
|
+
def self.run &block
|
4
|
+
start_time = Time.now
|
5
|
+
puts "Start Time: #{start_time}"
|
6
|
+
Thread.current[:id] = 'main'
|
7
|
+
|
8
|
+
yield
|
9
|
+
|
10
|
+
trap 'SIGCHLD' do
|
11
|
+
loop do
|
12
|
+
pid = Process.waitpid(-1, Process::WNOHANG) rescue nil
|
13
|
+
break unless pid
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
finish_time = Time.now
|
18
|
+
puts "Finish Time: #{finish_time} (Duration: #{((finish_time - start_time) / 60).round(2) } minutes)"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: console_runner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yury Karpovich
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-11 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.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
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: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.8'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: trollop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.1'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.1'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.9'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.9'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: colorize
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.8'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.8'
|
139
|
+
description: This gem provides you an ability to run any Ruby method from command-line
|
140
|
+
(no any code modifications required!!!)
|
141
|
+
email:
|
142
|
+
- spoonest@gmail.com
|
143
|
+
- yuri.karpovich@gmail.com
|
144
|
+
executables:
|
145
|
+
- c_run
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- ".coveralls.yml"
|
150
|
+
- ".gitignore"
|
151
|
+
- ".travis.yml"
|
152
|
+
- Gemfile
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- bin/console
|
157
|
+
- bin/setup
|
158
|
+
- console_runner.gemspec
|
159
|
+
- exe/c_run
|
160
|
+
- exe/c_run.rb
|
161
|
+
- lib/cmd_parser.rb
|
162
|
+
- lib/console_runner.rb
|
163
|
+
- lib/console_runner/version.rb
|
164
|
+
- lib/console_runner_error.rb
|
165
|
+
- lib/file_parser.rb
|
166
|
+
- lib/runner.rb
|
167
|
+
homepage: https://github.com/yuri-karpovich/console_runner
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata:
|
171
|
+
allowed_push_host: https://rubygems.org
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options: []
|
174
|
+
require_paths:
|
175
|
+
- lib
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
requirements: []
|
187
|
+
rubyforge_project:
|
188
|
+
rubygems_version: 2.4.5.1
|
189
|
+
signing_key:
|
190
|
+
specification_version: 4
|
191
|
+
summary: Command-line runner for ruby code.
|
192
|
+
test_files: []
|