toys 0.2.1 → 0.2.2

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