smc-get 0.1.0

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.
@@ -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