smc-get 0.2.0.beta1 → 0.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ #Encoding: UTF-8
2
+ ################################################################################
3
+ # This file is part of smc-get.
4
+ # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
+ # Copyright (C) 2011 Marvin Gülker
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ################################################################################
20
+ #
21
+ #This is the configuration file for smc-get.
22
+ ---
23
+ #This is the directory where everything gets installed into.
24
+ #Set this to the installation directory of SMC, e.g.
25
+ #/usr/share/smc.
26
+ data_directory: "/usr/share/smc"
27
+ #This is the URL of the repository where packages are downloaded from.
28
+ repo_url: "https://raw.github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/master/packages"
29
+ #This is the maximum number of tries smc-get makes in order to
30
+ #download a package.
31
+ max_tries: 3
32
+
@@ -1,23 +1,23 @@
1
- #Encoding: UTF-8
2
- ################################################################################
3
- # This file is part of smc-get.
4
- # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
- # Copyright (C) 2011 Marvin Gülker
6
- #
7
- # This program is free software: you can redistribute it and/or modify
8
- # it under the terms of the GNU General Public License as published by
9
- # the Free Software Foundation, either version 3 of the License, or
10
- # (at your option) any later version.
11
- #
12
- # This program is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- # GNU General Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License
18
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
- ################################################################################
20
-
21
- require_relative "smc_get/smc_get"
22
-
1
+ #Encoding: UTF-8
2
+ ################################################################################
3
+ # This file is part of smc-get.
4
+ # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
+ # Copyright (C) 2011 Marvin Gülker
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ################################################################################
20
+
21
+ require_relative "smc_get/smc_get"
22
+
23
23
  # vim:set ts=8 sts=2 sw=2 et: #
