skippy 0.1.1.a → 0.2.0.a

Sign up to get free protection for your applications and to get access to all the features.
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