toys 0.2.1 → 0.2.2

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,7 +1,37 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
1
30
  require "fileutils"
2
31
 
3
- module Toys
4
- module Helpers
5
- FileUtils = ::FileUtils
6
- end
32
+ module Toys::Helpers
33
+ ##
34
+ # File system utilities. See the "fileutils" standard library.
35
+ #
36
+ FileUtils = ::FileUtils
7
37
  end
data/lib/toys/lookup.rb CHANGED
@@ -1,25 +1,46 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
1
30
  module Toys
31
+ ##
32
+ # The lookup service that finds a tool given a set of arguments
33
+ #
2
34
  class Lookup
3
35
  def initialize(config_dir_name: nil, config_file_name: nil,
4
36
  index_file_name: nil, preload_file_name: nil)
5
37
  @config_dir_name = config_dir_name
6
- if @config_dir_name && File.extname(@config_dir_name) == ".rb"
7
- raise LookupError, "Illegal config dir name #{@config_dir_name.inspect}"
8
- end
9
38
  @config_file_name = config_file_name
10
- if @config_file_name && File.extname(@config_file_name) != ".rb"
11
- raise LookupError, "Illegal config file name #{@config_file_name.inspect}"
12
- end
13
39
  @index_file_name = index_file_name
14
- if @index_file_name && File.extname(@index_file_name) != ".rb"
15
- raise LookupError, "Illegal index file name #{@index_file_name.inspect}"
16
- end
17
40
  @preload_file_name = preload_file_name
18
- if @preload_file_name && File.extname(@preload_file_name) != ".rb"
19
- raise LookupError, "Illegal preload file name #{@preload_file_name.inspect}"
20
- end
41
+ check_init_options
21
42
  @load_worklist = []
22
- @tools = {[] => [Tool.new(nil, nil), nil]}
43
+ @tools = {[] => [Tool.new(self, []), nil]}
23
44
  @max_priority = @min_priority = 0
24
45
  end
25
46
 
@@ -27,37 +48,47 @@ module Toys
27
48
  paths = Array(paths)
28
49
  paths = paths.reverse if high_priority
29
50
  paths.each do |path|
30
- path = check_path(path)
31
- priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
32
- @load_worklist << [path, [], priority]
51
+ add_path(path, high_priority: high_priority)
33
52
  end
34
53
  self
35
54
  end
36
55
 
56
+ def add_path(path, high_priority: false)
57
+ path = check_path(path)
58
+ priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
59
+ @load_worklist << [path, [], priority]
60
+ self
61
+ end
62
+
37
63
  def add_config_paths(paths, high_priority: false)
38
64
  paths = Array(paths)
39
65
  paths = paths.reverse if high_priority
40
66
  paths.each do |path|
41
- path = check_path(path, type: :dir)
42
- priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
43
- if @config_file_name
44
- p = File.join(path, @config_file_name)
45
- if !File.directory?(p) && File.readable?(p)
46
- @load_worklist << [p, [], priority]
47
- end
67
+ add_config_path(path, high_priority: high_priority)
68
+ end
69
+ self
70
+ end
71
+
72
+ def add_config_path(path, high_priority: false)
73
+ path = check_path(path, type: :dir)
74
+ priority = high_priority ? (@max_priority += 1) : (@min_priority -= 1)
75
+ if @config_file_name
76
+ p = ::File.join(path, @config_file_name)
77
+ if !::File.directory?(p) && ::File.readable?(p)
78
+ @load_worklist << [p, [], priority]
48
79
  end
49
- if @config_dir_name
50
- p = File.join(path, @config_dir_name)
51
- if File.directory?(p) && File.readable?(p)
52
- @load_worklist << [p, [], priority]
53
- end
80
+ end
81
+ if @config_dir_name
82
+ p = ::File.join(path, @config_dir_name)
83
+ if ::File.directory?(p) && ::File.readable?(p)
84
+ @load_worklist << [p, [], priority]
54
85
  end
55
86
  end
56
87
  self
57
88
  end
58
89
 
59
90
  def lookup(args)
60
- orig_prefix = args.take_while{ |arg| !arg.start_with?("-") }
91
+ orig_prefix = args.take_while { |arg| !arg.start_with?("-") }
61
92
  cur_prefix = orig_prefix.dup
62
93
  loop do
63
94
  load_for_prefix(cur_prefix)
@@ -70,15 +101,25 @@ module Toys
70
101
  end
71
102
  end
72
103
 
104
+ def execute(context_base, args)
105
+ tool = lookup(args)
106
+ tool.execute(context_base, args.slice(tool.full_name.length..-1))
107
+ end
108
+
109
+ def exact_tool(words)
110
+ return nil unless tool_defined?(words)
111
+ @tools[words].first
112
+ end
113
+
73
114
  def get_tool(words, priority)
