smc-get 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,148 @@
1
+ = SMC-GET -- level repository manager for Secret Maryo Chronicles
2
+ smc-get - Library and command-line for managing SMC level packages.
3
+
4
+ Author:: Luiji Maryo (mailto:luiji@users.sourceforge.net)
5
+ Contributor:: Marvin Gülker (mailto:sutniuq@gmx.net)
6
+ Copyright:: Copyright (C) 2010-2011 Entertaining Software, Inc.
7
+ Copyright:: Copyright (C) 2011 Marvin Gülker
8
+ License:: GNU General Public License (see COPYING)
9
+
10
+ = Synopsis for use as a command-line tool
11
+ smc-get install mypackage
12
+ smc-get uninstall mypackage
13
+ smc-get help
14
+
15
+ = Synopsis for use as a library
16
+ require 'smc_get'
17
+ #Initialize the library
18
+ SmcGet.setup(
19
+ #From where to download packages
20
+ "https://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/raw/master/",
21
+ #Where to install packages (your SMC installation)
22
+ "/usr/local/share/smc"
23
+ )
24
+ #Get a package
25
+ pkg = SmcGet::Package.new("mypackage")
26
+ #Install it!
27
+ pkg.install
28
+ #...or remove it.
29
+ pkg.uninstall
30
+
31
+ = Description
32
+ smc-get is a library and command-line tool for installing, uninstalling, etc.
33
+ level packages from the Secret Maryo Chronicles Contributed Levels Repository.
34
+
35
+ == Prototype Notice
36
+
37
+ This program is a prototype for functionality that will eventually be merged
38
+ with Secret Maryo Chronicles. It is subject to change and should not be used
39
+ for anything other then testing.
40
+
41
+ = Usage
42
+
43
+ == Using it as a command-line
44
+ smc-get's main purpose is to be used as a command-line utility. It should be
45
+ executed in the syntax:
46
+ smc-get COMMAND [PARAMETERS...]
47
+ Where <tt>COMMAND</tt> is the command and <tt>PARAMATERS...</tt> are the
48
+ parameters for that command.
49
+
50
+ To get help on the various commands, use the help command.
51
+ smc-get help [COMMAND]
52
+ Ommit the +COMMAND+ if you want general usage information.
53
+
54
+ === Configuration file
55
+ smc-get requires a configuration file when used as a commandline tool.
56
+ By default it searches for <tt>smc-get.yml</tt> in the <b>config/</b>
57
+ subdirectory of your smc-get installation, then for <tt>smc-get-conf.yml</tt>
58
+ in the user's home directory and then for the file specified via the <tt>-c</tt>
59
+ commandline switch, if existant. Values set in later evaluated configuration
60
+ files override those in prevously evaluated ones; see
61
+ smc-get help
62
+ for more explanation. If you want to use the CUI from your scripts, you
63
+ can specify a config file via <tt>-c</tt>, but make sure it contains all
64
+ possible options, otherwise those in the global or user-level config files
65
+ may affect your program. See below for an example.
66
+
67
+ Be careful when using +sudo+, because it changes the environment variables.
68
+ smc-get may not find your user-level configuration file when using sudo,
69
+ because it derives the path from the +USER+ environment variable which may
70
+ get set to +root+ when using +sudo+.
71
+
72
+ In the configuration file you can set some general options, look into
73
+ the file which is quite self-explanatory. A sample configuration may
74
+ look like this:
75
+ data_directory: "/usr/local/share/smc"
76
+ repo_url: "https://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/raw/master/"
77
+
78
+ == Using it as a library
79
+ smc-get, although mainly targetted at being a command-line utility, may also be
80
+ used as a library. This is useful when, for instance, creating a GUI front-end.
81
+
82
+ To initialize the library, indicate which repository you are using and
83
+ where you want smc-get to install the packages to (which is usually your
84
+ SMC installation path)
85
+ SmcGet.setup(
86
+ "https://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/raw/master/",
87
+ "/usr/local/share/smc"
88
+ )
89
+
90
+ For interacting with the SMC repository, smc-get exposes an object-oriented API
91
+ centered around the SmcGet::Package class. After you initialized the library
92
+ as explained above, you can query, install and uninstall packages.
93
+ pkg = SmcGet::Package.new("mypackage")
94
+ pkg.install
95
+
96
+ If the function fails, it will raise various exceptions. When creating a GUI,
97
+ you should catch these exceptions and present them as message boxes.
98
+ begin
99
+ Package.new("mypackage").install
100
+ rescue SmcGet::Errors::NoSuchPackageError
101
+ alert 'No such package "mypackage"!'
102
+ end
103
+
104
+ All errors smc-get raises are subclasses of SmcGet::Errors::SmcGetError. That
105
+ means, if you rescue from this exception, you can handle all package-related
106
+ errors at once:
107
+ begin
108
+ Package.new("mypackage").install
109
+ rescue SmcGet::Errors::SmcGetError => e
110
+ alert e.message
111
+ end
112
+
113
+ Furhtermore, these error messages should have a pretty informative message set,
114
+ so unless you want to localize the error messages you can just reach them
115
+ through (the messages, not the whole exceptions, of course).
116
+
117
+ The configuration file only contains settings for the commandline user
118
+ interface, so you don't need it here. If you want to use the CUI from
119
+ your scripts, do
120
+ require "smc_get"
121
+ require "smc_get/cui"
122
+
123
+ cui = SmcGet::CUI.new(ARGV)
124
+ cui.start
125
+ The configuration file from the <b>config/</b> directory will automatically
126
+ be loaded as well as the user-level configuration file; you may specify another
127
+ configuration file by placing the appropriate option in ARGV:
128
+ ARGV.unshift("-c")
129
+ ARGV.unshift("YOURCONFIGFILE.yml")
130
+
131
+ cui = SmcGet::CUI.new(ARGV)
132
+ cui.start
133
+
134
+ = License
135
+
136
+ smc-get is licensed under the GNU General Public License. For more information,
137
+ see COPYING.
138
+
139
+ = Website
140
+
141
+ This software was written by Entertaining Software, Inc. Visit us at
142
+ http://www.entertainingsoftware.com.
143
+
144
+ This software is hosted at GitHub. The project page can be found at
145
+ http://github.com/Luiji/smc-get.
146
+
147
+ This software was written for Secret Maryo Chronicles. Visit them at
148
+ http://www.secretmaryo.org.
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ ################################################################################
4
+ # This file is part of smc-get.
5
+ # Copyright (C) 2010-2011 Entertaining Software, Inc.
6
+ # Copyright (C) 2011 Marvin Gülker
7
+ #
8
+ # This program is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ ################################################################################
21
+
22
+ if RUBY_VERSION =~ /^1.8/ #1.8 doesn't have require_relative
23
+ require File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib", "smc_get", "smc_get")
24
+ require File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib", "smc_get", "cui")
25
+ else
26
+ require_relative "../lib/smc_get/smc_get"
27
+ require_relative "../lib/smc_get/cui"
28
+ end
29
+
30
+ cui = SmcGet::CUI.new(ARGV)
31
+ cui.start
@@ -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://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
+
@@ -0,0 +1,21 @@
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"
@@ -0,0 +1,289 @@
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