skippy 0.1.1.a → 0.2.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.gitignore +5 -0
  4. data/.idea/.rakeTasks +7 -0
  5. data/.idea/codeStyleSettings.xml +9 -0
  6. data/.idea/encodings.xml +6 -0
  7. data/.idea/inspectionProfiles/Project_Default.xml +8 -0
  8. data/.idea/misc.xml +4 -0
  9. data/.idea/modules.xml +8 -0
  10. data/.idea/runConfigurations/All_Features.xml +32 -0
  11. data/.idea/runConfigurations/test__skippy.xml +21 -0
  12. data/.idea/skippy.iml +83 -0
  13. data/.idea/vcs.xml +6 -0
  14. data/.vscode/launch.json +61 -0
  15. data/.vscode/settings.json +3 -2
  16. data/.vscode/tasks.json +16 -0
  17. data/Gemfile +5 -0
  18. data/Rakefile +4 -4
  19. data/app/boot.rb +1 -1
  20. data/app/commands/debug.rb +2 -1
  21. data/app/commands/lib.rb +46 -0
  22. data/app/commands/new.rb +4 -3
  23. data/app/commands/template.rb +3 -3
  24. data/app/resources/commands/example.rb +1 -1
  25. data/app/templates/standard/%ext_name%.rb.tt +1 -1
  26. data/app/templates/webdialog/{extension.rb.erb → %ext_name%.rb.tt} +1 -1
  27. data/app/templates/webdialog/{extension → %ext_name%}/html/dialog.html +0 -0
  28. data/app/templates/webdialog/{extension/main.rb.erb → %ext_name%/main.rb.tt} +0 -0
  29. data/bin/aruba +17 -0
  30. data/bin/cucumber +17 -0
  31. data/bin/htmldiff +17 -0
  32. data/bin/ldiff +17 -0
  33. data/cSpell.json +18 -0
  34. data/fixtures/my_lib/skippy.json +5 -0
  35. data/fixtures/my_lib/src/command.rb +4 -0
  36. data/fixtures/my_lib/src/geometry.rb +4 -0
  37. data/fixtures/my_lib/src/tool.rb +4 -0
  38. data/fixtures/my_project/skippy.json +8 -0
  39. data/fixtures/my_project/skippy/commands/example.rb +14 -0
  40. data/fixtures/my_project/src/hello_world.rb +47 -0
  41. data/fixtures/my_project/src/hello_world/extension.json +10 -0
  42. data/fixtures/my_project/src/hello_world/main.rb +21 -0
  43. data/lib/skippy.rb +7 -3
  44. data/lib/skippy/app.rb +7 -5
  45. data/lib/skippy/cli.rb +12 -12
  46. data/lib/skippy/config.rb +135 -0
  47. data/lib/skippy/config_accessors.rb +43 -0
  48. data/lib/skippy/error.rb +1 -1
  49. data/lib/skippy/helpers/file.rb +14 -0
  50. data/lib/skippy/lib_module.rb +47 -0
  51. data/lib/skippy/library.rb +57 -0
  52. data/lib/skippy/library_manager.rb +75 -0
  53. data/lib/skippy/module_manager.rb +103 -0
  54. data/lib/skippy/namespace.rb +7 -3
  55. data/lib/skippy/project.rb +55 -26
  56. data/lib/skippy/version.rb +1 -1
  57. data/skippy.gemspec +14 -12
  58. metadata +69 -8
  59. data/lib/skippy/skippy.rb +0 -9
@@ -0,0 +1,43 @@
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ module Skippy::ConfigAccessors
5
+
6
+ private
7
+
8
+ def config_attr(*symbols, key: nil, type: nil)
9
+ config_attr_reader(*symbols, key: key, type: type)
10
+ config_attr_writer(*symbols, key: key, type: type)
11
+ nil
12
+ end
13
+
14
+ def config_attr_reader(*symbols, key: nil, type: nil)
15
+ self.class_eval {
16
+ symbols.each { |symbol|
17
+ raise TypeError unless symbol.is_a?(Symbol)
18
+ define_method(symbol) {
19
+ value = @config.get(key || symbol)
20
+ value = type.new(value) if type && !value.is_a?(type)
21
+ value
22
+ }
23
+ }
24
+ }
25
+ nil
26
+ end
27
+
28
+ def config_attr_writer(*symbols, key: nil, type: nil)
29
+ self.class_eval {
30
+ symbols.each { |symbol|
31
+ raise TypeError unless symbol.is_a?(Symbol)
32
+ symbol_set = "#{symbol}=".intern
33
+ define_method(symbol_set) { |value|
34
+ value = type.new(value) if type && !value.is_a?(type)
35
+ @config.set(key || symbol, value)
36
+ value
37
+ }
38
+ }
39
+ }
40
+ nil
41
+ end
42
+
43
+ end
@@ -1,4 +1,4 @@
1
1
  module Skippy
