arli 0.8.3 → 0.9.0

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.
@@ -1,4 +1,3 @@
1
- require 'colored2'
2
1
  require 'arli/cli/app'
3
2
  require 'pp'
4
3
  module Arli
@@ -4,12 +4,12 @@ require 'open3'
4
4
  require 'arli'
5
5
  require 'arli/version'
6
6
  require 'arli/errors'
7
- require 'arli/output'
7
+ require 'arli/helpers/output'
8
8
 
9
9
  module Arli
10
10
  module Commands
11
11
  class Base
12
- include Arli::Output
12
+ include Arli::Helpers::Output
13
13
 
14
14
  attr_accessor :config, :name
15
15
 
@@ -6,38 +6,65 @@ require 'arli'
6
6
  require 'arduino/library'
7
7
  require_relative 'base'
8
8
  require_relative 'bundle'
9
+ require 'arli/library'
9
10
 
10
11
  module Arli
11
12
  module Commands
12
- class Install < Bundle
13
+ class Install < Base
13
14
  require 'arduino/library/include'
14
15
 
15
- attr_accessor :library
16
+ attr_accessor :library,
17
+ :arlifile,
18
+ :install_argument,
19
+ :install_method
20
+
21
+ include ::Arli::Library
16
22
 
17
23
  def setup
18
- validate_argument
24
+ super
25
+
26
+ self.install_argument = runtime.argv.first
27
+ raise InvalidInstallSyntaxError,
28
+ "Missing installation argument: a name, a file or a URL." unless install_argument
19
29
 
20
- self.library = identify_library(runtime.argv.first)
21
- validate_library
30
+ self.library = identify_library(install_argument)
31
+ raise Arli::Errors::LibraryNotFound,
32
+ "Library #{cfg.to_hash} was not found" unless library
22
33
 
23
- self.arlifile = Arli::ArliFile.new(config: config, libraries: [library]) if library
34
+ self.arlifile = Arli::ArliFile.new(config: config, libraries: [ library ])
35
+ if config.trace
36
+ info("found library using #{install_method}:\n#{library.inspect}")
37
+ end
38
+ end
39
+
40
+ def run
41
+ arlifile.install
24
42
  end
25
43
 
26
44
  # arg can be 'Adafruit GFX Library'
27
45
  def identify_library(arg)