@@ -1,325 +1,325 @@
1
- #Encoding: UTF-8
2
- ################################################################################
3
- # This file is part of smc-get.
4
- # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
- # Copyright (C) 2011 Marvin Gülker
6
- #
7
- # This program is free software: you can redistribute it and/or modify
8
- # it under the terms of the GNU General Public License as published by
9
- # the Free Software Foundation, either version 3 of the License, or
10
- # (at your option) any later version.
11
- #
12
- # This program is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- # GNU General Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License
18
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
- ################################################################################
20
-
21
- #Load all the files in the cui_commands directory.
22
- require_relative "cui_commands/command"
23
- Dir[File.join(File.expand_path(File.dirname(__FILE__)), "cui_commands", "*.rb")].each do |filename|
24
- require_relative "cui_commands/#{File.basename(filename)}"
25
- end
26
-
27
- module SmcGet
28
-
29
- #This is the Console User Interface smc-get exposes. Each command
30
- #is represented by a class in the CUICommands module. Those classes
31
- #have three methods: ::help, which should return a summary on how to use
32
- #the command (NOT print it, that's done by an internal method), #parse,
33
- #which gets the command-line for the subcommand passed in form of an
34
- #array you should destructively (i.e. removing the elements) analyse,
35
- #and #execute which finally executes the command. You are free to print out
36
- #anything inside that method.
37
- #
38
- #Note that inside the #parse method you can set instance variables
39
- #as you would do for any normal class. You can then grep their values
40
- #inside the #execute method. Furthermore, if inside #parse you detect
41
- #an error in the commandline the user provided to you, raise the
42
- #CUI::InvalidCommandline exception with a meaningful message which will
43
- #then be presented to the user.
44
- #
45
- #The last things one should do when adding a command, is to provide
46
- #a ::help class method that returns help on the usage of the command
47
- #(it will be called when the user issues <tt>smc-get help YOURCOMMAND</tt>
48
- #and it's return value will be shown to the user) and to provide
49
- #a ::summary class method whose return value is integrated in the
50
- #output of <tt>smc-get help</tt> under the COMMANDS section.
51
- #
52
- #In every method you add, you can make use of the CUI.debug method. If you
53
- #hand it a string, it will be printed only if running in debug mode, and
54
- #if you hand it any other object, it's +inspect+ value will be printed--if
55
- #running in debug mode. See the SmcGet::CUICommands::Command class's
56
- #documentation for more information on the hook methods.
57
- #
58
- #Internally the flow is as follows:
59
- #1. The user calls CUI#initialize with ARGV.
60
- #2. That method triggers CUI#parse_commandline and passes ARGV to it.
61
- #3. #parse_commandline analyses the array it receives and figures out
62
- # what command class inside the CUICommands module to instantiate.
63
- #4. CUICommand::Command.new calls #parse on the instantiated Command
64
- # object (this is a subclass of CUICommand::Command). Note that
65
- # smc-get has not been set up for now, and calls to Repository#install or
66
- # the like will fail.
67
- #5. The user calls CUI#start.
68
- #6. #start looks into @command and invokes the #execute method on it.
69
- #7. #start shuts down the interpreter via #exit. If the command execution
70
- # method returned an integer value, it is used as the exit status.
71
- #
72
- #Have a look at the existing commands to see how it works. Especially
73
- #+help+ is quite easy to understand, so begin there.
74
- class CUI
75
-
76
- #Default location of the configuration file.
77
- DEFAULT_CONFIG_FILE = CONFIG_DIR + "smc-get.yml"
78
- #The user’s personal directory.
79
- USER_DIR = Pathname.new(ENV["HOME"])
80
- #The user-level smc-get configuration file.
81
- USER_CONFIG_FILE = USER_DIR + ".smc-get-conf.yml"
82
- #A user’s personal SMC data directory.
83
- USER_SMC_DIR = USER_DIR + ".smc"
84
- #The help message displayed to the user when issueing "help".
85
- GENERAL_HELP =<<EOF
86
- USAGE:
87
- #{File.basename($0)} [OPTIONS] COMMAND [PARAMETERS...]
88
-
89
- DESCRIPTION:
90
- Install and uninstall levels from the Secret Maryo Chronicles contributed level
91
- repository.
92
-
93
- COMMANDS:
94
- #{str = ''
95
- CUICommands.constants.sort.each do |c|
96
- next if c == :Command or c == :InvalidCommandline
97
- str << ' ' << CUICommands.const_get(c).summary << "\n"
98
- end
99
- str}
100
-
101
- Use "help COMMAND" to get help on a specific command. "help" without an
102
- argument displays this message.
103
-
104
- OPTIONS FOR #{File.basename($0).upcase} ITSELF
105
- -c\t--config-file FILE\tUse FILE as the configuration file.
106
- -d\t--data-directory DIR\tSet the directory where to save packages into.
107
- -D\t--debug\t\t\tEnter debug mode. A normal user shouldn't use this.
108
- -r\t--repo-url URL\t\tSet the URL of the remote package repository.
109
-
110
- CONFIGURATION FILES
111
- You can use three kinds of configuration files with #{File.basename($0)}. They are,
112
- in the order in which they are evaluated:
113
-
114
- 1. Global configuration file #{DEFAULT_CONFIG_FILE}.
115
- 2. If existant, user-level configuration file #{USER_CONFIG_FILE}.
116
- 3. If existant, configuration file given on the commandline via the -c option.
117
-
118
- Configuration files loaded later overwrite values set in previously loaded
119
- configuration files, i.e. values set in the configuration file provided via
120
- the commandline override those in the global and user-level configuration
121
- file, and those in the user-level configuration file override those in the
122
- global configuration file, etc.
123
- There is a 4th way to set options for #{File.basename($0)}:
124
-
125
- 4. Options given via the commandline
126
-
127
- They override anything set in the configuration files, so specifying
128
- '-d /opt/smc' on the commandline would override any 'data_directory'
129
- setting in any of the configuration files.
130
-
131
- BUG REPORTING
132
-
133
- Report bugs at: https://github.com/Luiji/smc-get/issues
134
-
135
- OTHER
136
-
137
- smc-get project page: https://github.com/Luiji/smc-get
138
- smc home page: http://www.secretmaryo.org/
139
- EOF
140
-
141
- attr_reader :config
142
- attr_reader :remote_repository
143
- attr_reader :local_repository
144
-
145
- #Writes <tt>obj.inspect</tt> to $stdout if the CUI is running in debug
146
- #mode. If +obj+ is a string, it is simply written out.
147
- def self.debug(obj)
148
- if @DEBUG_MODE
149
- if obj.kind_of? String
150
- puts obj
151
- else
152
- puts(obj.inspect)
153
- end
154
- end
155
- end
156
-
157
- @DEBUG_MODE = false
158
-
159
- #Returns wheather or not we're running in debug mode.
160
- def self.debug_mode?
161
- @DEBUG_MODE
162
- end
163
-
164
- #Set to +true+ to enable debug mode.
165
- def self.debug_mode=(val)
166
- @DEBUG_MODE = val
167
- end
168
-
169
- #Creates a new CUI. Pass in ARGV or a set of command-line arguments
170
- #you want to and read this class's documentation for knowing what
171
- #happens then. Call #start on the returned object when you want
172
- #to execute everything.
173
- def initialize(argv)
174
- @config = {}
175
- parse_commandline(argv)
176
- load_config_file
177
- SmcGet.setup
178
- begin
179
- @local_repository = SmcGet::LocalRepository.new(@config[:data_directory])
180
- @remote_repository = SmcGet::RemoteRepository.new(@config[:repo_url])
181
- rescue Errors::InvalidRepository => e
182
- $stderr.puts("WARNING: Couldn't connect to this repository:")
183
- $stderr.puts(e.repository_uri)
184
- $stderr.puts("Reason: #{e.message}")
185
- end
186
- end
187
-
188
- #Starts executing of the CUI. This method never returns, it
189
- #calls #exit after the command has finished.
190
- def start
191
- begin
192
- ret = @command.execute(@config)
193
- #If numbers are returned they are supposed to be the exit code.
194
- if ret.kind_of? Integer
195
- exit ret
196
- else
197
- exit
198
- end
199
- rescue Errno::EACCES, Errors::SmcGetError => e
200
- $stderr.puts("ERROR: #{e.message}") #All SmcGetErrors should have an informative message (and the EACCES one too)
201
- if self.class.debug_mode?
202
- $stderr.puts "====DEBUGGING INFORMATION===="
203
- $stderr.puts "Class: #{e.class}"
204
- $stderr.puts "Message: #{e.message}"
205
- $stderr.puts "Backtrace: #{e.backtrace.join("\n\t")}"
206
- end
207
- exit 2
208
- rescue => e #Ouch. Fatal error not intended.
209
- $stderr.puts("[BUG] #{e.class}")
210
- $stderr.puts("Please file a bug report at https://github.com/Luiji/smc-get/issues")
211
- $stderr.puts("and attach this message. Describe what you did so we can")
212
- $stderr.puts("reproduce it. ")
213
- raise #Bubble up
214
- end
215
- end
216
-
217
- private
218
-
219
- #Destructively searches through +argv+ (i.e. emptying it) and
220
- #sets the CLI up. Besides when +help+ is used as the
221
- #command, nothing is actually executed.
222
- #
223
- #This method instantiates one of the various classes in the
224
- #CUICommands module, making smc-get easily extendable by
225
- #adding a new class inside that module.
226
- def parse_commandline(argv)
227
- #Get options for smc-get itself, rather than it's subcommands. Values set
228
- #here override anything set in any configuration file; but since the
229
- #keys are turned to symbols in #load_config_file, we have to use
230
- #strings as keys here (otherwise merging with the config files'
231
- #settings would fail).
232
- @cmd_config = nil
233
- while !argv.empty? and argv.first.start_with?("-") #All options start with a hyphen, commands cannot
234
- arg = argv.shift
235
- case arg
236
- when "-c", "--config-file" then @cmd_config = Pathname.new(argv.shift)
237
- when "-d", "--data-directory" then @config["data_directory"] = argv.shift
238
- when "-D", "--debug" then CUI.debug_mode = true
239
- when "-r", "--repo-url" then @config["repo_url"] = argv.shift
240
- else
241
- $stderr.puts("Invalid option #{arg}.")
242
- $stderr.puts("Try #$0 help.")
243
- exit 1
244
- end
245
- end
246
-
247
- #If nothing is left, the command was left out. Assume 'help'.
248
- argv << "help" if argv.empty?
249
-
250
- #Now parse the subcommand.
251
- command = argv.shift.to_sym
252
- CUI.debug("Found subcommand #{command}.")
253
- sym = :"#{command.capitalize}Command"
254
- begin
255
- if CUICommands.const_defined?(sym)
256
- begin
257
- @command = CUICommands.const_get(sym).new(self, argv)
258
- rescue CUICommands::InvalidCommandline => e
259
- $stderr.puts(e.message)
260
- $stderr.puts("Try #$0 help.")
261
- exit 1
262
- end
263
- else
264
- $stderr.puts "Unrecognized command #{command}. Try 'help'."
265
- exit 1
266
- end
267
- rescue NameError => e
268
- $stderr.puts "Unrecognized command #{command}. Try 'help'."
269
- if CUI.debug_mode?
270
- $stderr.puts("Class: #{e.class}")
271
- $stderr.puts("Message: #{e.message}")
272
- $stderr.puts("Backtrace:")
273
- $stderr.puts(e.backtrace.join("\n\t"))
274
- end
275
- exit 1
276
- end
277
- end
278
-
279
- #Loads the configuration file from the <b>config/</b> directory.
280
- def load_config_file
281
- #First, load the global configuration file.
282
- CUI.debug("Loading global config #{DEFAULT_CONFIG_FILE}.")
283
- hsh = YAML.load_file(DEFAULT_CONFIG_FILE)
284
- CUI.debug(hsh)
285
-
286
- #Second, load the user config which overrides values set in
287
- #the global config.
288
- CUI.debug("Loading user-level config #{USER_CONFIG_FILE}.")
289
- if USER_CONFIG_FILE.file?
290
- hsh.merge!(YAML.load_file(USER_CONFIG_FILE.to_s))
291
- else
292
- CUI.debug("Not found.")
293
- end
294
- CUI.debug(hsh)
295
-
296
- #Third, load the config file from the commandline, if any. This overrides
297
- #values set in the user and global config.
298
- if @cmd_config
299
- CUI.debug("Loading -c option config #{@cmd_config}.")
300
- if @cmd_config.file?
301
- hsh.merge!(YAML.load_file(@cmd_config.to_s))
302
- else
303
- $stderr.puts("Configuration file #{@cmd_config} not found.")
304
- end
305
- CUI.debug(hsh)
306
- end
307
-
308
- #Fourth, check for values on the commandline. They override anything
309
- #set previously. They are set directly in @config, so we simply have
310
- #to retain the old values in it.
311
- CUI.debug("Loading commandline options.")
312
- @config.merge!(hsh){|key, old_val, new_val| old_val}
313
- CUI.debug(@config)
314
-
315
- #Fifth, turn all keys into symbols, because that's more Ruby-like.
316
- CUI.debug("Converting to symbols.")
317
- @config = Hash[@config.map{|k, v| [k.to_sym, v]}]
318
- CUI.debug(@config)
319
- end
320
-
321
- end
322
-
323
- end
324
-
325
- # vim:set ts=8 sts=2 sw=2 et: #
1
+ #Encoding: UTF-8
2
+ ################################################################################
3
+ # This file is part of smc-get.
4
+ # Copyright (C) 2010-2011 Entertaining Software, Inc.
5
+ # Copyright (C) 2011 Marvin Gülker
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ################################################################################
20
+
21
+ #Load all the files in the cui_commands directory.
22
+ require_relative "cui_commands/command"
23
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)), "cui_commands", "*.rb")].each do |filename|
24
+ require_relative "cui_commands/#{File.basename(filename)}"
25
+ end
26
+
27
+ module SmcGet
28
+
29
+ #This is the Console User Interface smc-get exposes. Each command
30
+ #is represented by a class in the CUICommands module. Those classes
31
+ #have three methods: ::help, which should return a summary on how to use
32
+ #the command (NOT print it, that's done by an internal method), #parse,
33
+ #which gets the command-line for the subcommand passed in form of an
34
+ #array you should destructively (i.e. removing the elements) analyse,
35
+ #and #execute which finally executes the command. You are free to print out
36
+ #anything inside that method.
37
+ #
38
+ #Note that inside the #parse method you can set instance variables
39
+ #as you would do for any normal class. You can then grep their values
40
+ #inside the #execute method. Furthermore, if inside #parse you detect
41
+ #an error in the commandline the user provided to you, raise the
42
+ #CUI::InvalidCommandline exception with a meaningful message which will
43
+ #then be presented to the user.
44
+ #
45
+ #The last things one should do when adding a command, is to provide
46
+ #a ::help class method that returns help on the usage of the command
47
+ #(it will be called when the user issues <tt>smc-get help YOURCOMMAND</tt>
48
+ #and it's return value will be shown to the user) and to provide
49
+ #a ::summary class method whose return value is integrated in the
50
+ #output of <tt>smc-get help</tt> under the COMMANDS section.
51
+ #
52
+ #In every method you add, you can make use of the CUI.debug method. If you
53
+ #hand it a string, it will be printed only if running in debug mode, and
54
+ #if you hand it any other object, it's +inspect+ value will be printed--if
55
+ #running in debug mode. See the SmcGet::CUICommands::Command class's
56
+ #documentation for more information on the hook methods.
57
+ #
58
+ #Internally the flow is as follows:
59
+ #1. The user calls CUI#initialize with ARGV.
60
+ #2. That method triggers CUI#parse_commandline and passes ARGV to it.
61
+ #3. #parse_commandline analyses the array it receives and figures out
62
+ # what command class inside the CUICommands module to instantiate.
63
+ #4. CUICommand::Command.new calls #parse on the instantiated Command
64
+ # object (this is a subclass of CUICommand::Command). Note that
65
+ # smc-get has not been set up for now, and calls to Repository#install or
66
+ # the like will fail.
67
+ #5. The user calls CUI#start.
68
+ #6. #start looks into @command and invokes the #execute method on it.
69
+ #7. #start shuts down the interpreter via #exit. If the command execution
70
+ # method returned an integer value, it is used as the exit status.
71
+ #
72
+ #Have a look at the existing commands to see how it works. Especially
73
+ #+help+ is quite easy to understand, so begin there.
74
+ class CUI
75
+
76
+ #Default location of the configuration file.
77
+ DEFAULT_CONFIG_FILE = CONFIG_DIR + "smc-get.yml"
78
+ #The user’s personal directory.
79
+ USER_DIR = Pathname.new(ENV["HOME"])
80
+ #The user-level smc-get configuration file.
81
+ USER_CONFIG_FILE = USER_DIR + ".smc-get-conf.yml"
82
+ #A user’s personal SMC data directory.
83
+ USER_SMC_DIR = USER_DIR + ".smc"
84
+ #The help message displayed to the user when issueing "help".
85
+ GENERAL_HELP =<<EOF
86
+ USAGE:
87
+ #{File.basename($0)} [OPTIONS] COMMAND [PARAMETERS...]
88
+
89
+ DESCRIPTION:
90
+ Install and uninstall levels from the Secret Maryo Chronicles contributed level
91
+ repository.
92
+
93
+ COMMANDS:
94
+ #{str = ''
95
+ CUICommands.constants.sort.each do |c|
96
+ next if c == :Command or c == :InvalidCommandline
97
+ str << ' ' << CUICommands.const_get(c).summary << "\n"
98
+ end
99
+ str}
100
+
101
+ Use "help COMMAND" to get help on a specific command. "help" without an
102
+ argument displays this message.
103
+
104
+ OPTIONS FOR #{File.basename($0).upcase} ITSELF
105
+ -c\t--config-file FILE\tUse FILE as the configuration file.
106
+ -d\t--data-directory DIR\tSet the directory where to save packages into.
107
+ -D\t--debug\t\t\tEnter debug mode. A normal user shouldn't use this.
108
+ -r\t--repo-url URL\t\tSet the URL of the remote package repository.
109
+
110
+ CONFIGURATION FILES
111
+ You can use three kinds of configuration files with #{File.basename($0)}. They are,
112
+ in the order in which they are evaluated:
113
+
114
+ 1. Global configuration file #{DEFAULT_CONFIG_FILE}.
115
+ 2. If existant, user-level configuration file #{USER_CONFIG_FILE}.
116
+ 3. If existant, configuration file given on the commandline via the -c option.
117
+
118
+ Configuration files loaded later overwrite values set in previously loaded
119
+ configuration files, i.e. values set in the configuration file provided via
120
+ the commandline override those in the global and user-level configuration
121
+ file, and those in the user-level configuration file override those in the
122
+ global configuration file, etc.
123
+ There is a 4th way to set options for #{File.basename($0)}:
124
+
125
+ 4. Options given via the commandline
126
+
127
+ They override anything set in the configuration files, so specifying
128
+ '-d /opt/smc' on the commandline would override any 'data_directory'
129
+ setting in any of the configuration files.
130
+
131
+ BUG REPORTING
132
+
133
+ Report bugs at: https://github.com/Luiji/smc-get/issues
134
+
135
+ OTHER
136
+
137
+ smc-get project page: https://github.com/Luiji/smc-get
138
+ smc home page: http://www.secretmaryo.org/
139
+ EOF
140
+
141
+ attr_reader :config
142
+ attr_reader :remote_repository
143
+ attr_reader :local_repository
144
+
145
+ #Writes <tt>obj.inspect</tt> to $stdout if the CUI is running in debug
146
+ #mode. If +obj+ is a string, it is simply written out.
147
+ def self.debug(obj)
148
+ if @DEBUG_MODE
149
+ if obj.kind_of? String
150
+ puts obj
151
+ else
152
+ puts(obj.inspect)
153
+ end
154
+ end
155
+ end
156
+
157
+ @DEBUG_MODE = false
158
+
159
+ #Returns wheather or not we're running in debug mode.
160
+ def self.debug_mode?
161
+ @DEBUG_MODE
162
+ end
163
+
164
+ #Set to +true+ to enable debug mode.
165
+ def self.debug_mode=(val)
166
+ @DEBUG_MODE = val
167
+ end
168
+
169
+ #Creates a new CUI. Pass in ARGV or a set of command-line arguments
170
+ #you want to and read this class's documentation for knowing what
171
+ #happens then. Call #start on the returned object when you want
172
+ #to execute everything.
173
+ def initialize(argv)
174
+ @config = {}
175
+ parse_commandline(argv)
176
+ load_config_file
177
+ SmcGet.setup
178
+ begin
179
+ @local_repository = SmcGet::LocalRepository.new(@config[:data_directory])
180
+ @remote_repository = SmcGet::RemoteRepository.new(@config[:repo_url])
181
+ rescue Errors::InvalidRepository => e
182
+ $stderr.puts("WARNING: Couldn't connect to this repository:")
183
+ $stderr.puts(e.repository_uri)
184
+ $stderr.puts("Reason: #{e.message}")
185
+ end
186
+ end
187
+
188
+ #Starts executing of the CUI. This method never returns, it
189
+ #calls #exit after the command has finished.
190
+ def start
191
+ begin
192
+ ret = @command.execute(@config)
193
+ #If numbers are returned they are supposed to be the exit code.
194
+ if ret.kind_of? Integer
195
+ exit ret
196
+ else
197
+ exit
198
+ end
199
+ rescue Errno::EACCES, Errors::SmcGetError => e
200
+ $stderr.puts("ERROR: #{e.message}") #All SmcGetErrors should have an informative message (and the EACCES one too)
201
+ if self.class.debug_mode?
202
+ $stderr.puts "====DEBUGGING INFORMATION===="
203
+ $stderr.puts "Class: #{e.class}"
204
+ $stderr.puts "Message: #{e.message}"
205
+ $stderr.puts "Backtrace: #{e.backtrace.join("\n\t")}"
206
+ end
207
+ exit 2
208
+ rescue => e #Ouch. Fatal error not intended.
209
+ $stderr.puts("[BUG] #{e.class}")
210
+ $stderr.puts("Please file a bug report at https://github.com/Luiji/smc-get/issues")
211
+ $stderr.puts("and attach this message. Describe what you did so we can")
212
+ $stderr.puts("reproduce it. ")
213
+ raise #Bubble up
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ #Destructively searches through +argv+ (i.e. emptying it) and
220
+ #sets the CLI up. Besides when +help+ is used as the
221
+ #command, nothing is actually executed.
222
+ #
223
+ #This method instantiates one of the various classes in the
224
+ #CUICommands module, making smc-get easily extendable by
225
+ #adding a new class inside that module.
226
+ def parse_commandline(argv)
227
+ #Get options for smc-get itself, rather than it's subcommands. Values set
228
+ #here override anything set in any configuration file; but since the
229
+ #keys are turned to symbols in #load_config_file, we have to use
230
+ #strings as keys here (otherwise merging with the config files'
231
+ #settings would fail).
232
+ @cmd_config = nil
233
+ while !argv.empty? and argv.first.start_with?("-") #All options start with a hyphen, commands cannot
234
+ arg = argv.shift
235
+ case arg
236
+ when "-c", "--config-file" then @cmd_config = Pathname.new(argv.shift)
237
+ when "-d", "--data-directory" then @config["data_directory"] = argv.shift
238
+ when "-D", "--debug" then CUI.debug_mode = true
239
+ when "-r", "--repo-url" then @config["repo_url"] = argv.shift
240
+ else
241
+ $stderr.puts("Invalid option #{arg}.")
242
+ $stderr.puts("Try #$0 help.")
243
+ exit 1
244
+ end
245
+ end
246
+
247
+ #If nothing is left, the command was left out. Assume 'help'.
248
+ argv << "help" if argv.empty?
249
+
250
+ #Now parse the subcommand.
251
+ command = argv.shift.to_sym
252
+ CUI.debug("Found subcommand #{command}.")
253
+ sym = :"#{command.capitalize}Command"
254
+ begin
255
+ if CUICommands.const_defined?(sym)
256
+ begin
257
+ @command = CUICommands.const_get(sym).new(self, argv)
258
+ rescue CUICommands::InvalidCommandline => e
259
+ $stderr.puts(e.message)
260
+ $stderr.puts("Try #$0 help.")
261
+ exit 1
262
+ end
263
+ else
264
+ $stderr.puts "Unrecognized command #{command}. Try 'help'."
265
+ exit 1
266
+ end
267
+ rescue NameError => e
268
+ $stderr.puts "Unrecognized command #{command}. Try 'help'."
269
+ if CUI.debug_mode?
270
+ $stderr.puts("Class: #{e.class}")
271
+ $stderr.puts("Message: #{e.message}")
272
+ $stderr.puts("Backtrace:")
273
+ $stderr.puts(e.backtrace.join("\n\t"))
274
+ end
275
+ exit 1
276
+ end
277
+ end
278
+
279
+ #Loads the configuration file from the <b>config/</b> directory.
280
+ def load_config_file
281
+ #First, load the global configuration file.
282
+ CUI.debug("Loading global config #{DEFAULT_CONFIG_FILE}.")
283
+ hsh = YAML.load_file(DEFAULT_CONFIG_FILE)
284
+ CUI.debug(hsh)
285
+
286
+ #Second, load the user config which overrides values set in
287
+ #the global config.
288
+ CUI.debug("Loading user-level config #{USER_CONFIG_FILE}.")
289
+ if USER_CONFIG_FILE.file?
290
+ hsh.merge!(YAML.load_file(USER_CONFIG_FILE.to_s))
291
+ else
292
+ CUI.debug("Not found.")
293
+ end
294
+ CUI.debug(hsh)
295
+
296
+ #Third, load the config file from the commandline, if any. This overrides
297
+ #values set in the user and global config.
298
+ if @cmd_config
299
+ CUI.debug("Loading -c option config #{@cmd_config}.")
300
+ if @cmd_config.file?
301
+ hsh.merge!(YAML.load_file(@cmd_config.to_s))
302
+ else
303
+ $stderr.puts("Configuration file #{@cmd_config} not found.")
304
+ end
305
+ CUI.debug(hsh)
306
+ end
307
+
308
+ #Fourth, check for values on the commandline. They override anything
309
+ #set previously. They are set directly in @config, so we simply have
310
+ #to retain the old values in it.
311
+ CUI.debug("Loading commandline options.")
312
+ @config.merge!(hsh){|key, old_val, new_val| old_val}
313
+ CUI.debug(@config)
314
+
315
+ #Fifth, turn all keys into symbols, because that's more Ruby-like.
316
+ CUI.debug("Converting to symbols.")
317
+ @config = Hash[@config.map{|k, v| [k.to_sym, v]}]
318
+ CUI.debug(@config)
319
+ end
320
+
321
+ end
322
+
323
+ end
324
+
325
+ # vim:set ts=8 sts=2 sw=2 et: #