skippy 0.1.0.a

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6005a25ffc1f9757b4cf9656d97be15351463830
4
+ data.tar.gz: da9d85e6b9c72eeee60b18fed419722e13829a64
5
+ SHA512:
6
+ metadata.gz: fc7071ef383374ae27cc9fa15d15544dac943d07a53832f91712f8f8a44d9c1d7eeb0286babae6a3cfdb3b4d91b074a4693f2ab825d6f700961942d6fd5ec4c4
7
+ data.tar.gz: 0ce7d0c9935e0f607e1367062a297ccc83788efebea2330f282bd1d6f8608b4a72f5fb158a8f26a5f5b9e893c94ef9727e29c2b15039cc5cf7d835ebb81dd508
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ *.sublime-workspace
@@ -0,0 +1,4 @@
1
+ // Place your settings in this file to overwrite default and user settings.
2
+ {
3
+ "editor.wrappingColumn": 80
4
+ }
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in skippy.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Thomas Thomassen
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.
@@ -0,0 +1,61 @@
1
+ # Skippy
2
+
3
+ Skippy is a Command Line Interface which aims to automate common developer tasks for SketchUp Ruby extension development.
4
+
5
+ It is currently in very early stage of development so many things are incomplete. Feedback and contributions are welcome.
6
+
7
+ Some of the main goals are:
8
+
9
+ * Automate common tasks
10
+ * Packaging the extension
11
+ * Running build scripts
12
+ * Easy interface to add per-project custom commands/tasks
13
+ * Quick initialization of new project with templates
14
+ * Library dependency management
15
+ * Pull in third-party modules into extension namespace
16
+ * Add/Remove/Update dependencies
17
+
18
+ ## Installation
19
+
20
+ $ gem install skippy
21
+
22
+ ## Usage
23
+
24
+ TODO: Write more detailed usage instructions here.
25
+
26
+ Install the gem on your system, afterwards the `skippy` command should become available.
27
+
28
+ ### Quick-Reference
29
+
30
+ Type `skippy` to list available commands.
31
+
32
+ Type `skippy help [COMMAND]` for more information on how to use each command.
33
+
34
+ Use `skippy new` to create a new project in the current folder.
35
+
36
+ You can add custom per-project commands to a `skippy` folder in your project. Look at `skippy/example.rb` for an example of a simple custom command.
37
+
38
+ ### Power of Thor
39
+
40
+ Skippy is built on [Thor](https://github.com/erikhuda/thor). Refer to [Thor's Website](http://whatisthor.com/) and [documentation](http://www.rubydoc.info/github/wycats/thor/index) for details on creating commands.
41
+
42
+ When creating Skippy command use the following replacements:
43
+
44
+ * Instead of class `Thor`, use `Skippy::Command`
45
+ * Instead of class `Thor::Group`, use `Skippy::Command::Group`
46
+
47
+ ## Development
48
+
49
+ 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.
50
+
51
+ 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).
52
+
53
+ ## Contributing
54
+
55
+ Bug reports and pull requests are welcome on GitHub at https://github.com/thomthom/skippy.
56
+
57
+
58
+ ## License
59
+
60
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
61
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,25 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": "."
6
+ },
7
+ {
8
+ "path": "C:\\Users\\tthomas2\\SourceTree\\thor"
9
+ }
10
+ ],
11
+ "settings":
12
+ {
13
+ "default_encoding": "UTF-8",
14
+ "ensure_newline_at_eof_on_save": true,
15
+ "rulers":
16
+ [
17
+ 80
18
+ ],
19
+ "show_encoding": true,
20
+ "show_line_endings": true,
21
+ "tab_size": 2,
22
+ "translate_tabs_to_spaces": true,
23
+ "trim_trailing_white_space_on_save": true
24
+ }
25
+ }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Set the program name explicitly. Otherwise Thor will use the filename in the
4
+ # banner for the command help.
5
+ $PROGRAM_NAME = 'skippy'
6
+
7
+ # TODO(thomthom): Temporary set for development without having to set the ENV.
8
+ # Thor on Windows will by default not use colors. Using Cmder colors will work.
9
+ # Also in the latest Windows 10 colors appear to work. Not sure how older
10
+ # versions behave.
11
+ ENV['THOR_SHELL'] = 'Color' if $stdout.isatty
12
+
13
+ # Thor require DL which under Windows and Ruby 2.0 will yield a warning:
14
+ # DL is deprecated, please use Fiddle
15
+ #
16
+ # To avoid this appearing every time this tool is invoked, warnings are
17
+ # temporarily suppressed.
18
+ begin
19
+ original_verbose = $VERBOSE
20
+ $VERBOSE = nil
21
+ require 'thor'
22
+ # The Thor::Runner also needs to be loaded, as Skippy::CLI will call many of
23
+ # of the same methods - in many cases emulating what it do.
24
+ require "thor/runner"
25
+ # This is also needed to be set in order for Thor's utilities to output
26
+ # command names correctly.
27
+ $thor_runner = true
28
+ ensure
29
+ $VERBOSE = original_verbose
30
+ end
31
+
32
+ # Load skippy components the bootstrapper needs.
33
+ require 'skippy/app'
34
+ Skippy::App.boot(__FILE__)
35
+
36
+ # Everything is ready to start the CLI.
37
+ require 'skippy/cli'
38
+ require 'skippy/error'
39
+ begin
40
+ Skippy::CLI.start
41
+ rescue Skippy::Error => error
42
+ Skippy::CLI.display_error(error)
43
+ end
@@ -0,0 +1,15 @@
1
+ require 'skippy/namespace'
2
+
3
+ class Debug < Skippy::Command
4
+
5
+ include Thor::Actions
6
+
7
+ desc 'clean', 'Cleans out project files'
8
+ def clean
9
+ say "Cleaning out project files..."
10
+ remove_file Skippy::Project::PROJECT_FILENAME
11
+ remove_dir 'src'
12
+ remove_dir 'skippy'
13
+ end
14
+
15
+ end
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+
3
+ require 'skippy/app'
4
+ require 'skippy/group'
5
+ require 'skippy/project'
6
+ require 'skippy/template'
7
+
8
+ class New < Skippy::Command::Group
9
+
10
+ include Thor::Actions
11
+
12
+ argument :namespace,
13
+ :type => :string,
14
+ :desc => 'The namespace the extension will use'
15
+
16
+ class_option :template,
17
+ :type => :string,
18
+ :desc => 'The template used to generate the project files',
19
+ :default => 'standard'
20
+
21
+ source_paths << Skippy.app.resources
22
+
23
+ attr_reader :project
24
+
25
+ def initialize_project
26
+ @project = Skippy::Project.new(Dir.pwd)
27
+ if project.exist?
28
+ raise Skippy::Error, "A project already exist: #{project.filename}"
29
+ end
30
+ project.namespace = namespace
31
+ end
32
+
33
+ def validate_template
34
+ template_path = File.join(self.class.source_root, options[:template])
35
+ unless File.directory?(template_path)
36
+ raise Skippy::Error, %(Template "#{options[:template]}" not found)
37
+ end
38
+ end
39
+
40
+ def create_project_json
41
+ say ''
42
+ say 'Generating skippy.json...'
43
+ say ''
44
+ say project.filename
45
+ say project.to_json, :yellow
46
+ project.save
47
+ end
48
+
49
+ def compile_templates
50
+ say ''
51
+ say "Compiling template '#{options[:template]}'..."
52
+ say ''
53
+ directory(options[:template], 'src')
54
+ end
55
+
56
+ def create_extension_json
57
+ extension_info = {
58
+ name: project.name,
59
+ description: project.description,
60
+ creator: project.author,
61
+ copyright: project.copyright,
62
+ license: project.license,
63
+ product_id: project.namespace.to_a.join('_'),
64
+ version: "0.1.0",
65
+ build: "1",
66
+ }
67
+ json = JSON.pretty_generate(extension_info)
68
+ json_filename = "src/#{project.namespace.to_underscore}/extension.json"
69
+ create_file(json_filename, json)
70
+ end
71
+
72
+ def create_example_skippy_command
73
+ copy_file('commands/example.rb', 'skippy/example.rb')
74
+ end
75
+
76
+ def finalize
77
+ say ''
78
+ say "Project for #{namespace} created.", :green
79
+ end
80
+
81
+ # These are methods to be used by the template engine when it compiles the
82
+ # templates and expands the filenames.
83
+ no_commands do
84
+
85
+ # @return [String] The basename for the extension files.
86
+ def ext_name
87
+ project.namespace.to_underscore
88
+ end
89
+
90
+ end # no_commands
91
+
92
+ # Needed as base for Thor::Actions' file actions.
93
+ def self.source_root
94
+ Skippy.app.templates_source_path
95
+ end
96
+
97
+ end
@@ -0,0 +1,27 @@
1
+ class Template < Skippy::Command
2
+
3
+ desc 'list', 'List all known templates'
4
+ def list
5
+ say 'Available templates:', :yellow
6
+ templates = Skippy.app.templates
7
+ if templates.empty?
8
+ say ' No templates found'
9
+ else
10
+ templates.each { |template|
11
+ say " #{template.name}", :green
12
+ }
13
+ end
14
+ end
15
+ default_command(:list)
16
+
17
+ desc 'install', 'Install a new template'
18
+ def install(source)
19
+ raise Skippy::Error, 'Not implemented'
20
+ end
21
+
22
+ desc 'remove', 'Remove an installed template'
23
+ def remove(template_name)
24
+ raise Skippy::Error, 'Not implemented'
25
+ end
26
+
27
+ end
@@ -0,0 +1,14 @@
1
+ class Hello < Skippy::Command
2
+
3
+ desc 'world PERSON', 'Oh, hi there!'
4
+ def world(person)
5
+ say "Hello #{person}"
6
+ end
7
+ default_command(:world)
8
+
9
+ desc 'universe', 'Greets the universe in general'
10
+ def universe(person)
11
+ say "DARK IN HERE, ISN'T IT?"
12
+ end
13
+
14
+ end
@@ -0,0 +1,45 @@
1
+ #-------------------------------------------------------------------------------
2
+ #
3
+ # Author: <%= project.author %>
4
+ # Copyright: <%= project.copyright %>
5
+ # License: <%= project.license %>
6
+ #
7
+ #-------------------------------------------------------------------------------
8
+
9
+ require 'json'
10
+
11
+ require 'extensions.rb'
12
+ require 'sketchup.rb'
13
+
14
+ <%= project.namespace.open %>
15
+
16
+ file = __FILE__.dup
17
+ # Account for Ruby encoding bug under Windows.
18
+ file.force_encoding('UTF-8') if file.respond_to?(:force_encoding)
19
+ # Support folder should be named the same as the root .rb file.
20
+ folder_name = File.basename(file, '.*')
21
+
22
+ # Path to the root .rb file (this file).
23
+ PATH_ROOT = File.dirname(file).freeze
24
+
25
+ # Path to the support folder.
26
+ PATH = File.join(PATH_ROOT, folder_name).freeze
27
+
28
+ # Extension information.
29
+ extension_json_file = File.join(PATH, 'extension.json')
30
+ extension_json = File.read(extension_json_file)
31
+ EXTENSION = ::JSON.parse(extension_json, symbolize_names: true).freeze
32
+
33
+ unless file_loaded?(__FILE__)
34
+ loader = File.join(PATH, 'bootstrap')
35
+ @extension = SketchupExtension.new(EXTENSION[:name], loader)
36
+ @extension.description = EXTENSION[:description]
37
+ @extension.version = EXTENSION[:version]
38
+ @extension.copyright = EXTENSION[:copyright]
39
+ @extension.creator = EXTENSION[:creator]
40
+ Sketchup.register_extension(@extension, true)
41
+ end
42
+
43
+ <%= project.namespace.close %>
44
+
45
+ file_loaded(__FILE__)
@@ -0,0 +1,21 @@
1
+ require 'sketchup.rb'
2
+
3
+ module <%= project.namespace %>
4
+
5
+ unless file_loaded?(__FILE__)
6
+ menu = UI.menu('Plugins').add_submenu(EXTENSION[:name])
7
+ menu.add_item('Make Magic') { self.make_magic }
8
+ menu.add_separator
9
+ menu.add_item('Help...') { self.open_help }
10
+ file_loaded(__FILE__)
11
+ end
12
+
13
+ def self.make_magic
14
+ # Do magic here...
15
+ end
16
+
17
+ def self.open_help
18
+ UI.openURL(EXTENSION[:url])
19
+ end
20
+
21
+ end # module
@@ -0,0 +1,45 @@
1
+ #-------------------------------------------------------------------------------
2
+ #
3
+ # Author: <%= project.author %>
4
+ # Copyright: <%= project.copyright %>
5
+ # License: <%= project.license %>
6
+ #
7
+ #-------------------------------------------------------------------------------
8
+
9
+ require 'json'
10
+
11
+ require 'extensions.rb'
12
+ require 'sketchup.rb'
13
+
14
+ <%= project.namespace.open %>
15
+
16
+ file = __FILE__.dup
17
+ # Account for Ruby encoding bug under Windows.
18
+ file.force_encoding('UTF-8') if file.respond_to?(:force_encoding)
19
+ # Support folder should be named the same as the root .rb file.
20
+ folder_name = File.basename(file, '.*')
21
+
22
+ # Path to the root .rb file (this file).
23
+ PATH_ROOT = File.dirname(file).freeze
24
+
25
+ # Path to the support folder.
26
+ PATH = File.join(PATH_ROOT, folder_name).freeze
27
+
28
+ # Extension information.
29
+ extension_json_file = File.join(PATH, 'extension.json')
30
+ extension_json = File.read(extension_json_file)
31
+ EXTENSION = ::JSON.parse(extension_json, symbolize_names: true).freeze
32
+
33
+ unless file_loaded?(__FILE__)
34
+ loader = File.join(PATH, 'bootstrap')
35
+ @extension = SketchupExtension.new(EXTENSION[:name], loader)
36
+ @extension.description = EXTENSION[:description]
37
+ @extension.version = EXTENSION[:version]
38
+ @extension.copyright = EXTENSION[:copyright]
39
+ @extension.creator = EXTENSION[:creator]
40
+ Sketchup.register_extension(@extension, true)
41
+ end
42
+
43
+ <%= project.namespace.close %>
44
+
45
+ file_loaded(__FILE__)
@@ -0,0 +1,6 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+ Hello World
5
+ </body>
6
+ </html>
@@ -0,0 +1,24 @@
1
+ require 'sketchup.rb'
2
+
3
+ module <%= project.namespace %>
4
+
5
+ unless file_loaded?(__FILE__)
6
+ menu = UI.menu('Plugins').add_submenu(EXTENSION[:name])
7
+ menu.add_item('Open Dialog') { self.open_dialog }
8
+ menu.add_separator
9
+ menu.add_item('Help...') { self.open_help }
10
+ file_loaded(__FILE__)
11
+ end
12
+
13
+ def self.open_dialog
14
+ file_path = File.join(PATH, 'html', 'dialog.html')
15
+ @dialog = UI::WebDialog.new
16
+ @dialog.set_file(file_path)
17
+ @dialog.show
18
+ end
19
+
20
+ def self.open_help
21
+ UI.openURL(EXTENSION[:url])
22
+ end
23
+
24
+ end # module
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'bundler' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("bundler", "bundler")
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "skippy"
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
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rake' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'skippy' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("skippy", "skippy")
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'thor' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("thor", "thor")
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../app/boot'
@@ -0,0 +1,5 @@
1
+ require "skippy/version"
2
+
3
+ module Skippy
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,62 @@
1
+ require 'pathname'
2
+
3
+ require 'skippy/command'
4
+ require 'skippy/group'
5
+ require 'skippy/template'
6
+ require 'skippy/skippy'
7
+
8
+ class Skippy::App
9
+
10
+ # @param [String] boot_loader_path
11
+ # @return [Skippy::App]
12
+ def self.boot(boot_loader_path)
13
+ Skippy.app = Skippy::App.new(boot_loader_path)
14
+ Skippy.app.boot
15
+ end
16
+
17
+ attr_reader :path
18
+
19
+ # @param [String] boot_loader_path
20
+ def initialize(boot_loader_path)
21
+ @boot_loader_path = File.expand_path(boot_loader_path)
22
+ @path = File.dirname(@boot_loader_path)
23
+ end
24
+
25
+ def boot
26
+ boot_commands
27
+ end
28
+
29
+ def resources(item = nil)
30
+ resource = Pathname.new(File.join(path, 'resources'))
31
+ item ? resource.join(item) : resource
32
+ end
33
+
34
+ # @return [Array<String>]
35
+ def templates_source_path
36
+ Pathname.new(File.join(path, 'templates'))
37
+ end
38
+
39
+ def templates
40
+ result = []
41
+ templates_source_path.entries.each { |entry|
42
+ template_path = templates_source_path.join(entry)
43
+ next unless template_path.directory?
44
+ next if %[. ..].include?(entry.basename.to_s)
45
+ result << Skippy::Template.new(entry)
46
+ }
47
+ result
48
+ end
49
+
50
+ private
51
+
52
+ # @return [Array<String>] loaded files
53
+ def boot_commands
54
+ # Load the default skippy commands.
55
+ path_commands = File.join(path, 'commands')
56
+ commands_pattern = File.join(path_commands, '*.rb')
57
+ Dir.glob(commands_pattern) { |filename|
58
+ require filename
59
+ }
60
+ end
61
+
62
+ end
@@ -0,0 +1,191 @@
1
+ require 'skippy/app'
2
+ require 'skippy/command'
3
+ require 'skippy/group'
4
+ require 'skippy/project'
5
+ require 'skippy/version'
6
+
7
+ # The Skippy::CLI class emulates much of what Thor::Runner do. It takes care of
8
+ # finding skippy projects and loading commands.
9
+ #
10
+ # The difference is mainly in how skippy vs thor present the commands.
11
+ #
12
+ # TODO(thomthom): ...or should it?
13
+ # Thor let you install commands globally, where as skippy does not.
14
+ #
15
+ # Skippy will list all known commands when invoked without any arguments.
16
+ #
17
+ # The code in this class will often refer to thor - when things have been copied
18
+ # verbatim. Makes it easier to update if needed.
19
+ class Skippy::CLI < Skippy::Command
20
+
21
+ class << self
22
+
23
+ # @param [Skippy::Error] error
24
+ def display_error(error)
25
+ shell = Thor::Base.shell.new
26
+ message = " #{error.message} "
27
+ message = shell.set_color(message, :white)
28
+ message = shell.set_color(message, :on_red)
29
+ shell.error message
30
+ end
31
+
32
+ end # Class methods
33
+
34
+ default_command :list
35
+
36
+ # Verbatim copy from Thor::Runner:
37
+ # Override Thor#help so it can give information about any class and any method.
38
+ #
39
+ def help(meth = nil)
40
+ if meth && !self.respond_to?(meth)
41
+ initialize_thorfiles(meth)
42
+ klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
43
+ self.class.handle_no_command_error(command, false) if klass.nil?
44
+ klass.start(["-h", command].compact, :shell => shell)
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ # Verbatim copy from Thor::Runner:
51
+ # If a command is not found on Thor::Runner, method missing is invoked and
52
+ # Thor::Runner is then responsible for finding the command in all classes.
53
+ #
54
+ def method_missing(meth, *args)
55
+ meth = meth.to_s
56
+ initialize_thorfiles(meth)
57
+ klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
58
+ self.class.handle_no_command_error(command, false) if klass.nil?
59
+ args.unshift(command) if command
60
+ klass.start(args, :shell => shell)
61
+ end
62
+
63
+ # Verbatim copy from Thor::Runner:
64
+ desc "list [SEARCH]", "List the available #{$PROGRAM_NAME} commands (--substring means .*SEARCH)"
65
+ method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
66
+ def list(search = "")
67
+ initialize_thorfiles
68
+
69
+ search = ".*#{search}" if options["substring"]
70
+ search = /^#{search}.*/i
71
+ group = options[:group] || "standard"
72
+
73
+ klasses = Thor::Base.subclasses.select do |k|
74
+ (options[:all] || k.group == group) && k.namespace =~ search
75
+ end
76
+
77
+ display_klasses(false, false, klasses)
78
+ end
79
+
80
+ private
81
+
82
+ # Based on Thor::Runner, with exception of program name.
83
+ def self.banner(command, all = false, subcommand = false)
84
+ "#{$PROGRAM_NAME} " + command.formatted_usage(self, all, subcommand)
85
+ end
86
+
87
+ # Verbatim copy from Thor::Runner:
88
+ def self.exit_on_failure?
89
+ true
90
+ end
91
+
92
+ # This is one of the places this runner differ from Thor::Runner. It will
93
+ # instead load files for the current project.
94
+ #
95
+ # TODO(thomthom): Original arguments kept around for now, so avoid altering
96
+ # the methods that calls this. It might be that these arguments might be
97
+ # useful for optimizations later.
98
+ def initialize_thorfiles(_relevant_to = nil, _skip_lookup = false)
99
+ project = Skippy::Project.new(Dir.pwd)
100
+ return unless project.exist?
101
+ project.command_files { |filename|
102
+ unless Thor::Base.subclass_files.keys.include?(File.expand_path(filename))
103
+ Thor::Util.load_thorfile(filename, nil, options[:debug])
104
+ end
105
+ }
106
+ end
107
+
108
+ def display_app_banner
109
+ program_name = shell.set_color($PROGRAM_NAME.capitalize, :green)
110
+ version = shell.set_color('version', :clear)
111
+ program_version = shell.set_color(Skippy::VERSION, :yellow)
112
+ say "#{program_name} #{version} #{program_version}"
113
+ end
114
+
115
+ # Based on Thor::Runner:
116
+ def display_klasses(with_modules = false, show_internal = false, klasses = Thor::Base.subclasses)
117
+ unless show_internal
118
+ klasses -= [
119
+ Thor, Thor::Runner, Thor::Group,
120
+ Skippy, Skippy::CLI, Skippy::Command, Skippy::Command::Group
121
+ ]
122
+ end
123
+
124
+ fail Error, "No #{$PROGRAM_NAME.capitalize} commands available" if klasses.empty?
125
+
126
+ list = Hash.new { |h, k| h[k] = [] }
127
+ groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
128
+
129
+ # Get classes which inherit from Thor
130
+ (klasses - groups).each { |k|
131
+ list[k.namespace.split(":").first] += k.printable_commands(false)
132
+ }
133
+
134
+ # Get classes which inherit from Thor::Base
135
+ groups.map! { |k| k.printable_commands(false).first }
136
+ # Thor:Runner put these under 'root', but here we just avoid any name at
137
+ # all together.
138
+ list[''] = groups
139
+
140
+ display_app_banner
141
+ say
142
+ say 'Available commands:', :yellow
143
+
144
+ # Align all command descriptions. This means computing a fixed width for
145
+ # the first column.
146
+ col_width = list.map { |_, rows|
147
+ rows.map { |col| col.first.size }.max || 0
148
+ }.max
149
+
150
+ # Order namespaces with default coming first
151
+ list = list.sort { |a, b|
152
+ a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "")
153
+ }
154
+ list.each { |n, commands|
155
+ display_commands(n, commands, col_width) unless commands.empty?
156
+ }
157
+ end
158
+
159
+ # Based on Thor::Runner:
160
+ def display_commands(namespace, list, col_width)
161
+ list.sort! { |a, b| a[0] <=> b[0] }
162
+
163
+ say shell.set_color(namespace, :yellow, true) unless namespace.empty?
164
+
165
+ list.each { |row|
166
+ row[0] = shell.set_color(row[0], :green) + shell.set_color('', :clear)
167
+ }
168
+ # TODO(thomthom): For some reason the column appear as half the width.
169
+ # Not sure why, so for now we apply this hack.
170
+ # TODO(thomthom): Because of the odd issue with col_width mentioned in
171
+ # `display_klasses` the table isn't truncated. Can probably re-enable if
172
+ # the col_width issue is fixed.
173
+ #print_table(list, :truncate => true, :indent => 2, :colwidth => col_width)
174
+ width = (col_width + 2) * 2
175
+ print_table(list, :indent => 2, :colwidth => width)
176
+ end
177
+ alias_method :display_tasks, :display_commands
178
+
179
+ # Based on Thor::Runner, skipping the yaml stuff:
180
+ def show_modules
181
+ info = []
182
+ labels = %w[Modules Namespaces]
183
+
184
+ info << labels
185
+ info << ["-" * labels[0].size, "-" * labels[1].size]
186
+
187
+ print_table info
188
+ say ""
189
+ end
190
+
191
+ end
@@ -0,0 +1,15 @@
1
+ require 'thor'
2
+
3
+ module Skippy
4
+ class Command < Thor
5
+
6
+ protected
7
+
8
+ # Customize the banner as we don't care for the 'skippy' prefix for each
9
+ # item in the list.
10
+ def self.banner(command, namespace = nil, subcommand = false)
11
+ "#{command.formatted_usage(self, true, subcommand)}"
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module Skippy
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ require 'thor/group'
2
+
3
+ require 'skippy/command'
4
+
5
+ module Skippy
6
+ class Command::Group < Thor::Group
7
+
8
+ protected
9
+
10
+ # Customize the banner as we don't care for the 'skippy' prefix for each
11
+ # item in the list.
12
+ def self.banner
13
+ "#{self_command.formatted_usage(self, false)}"
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,55 @@
1
+ class Skippy::Namespace
2
+
3
+ def initialize(namespace)
4
+ unless valid?(namespace)
5
+ raise Skippy::Error, "'#{namespace}' is not a valid Ruby namespace"
6
+ end
7
+ @namespace = namespace
8
+ end
9
+
10
+ def basename
11
+ to_a.last
12
+ end
13
+
14
+ def open
15
+ @open ||= to_a.map { |part| "module #{part}" }.join("\n")
16
+ @open
17
+ end
18
+
19
+ def close
20
+ @close ||= to_a.reverse.map { |part| "end # module #{part}" }.join("\n")
21
+ @close
22
+ end
23
+
24
+ def to_a
25
+ parts(@namespace)
26
+ end
27
+
28
+ def to_name
29
+ basename_words(basename).join(' ')
30
+ end
31
+
32
+ def to_s
33
+ @namespace.dup
34
+ end
35
+
36
+ def to_underscore
37
+ basename_words(basename).map { |word| word.downcase }.join('_')
38
+ end
39
+
40
+ private
41
+
42
+ def basename_words(namespace_basename)
43
+ result = namespace_basename.scan(/[[:upper:]]+[[:lower:][:digit:]]*/)
44
+ result.empty? ? [namespace_basename.dup] : result
45
+ end
46
+
47
+ def parts(namespace)
48
+ namespace.split('::')
49
+ end
50
+
51
+ def valid?(namespace)
52
+ parts(namespace).all? { |part| /^[[:upper:]]/.match(part) }
53
+ end
54
+
55
+ end
@@ -0,0 +1,90 @@
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ require 'skippy/namespace'
5
+
6
+ class Skippy::Project
7
+
8
+ PROJECT_FILENAME = 'skippy.json'.freeze
9
+
10
+ attr_reader :name, :namespace, :path, :author, :copyright, :license
11
+ attr_accessor :description
12
+
13
+ # Initialize a project for the provided path. If the path is within a project
14
+ # path the base path of the project will be found. Otherwise it's assumed that
15
+ # the path is the base for a new project.
16
+ #
17
+ # @param [Pathname, String] path
18
+ def initialize(path)
19
+ @path = find_project_path(path) || Pathname.new(path)
20
+ @namespace = Skippy::Namespace.new('Untitled')
21
+ @name = ''
22
+ @description = ''
23
+ @author = 'Unknown'
24
+ @copyright = "Copyright (c) #{Time.now.year}"
25
+ @license = 'None'
26
+ end
27
+
28
+ # @yield [filename]
29
+ # @yieldparam [String] filename the path to custom Skippy command
30
+ def command_files(&block)
31
+ files_pattern = File.join(path, 'skippy', '**', '*.rb')
32
+ Dir.glob(files_pattern) { |filename|
33
+ block.call(filename)
34
+ }
35
+ end
36
+
37
+ # Checks if a project exist on disk. If not it's just transient.
38
+ def exist?
39
+ File.exist?(filename)
40
+ end
41
+
42
+ # Full path to the project's configuration file. This file may not exist.
43
+ # @return [String]
44
+ def filename
45
+ File.join(path, PROJECT_FILENAME)
46
+ end
47
+
48
+ # @return [String]
49
+ def name
50
+ @name.empty? ? namespace.to_name : @name
51
+ end
52
+
53
+ # @param [String] namespace
54
+ def namespace=(namespace)
55
+ @namespace = Skippy::Namespace.new(namespace)
56
+ end
57
+
58
+ # Commits the project to disk.
59
+ def save
60
+ File.write(filename, to_json)
61
+ end
62
+
63
+ # @return [String]
64
+ def to_json
65
+ project_config = {
66
+ namespace: namespace,
67
+ name: name,
68
+ description: description
69
+ }
70
+ JSON.pretty_generate(project_config)
71
+ end
72
+
73
+ private
74
+
75
+ # Finds the root of a project based on any path within the project.
76
+ #
77
+ # @param [String] path
78
+ # return [Pathname, nil]
79
+ def find_project_path(path)
80
+ pathname = Pathname.new(path)
81
+ loop do
82
+ project_file = pathname.join(PROJECT_FILENAME)
83
+ return pathname if project_file.exist?
84
+ break if pathname.root?
85
+ pathname = pathname.parent
86
+ end
87
+ nil
88
+ end
89
+
90
+ end
@@ -0,0 +1,9 @@
1
+ require 'skippy/version'
2
+
3
+ module Skippy
4
+
5
+ class << self
6
+ attr_accessor :app
7
+ end
8
+
9
+ end
@@ -0,0 +1,3 @@
1
+ module Skippy
2
+ VERSION = '0.1.0.a'.freeze
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'skippy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "skippy"
8
+ spec.version = Skippy::VERSION
9
+ spec.authors = ["Thomas Thomassen"]
10
+ spec.email = ["thomas@thomthom.net"]
11
+
12
+ spec.summary = %q{CLI development tool for SketchUp extensions.}
13
+ spec.description = %q{Automate common tasks for SketchUp extension development, including managing library dependencies.}
14
+ spec.homepage = "https://github.com/thomthom/skippy"
15
+ spec.license = "MIT"
16
+
17
+ spec.required_ruby_version = ">= 2.0"
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
+ f.match(%r{^(test|spec|features)/})
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.13"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.0"
29
+
30
+ spec.add_dependency "thor"
31
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: skippy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.a
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Thomassen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-01-01 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: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Automate common tasks for SketchUp extension development, including managing
70
+ library dependencies.
71
+ email:
72
+ - thomas@thomthom.net
73
+ executables:
74
+ - skippy
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".vscode/settings.json"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - Skippy.sublime-project
85
+ - app/boot.rb
86
+ - app/commands/debug.rb
87
+ - app/commands/new.rb
88
+ - app/commands/template.rb
89
+ - app/resources/commands/example.rb
90
+ - app/templates/standard/%ext_name%.rb.tt
91
+ - app/templates/standard/%ext_name%/main.rb.tt
92
+ - app/templates/webdialog/extension.rb.erb
93
+ - app/templates/webdialog/extension/html/dialog.html
94
+ - app/templates/webdialog/extension/main.rb.erb
95
+ - bin/bundler
96
+ - bin/console
97
+ - bin/rake
98
+ - bin/setup
99
+ - bin/skippy
100
+ - bin/thor
101
+ - exe/skippy
102
+ - lib/skippy.rb
103
+ - lib/skippy/app.rb
104
+ - lib/skippy/cli.rb
105
+ - lib/skippy/command.rb
106
+ - lib/skippy/error.rb
107
+ - lib/skippy/group.rb
108
+ - lib/skippy/namespace.rb
109
+ - lib/skippy/project.rb
110
+ - lib/skippy/skippy.rb
111
+ - lib/skippy/version.rb
112
+ - skippy.gemspec
113
+ homepage: https://github.com/thomthom/skippy
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '2.0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">"
129
+ - !ruby/object:Gem::Version
130
+ version: 1.3.1
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.0.3
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: CLI development tool for SketchUp extensions.
137
+ test_files: []
138
+ has_rdoc: