furoshiki 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +9 -0
  5. data/README.rdoc +33 -28
  6. data/Rakefile +3 -1
  7. data/furoshiki.gemspec +2 -1
  8. data/lib/furoshiki/exceptions.rb +3 -0
  9. data/lib/furoshiki/shoes/configuration.rb +146 -0
  10. data/lib/furoshiki/shoes/swt_app.rb +217 -0
  11. data/lib/furoshiki/shoes/swt_jar.rb +62 -0
  12. data/lib/furoshiki/shoes.rb +15 -0
  13. data/lib/furoshiki/version.rb +1 -1
  14. data/lib/furoshiki/zip/directory.rb +19 -0
  15. data/lib/furoshiki/zip/directory_contents.rb +20 -0
  16. data/lib/furoshiki/zip/recursive.rb +58 -0
  17. data/lib/furoshiki/zip.rb +2 -0
  18. data/lib/furoshiki.rb +3 -1
  19. data/lib/warbler/traits/shoes.rb +51 -0
  20. data/spec/shoes/configuration_spec.rb +156 -0
  21. data/spec/shoes/spec_helper.rb +68 -0
  22. data/spec/shoes/support/shared_config.rb +6 -0
  23. data/spec/shoes/swt_app_spec.rb +119 -0
  24. data/spec/shoes/swt_jar_spec.rb +45 -0
  25. data/spec/shoes/test_app/app.yaml +17 -0
  26. data/spec/shoes/test_app/bin/hello_world +3 -0
  27. data/spec/shoes/test_app/dir_to_ignore/file_to_ignore.txt +1 -0
  28. data/spec/shoes/test_app/img/boots.icns +0 -0
  29. data/spec/shoes/test_app/img/boots.ico +0 -0
  30. data/spec/shoes/test_app/img/boots_512x512x32.png +0 -0
  31. data/spec/shoes/test_app/sibling.rb +1 -0
  32. data/spec/spec_helper.rb +32 -0
  33. data/spec/support/shared_zip.rb +21 -0
  34. data/spec/support/zip/a/a.rb +3 -0
  35. data/spec/support/zip/a/b/b.png +0 -0
  36. data/spec/support/zip/a/b/c/c.rb +3 -0
  37. data/spec/zip/directory_contents_spec.rb +25 -0
  38. data/spec/zip/directory_spec.rb +30 -0
  39. data/vendor/appbundler-1.0.jar +0 -0
  40. metadata +103 -47
  41. data/.rvmrc +0 -1
  42. data/Gemfile.lock +0 -7
  43. data/lib/furoshiki/rake_task.rb +0 -19
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: de386c74229b401a317c15d3c8ddb5e2b0af9ab4
4
+ data.tar.gz: 9c590ec9777006a102a182f6ee5586b5c2ce5c3b
5
+ SHA512:
6
+ metadata.gz: 0e8b5ab686470eb569929192edae4584c911edc8deac8c545d15780034b562e2d9a01fc6e37fa8d6863df07a895cb624cf0bc01ffa491aa58d68ea7cebce973e
7
+ data.tar.gz: 96eda86a72af8d7888eabc82863b748eca4aa673f6a51c3865522b6d6afb8295509f9d795f37d07cdf5202b6f7bc8adef80920aebeb3569c41991ec84d5ee0b3
data/.gitignore CHANGED
@@ -17,5 +17,8 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
+ .rvmrc
21
+ Gemfile.lock
20
22
 
21
23
  ## PROJECT::SPECIFIC
24
+ spec/shoes/.furoshiki
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ -c
2
+ --order random
data/Gemfile CHANGED
@@ -1,3 +1,12 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  gem "rake"
4
+ gem "plist"
5
+ gem "warbler"
6
+
7
+ # Only for packaging Shoes apps (there is no GUI)
8
+ gem "shoes"#, :github => "shoes/shoes4"
9
+
10
+ group :test, :development do
11
+ gem "rspec", "~> 2.12"
12
+ end
data/README.rdoc CHANGED
@@ -1,50 +1,55 @@
1
1
  = furoshiki