74
- if @tools.key?(words)
115
+ if tool_defined?(words)
75
116
  tool, tool_priority = @tools[words]
76
117
  return tool if tool_priority.nil? || tool_priority == priority
77
118
  return nil if tool_priority > priority
78
119
  end
79
120
  parent = get_tool(words[0..-2], priority)
80
121
  return nil if parent.nil?
81
- tool = Tool.new(parent, words.last)
122
+ tool = Tool.new(self, words)
82
123
  @tools[words] = [tool, priority]
83
124
  tool
84
125
  end
@@ -91,30 +132,48 @@ module Toys
91
132
  found_tools = []
92
133
  len = words.length
93
134
  @tools.each do |n, tp|
94
- if n.length > 0
95
- if !recursive && n.slice(0..-2) == words ||
96
- recursive && n.length > len && n.slice(0, len) == words
97
- found_tools << tp.first
98
- end
135
+ next if n.empty?
136
+ if recursive
137
+ next if n.length <= len || n.slice(0, len) != words
138
+ else
139
+ next unless n.slice(0..-2) == words
99
140
  end
141
+ found_tools << tp.first
100
142
  end
101
- found_tools.sort do |a, b|
102
- a = a.full_name
103
- b = b.full_name
104
- while !a.empty? && !b.empty? && a.first == b.first
105
- a = a.slice(1..-1)
106
- b = b.slice(1..-1)
107
- end
108
- a.first.to_s <=> b.first.to_s
109
- end
143
+ sort_tools_by_name(found_tools)
110
144
  end
111
145
 
112
146
  def include_path(path, words, remaining_words, priority)
113
147
  handle_path(check_path(path), words, remaining_words, priority)
114
148
  end
115
149
 
150
+ def self.next_remaining_words(remaining_words, word)
151
+ if remaining_words.nil?
152
+ nil
153
+ elsif remaining_words.empty?
154
+ remaining_words
155
+ elsif remaining_words.first == word
156
+ remaining_words.slice(1..-1)
157
+ end
158
+ end
159
+
116
160
  private
117
161
 
162
+ def check_init_options
163
+ if @config_dir_name && ::File.extname(@config_dir_name) == ".rb"
164
+ raise LookupError, "Illegal config dir name #{@config_dir_name.inspect}"
165
+ end
166
+ if @config_file_name && ::File.extname(@config_file_name) != ".rb"
167
+ raise LookupError, "Illegal config file name #{@config_file_name.inspect}"
168
+ end
169
+ if @index_file_name && ::File.extname(@index_file_name) != ".rb"
170
+ raise LookupError, "Illegal index file name #{@index_file_name.inspect}"
171
+ end
172
+ if @preload_file_name && ::File.extname(@preload_file_name) != ".rb"
173
+ raise LookupError, "Illegal preload file name #{@preload_file_name.inspect}"
174
+ end
175
+ end
176
+
118
177
  def load_for_prefix(prefix)
119
178
  cur_worklist = @load_worklist
120
179
  @load_worklist = []
@@ -132,56 +191,55 @@ module Toys
132
191
  end
133
192
 
134
193
  def load_path(path, words, remaining_words, priority)
135
- if File.extname(path) == ".rb"
194
+ if ::File.extname(path) == ".rb"
136
195
  tool = get_tool(words, priority)
137
196
  if tool
138
- Parser.parse(path, tool, remaining_words, priority, self, IO.read(path))
197
+ Builder.build(path, tool, remaining_words, priority, self, ::IO.read(path))
139
198
  end
140
199
  else
141
- if @preload_file_name
142
- preload_path = File.join(path, @preload_file_name)
143
- if File.exist?(preload_path)
144
- preload_path = check_path(preload_path, type: :file)
145
- require preload_path
146
- end
147
- end
148
- if @index_file_name
149
- index_path = File.join(path, @index_file_name)
150
- if File.exist?(index_path)
151
- index_path = check_path(index_path, type: :file)
152
- load_path(index_path, words, remaining_words, priority)
153
- end
154
- end
155
- Dir.entries(path).each do |child|
156
- if !child.start_with?(".") && child != @preload_file_name && child != @index_file_name
157
- child_path = check_path(File.join(path, child))
158
- child_word = File.basename(child, ".rb")
159
- next_words = words + [child_word]
160
- next_remaining_words =
161
- if remaining_words.empty?
162
- remaining_words
163
- elsif child_word == remaining_words.first
164
- remaining_words.slice(1..-1)
165
- else
166
- nil
167
- end
168
- handle_path(child_path, next_words, next_remaining_words, priority)
169
- end
200
+ require_preload_in(path)
201
+ load_index_in(path, words, remaining_words, priority)
202
+ ::Dir.entries(path).each do |child|
203
+ load_child_in(path, child, words, remaining_words, priority)
170
204
  end
171
205
  end
172
206
  end
173
207
 
208
+ def require_preload_in(path)
209
+ return unless @preload_file_name
210
+ preload_path = ::File.join(path, @preload_file_name)
211
+ preload_path = check_path(preload_path, type: :file, lenient: true)
212
+ require preload_path if preload_path
213
+ end
214
+
215
+ def load_index_in(path, words, remaining_words, priority)
216
+ return unless @index_file_name
217
+ index_path = ::File.join(path, @index_file_name)
218
+ index_path = check_path(index_path, type: :file, lenient: true)
219
+ load_path(index_path, words, remaining_words, priority) if index_path
220
+ end
221
+
222
+ def load_child_in(path, child, words, remaining_words, priority)
223
+ return if child.start_with?(".")
224
+ return if [@preload_file_name, @index_file_name].include?(child)
225
+ child_path = check_path(::File.join(path, child))
226
+ child_word = ::File.basename(child, ".rb")
227
+ next_words = words + [child_word]
228
+ next_remaining = Lookup.next_remaining_words(remaining_words, child_word)
229
+ handle_path(child_path, next_words, next_remaining, priority)
230
+ end
231
+
174
232
  def check_path(path, lenient: false, type: nil)
175
- path = File.expand_path(path)
176
- type ||= File.extname(path) == ".rb" ? :file : :dir
233
+ path = ::File.expand_path(path)
234
+ type ||= ::File.extname(path) == ".rb" ? :file : :dir
177
235
  case type
178
236
  when :file
179
- if File.directory?(path) || !File.readable?(path)
237
+ if ::File.directory?(path) || !::File.readable?(path)
180
238
  return nil if lenient
181
239
  raise LookupError, "Cannot read file #{path}"
182
240
  end
183
241
  when :dir
184
- if !File.directory?(path) || !File.readable?(path)
242
+ if !::File.directory?(path) || !::File.readable?(path)
185
243
  return nil if lenient
186
244
  raise LookupError, "Cannot read directory #{path}"
187
245
  end
@@ -191,10 +249,23 @@ module Toys
191
249
  path
192
250
  end
193
251
 
252
+ def sort_tools_by_name(tools)
253
+ tools.sort do |a, b|
254
+ a = a.full_name
255
+ b = b.full_name
256
+ while !a.empty? && !b.empty? && a.first == b.first
257
+ a = a.slice(1..-1)
258
+ b = b.slice(1..-1)
259
+ end
260
+ a.first.to_s <=> b.first.to_s
261
+ end
262
+ end
263
+
194
264
  def calc_remaining_words(words1, words2)
195
265
  index = 0
266
+ lengths = [words1.length, words2.length]
196
267
  loop do
197
- return words1.slice(index..-1) if index == words1.length || index == words2.length
268
+ return words1.slice(index..-1) if lengths.include?(index)
198
269
  return nil if words1[index] != words2[index]
199
270
  index += 1
200
271
  end
data/lib/toys/template.rb CHANGED
@@ -1,21 +1,50 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
1
30
  module Toys
2
- class Template
3
- def initialize
4
- @opts_initializer = ->(opts){opts}
5
- @expander = ->(opts){}
31
+ ##
32
+ # A template definition. Template classes should include this module.
33
+ #
34
+ module Template
35
+ def self.included(mod)
36
+ mod.extend(ClassMethods)
6
37
  end
7
38
 
8
- def to_init_opts(&block)
9
- @opts_initializer = block
10
- self
11
- end
39
+ ##
40
+ # Class methods that will be added to a template class.
41
+ #
42
+ module ClassMethods
43
+ def to_expand(&block)
44
+ @expander = block
45
+ end
12
46
 
13
- def to_expand(&block)
14
- @expander = block
15
- self
47
+ attr_accessor :expander
16
48
  end
17
-
18
- attr_reader :opts_initializer
19
- attr_reader :expander
20
49
  end
21
50
  end
@@ -0,0 +1,67 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ module Toys::Templates
31
+ ##
32
+ # A template for tools that clean build artifacts
33
+ #
34
+ class Clean
35
+ include ::Toys::Template
36
+
37
+ def initialize(opts = {})
38
+ @name = opts[:name] || "clean"
39
+ @paths = opts[:paths] || []
40
+ end
41
+
42
+ attr_accessor :name
43
+ attr_accessor :paths
44
+
45
+ to_expand do |template|
46
+ name(template.name) do
47
+ short_desc "Clean built files and directories"
48
+
49
+ use :file_utils
50
+
51
+ execute do
52
+ files = []
53
+ patterns = Array(template.paths)
54
+ patterns = ["lib/**/*.rb"] if patterns.empty?
55
+ patterns.each do |pattern|
56
+ files.concat(::Dir.glob(pattern))
57
+ end
58
+ files.uniq!
59
+
60
+ files.each do |file|
61
+ rm_rf file
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end