2
2
  class Error < StandardError
3
3
  end
4
- end
4
+ end
@@ -0,0 +1,14 @@
1
+ module Skippy::Helpers
2
+ module File
3
+
4
+ # @param [Pathname]
5
+ # @return [Array<Pathname>]
6
+ def directories(pathname)
7
+ return [] unless pathname.exist?
8
+ pathname.children.select { |child|
9
+ child.directory?
10
+ }
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,47 @@
1
+ require 'pathname'
2
+
3
+ require 'skippy/library'
4
+
5
+ class Skippy::LibModule
6
+
7
+ attr_reader :path
8
+
9
+ class ModuleNotFoundError < Skippy::Error; end
10
+
11
+ # @param [String] path
12
+ def initialize(path)
13
+ @path = Pathname.new(path)
14
+ raise ModuleNotFoundError, @path.to_s unless @path.file?
15
+ end
16
+
17
+ # @param [String]
18
+ def basename
19
+ path.basename('.*').to_s
20
+ end
21
+
22
+ # @return [Skippy::Library]
23
+ def library
24
+ Skippy::Library.new(library_path)
25
+ end
26
+
27
+ # @param [String]
28
+ def name
29
+ "#{library_name}/#{basename}"
30
+ end
31
+
32
+ # @param [String]
33
+ def to_s
34
+ name
35
+ end
36
+
37
+ private
38
+
39
+ def library_name
40
+ library_path.basename.to_s
41
+ end
42
+
43
+ def library_path
44
+ path.parent.parent
45
+ end
46
+
47
+ end
@@ -0,0 +1,57 @@
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ require 'skippy/helpers/file'
5
+ require 'skippy/config'
6
+ require 'skippy/config_accessors'
7
+ require 'skippy/lib_module'
8
+
9
+ class Skippy::Library
10
+
11
+ extend Skippy::ConfigAccessors
12
+
13
+ include Skippy::Helpers::File
14
+
15
+ CONFIG_FILENAME = 'skippy.json'.freeze
16
+
17
+ attr_reader :path
18
+
19
+ config_attr_reader :title, key: :name # TODO(thomthom): Clean up this kludge.
20
+ config_attr_reader :version
21
+
22
+ class LibraryNotFoundError < Skippy::Error; end
23
+
24
+ def initialize(path)
25
+ @path = Pathname.new(path)
26
+ raise LibraryNotFoundError, @path.to_s unless @path.directory?
27
+ # noinspection RubyResolve
28
+ @config = Skippy::Config.load(config_file)
29
+ end
30
+
31
+ def name
32
+ path.basename.to_s
33
+ end
34
+
35
+ def modules
36
+ libs = modules_path.children(false).select { |file|
37
+ file.extname.downcase == '.rb'
38
+ }
39
+ libs.map! { |lib|
40
+ path = modules_path.join(lib)
41
+ Skippy::LibModule.new(path)
42
+ }
43
+ libs
44
+ end
45
+
46
+ private
47
+
48
+ def config_file
49
+ path.join(CONFIG_FILENAME)
50
+ end
51
+
52
+ def modules_path
53
+ # TODO(thomthom): Make this configurable and default to 'lib'?
54
+ path.join('src')
55
+ end
56
+
57
+ end
@@ -0,0 +1,75 @@
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ require 'skippy/helpers/file'
5
+ require 'skippy/library'
6
+ require 'skippy/project'
7
+
8
+ class Skippy::LibraryManager
9
+
10
+ include Enumerable
11
+ include Skippy::Helpers::File
12
+
13
+ attr_reader :project
14
+
15
+ # @param [Skippy::Project] project
16
+ def initialize(project)
17
+ raise TypeError, 'expected a Project' unless project.is_a?(Skippy::Project)
18
+ @project = project
19
+ end
20
+
21
+ # @yield [Skippy::Library]
22
+ def each
23
+ directories(path).each { |lib_path|
24
+ yield Skippy::Library.new(lib_path)
25
+ }
26
+ self
27
+ end
28
+
29
+ def empty?
30
+ to_a.empty?
31
+ end
32
+
33
+ # @param [String] module_name
34
+ # @return [Skippy::LibModule, nil]
35
+ def find_module(module_name)
36
+ library_name, module_name = module_name.split('/')
37
+ raise ArgumentError, 'expected a module path' if library_name.nil? || module_name.nil?
38
+ library = find { |lib| lib.name == library_name }
39
+ return nil if library.nil?
40
+ library.modules.find { |mod| mod.basename == module_name }
41
+ end
42
+
43
+ # @param [Pathname, String] source
44
+ def install(source)
45
+ raise Skippy::Project::ProjectNotSavedError unless project.exist?
46
+ library = Skippy::Library.new(source)
47
+
48
+ target = path.join(library.name)
49
+
50
+ FileUtils.mkdir_p(path)
51
+ FileUtils.copy_entry(source, target)
52
+
53
+ project.config.push(:libraries, {
54
+ name: library.name,
55
+ version: library.version,
56
+ source: source
57
+ })
58
+
59
+ project.save
60
+
61
+ library
62
+ end
63
+
64
+ # @return [Integer]
65
+ def length
66
+ to_a.length
67
+ end
68
+ alias :size :length
69
+
70
+ # @return [Pathname]
71
+ def path
72
+ project.path('.skippy/libs')
73
+ end
74
+
75
+ end
@@ -0,0 +1,103 @@
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ require 'skippy/helpers/file'
5
+ require 'skippy/lib_module'
6
+ require 'skippy/project'
7
+
8
+ class Skippy::ModuleManager
9
+
10
+ include Enumerable
11
+ include Skippy::Helpers::File
12
+
13
+ attr_reader :project
14
+
15
+ # @param [Skippy::Project] project
16
+ def initialize(project)
17
+ raise TypeError, 'expected a Project' unless project.is_a?(Skippy::Project)
18
+ @project = project
19
+ end
20
+
21
+ # @yield [Skippy::LibModule]
22
+ def each
23
+ directories(path).each { |library_path|
24
+ library_path.each_child { |module_file|
25
+ next unless module_file.file?
26
+ next unless module_file.extname == '.rb'
27
+ yield Skippy::LibModule.new(module_file)
28
+ }
29
+ }
30
+ self
31
+ end
32
+
33
+ def empty?
34
+ to_a.empty?
35
+ end
36
+
37
+ # @param [String] module_name
38
+ # @return [Skippy::LibModule, nil]
39
+ def find_module(module_name)
40
+ find { |lib_module| lib_module.name == module_name }
41
+ end
42
+
43
+ # @param [Skippy::LibModule, String] lib_module
44
+ def installed?(lib_module)
45
+ module_name = lib_module.is_a?(Skippy::LibModule) ? lib_module.name : lib_module
46
+ project = Skippy::Project.current
47
+ project && project.config.get(:modules, []).any? { |mod| mod == module_name }
48
+ end
49
+
50
+ # @param [String] module_name
51
+ # @return [Skippy::LibModule]
52
+ def use(module_name)
53
+ raise Skippy::Project::ProjectNotSavedError unless project.exist?
54
+
55
+ lib_module = project.libraries.find_module(module_name)
56
+ raise Skippy::LibModule::ModuleNotFoundError,
57
+ "module '#{module_name}' not found" if lib_module.nil?
58
+
59
+ source = lib_module.path
60
+ target = path.join(lib_module.library.name, lib_module.path.basename)
61
+
62
+ copy_module(source, target)
63
+
64
+ project.config.push(:modules, lib_module.to_s)
65
+
66
+ project.save
67
+
68
+ lib_module
69
+ end
70
+
71
+ # @return [Integer]
72
+ def length
73
+ to_a.length
74
+ end
75
+ alias :size :length
76
+
77
+ # @return [Pathname]
78
+ def path
79
+ project.path.join('src', project.namespace.to_underscore, 'vendor')
80
+ end
81
+
82
+ private
83
+
84
+ # @param [Pathname, String] source
85
+ # @param [Pathname, String] target
86
+ def copy_module(source, target)
87
+ FileUtils.mkdir_p(target.parent)
88
+ content = File.read(source)
89
+ transform_module(content)
90
+ File.write(target, content)
91
+ end
92
+
93
+ # Transform the module content with `SkippyLib` placeholder replaced with
94
+ # the project namespace.
95
+ #
96
+ # @param [String] content
97
+ # @return [String]
98
+ def transform_module(content)
99
+ content.gsub!('SkippyLib', project.namespace)
100
+ content
101
+ end
102
+
103
+ end
@@ -1,3 +1,5 @@
1
+ require 'skippy/error'
2
+
1
3
  class Skippy::Namespace
2
4
 
3
5
  def initialize(namespace)
@@ -13,12 +15,10 @@ class Skippy::Namespace
13
15
 
14
16
  def open
15
17
  @open ||= to_a.map { |part| "module #{part}" }.join("\n")
16
- @open
17
18
  end
18
19
 
19
20
  def close
20
21
  @close ||= to_a.reverse.map { |part| "end # module #{part}" }.join("\n")
21
- @close
22
22
  end
23
23
 
24
24
  def to_a
@@ -33,6 +33,10 @@ class Skippy::Namespace
33
33
  @namespace.dup
34
34
  end
35
35
 
36
+ def to_str
37
+ to_s
38
+ end
39
+
36
40
  def to_underscore
37
41
  basename_words(basename).map { |word| word.downcase }.join('_')
38
42
  end
@@ -52,4 +56,4 @@ class Skippy::Namespace
52
56
  parts(namespace).all? { |part| /^[[:upper:]]/.match(part) }
53
57
  end
54
58
 
55
- end
59
+ end
@@ -1,14 +1,43 @@
1
1
  require 'json'
2
2
  require 'pathname'
3
3
 
4
+ require 'skippy/helpers/file'
5
+ require 'skippy/config'
6
+ require 'skippy/config_accessors'
7
+ require 'skippy/error'
8
+ require 'skippy/library_manager'
4
9
  require 'skippy/namespace'