2
2
 
3
- 風呂敷 (Furoshiki) is a simple way to package your Ruby applications for distribution on Windows, Linux, and OSX. A 風呂敷 is "a type of traditional Japanese wrapping cloth that were frequently used to transport clothes, gifts, or other goods."
3
+ 風呂敷 (Furoshiki) is a simple way to package your Ruby applications for
4
+ distribution on Windows, Linux, and OSX. A 風呂敷 is "a type of traditional
5
+ Japanese wrapping cloth that were frequently used to transport clothes, gifts,
6
+ or other goods."
4
7
 
5
- In our case, you can distribute your Ruby script with 風呂敷 by adding this to your Rakefile:
8
+ == ALPHA WARNING
6
9
 
7
- require 'furoshiki'
10
+ This is all super alpha, and is subject to change at any time.
8
11
 
9
- Furoshiki::RakeTask.new
12
+ == Packaging Shoes apps
10
13
 
11
- If you'd like to set a different entry point, just configure your task:
14
+ For the moment, we only really package Shoes apps. See ALPHA WARNING above ;)
12
15
 
13
- Furoshiki::RakeTask.new do
14
- entry "SomeClass#some_method"
15
- end
16
+ path = 'path/to/your/shoes-app' # See Furoshiki::Shoes::Configuration
17
+ config = Furoshiki::Shoes::Configuration.load(path)
18
+ packager = Furoshiki::Shoes.new(:swt, :app, config)
19
+ packager.package
16
20
 
17
- and it'll do the right thing.
21
+ Or use the shoes4 command line interface:
18
22
 
19
- == ALPHA WARNING
23
+ bin/shoes -p swt:app path/to/your/shoes-app
20
24
 
21
- This is all super alpha, and is subject to change at any time.
25
+ Eventually, we will be able to package non-Shoes apps too.
22
26
 
23
27
  == Inspiration
24
28
 
25
- The inspiriation for 風呂敷 came from Shoes[http://shoesrb.com]. One of Shoes' best features is packaging up scripts made with Shoes into their own executables. You could take any little script and make a .app, .exe, or .run file. However, this code was really complicated, and made Shoes hard to maintain. It was often the source of people's problems when trying to build Shoes of their own. So, while working on Shoes 4, I decided to see if anyone else was doing anything similar. I came across the .app for libgosu[http://code.google.com/p/gosu/wiki/RubyPackagingOnOsx], which was doing a similar thing. After emailing Julian, I decided that if this was useful for Shoes, and useful for libgosu, it'd probably be useful for other projects, too. So I've set out to figure out the Shoes packager, play with the libgosu versions, and come up with an easy way for you to bundle and distribute GUI programs written in Ruby.
29
+ The inspiriation for 風呂敷 came from Shoes[http://shoesrb.com]. One of Shoes'
30
+ best features is packaging up scripts made with Shoes into their own
31
+ executables. You could take any little script and make a .app, .exe, or .run
32
+ file. However, this code was really complicated, and made Shoes hard to
33
+ maintain. It was often the source of people's problems when trying to build
34
+ Shoes of their own. So, while working on Shoes 4, I decided to see if anyone
35
+ else was doing anything similar. I came across the .app for
36
+ libgosu[http://code.google.com/p/gosu/wiki/RubyPackagingOnOsx], which was doing
37
+ a similar thing. After emailing Julian, I decided that if this was useful for
38
+ Shoes, and useful for libgosu, it'd probably be useful for other projects, too.
39
+ So I've set out to figure out the Shoes packager, play with the libgosu
40
+ versions, and come up with an easy way for you to bundle and distribute GUI
41
+ programs written in Ruby.
26
42
 
27
43
  == Helping out with 風呂敷
28
44
 
29
- 風呂敷uses the following 'stack':
30
-
31
- * Ruby 1.9.2
32
- * Bundler
33
- * git flow
34
-
35
- I personally use rvm, so I also have a .rvmrc set up. It'll switch to the right Ruby when you cd into the directory, so you might see something about needing to create a gemset. If you use rvm, it's probably a good idea:
36
-
37
- $ rvm gemset create furoshiki
38
-
39
- Since you've made a new gemset, you'll probably need to install bundler:
45
+ It's easy! Just clone it down and do the usual bundler thing:
40
46
 
47
+ $ git clone https://github.com/shoes/furoshiki.git
48
+ $ cd furoshiki
41
49
  $ gem install bundler
50
+ $ bundle
42
51
 
43
- and then you should be good to go. Currently, we have no dependancies. I'm sure that'll change. :)
44
-
45
- === A note about git flow
46
-
47
- git-flow[http://github.com/nvie/gitflow] is totally awesome, so I use it to manage my branches. I'm just using the default settings. What this means for you: you can use git-flow, but if you don't, master is just for stable stuff, develop is where all the action is happening. Patches and pull requests should be created off of develop.
52
+ Easy-peasy.
48
53
 
49
54
  === Note on Patches/Pull Requests
50
55
 
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'bundler'
2
+ require 'rake/clean'
2
3
  Bundler::GemHelper.install_tasks
3
4
 
4
5
  @types = %w[rb]
@@ -16,4 +17,5 @@ namespace :whitespace do
16
17
  end
17
18
  end
18
19
  end
19
-
20
+
21
+ CLEAN.include('spec/shoes/.furoshiki', 'spec/shoes/sample_app')
data/furoshiki.gemspec CHANGED
@@ -16,5 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
17
  s.require_paths = ["lib"]
18
18
 
19
- #s.add_dependency "gem", "version"
19
+ s.add_dependency "warbler"
20
+ s.add_dependency "plist"
20
21
  end
@@ -0,0 +1,3 @@
1
+ module Furoshiki
2
+ class DownloadError < StandardError; end
3
+ end
@@ -0,0 +1,146 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+
4
+ module Furoshiki
5
+ module Shoes
6
+ # Configuration for Shoes packagers.
7
+ #
8
+ # @example
9
+ # config_file = '/path/to/app.yaml'
10
+ # config = Shoes::Package::Configuration.load(config_file)
11
+ #
12
+ # If your configuration uses hashes, the keys will always be
13
+ # symbols, even if you have created it with string keys. It's just
14
+ # easier that way.
15
+ #
16
+ # This is a value object. If you need to modify your configuration
17
+ # after initialization, dump it with #to_hash, make your changes,
18
+ # and instantiate a new object.
19
+ class Configuration
20
+ # Convenience method for loading config from a file. Note that you
21
+ # can pass four kinds of paths to the loader. Given the following
22
+ # file structure:
23
+ #
24
+ # ├── a
25
+ # │   ├── app.yaml
26
+ # │   └── shoes-app-a.rb
27
+ # └── b
28
+ # └── shoes-app-b.rb
29
+ #
30
+ # To package an app that has an `app.yaml`, like `shoes-app-a.rb`,
31
+ # you can call the loader with any of:
32
+ #
33
+ # - a/app.yaml
34
+ # - a
35
+ # - a/shoes-app-a.rb
36
+ #
37
+ # These will all find and use your configuration in `a/app.yaml`.
38
+ # To package an app that does not have an `app.yaml`, like
39
+ # `b/shoes-app-b.rb`, you must call the loader with the path of
40
+ # the script itself. Note that without an `app.yaml`, you will
41
+ # only bundle a single file, and your app will simply use the
42
+ # Shoes app icon.
43
+ #
44
+ # @overload load(path)
45
+ # @param [String] path location of the app's 'app.yaml'
46
+ # @overload load(path)
47
+ # @param [String] path location of the directory that
48
+ # contains the app's 'app.yaml'
49
+ # @overload load(path)
50
+ # @param [String] path location of the app
51
+ def self.load(path = 'app.yaml')
52
+ pathname = Pathname.new(path)
53
+ app_yaml = Pathname.new('app.yaml')
54
+
55
+ dummy_file = Struct.new(:read)
56
+
57
+ if pathname.basename == app_yaml
58
+ file, dir = pathname, pathname.dirname
59
+ elsif pathname.directory?
60
+ file, dir = pathname.join(app_yaml), pathname
61
+ elsif pathname.file? && pathname.parent.children.include?(pathname.parent.join app_yaml)
62
+ file, dir = pathname.parent.join(app_yaml), pathname.parent
63
+ else
64
+ # Can't find any 'app.yaml', so assume we just want to wrap
65
+ # this file. If it exists, load default options. If not, let
66
+ # the filesystem raise an error.
67
+ default_options = {
68
+ run: pathname.basename.to_s,
69
+ name: pathname.basename(pathname.extname).to_s.gsub(/\W/, '-')
70
+ }.to_yaml
71
+ options = pathname.exist? ? default_options : pathname
72
+ file = dummy_file.new(options)
73
+ dir = pathname.parent
74
+ end
75
+ new YAML.load(file.read), dir
76
+ end
77
+
78
+ # @param [Hash] config user options
79
+ # @param [String] working_dir directory in which do packaging work
80
+ def initialize(config = {}, working_dir = Dir.pwd)
81
+ defaults = {
82
+ name: 'Shoes App',
83
+ version: '0.0.0',
84
+ release: 'Rookie',
85
+ run: nil,
86
+ ignore: 'pkg',
87
+ icons: {
88
+ #osx: 'path/to/default/App.icns',
89
+ #gtk: 'path/to/default/app.png',
90
+ #win32: 'path/to/default/App.ico',
91
+ },
92
+ dmg: {
93
+ ds_store: 'path/to/default/.DS_Store',
94
+ background: 'path/to/default/background.png'
95
+ }
96
+ }
97
+
98
+ # Overwrite defaults with supplied config
99
+ @config = config.inject(defaults) { |c, (k, v)| set_symbol_key c, k, v }
100
+
101
+ # Ensure that we always have what we need
102
+ @config[:shortname] ||= @config[:name].downcase.gsub(/\W+/, '')
103
+ [:ignore, :gems].each { |k| @config[k] = Array(@config[k]) }
104
+ @config[:gems] << 'shoes'
105
+
106
+ # Define reader for each key
107
+ metaclass = class << self; self; end
108
+ @config.keys.each do |k|
109
+ metaclass.send(:define_method, k) do
110
+ @config[k]
111
+ end
112
+ end
113
+
114
+ @working_dir = Pathname.new(working_dir)
115
+ end
116
+
117
+ # @return [Pathname] the current working directory
118
+ attr_reader :working_dir
119
+
120
+ def to_hash
121
+ @config
122
+ end
123
+
124
+ def ==(other)
125
+ super unless other.class == self.class && other.respond_to?(:to_hash)
126
+ @config == other.to_hash
127
+ end
128
+
129
+ private
130
+ # Ensure symbol keys, even in nested hashes
131
+ #
132
+ # @param [Hash] config the hash to set (key: value) on
133
+ # @param [#to_sym] k the key
134
+ # @param [Object] v the value
135
+ # @return [Hash] an updated hash
136
+ def set_symbol_key(config, k, v)
137
+ if v.kind_of? Hash
138
+ config[k.to_sym] = v.inject({}) { |hash, (k, v)| set_symbol_key(hash, k, v) }
139
+ else
140
+ config[k.to_sym] = v
141
+ end
142
+ config
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,217 @@
1
+ require 'furoshiki/exceptions'
2
+ require 'furoshiki/shoes/configuration'
3
+ require 'furoshiki/zip/directory'
4
+ require 'furoshiki/shoes/swt_jar'
5
+ require 'fileutils'
6
+ require 'plist'
7
+ require 'open-uri'
8
+ require 'net/http'
9
+
10
+ module Furoshiki
11
+ module Shoes
12
+ class SwtApp
13
+ include FileUtils
14
+
15
+ # @param [Shoes::Package::Configuration] config user configuration
16
+ def initialize(config)
17
+ @config = config
18
+ home = ENV['FUROSHIKI_HOME'] || Dir.home
19
+ @cache_dir = Pathname.new(home).join('.furoshiki', 'cache')
20
+ @default_package_dir = working_dir.join('pkg')
21
+ @package_dir = default_package_dir
22
+ @default_template_path = cache_dir.join(template_filename)
23
+ @template_path = default_template_path
24
+ @tmp = @package_dir.join('tmp')
25
+ end
26
+
27
+ # @return [Pathname] default package directory: ./pkg
28
+ attr_reader :default_package_dir
29
+
30
+ # @return [Pathname] package directory
31
+ attr_accessor :package_dir
32
+
33
+ # @return [Pathname] default path to .app template
34
+ attr_reader :default_template_path
35
+
36
+ # @return [Pathname] path to .app template
37
+ attr_accessor :template_path
38
+
39
+ # @return [Pathname] cache directory
40
+ attr_reader :cache_dir
41
+
42
+ attr_reader :config
43
+
44
+ attr_reader :tmp
45
+
46
+ def package
47
+ remove_tmp
48
+ create_tmp
49
+ cache_template
50
+ extract_template
51
+ inject_icon
52
+ inject_config
53
+ jar_path = ensure_jar_exists
54
+ inject_jar jar_path
55
+ move_to_package_dir tmp_app_path
56
+ tweak_permissions
57
+ rescue => e
58
+ raise e
59
+ ensure
60
+ remove_tmp
61
+ end
62
+
63
+ def create_tmp
64
+ tmp.mkpath
65
+ end
66
+
67
+ def remove_tmp
68
+ tmp.rmtree if tmp.exist?
69
+ end
70
+
71
+ def cache_template
72
+ cache_dir.mkpath unless cache_dir.exist?
73
+ download_template unless template_path.size?
74
+ end
75
+
76
+ def template_basename
77
+ 'shoes-app-template'
78
+ end
79
+
80
+ def template_extension
81
+ '.zip'
82
+ end
83
+
84
+ def template_filename
85
+ "#{template_basename}#{template_extension}"
86
+ end
87
+
88
+ def latest_template_version
89
+ '0.0.1'
90
+ end
91
+
92
+ def download_template
93
+ download remote_template_url, template_path
94
+ end
95
+
96
+ def download(remote_url, local_path)
97
+ download_following_redirects remote_url, local_path
98
+ end
99
+
100
+ def download_following_redirects(remote_url, local_path, redirect_limit = 5)
101
+ raise Furoshiki::DownloadError, "Too many redirects" if redirect_limit == 0
102
+
103
+ uri = URI(remote_url)
104
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
105
+ request = Net::HTTP::Get.new(uri.request_uri)
106
+ http.request request do |response|
107
+ case response
108
+ when Net::HTTPSuccess then
109
+ warn "Downloading #{remote_url} to #{local_path}"
110
+ open(local_path, 'wb') do |file|
111
+ response.read_body do |chunk|
112
+ file.write chunk
113
+ end
114
+ end
115
+ when Net::HTTPRedirection then
116
+ location = response['location']
117
+ warn "Redirected to #{location}"
118
+ download_following_redirects(location, local_path, redirect_limit - 1)
119
+ else
120
+ raise Furoshiki::DownloadError, "Couldn't download app template at #{remote_url}. #{response.value}"
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ def downloads_url
127
+ "http://shoesrb.com/downloads"
128
+ end
129
+
130
+ def remote_template_url
131
+ "#{downloads_url}/#{template_basename}-#{latest_template_version}#{template_extension}"
132
+ end
133
+
134
+ def move_to_package_dir(path)
135
+ dest = package_dir.join(path.basename)
136
+ dest.rmtree if dest.exist?
137
+ mv path.to_s, dest
138
+ end
139
+
140
+ def ensure_jar_exists
141
+ jar = SwtJar.new(@config)
142
+ path = tmp.join(jar.filename)
143
+ jar.package(tmp) unless File.exist?(path)
144
+ path
145
+ end
146
+
147
+ # Injects JAR into APP. The JAR should be the only item in the
148
+ # Contents/Java directory. If this directory contains more than one
149
+ # JAR, the "first" one gets run, which may not be what we want.
150
+ #
151
+ # @param [Pathname, String] jar_path the location of the JAR to inject
152
+ def inject_jar(jar_path)
153
+ jar_dir = tmp_app_path.join('Contents/Java')
154
+ jar_dir.rmtree
155
+ jar_dir.mkdir
156
+ cp Pathname.new(jar_path), jar_dir
157
+ end
158
+
159
+ def extract_template
160
+ raise IOError, "Couldn't find app template at #{template_path}." unless template_path.size?
161
+ extracted_app = nil
162
+ ::Zip::ZipFile.new(template_path).each do |entry|
163
+ # Fragile hack
164
+ extracted_app = template_path.join(entry.name) if Pathname.new(entry.name).extname == '.app'
165
+ p = tmp.join(entry.name)
166
+ p.dirname.mkpath
167
+ entry.extract(p)
168
+ end
169
+ mv tmp.join(extracted_app.basename.to_s), tmp_app_path
170
+ end
171
+
172
+ def inject_config
173
+ plist = tmp_app_path.join 'Contents/Info.plist'
174
+ template = Plist.parse_xml(plist)
175
+ template['CFBundleIdentifier'] = "com.hackety.shoes.#{config.shortname}"
176
+ template['CFBundleDisplayName'] = config.name
177
+ template['CFBundleName'] = config.name
178
+ template['CFBundleVersion'] = config.version
179
+ template['CFBundleIconFile'] = Pathname.new(config.icons[:osx]).basename.to_s if config.icons[:osx]
180
+ File.open(plist, 'w') { |f| f.write template.to_plist }
181
+ end
182
+
183
+ def inject_icon
184
+ if config.icons[:osx]
185
+ icon_path = working_dir.join(config.icons[:osx])
186
+ raise IOError, "Couldn't find app icon at #{icon_path}" unless icon_path.exist?
187
+ resources_dir = tmp_app_path.join('Contents/Resources')
188
+ cp icon_path, resources_dir.join(icon_path.basename)
189
+ end
190
+ end
191
+
192
+ def tweak_permissions
193
+ executable_path.chmod 0755
194
+ end
195
+
196
+ def app_name
197
+ "#{config.name}.app"
198
+ end
199
+
200
+ def tmp_app_path
201
+ tmp.join app_name
202
+ end
203
+
204
+ def app_path
205
+ package_dir.join app_name
206
+ end
207
+
208
+ def executable_path
209
+ app_path.join('Contents/MacOS/JavaAppLauncher')
210
+ end
211
+
212
+ def working_dir
213
+ config.working_dir
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,62 @@
1
+ require 'furoshiki/shoes/configuration'
2
+ require 'warbler'
3
+ require 'warbler/traits/shoes'
4
+
5
+ module Furoshiki
6
+ module Shoes
7
+ class SwtJar
8
+ # @param [Furoshiki::Shoes::Configuration] config user configuration
9
+ def initialize(config = nil)
10
+ @shoes_config = config || Furoshiki::Shoes::Configuration.load
11
+ Dir.chdir working_dir do
12
+ @config = Warbler::Config.new do |config|
13
+ config.jar_name = @shoes_config.shortname
14
+ config.pathmaps.application = ['shoes-app/%p']
15
+ specs = @shoes_config.gems.map { |g| Gem::Specification.find_by_name(g) }
16
+ dependencies = specs.map { |s| s.runtime_dependencies }.flatten
17
+ (specs + dependencies).uniq.each { |g| config.gems << g }
18
+ ignore = @shoes_config.ignore.map do |f|
19
+ path = f.to_s
20
+ children = Dir.glob("#{path}/**/*") if File.directory?(path)
21
+ [path, *children]
22
+ end.flatten
23
+ config.excludes.add FileList.new(ignore.flatten).pathmap(config.pathmaps.application.first)
24
+ config.gem_excludes += [/^samples/, /^examples/, /^test/, /^spec/]
25
+ end
26
+ @config.extend ShoesWarblerConfig
27
+ @config.run = @shoes_config.run.split(/\s/).first
28
+ end
29
+ end
30
+
31
+ def package(dir = default_dir)
32
+ Dir.chdir working_dir do
33
+ jar = Warbler::Jar.new
34
+ jar.apply @config
35
+ package_dir = dir.relative_path_from(working_dir)
36
+ package_dir.mkpath
37
+ path = package_dir.join(filename).to_s
38
+ jar.create path
39
+ File.expand_path path
40
+ end
41
+ end
42
+
43
+ def default_dir
44
+ working_dir.join 'pkg'
45
+ end
46
+
47
+ def filename
48
+ "#{@config.jar_name}.#{@config.jar_extension}"
49
+ end
50
+
51
+ def working_dir
52
+ @shoes_config.working_dir
53
+ end
54
+
55
+ private
56
+ # Adds Shoes-specific functionality to the Warbler Config
57
+ module ShoesWarblerConfig
58
+ attr_accessor :run
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,15 @@
1
+ require 'furoshiki/shoes/swt_jar'
2
+ require 'furoshiki/shoes/swt_app'
3
+
4
+ module Furoshiki
5
+ module Shoes
6
+ def self.new(backend, wrapper, config)
7
+ class_name = class_name_for(backend, wrapper)
8
+ self.const_get(class_name).new(config)
9
+ end
10
+
11
+ def self.class_name_for(backend, wrapper)
12
+ [backend, wrapper].map { |name| name.to_s.capitalize }.join
13
+ end
14
+ end
15
+ end
@@ -1,4 +1,4 @@
1
1
  module Furoshiki
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
4
4
 
@@ -0,0 +1,19 @@
1
+ require 'furoshiki/zip/recursive'
2
+
3
+ module Furoshiki
4
+ module Zip
5
+ class Directory
6
+ # @param [#to_s] input_dir the directory to zip
7
+ # @param [#to_s] output_file the location of the output archive
8
+ def initialize(input_dir, output_file)
9
+ @input_dir = Pathname.new(input_dir)
10
+ @zip = Recursive.new(output_file)
11
+ end
12
+
13
+ # Zip the whole input directory, including the root
14
+ def write
15
+ @zip.write [@input_dir.basename], @input_dir.parent, ''
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require 'furoshiki/zip/recursive'
2
+
3
+ module Furoshiki
4
+ module Zip
5
+ class DirectoryContents
6
+ # @param [#to_s] input_dir the directory to zip
7
+ # @param [#to_s] output_file the location of the output archive
8
+ def initialize(input_dir, output_file)
9
+ @input_dir = Pathname.new(input_dir)
10
+ @zip = Recursive.new(output_file)
11
+ end
12
+
13
+ # Zip the contents of the input directory, without the root.
14
+ def write
15
+ entries = @input_dir.children(false)
16
+ @zip.write entries, @input_dir, ''
17
+ end
18
+ end
19
+ end
20
+ end