28
- if File.exist?(arg)
29
- begin
30
- Arduino::Library::Model.from(arg)
31
- rescue
32
- nil
33
- end
34
- elsif arg =~ %r[https?://]
35
- Arduino::Library::Model.from_hash(url: arg, name: File.basename(arg))
36
- else
37
- results = search(name: /^#{arg}$/i)
38
- validate_search(arg, results)
39
- results.sort.last if results && !results.empty?
40
- end
46
+ results = if arg =~ %r[https?://]i
47
+ self.install_method = :url
48
+ r = search(url: /^#{arg}$/i)
49
+ if r.empty?
50
+ self.install_method = :website
51
+ r = search(website: /^#{arg}$/i)
52
+ end
53
+ if r.empty?
54
+ self.install_method = :custom
55
+ r = [ Arduino::Library::Model.from_hash(url: arg, name: File.basename(arg)) ]
56
+ end
57
+ r
58
+ elsif File.exist?(arg) || arg =~ /\.zip$/
59
+ self.install_method = :archiveFileName
60
+ search(archiveFileName: "#{File.basename(arg)}")
61
+ else
62
+ self.install_method = :name
63
+ search(name: /^#{arg}$/)
64
+ end
65
+
66
+ validate_search(arg, results)
67
+ results.sort.last if results && !results.empty?
41
68
  end
42
69
 
43
70
  def params
@@ -52,19 +79,10 @@ module Arli
52
79
 
53
80
  def validate_search(arg, results)
54
81
  raise Arli::Errors::LibraryNotFound,
55
- "Can't find library by argument #{arg.bold.yellow}" if results.nil? || results.empty?
82
+ "Can't find library by argument #{arg.bold.yellow}, searching by #{install_method}" if results.nil? || results.empty?
83
+ dupes = results.map(&:name).uniq.size
56
84
  raise Arli::Errors::TooManyMatchesError,
57
- "More than one match found for #{arg.bold.yellow}" if results.map(&:name).uniq.size > 1
58
- end
59
-
60
- def validate_library
61
- raise Arli::Errors::LibraryNotFound,
62
- "Library #{cfg.to_hash} was not found" unless library
63
- end
64
-
65
- def validate_argument
66
- raise InvalidInstallSyntaxError,
67
- "Missing installation argument: a name, a file or a URL." unless runtime.argv.first
85
+ "More than one match found for #{arg.bold.yellow} — #{dupes} libraries matched" if dupes > 1
68
86
  end
69
87
 
70
88
  def cfg
@@ -4,53 +4,42 @@ require 'open3'
4
4
  require 'arli'
5
5
  require 'arli/commands/base'
6
6
  require 'arli/errors'
7
+ require 'arli/library/multi_version'
7
8
  require 'arduino/library'
8
9
 
10
+
9
11
  module Arli
10
12
  module Commands
11
13
  class Search < Base
12
14
 
13
- LibraryWithVersion = Struct.new(:name, :versions)
14
-
15
15
  require 'arduino/library/include'
16
16
 
17
17
  attr_accessor :search_string,
18
18
  :search_opts,
19
+ :search_method,
19
20
  :results,
20
21
  :limit,
21
22
  :database,
22
23
  :format,
23
- :hash,
24
- :custom_print
24
+ :unique_libraries
25
25
 
26
26
  def initialize(*args)
27
27
  super(*args)
28
- self.format = :short
29
- self.hash = Hash.new
30
- self.custom_print = true
28
+ self.format = config.search.results.output_format
29
+ valid_methods = Arli::Library::MultiVersion.format_methods
30
+ raise Arli::Errors::InvalidSearchSyntaxError,
31
+ "invalid format #{format}" unless valid_methods.include?(format)
31
32
  end
32
33
 
33
34
  def run
34
- self.search_opts = process_search_options!
35
- self.results = search(database, **search_opts).sort
36
-
37
- results.map do |lib|
38
- hash[lib.name] ||= LibraryWithVersion.new(lib.name, [])
39
- hash[lib.name].versions << lib.version
40
-
41
- method = "to_s_#{format}".to_sym
42
- if lib.respond_to?(method)
43
- self.custom_print = false
44
- lib.send(method)
45
- end
46
- end
35
+ self.search_opts = process_search_options!
36
+ self.results = search(database, **search_opts).sort
37
+ self.unique_libraries = Set.new
47
38
 
48
- if custom_print
49
- previous = nil
50
- results.each do |lib|
51
- print_lib(hash[lib.name]) unless previous == lib.name
52
- previous = lib.name
53
- end
39
+ results.map { |lib| add_lib_or_version(lib) }
40
+
41
+ unique_libraries.each do |multi_version|
42
+ puts multi_version.send("to_s_#{format}".to_sym)
54
43
  end
55
44
  print_total_with_help
56
45
  rescue Exception => e
@@ -58,10 +47,13 @@ module Arli
58
47
  puts e.backtrace.join("\n") if ENV['DEBUG']
59
48
  end
60
49
 
61
- def print_lib(lib)
62
- $stdout.puts "#{lib.name.bold.magenta} " +
63
- (lib.versions ?
64
- "(#{lib.versions.size} versions: #{lib.versions.reverse[0..5].join(', ').blue})": '')
50
+ def add_lib_or_version(lib)
51
+ a_version = Arli::Library::MultiVersion.new(lib)
52
+ if unique_libraries.include?(a_version)
53
+ unique_libraries.find { |l| l.name == a_version.name }&.add_version(library: lib)
54
+ else
55
+ unique_libraries << a_version
56
+ end
65
57
  end
66
58
 
67
59
  def process_search_options!
@@ -71,9 +63,12 @@ module Arli
71
63
  self.limit = config.search.results.limit
72
64
  search_opts = {}
73
65
  begin
74
- search_opts = eval("{ #{search_string} }")
66
+ params_code = "{ #{search_string} }"
67
+ puts "Evaluating: [#{params_code.blue}]\nSearch Method: [#{search_method.to_s.green}]" if config.trace
68
+ search_opts = eval(params_code)
69
+
75
70
  rescue => e
76
- handle_error(e)
71
+ handle_and_raise_error(e)
77
72
  end
78
73
 
79
74
  unless search_opts.is_a?(::Hash) && search_opts.size > 0
@@ -91,9 +86,19 @@ module Arli
91
86
  def extract_search_argument!
92
87
  search = runtime.argv.first
93
88
  if search =~ /:/
89
+ self.search_method = :ruby
94
90
  search
91
+ elsif search.start_with?('/')
92
+ self.search_method = :regex_name_and_url
93
+ # exact match
94
+ "#{config.search.default_field}: #{search}, archiveFileName: #{search}"
95
+ elsif search.start_with?('=')
96
+ self.search_method = :equals
97
+ # exact match
98
+ "#{config.search.default_field}: '#{search[1..-1]}'"
95
99
  elsif search
96
- "#{config.search.default_field}: /^#{search}$/"
100
+ self.search_method = :regex
101
+ "#{config.search.default_field}: /#{search.downcase}/i"
97
102
  end
98
103
  end
99
104
 
@@ -118,10 +123,10 @@ module Arli
118
123
  def print_total_with_help
119
124
  puts "———————————————————————"
120
125
  puts " Total Versions : #{results.size.to_s.bold.magenta}\n"
121
- puts "Unique Libraries : #{hash.keys.size.to_s.bold.magenta}\n"
126
+ puts "Unique Libraries : #{unique_libraries.size.to_s.bold.magenta}\n"
122
127
  puts "———————————————————————"
123
128
  if results.size == Arli::Configuration::DEFAULT_RESULTS_LIMIT
124
- puts "Hint: use #{'-m 0'.bold.green} to disable the limit, or set it to another value."
129
+ puts "Hint: use #{'-m 5'.bold.green} to limit the result set."
125
130
  end
126
131
  end
127
132
 
@@ -9,7 +9,7 @@ module Arli
9
9
  DEFAULT_LOCK_FILENAME = (DEFAULT_FILENAME + '.lock').freeze
10
10
  ACTIONS_WHEN_EXISTS = %i(backup overwrite abort)
11
11
  ARLI_COMMAND = 'arli'.freeze
12
- DEFAULT_RESULTS_LIMIT = 100
12
+ DEFAULT_RESULTS_LIMIT = 0
13
13
 
14
14
  extend Dry::Configurable
15
15
 
@@ -43,6 +43,7 @@ module Arli
43
43
  # Global flags
44
44
  setting :debug, ENV['ARLI_DEBUG'] || false
45
45
  setting :trace, false
46
+ setting :no_color, false
46
47
  setting :dry_run, false
47
48
  setting :verbose, false
48
49
  setting :help, false
@@ -55,7 +56,7 @@ module Arli
55
56
  setting :results do
56
57
  setting :attrs
57
58
  setting :limit, DEFAULT_RESULTS_LIMIT
58
- setting :format, :inspect
59
+ setting :output_format, :short
59
60
  end
60
61
  end
61
62
 
@@ -64,7 +65,7 @@ module Arli
64
65
  setting :path, ::Dir.pwd
65
66
  setting :name, ::Arli::Configuration::DEFAULT_FILENAME
66
67
  setting :lock_name, ::Arli::Configuration::DEFAULT_LOCK_FILENAME
67
- setting :lock_format, :json
68
+ setting :lock_format, :text
68
69
  end
69
70
 
70
71
  setting :bundle do
@@ -6,4 +6,10 @@ class String
6
6
  tr('-', '_').
7
7
  downcase
8
8
  end
9
+
10
+ def reformat_wrapped(width = 70, indent_with = 8)
11
+ ind = (' ' * indent_with)
12
+ (ind + self.gsub(/\s+/, ' ').gsub(/(.{1,#{width}})( |\Z)/, "\\1\n" + ind))
13
+ end
14
+
9
15
  end
@@ -0,0 +1,48 @@
1
+ module Arli
2
+ module Helpers
3
+ module Inherited
4
+
5
+ module ClassMethods
6
+ def set_or_get(var_name, val = nil, set_nil = false)
7
+ var = "@#{var_name}".to_sym
8
+ self.instance_variable_set(var, val) if val
9
+ self.instance_variable_set(var, nil) if set_nil
10
+ self.instance_variable_get(var)
11
+ end
12
+
13
+ def short_name
14
+ name.gsub(/.*::/, '').underscore.to_sym
15
+ end
16
+
17
+ def attr_assignable(*attrs)
18
+ self.class.instance_eval do
19
+ attrs.each do |attribute|
20
+ send(:define_method, attribute) do |val = nil, **opts|
21
+ set_or_get(attribute, val, opts && opts[:nil])
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ module Subclassing
29
+ def included(klass)
30
+ klass.instance_eval do
31
+ class << self
32
+ include ::Arli::Helpers::Inherited
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.included(base)
39
+ base.instance_eval do
40
+ class << self
41
+ include(::Arli::Helpers::Inherited::ClassMethods)
42
+ end
43
+ end
44
+ base.extend(Subclassing) if base.is_a?(Class)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,187 @@
1
+ require 'colored2'
2
+ require 'tty-cursor'
3
+
4
+ module Arli
5
+ module Helpers
6
+ module Output
7
+ CHAR_FAILURE = '✖'.red
8
+ CHAR_SUCCESS = '✔'.green
9
+
10
+ class << self
11
+ attr_accessor :enabled, :cursor
12
+
13
+ def enable!
14
+ self.enabled = true
15
+ end
16
+
17
+ def enabled?
18
+ self.enabled
19
+ end
20
+
21
+ def disable!
22
+ self.enabled = false
23
+ end
24
+ end
25
+
26
+ self.enable!
27
+ self.cursor = TTY::Cursor
28
+
29
+ def cursor
30
+ Arli::Helpers::Output.cursor
31
+ end
32
+
33
+ def info(msg, header = nil)
34
+ __pf('%-20s', header.blue) if header
35
+ __pf((header ? ' : ' : '') + msg + "\n") if msg
36
+ end
37
+
38
+ def debug(msg)
39
+ __pf('%-20s', header.blue) if header
40
+ __pf((header ? ' : ' : '') + msg + "\n") if msg
41
+ end
42
+
43
+ def error(msg, exception = nil)
44
+ __pf "#{msg.to_s.red}\n" if msg
45
+ __pf "#{exception.inspect.red}\n\n" if exception
46
+ end
47
+
48
+ def report_exception(e, header = nil)
49
+ if header
50
+ __pf header.bold.yellow + ': '
51
+ else
52
+ __pf 'Error: '.bold.red
53
+ end
54
+ error e.message if (e && e.respond_to?(:message))
55
+ if e && Arli.config.trace
56
+ __pf "\n"
57
+ __pf 'Top 10 stack trace'.bold.yellow + "\n"
58
+ __pf e.backtrace.reverse[-10..-1].join("\n").red + "\n"
59
+ elsif e
60
+ __pf "\nUse -t (--trace) for detailed exception\n" +
61
+ "or -D (--debug) to print Arli config\n"
62
+ end
63
+ end
64
+
65
+ def raise_invalid_arli_command!(cmd, e = nil)
66
+ raise Arli::Errors::InvalidCommandError.new(cmd)
67
+ end
68
+
69
+ # Shortcuts disabled in tests
70
+ def ___(msg = nil, newline = false)
71
+ return unless Arli::Helpers::Output.enabled?
72
+ __pf msg if msg
73
+ __pt if newline
74
+ end
75
+
76
+ def __pt(*args)
77
+ puts(*args) if Arli::Helpers::Output.enabled?
78
+ end
79
+
80
+ def __p(*args)
81
+ print(*args) if Arli::Helpers::Output.enabled?
82
+ end
83
+
84
+ def __pf(*args)
85
+ printf(*args) if Arli::Helpers::Output.enabled?
86
+ end
87
+
88
+
89
+ def print_target_dir(d, verb = 'installed')
90
+ print_action_success(d.green, "#{verb} #{d.green} ")
91
+ end
92
+
93
+ def print_action_starting(action_name)
94
+ if verbose?
95
+ indent_cursor
96
+ ___ "⇨ #{action_name.yellow} ... "
97
+ end
98
+ if block_given?
99
+ yield
100
+ ok if verbose?
101
+ end
102
+ end
103
+
104
+ def print_action_success(short, verbose = nil)
105
+ if verbose? && !quiet?
106
+ indent_cursor
107
+ ___ "⇨ #{verbose || short} #{CHAR_SUCCESS}"
108
+ elsif !quiet?
109
+ ___ "#{short} #{CHAR_SUCCESS} "
110
+ end
111
+ end
112
+
113
+ def print_action_failure(short, verbose = nil)
114
+ if verbose? && !quiet?
115
+ indent_cursor
116
+ ___ "⇨ #{verbose || short} #{CHAR_FAILURE}\n"
117
+ elsif !quiet?
118
+ ___ "#{short} #{CHAR_FAILURE} "
119
+ end
120
+ end
121
+
122
+ def action_fail(action, exception)
123
+ print_action_failure(action.class.short_name,
124
+ "#{action.class.short_name} failed with #{exception.message.red}\n" +
125
+ "Action Description: #{action.class.description}")
126
+ raise(exception)
127
+ end
128
+
129
+ def action_ok(action)
130
+ print_action_success(action.action_name)
131
+ end
132
+
133
+ def ok
134
+ ___ " #{CHAR_SUCCESS} "
135
+ end
136
+
137
+ def fuck
138
+ ___ " #{CHAR_FAILURE} "
139
+ end
140
+
141
+ def header(command: nil)
142
+ out = "\n#{hr}\n"
143
+ out << "Arli (#{::Arli::VERSION.yellow})"
144
+ out << ", Command: #{command.name.to_s.magenta.bold}" if command
145
+ if command && command.params && Arli.config.verbose
146
+ out << "\n#{command.params.to_s.blue}\n"
147
+ end
148
+ out << "\nLibrary Path: #{Arli.default_library_path.green}\n"
149
+ out << "#{hr}\n"
150
+ info out
151
+ end
152
+
153
+ def hr
154
+ ('-' * (ENV['COLUMNS'] || 80)).red.dark
155
+ end
156
+
157
+ # Some shortcuts
158
+ def verbose?
159
+ config.verbose && !quiet?
160
+ end
161
+
162
+ def quiet?
163
+ config.quiet
164
+ end
165
+
166
+ def overwrite?
167
+ config.if_exists.overwrite
168
+ end
169
+
170
+ def backup?
171
+ config.if_exists.backup
172
+ end
173
+
174
+ def abort?
175
+ config.if_exists.abort
176
+ end
177
+
178
+ def debug?
179
+ config.debug
180
+ end
181
+
182
+ def indent_cursor(value = 40)
183
+ ___ cursor.column(value)
184
+ end
185
+ end
186
+ end
187
+ end