10
+ require 'skippy/module_manager'
5
11
 
6
12
  class Skippy::Project
7
13
 
14
+ extend Skippy::ConfigAccessors
15
+
16
+ include Skippy::Helpers::File
17
+
8
18
  PROJECT_FILENAME = 'skippy.json'.freeze
9
19
 
10
- attr_reader :name, :namespace, :path, :author, :copyright, :license
11
- attr_accessor :description
20
+ attr_reader :config
21
+ attr_reader :libraries
22
+ attr_reader :modules
23
+
24
+ config_attr :author, :copyright, :description, :license, :name
25
+ config_attr :namespace, type: Skippy::Namespace
26
+
27
+ class ProjectNotFoundError < Skippy::Error; end
28
+ class ProjectNotSavedError < Skippy::Error; end
29
+
30
+ # @return [Skippy::Project, nil]
31
+ def self.current
32
+ Skippy::Project.new(Dir.pwd)
33
+ end
34
+
35
+ # @return [Skippy::Project]
36
+ def self.current_or_fail
37
+ project = self.current
38
+ raise ProjectNotFoundError unless project.exist?
39
+ project
40
+ end
12
41
 
13
42
  # Initialize a project for the provided path. If the path is within a project
14
43
  # path the base path of the project will be found. Otherwise it's assumed that
@@ -17,12 +46,10 @@ class Skippy::Project
17
46
  # @param [Pathname, String] path
18
47
  def initialize(path)
19
48
  @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'
49
+ # noinspection RubyResolve
50
+ @config = Skippy::Config.load(filename, defaults)
51
+ @libraries = Skippy::LibraryManager.new(self)
52
+ @modules = Skippy::ModuleManager.new(self)
26
53
  end
27
54
 
28
55
  # @yield [filename]
@@ -36,42 +63,44 @@ class Skippy::Project
36
63
 
37
64
  # Checks if a project exist on disk. If not it's just transient.
38
65
  def exist?
39
- File.exist?(filename)
66
+ filename.exist?
40
67
  end
41
68
 
42
69
  # Full path to the project's configuration file. This file may not exist.
43
- # @return [String]
70
+ # @return [Pathname]
44
71
  def filename
45
- File.join(path, PROJECT_FILENAME)
46
- end
47
-
48
- # @return [String]
49
- def name
50
- @name.empty? ? namespace.to_name : @name
72
+ path.join(PROJECT_FILENAME)
51
73
  end
52
74
 
53
- # @param [String] namespace
54
- def namespace=(namespace)
55
- @namespace = Skippy::Namespace.new(namespace)
75
+ # @param [Pathname, String] sub_path
76
+ # @return [Pathname]
77
+ def path(sub_path = '')
78
+ @path.join(sub_path)
56
79
  end
57
80
 
58
81
  # Commits the project to disk.
59
82
  def save
60
- File.write(filename, to_json)
83
+ @config.save_as(filename)
61
84
  end
62
85
 
63
86
  # @return [String]
64
87
  def to_json
65
- project_config = {
66
- namespace: namespace,
67
- name: name,
68
- description: description
69
- }
70
- JSON.pretty_generate(project_config)
88
+ JSON.pretty_generate(@config)
71
89
  end
72
90
 
73
91
  private
74
92
 
93
+ def defaults
94
+ {
95
+ name: 'Untitled',
96
+ description: '',
97
+ namespace: Skippy::Namespace.new('Untitled'),
98
+ author: 'Unknown',
99
+ copyright: "Copyright (c) #{Time.now.year}",
100
+ license: 'None',
101
+ }
102
+ end
103
+
75
104
  # Finds the root of a project based on any path within the project.
76
105
  #
77
106
  # @param [String] path