cli-topic 0.9.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.
@@ -0,0 +1,84 @@
1
+ require 'set'
2
+ require 'clitopic/topics'
3
+ require 'clitopic/utils'
4
+
5
+ module Clitopic
6
+ module Topic
7
+ class Base
8
+ attr_accessor :name, :description, :short_description, :hidden, :banner
9
+
10
+ def initialize(opts={}, force=false)
11
+ opts = {hidden: false}.merge(opts)
12
+ if !opts.has_key?(:name)
13
+ raise ArgumentError.new("missing Topic name")
14
+ end
15
+ @description = opts[:description]
16
+ @short_description = opts[:short_description]
17
+ @name = opts[:name]
18
+ @hidden = opts[:hidden]
19
+ @banner = opts[:banner]
20
+ end
21
+
22
+ def commands
23
+ @commands ||= {}
24
+ end
25
+
26
+
27
+ def short_description
28
+ if @short_description.nil?
29
+ if description
30
+ @short_description = description.split("\n").first
31
+ end
32
+ end
33
+ return @short_description
34
+ end
35
+
36
+ def name(arg=nil)
37
+ @name ||= arg
38
+ end
39
+
40
+ def description(arg=nil)
41
+ @description ||= arg
42
+ end
43
+
44
+ def hidden (arg=false)
45
+ @hidden ||= arg
46
+ end
47
+
48
+ def topic_options
49
+ self.class.topic_options
50
+ end
51
+
52
+ alias :hidden? :hidden
53
+
54
+ private
55
+
56
+ class << self
57
+ attr_accessor :instance
58
+ def option(name, *args, &blk)
59
+ opt = Clitopic::Utils.parse_option(name, *args, &blk)
60
+ if !opt[:default].nil?
61
+ options[name] = opt[:default]
62
+ end
63
+ topic_options << opt
64
+ end
65
+
66
+ def topic_options
67
+ @topic_options ||= []
68
+ end
69
+
70
+ def register(opts={}, force=false)
71
+ topic = self.new(opts, force)
72
+ if Topics[topic.name].nil? && force
73
+ raise TopicAlreadyExists.new ("Topic: #{topic.name} already exists: #{Topics[topic.name].class.name}")
74
+ else
75
+ if self.class != Clitopic::Topic::Base
76
+ @instance = topic
77
+ end
78
+ Topics[topic.name] = topic
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,4 @@
1
+ require 'clitopic/topic/base'
2
+ Dir[File.join(File.dirname(__FILE__), "topic", "*.rb")].each do |file|
3
+ require file
4
+ end
@@ -0,0 +1,23 @@
1
+ module Clitopic
2
+ class TopicAlreadyExists < ArgumentError; end
3
+
4
+ class Topics
5
+ class << self
6
+ require 'clitopic/topic/base'
7
+ def []=(key, val)
8
+ if not (val.is_a?(Clitopic::Topic::Base))
9
+ raise ArgumentError.new("#{val} is not a Topic")
10
+ end
11
+ topics[key] = val
12
+ end
13
+
14
+ def [](key)
15
+ topics[key]
16
+ end
17
+
18
+ def topics
19
+ @@topics ||= {}
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,65 @@
1
+ class String
2
+ def indent!(amount, indent_string=nil, indent_empty_lines=false)
3
+ indent_string = indent_string || self[/^[ \t]/] || ' '
4
+ re = indent_empty_lines ? /^/ : /^(?!$)/
5
+ gsub!(re, indent_string * amount)
6
+ end
7
+
8
+ def indent(amount, indent_string=nil, indent_empty_lines=false)
9
+ dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
10
+ end
11
+
12
+ def underscore
13
+ self.gsub(/::/, '/').
14
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
15
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
16
+ tr("-", "_").
17
+ downcase
18
+ end
19
+ end
20
+
21
+ module Clitopic
22
+ module Utils
23
+ class << self
24
+ def has_git?
25
+ %x{ git --version }
26
+ $?.success?
27
+ end
28
+
29
+ def git(args)
30
+ return "" unless has_git?
31
+ flattened_args = [args].flatten.compact.join(" ")
32
+ %x{ git #{flattened_args} 2>&1 }.strip
33
+ end
34
+
35
+ def retry_on_exception(*exceptions)
36
+ retry_count = 0
37
+ begin
38
+ yield
39
+ rescue *exceptions => ex
40
+ raise ex if retry_count >= 3
41
+ sleep 3
42
+ retry_count += 1
43
+ retry
44
+ end
45
+ end
46
+
47
+ def parse_option(name, *args, &blk)
48
+ # args.sort.reverse gives -l, --long order
49
+ default = nil
50
+ required = false
51
+ args.each do |a|
52
+ if a.is_a?(Hash)
53
+ if a.has_key?(:default)
54
+ default = a[:default]
55
+ end
56
+ if a.has_key?(:required)
57
+ required = a[:required]
58
+ end
59
+ end
60
+ end
61
+ return { :name => name, :args => args, default: default, required: required, :proc => blk }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module Clitopic
2
+ VERSION = '0.9.1'
3
+ end
data/lib/clitopic.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'clitopic/version'
2
+ require 'clitopic/parsers'
3
+
4
+ module Clitopic
5
+ class << self
6
+ attr_accessor :debug, :commands_dir, :parser, :default_parser, :version, :default_files, :load_defaults, :name
7
+ def name
8
+ @name ||= 'clito'
9
+ end
10
+ def parser
11
+ @parser ||= default_parser
12
+ end
13
+
14
+ def load_defaults?
15
+ @load_defaults ||= true
16
+ end
17
+
18
+ def parser=(name)
19
+ Clitopic::Command::Base.extend name
20
+ @parser = name
21
+ end
22
+
23
+ def default_parser
24
+ @default_parsre ||= Clitopic::Parser::OptParser
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+ # Defaults
31
+ Clitopic.name = "clitopic"
32
+ Clitopic.debug = false
33
+ Clitopic.version = Clitopic::VERSION
34
+ Clitopic.load_defaults = true
35
+ Clitopic.default_files = [File.join(Dir.getwd, ".clitopic.yml"), File.join(Dir.home, ".clitopic.yml")]
36
+
37
+
38
+ # Commaon Options
39
+
40
+ # Clitopic::Commands.global_option(:v, "--version", "-v", "Show version") {puts Clitopic.version}
41
+
42
+ Clitopic::Commands.global_option(:load_defaults, "--defaults-file FILE", "Load default variables") do |file|
43
+ Clitopic::Commands.current_cmd.load_defaults(file)
44
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clitopic::Command::Base do
4
+
5
+ context 'register' do
6
+ context 'topic' do
7
+ it 'without topic should add command to global' do
8
+ expect(Clitopic::Commands.global_commands['root_cmd']).to eq RootCmd
9
+ end
10
+
11
+ it 'with topic name should add to existing topic' do
12
+ expect(Clitopic::Topics['a'].commands["cmd"]).to eq TopicaCmd
13
+ expect(Clitopic::Topics['a'].commands["index"]).to eq TopicaIndex
14
+ end
15
+
16
+ it 'with topic class should add to existing topic' do
17
+ expect(Clitopic::Topics['b'].commands["index"]).to eq TopicbIndex
18
+ end
19
+
20
+ it 'with topic attributes should create a topic then add cmd' do
21
+ expect(Clitopic::Topics['c'].commands["index"]).to eq TopiccIndex
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clitopic::Commands do
4
+
5
+ context 'Without topic' do
6
+ it 'should be include in global cmds' do
7
+ expect(Clitopic::Commands.global_commands['root_cmd']).to eq RootCmd
8
+ end
9
+
10
+ context ".prepare_run" do
11
+ it "should set @current_cmd" do
12
+ Clitopic::Commands.prepare_run("root_cmd")
13
+ expect(Clitopic::Commands.current_cmd).to eq RootCmd
14
+ end
15
+
16
+ it "@current_topic should be nil" do
17
+ Clitopic::Commands.prepare_run("root_cmd")
18
+ expect(Clitopic::Commands.current_topic).to be nil
19
+ end
20
+
21
+ it "should rescue to help cmd" do
22
+ Clitopic::Commands.prepare_run("unknowncmd")
23
+ expect(Clitopic::Commands.current_cmd).to eq 'help'
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'With topic' do
29
+ context ".prepare_run" do
30
+ it "should set @current_cmd" do
31
+ Clitopic::Commands.prepare_run("a:cmd")
32
+ expect(Clitopic::Commands.current_cmd).to eq TopicaCmd
33
+ end
34
+
35
+ it "@current_topic should be the topic" do
36
+ Clitopic::Commands.prepare_run("a:cmd")
37
+ expect(Clitopic::Commands.current_topic).to be_a TopicA
38
+ end
39
+
40
+ it "should rescue to help cmd" do
41
+ Clitopic::Commands.prepare_run("unknowncmd:titi")
42
+ expect(Clitopic::Commands.current_cmd).to eq 'help'
43
+ end
44
+
45
+ it "should return index Cmd if no subcommand" do
46
+ Clitopic::Commands.prepare_run("a")
47
+ expect(Clitopic::Commands.current_cmd).to eq TopicaIndex
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+
55
+ end
@@ -0,0 +1,134 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ require 'simplecov'
18
+ SimpleCov.start do
19
+ add_filter 'spec/'
20
+ add_group 'lib', 'lib'
21
+ end
22
+
23
+ require 'clitopic'
24
+ class TopicA < Clitopic::Topic::Base
25
+ register name: "a",
26
+ description: "describe a"
27
+ end
28
+
29
+ class TopicB < Clitopic::Topic::Base
30
+ register name: "b",
31
+ description: "describe b"
32
+ end
33
+
34
+ class Topic < Clitopic::Topic::Base
35
+ end
36
+
37
+ class RootCmd < Clitopic::Command::Base
38
+ register name: 'root_cmd',
39
+ description: "descrption1",
40
+ banner: "banner1"
41
+ end
42
+
43
+ class TopicaCmd < Clitopic::Command::Base
44
+ register name: 'cmd',
45
+ description: "descrption_cmd",
46
+ banner: "banner_cmd",
47
+ topic: 'a'
48
+ end
49
+
50
+ class TopicaIndex < Clitopic::Command::Base
51
+ register name: 'index', topic: 'a'
52
+ end
53
+
54
+ class TopicbIndex < Clitopic::Command::Base
55
+ register name: 'index', topic: TopicB
56
+ end
57
+
58
+ class TopiccIndex < Clitopic::Command::Base
59
+ register name: 'index', topic: {name: 'c', description: 'topic c'}
60
+ end
61
+
62
+ RSpec.configure do |config|
63
+ # rspec-expectations config goes here. You can use an alternate
64
+ # assertion/expectation library such as wrong or the stdlib/minitest
65
+ # assertions if you prefer.
66
+ config.expect_with :rspec do |expectations|
67
+ # This option will default to `true` in RSpec 4. It makes the `description`
68
+ # and `failure_message` of custom matchers include text for helper methods
69
+ # defined using `chain`, e.g.:
70
+ # be_bigger_than(2).and_smaller_than(4).description
71
+ # # => "be bigger than 2 and smaller than 4"
72
+ # ...rather than:
73
+ # # => "be bigger than 2"
74
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
75
+ end
76
+
77
+ # rspec-mocks config goes here. You can use an alternate test double
78
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
79
+ config.mock_with :rspec do |mocks|
80
+ # Prevents you from mocking or stubbing a method that does not exist on
81
+ # a real object. This is generally recommended, and will default to
82
+ # `true` in RSpec 4.
83
+ mocks.verify_partial_doubles = true
84
+ end
85
+
86
+ # The settings below are suggested to provide a good initial experience
87
+ # with RSpec, but feel free to customize to your heart's content.
88
+ =begin
89
+ # These two settings work together to allow you to limit a spec run
90
+ # to individual examples or groups you care about by tagging them with
91
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
92
+ # get run.
93
+ config.filter_run :focus
94
+ config.run_all_when_everything_filtered = true
95
+
96
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
97
+ # For more details, see:
98
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
99
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
100
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
101
+ config.disable_monkey_patching!
102
+
103
+ # This setting enables warnings. It's recommended, but in some cases may
104
+ # be too noisy due to issues in dependencies.
105
+ config.warnings = true
106
+
107
+ # Many RSpec users commonly either run the entire suite or an individual
108
+ # file, and it's useful to allow more verbose output when running an
109
+ # individual spec file.
110
+ if config.files_to_run.one?
111
+ # Use the documentation formatter for detailed output,
112
+ # unless a formatter has already been configured
113
+ # (e.g. via a command-line flag).
114
+ config.default_formatter = 'doc'
115
+ end
116
+
117
+ # Print the 10 slowest examples and example groups at the
118
+ # end of the spec run, to help surface which specs are running
119
+ # particularly slow.
120
+ config.profile_examples = 10
121
+
122
+ # Run specs in random order to surface order dependencies. If you find an
123
+ # order dependency and want to debug it, you can fix the order by providing
124
+ # the seed, which is printed after each run.
125
+ # --seed 1234
126
+ config.order = :random
127
+
128
+ # Seed global randomization in this process using the `--seed` CLI option.
129
+ # Setting this allows you to use `--seed` to deterministically reproduce
130
+ # test failures related to randomization by passing the same `--seed` value
131
+ # as the one that triggered the failure.
132
+ Kernel.srand config.seed
133
+ =end
134
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clitopic::Topic::Base do
4
+
5
+ context 'register' do
6
+ it 'should add topic to Topics' do
7
+ expect(Clitopic::Topics['a']).to be_a TopicA
8
+ end
9
+
10
+ it 'shoud Add instance to class' do
11
+ expect(Clitopic::Topics['a']).to eq TopicA.instance
12
+ end
13
+
14
+ it 'should failed if the name is already taken' do
15
+ expect {Topic.register(name: 'a', description: 'a bis')}.to raise_error Clitopic::TopicAlreadyExists
16
+ end
17
+
18
+ it 'should raise if no name is given' do
19
+ expect {Topic.register(description: 'a bis')}.to raise_error ArgumentError
20
+ end
21
+
22
+ it '.name should return name registred' do
23
+ expect(Clitopic::Topics['a'].name).to eq 'a'
24
+ end
25
+
26
+ it '.description should return description registred' do
27
+ expect(Clitopic::Topics['a'].description).to eq 'describe a'
28
+ end
29
+
30
+ it '.hidden should return hidden registred' do
31
+ expect(Clitopic::Topics['a'].hidden).to be false
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clitopic::Topics do
4
+
5
+ it "Topics[key] = topic should add the topic" do
6
+ topic = Clitopic::Topic::Base.new(name: 'new', description: 'desc')
7
+ Clitopic::Topics["c"] = topic
8
+ expect (Clitopic::Topics.topics['c']) == topic
9
+ end
10
+
11
+ it "Topics[key] = val should failed if it's not a Topic" do
12
+ expect {Clitopic::Topics["c"] = 3}.to raise_error ArgumentError
13
+ end
14
+
15
+ it ".topics should return the dict" do
16
+ expect(Clitopic::Topics.topics).to be_a Hash
17
+ end
18
+
19
+ it "Topics[key] should return the topic" do
20
+ expect(Clitopic::Topics['a']).to be_a TopicA
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe String do
4
+ context ".underscore" do
5
+ it "should snake_case string" do
6
+ expect('pipo'.underscore).to eq 'pipo'
7
+ expect('pi_po'.underscore).to eq 'pi_po'
8
+ expect('piPo'.underscore).to eq 'pi_po'
9
+ expect('PiPo'.underscore).to eq 'pi_po'
10
+ expect('PiPO'.underscore).to eq 'pi_po'
11
+ expect('PipO'.underscore).to eq 'pip_o'
12
+ expect('Pipo'.underscore).to eq 'pipo'
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clitopic::VERSION do
4
+ it "should be equal 0.0.1" do
5
+ expect(Clitopic::VERSION).to eql("0.0.1")
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cli-topic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ platform: ruby
6
+ authors:
7
+ - Antoine Legrand
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0
27
+ description: '["Small framework to build CLI organised in topics/subcommands.", "https://gitlab.com/ant31/cli-topics",
28
+ ["Antoine Legrand"]]'
29
+ email:
30
+ - ant.legrand@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - Changelog
36
+ - License
37
+ - README.md
38
+ - lib/clitopic.rb
39
+ - lib/clitopic/cli.rb
40
+ - lib/clitopic/command.rb
41
+ - lib/clitopic/command/base.rb
42
+ - lib/clitopic/command/clito.rb
43
+ - lib/clitopic/command/help.rb
44
+ - lib/clitopic/command/version.rb
45
+ - lib/clitopic/commands.rb
46
+ - lib/clitopic/helpers.rb
47
+ - lib/clitopic/parser/dummy.rb
48
+ - lib/clitopic/parser/option_parser.rb
49
+ - lib/clitopic/parsers.rb
50
+ - lib/clitopic/topic.rb
51
+ - lib/clitopic/topic/base.rb
52
+ - lib/clitopic/topics.rb
53
+ - lib/clitopic/utils.rb
54
+ - lib/clitopic/version.rb
55
+ - spec/command_base_spec.rb
56
+ - spec/commands_spec.rb
57
+ - spec/spec_helper.rb
58
+ - spec/topic_base_spec.rb
59
+ - spec/topics_spec.rb
60
+ - spec/utils_spec.rb
61
+ - spec/version_spec.rb
62
+ homepage: https://gitlab.com/ant31/cli-topics
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.0.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.4.5
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Small framework to build CLI.
86
+ test_files:
87
+ - spec/command_base_spec.rb
88
+ - spec/commands_spec.rb
89
+ - spec/spec_helper.rb
90
+ - spec/topic_base_spec.rb
91
+ - spec/topics_spec.rb
92
+ - spec/utils_spec.rb
93
+ - spec/version_spec.rb
94
+ has_rdoc: