smc-get 0.1.0 → 0.2.0.beta1

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.
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: #