smc-get 0.1.0 → 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
data/config/smc-get.yml CHANGED
@@ -1,32 +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://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/raw/master/"
29
- #This is the maximum number of tries smc-get makes in order to
30
- #download a package.
31
- max_tries: 3
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
+
data/lib/smc_get.rb CHANGED
@@ -1,21 +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"
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
+ # vim:set ts=8 sts=2 sw=2 et: #
data/lib/smc_get/cui.rb CHANGED
@@ -1,289 +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 Package.new 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
- #Name of the configuration file a user may put in his home
79
- #directory.
80
- USER_CONFIG_FILE_NAME = ".smc-get-conf.yml"
81
-
82
- #The help message displayed to the user when issueing "help".
83
- GENERAL_HELP =<<EOF
84
- USAGE:
85
- #{File.basename($0)} [OPTIONS] COMMAND [PARAMETERS...]
86
-
87
- DESCRIPTION:
88
- Install and uninstall levels from the Secret Maryo Chronicles contributed level
89
- repository.
90
-
91
- COMMANDS:
92
- #{str = ''
93
- CUICommands.constants.sort.each do |c|
94
- next if c == :Command or c == :InvalidCommandline
95
- str << ' ' << CUICommands.const_get(c).summary << "\n"
96
- end
97
- str}
98
-
99
- Use "help COMMAND" to get help on a specific command. "help" without an
100
- argument displays this message.
101
-
102
- OPTIONS FOR #{File.basename($0)} itself
103
- -c\t--config-file FILE\tUse FILE as the configuration file.
104
- -d\t--data-directory DIR\tSet the directory where to save packages into.
105
- -D\t--debug\t\t\tEnter debug mode. A normal user shouldn't use this.
106
- -r\t--repo-url URL\t\tSet the URL of the remote package repository.
107
-
108
- CONFIGURATION FILES
109
- You can use three kinds of configuration files with #{File.basename($0)}. They are,
110
- in the order in which they are evaluated:
111
-
112
- 1. Global configuration file #{DEFAULT_CONFIG_FILE}.
113
- 2. If existant, user-level configuration file #{File.join(ENV["HOME"], USER_CONFIG_FILE_NAME)}.
114
- 3. If existant, configuration file given on the commandline via the -c option.
115
-
116
- Configuration files loaded later overwrite values set in previously loaded
117
- configuration files, i.e. values set in the configuration file provided via
118
- the commandline override those in the global and user-level configuration
119
- file, and those in the user-level configuration file override those in the
120
- global configuration file, etc.
121
- There is a 4th way to set options for #{File.basename($0)}:
122
-
123
- 4. Options given via the commandline
124
-
125
- They override anything set in the configuration files, so specifying
126
- '-d /opt/smc' on the commandline would override any 'data_directory'
127
- setting in any of the configuration files.
128
-
129
- BUG REPORTING
130
-
131
- Report bugs to: luiji@users.sourceforge.net
132
- smc-get home page: <http://www.secretmaryo.org/>
133
- EOF
134
-
135
- #Writes <tt>obj.inspect</tt> to $stdout if the CUI is running in debug
136
- #mode. If +obj+ is a string, it is simply written out.
137
- def self.debug(obj)
138
- if @DEBUG_MODE
139
- if obj.kind_of? String
140
- puts obj
141
- else
142
- puts(obj.inspect)
143
- end
144
- end
145
- end
146
-
147
- @DEBUG_MODE = false
148
-
149
- #Returns wheather or not we're running in debug mode.
150
- def self.debug_mode?
151
- @DEBUG_MODE
152
- end
153
-
154
- #Set to +true+ to enable debug mode.
155
- def self.debug_mode=(val)
156
- @DEBUG_MODE = val
157
- end
158
-
159
- #Creates a new CUI. Pass in ARGV or a set of command-line arguments
160
- #you want to and read this class's documentation for knowing what
161
- #happens then. Call #start on the returned object when you want
162
- #to execute everything.
163
- def initialize(argv)
164
- @config = {}
165
- parse_commandline(argv)
166
- load_config_file
167
- SmcGet.setup(@config[:repo_url], @config[:data_directory])
168
- end
169
-
170
- #Starts executing of the CUI. This method never returns, it
171
- #calls #exit after the command has finished.
172
- def start
173
- begin
174
- ret = @command.execute(@config)
175
- #If numbers are returned they are supposed to be the exit code.
176
- if ret.kind_of? Integer
177
- exit ret
178
- else
179
- exit
180
- end
181
- rescue Errno::EACCES, Errors::SmcGetError => e
182
- $stderr.puts(e.message) #All SmcGetErrors should have an informative message (and the EACCES one too)
183
- exit 2
184
- rescue => e #Ouch. Fatal error not intended.
185
- $stderr.puts("[BUG] #{e.class}")
186
- $stderr.puts("Please file a bug report at https://github.com/Luiji/smc-get/issues")
187
- $stderr.puts("and attach this message. Describe what you did so we can")
188
- $stderr.puts("reproduce it. ")
189
- raise #Bubble up
190
- end
191
- end
192
-
193
- private
194
-
195
- #Destructively searches through +argv+ (i.e. emptying it) and
196
- #sets the CLI up. Besides when +help+ is used as the
197
- #command, nothing is actually executed.
198
- #
199
- #This method instantiates one of the various classes in the
200
- #CUICommands module, making smc-get easily extendable by
201
- #adding a new class inside that module.
202
- def parse_commandline(argv)
203
- #Get options for smc-get itself, rather than it's subcommands. Values set
204
- #here override anything set in any configuration file; but since the
205
- #keys are turned to symbols in #load_config_file, we have to use
206
- #strings as keys here (otherwise merging with the config files'
207
- #settings would fail).
208
- @cmd_config = nil
209
- while !argv.empty? and argv.first.start_with?("-") #All options start with a hyphen, commands cannot
210
- arg = argv.shift
211
- case arg
212
- when "-c", "--config-file" then @cmd_config = Pathname.new(argv.shift)
213
- when "-d", "--data-directory" then @config["data_directory"] = argv.shift
214
- when "-D", "--debug" then CUI.debug_mode = true
215
- when "-r", "--repo-url" then @config["repo_url"] = argv.shift
216
- else
217
- $stderr.puts("Invalid option #{arg}.")
218
- $stderr.puts("Try #$0 help.")
219
- exit 1
220
- end
221
- end
222
-
223
- #If nothing is left, the command was left out. Assume 'help'.
224
- argv << "help" if argv.empty?
225
-
226
- #Now parse the subcommand.
227
- command = argv.shift.to_sym
228
- CUI.debug("Found subcommand #{command}.")
229
- sym = :"#{command.capitalize}Command"
230
- if CUICommands.const_defined?(sym)
231
- begin
232
- @command = CUICommands.const_get(sym).new(argv)
233
- rescue CUICommands::InvalidCommandline => e
234
- $stderr.puts(e.message)
235
- $stderr.puts("Try #$0 help.")
236
- exit 1
237
- end
238
- else
239
- $stderr.puts "Unrecognized command #{command}. Try 'help'."
240
- exit 1
241
- end
242
- end
243
-
244
- #Loads the configuration file from the <b>config/</b> directory.
245
- def load_config_file
246
- #First, load the global configuration file.
247
- CUI.debug("Loading global config #{DEFAULT_CONFIG_FILE}.")
248
- hsh = YAML.load_file(DEFAULT_CONFIG_FILE)
249
- CUI.debug(hsh)
250
-
251
- #Second, load the user config which overrides values set in
252
- #the global config.
253
- user_config_file = Pathname.new(ENV["HOME"]) + USER_CONFIG_FILE_NAME
254
- CUI.debug("Loading user-level config #{user_config_file}.")
255
- if user_config_file.file?
256
- hsh.merge!(YAML.load_file(user_config_file.to_s))
257
- else
258
- CUI.debug("Not found.")
259
- end
260
- CUI.debug(hsh)
261
-
262
- #Third, load the config file from the commandline, if any. This overrides
263
- #values set in the user and global config.
264
- if @cmd_config
265
- CUI.debug("Loading -c option config #{@cmd_config}.")
266
- if @cmd_config.file?
267
- hsh.merge!(YAML.load_file(@cmd_config.to_s))
268
- else
269
- $stderr.puts("Configuration file #{@cmd_config} not found.")
270
- end
271
- CUI.debug(hsh)
272
- end
273
-
274
- #Fourth, check for values on the commandline. They override anything
275
- #set previously. They are set directly in @config, so we simply have
276
- #to retain the old values in it.
277
- CUI.debug("Loading commandline options.")
278
- @config.merge!(hsh){|key, old_val, new_val| old_val}
279
- CUI.debug(@config)
280
-
281
- #Fifth, turn all keys into symbols, because that's more Ruby-like.
282
- CUI.debug("Converting to symbols.")
283
- @config = Hash[@config.map{|k, v| [k.to_sym, v]}]
284
- CUI.debug(@config)
285
- end
286
-
287
- end
288
-
289
- end
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: #