shoes-package 4.0.0.pre3
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.
- checksums.yaml +7 -0
- data/Rakefile +1 -0
- data/lib/shoes/package/configuration.rb +156 -0
- data/lib/shoes/package/jar.rb +17 -0
- data/lib/shoes/package/jar_app.rb +18 -0
- data/lib/shoes/package/version.rb +5 -0
- data/lib/warbler/traits/shoes.rb +53 -0
- data/shoes-package.gemspec +21 -0
- data/spec/.furoshiki/cache/shoes-app-template.zip +0 -0
- data/spec/configuration_spec.rb +207 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/support/shared_config.rb +15 -0
- data/spec/swt_app_spec.rb +131 -0
- data/spec/swt_jar_spec.rb +66 -0
- data/spec/test_app/app.yaml +17 -0
- data/spec/test_app/bin/hello_world +3 -0
- data/spec/test_app/dir_to_ignore/file_to_ignore.txt +1 -0
- data/spec/test_app/img/boots.icns +0 -0
- data/spec/test_app/img/boots.ico +0 -0
- data/spec/test_app/img/boots_512x512x32.png +0 -0
- data/spec/test_app/sibling.rb +1 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e85a08b46b6fcdaa86a5b1609c14fb2e58c08677
|
4
|
+
data.tar.gz: b655bde5a2d94c020ed7a99a7eed8337cbb36d2f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d425a176814a1adfb819ed1296ad7d5f4308ce13b5928bdbbe933ad99a87c82714f179e37a2b2f5c22abcb43d052ef00d01e153d3f4ccdb54481b99351a4707c
|
7
|
+
data.tar.gz: 31d06640d06a1932e8826727d62720d499b9a1ae391a611818d98607d6aada43dc98b7fd4f4b6b7d903c2753bfeb7472b5a578b47d92a61afb0609e83a2e4ff1
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
require 'furoshiki/configuration'
|
4
|
+
require 'furoshiki/validator'
|
5
|
+
require 'furoshiki/warbler_extensions'
|
6
|
+
require 'warbler/traits/shoes'
|
7
|
+
|
8
|
+
class Shoes
|
9
|
+
module Package
|
10
|
+
# Configuration for Shoes packagers.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# config_file = '/path/to/app.yaml'
|
14
|
+
# config = Shoes::Package::Configuration.load(config_file)
|
15
|
+
#
|
16
|
+
module Configuration
|
17
|
+
extend ::Furoshiki::Util
|
18
|
+
|
19
|
+
JAR_APP_TEMPLATE_URL = 'https://s3.amazonaws.com/net.wasnotrice.shoes/wrappers/shoes-app-template-0.0.1.zip'
|
20
|
+
|
21
|
+
# Convenience method for loading config from a file. Note that you
|
22
|
+
# can pass four kinds of paths to the loader. Given the following
|
23
|
+
# file structure:
|
24
|
+
#
|
25
|
+
# ├── a
|
26
|
+
# │ ├── app.yaml
|
27
|
+
# │ └── shoes-app-a.rb
|
28
|
+
# └── b
|
29
|
+
# └── shoes-app-b.rb
|
30
|
+
#
|
31
|
+
# To package an app that has an `app.yaml`, like `shoes-app-a.rb`,
|
32
|
+
# you can call the loader with any of:
|
33
|
+
#
|
34
|
+
# - a/app.yaml
|
35
|
+
# - a
|
36
|
+
# - a/shoes-app-a.rb
|
37
|
+
#
|
38
|
+
# These will all find and use your configuration in `a/app.yaml`.
|
39
|
+
# To package an app that does not have an `app.yaml`, like
|
40
|
+
# `b/shoes-app-b.rb`, you must call the loader with the path of
|
41
|
+
# the script itself. Note that without an `app.yaml`, you will
|
42
|
+
# only bundle a single file, and your app will simply use the
|
43
|
+
# Shoes app icon.
|
44
|
+
#
|
45
|
+
# @overload load(path, base_config)
|
46
|
+
# @param [String] path location of the app's 'app.yaml'
|
47
|
+
# @param [Hash] base_config default values
|
48
|
+
# @overload load(path)
|
49
|
+
# @param [String] path location of the directory that
|
50
|
+
# contains the app's 'app.yaml'
|
51
|
+
# @param [Hash] base_config default values
|
52
|
+
# @overload load(path)
|
53
|
+
# @param [String] path location of the app
|
54
|
+
# @param [Hash] base_config default values
|
55
|
+
def self.load(path, base_config = {})
|
56
|
+
pathname = Pathname.new(path)
|
57
|
+
app_yaml = Pathname.new('app.yaml')
|
58
|
+
|
59
|
+
if pathname.basename == app_yaml
|
60
|
+
file, dir = pathname, pathname.dirname
|
61
|
+
elsif pathname.directory?
|
62
|
+
file, dir = pathname.join(app_yaml), pathname
|
63
|
+
elsif pathname.file? && pathname.parent.children.include?(pathname.parent.join app_yaml)
|
64
|
+
file, dir = pathname.parent.join(app_yaml), pathname.parent
|
65
|
+
else
|
66
|
+
file, dir = config_for_single_file_app(pathname)
|
67
|
+
end
|
68
|
+
|
69
|
+
config = YAML.load(file.read)
|
70
|
+
config[:working_dir] = dir
|
71
|
+
config[:gems] = merge_gems(base_config, config)
|
72
|
+
create(config)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param [Hash] config user options
|
76
|
+
def self.create(config = {})
|
77
|
+
defaults = {
|
78
|
+
name: 'Shoes App',
|
79
|
+
version: '0.0.0',
|
80
|
+
release: 'Rookie',
|
81
|
+
run: nil,
|
82
|
+
ignore: 'pkg',
|
83
|
+
# TODO: Add actual paths. Keep these keys for compatibility with Shoes 3
|
84
|
+
icons: {
|
85
|
+
#osx: 'path/to/default/App.icns',
|
86
|
+
#gtk: 'path/to/default/app.png',
|
87
|
+
#win32: 'path/to/default/App.ico',
|
88
|
+
},
|
89
|
+
# TODO: Add actual paths. Keep these keys for compatibility with Shoes 3
|
90
|
+
dmg: {
|
91
|
+
#ds_store: 'path/to/default/.DS_Store',
|
92
|
+
#background: 'path/to/default/background.png'
|
93
|
+
},
|
94
|
+
working_dir: Dir.pwd,
|
95
|
+
gems: ['shoes-core'],
|
96
|
+
validator: Validator,
|
97
|
+
warbler_extensions: WarblerExtensions
|
98
|
+
}
|
99
|
+
|
100
|
+
symbolized_config = deep_symbolize_keys(config)
|
101
|
+
symbolized_config[:gems] = merge_gems(defaults, symbolized_config)
|
102
|
+
Furoshiki::Configuration.new defaults.merge(symbolized_config)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
# If it exists, load default options. If not, let the filesystem raise an
|
107
|
+
# error.
|
108
|
+
def self.config_for_single_file_app(pathname)
|
109
|
+
default_options = {
|
110
|
+
run: pathname.basename.to_s,
|
111
|
+
name: pathname.basename(pathname.extname).to_s.gsub(/\W/, '-')
|
112
|
+
}.to_yaml
|
113
|
+
options = pathname.exist? ? default_options : pathname
|
114
|
+
dummy_file = Struct.new(:read)
|
115
|
+
[dummy_file.new(options), pathname.parent]
|
116
|
+
end
|
117
|
+
|
118
|
+
# We want to retain all of the gems, but simply merging the hash will
|
119
|
+
# replace the whole array, so we handle the gems separately. Note that
|
120
|
+
# keys may not have been symbolized yet
|
121
|
+
def self.merge_gems(base, additional)
|
122
|
+
base_gems = base.fetch(:gems) rescue base['gems']
|
123
|
+
additional_gems = additional.fetch(:gems) rescue additional['gems']
|
124
|
+
Array(base_gems).concat(Array(additional_gems)).uniq
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Validator < Furoshiki::Validator
|
129
|
+
def validate
|
130
|
+
unless config.run && config.working_dir.join(config.run).exist?
|
131
|
+
add_missing_file_error(config.run, "Run file")
|
132
|
+
end
|
133
|
+
|
134
|
+
if config.icons[:osx] && !config.working_dir.join(config.icons[:osx]).exist?
|
135
|
+
add_missing_file_error(config.icons[:osx], "OS X icon file")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class WarblerExtensions < Furoshiki::WarblerExtensions
|
141
|
+
def customize(warbler_config)
|
142
|
+
warbler_config.tap do |warbler|
|
143
|
+
warbler.pathmaps.application = ['shoes-app/%p']
|
144
|
+
warbler.extend ShoesWarblerConfig
|
145
|
+
warbler.run = @config.run.split(/\s/).first
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
# Adds Shoes-specific functionality to the Warbler Config
|
151
|
+
module ShoesWarblerConfig
|
152
|
+
attr_accessor :run
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'furoshiki/jar'
|
3
|
+
|
4
|
+
class Shoes
|
5
|
+
module Package
|
6
|
+
class Jar
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@packager, :package, :default_dir, :filename
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
config.gems << 'shoes-swt'
|
13
|
+
@packager = Furoshiki::Jar.new(config)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'furoshiki/jar_app'
|
3
|
+
|
4
|
+
class Shoes
|
5
|
+
module Package
|
6
|
+
class JarApp
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@packager, :package, :default_package_dir, :cache_dir,
|
10
|
+
:remote_template_url, :template_path
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
config.gems << 'shoes-swt'
|
14
|
+
@packager = Furoshiki::JarApp.new(config)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'warbler'
|
2
|
+
require 'warbler/traits'
|
3
|
+
require 'warbler/traits/furoshiki'
|
4
|
+
|
5
|
+
module Warbler
|
6
|
+
module Traits
|
7
|
+
# Hack to control the executable
|
8
|
+
class NoGemspec
|
9
|
+
def update_archive(jar); end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Disable this trait, since we are subclassing it (don't run twice)
|
13
|
+
class Furoshiki
|
14
|
+
def self.detect?; false; end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Shoes < Furoshiki
|
18
|
+
def self.detect?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.requires?(trait)
|
23
|
+
# Actually, it would be better to dump the NoGemspec trait, but since
|
24
|
+
# we can't do that, we can at least make sure that this trait gets
|
25
|
+
# processed later by declaring that it requires NoGemspec.
|
26
|
+
[Traits::Jar, Traits::NoGemspec].include? trait
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_configure
|
30
|
+
config.init_contents << StringIO.new("require 'shoes'\nShoes.configuration.backend = :swt\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_archive(jar)
|
34
|
+
super
|
35
|
+
add_main_rb(jar, apply_pathmaps(config, default_executable, :application))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Uses the `@config.run` if it exists. Otherwise, looks in the
|
39
|
+
# application's `bin` directory for an executable with the same name as
|
40
|
+
# the jar. If this also fails, defaults to the first executable (alphabetically) in the
|
41
|
+
# applications `bin` directory.
|
42
|
+
#
|
43
|
+
# @return [String] filename of the executable to run
|
44
|
+
def default_executable
|
45
|
+
return @config.run if @config.run
|
46
|
+
exes = Dir['bin/*'].sort
|
47
|
+
exe = exes.grep(/#{config.jar_name}/).first || exes.first
|
48
|
+
raise "No executable script found" unless exe
|
49
|
+
exe
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('lib/shoes/package/version')
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "shoes-package"
|
6
|
+
s.version = Shoes::Package::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Team Shoes"]
|
9
|
+
s.email = ["shoes@librelist.com"]
|
10
|
+
s.homepage = "https://github.com/shoes/shoes4"
|
11
|
+
s.summary = %q{Application packaging for Shoes, the best little GUI toolkit for Ruby.}
|
12
|
+
s.description = %q{Application packaging for Shoes, the best little GUI toolkit for Ruby. Shoes makes building for Mac, Windows, and Linux super simple.}
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split($/)
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "shoes-core", Shoes::Package::VERSION
|
20
|
+
s.add_dependency "furoshiki", "~> 0.3.0"
|
21
|
+
end
|
Binary file
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'shoes/package/configuration'
|
3
|
+
|
4
|
+
describe Shoes::Package::Configuration do
|
5
|
+
context "defaults" do
|
6
|
+
subject { Shoes::Package::Configuration.create }
|
7
|
+
|
8
|
+
its(:name) { should eq('Shoes App') }
|
9
|
+
its(:shortname) { should eq('shoesapp') }
|
10
|
+
its(:ignore) { should eq(['pkg']) }
|
11
|
+
its(:gems) { should include('shoes-core') }
|
12
|
+
its(:version) { should eq('0.0.0') }
|
13
|
+
its(:release) { should eq('Rookie') }
|
14
|
+
its(:icons) { should be_an_instance_of(Hash) }
|
15
|
+
its(:dmg) { should be_an_instance_of(Hash) }
|
16
|
+
its(:run) { should be_nil }
|
17
|
+
its(:working_dir) { should eq(Pathname.new(Dir.pwd)) }
|
18
|
+
it { is_expected.not_to be_valid } # no :run
|
19
|
+
|
20
|
+
# TODO: Implement
|
21
|
+
describe "#icons" do
|
22
|
+
it 'osx is nil' do
|
23
|
+
expect(subject.icons[:osx]).to be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'gtk is nil' do
|
27
|
+
expect(subject.icons[:gtk]).to be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'win32 is nil' do
|
31
|
+
expect(subject.icons[:win32]).to be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: Implement
|
36
|
+
describe "#dmg" do
|
37
|
+
it "ds_store is nil" do
|
38
|
+
expect(subject.dmg[:ds_store]).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "background is nil" do
|
42
|
+
expect(subject.dmg[:background]).to be_nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#to_hash" do
|
47
|
+
it "round-trips" do
|
48
|
+
expect(Shoes::Package::Configuration.create(subject.to_hash)).to eq(subject)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with options" do
|
54
|
+
include_context 'config'
|
55
|
+
subject { Shoes::Package::Configuration.load(@config_filename) }
|
56
|
+
|
57
|
+
its(:name) { should eq('Sugar Clouds') }
|
58
|
+
its(:shortname) { should eq('sweet-nebulae') }
|
59
|
+
its(:ignore) { should include('pkg') }
|
60
|
+
its(:run) { should eq('bin/hello_world') }
|
61
|
+
its(:gems) { should include('rspec') }
|
62
|
+
its(:gems) { should include('shoes-core') }
|
63
|
+
its(:version) { should eq('0.0.1') }
|
64
|
+
its(:release) { should eq('Mindfully') }
|
65
|
+
its(:icons) { should be_an_instance_of(Hash) }
|
66
|
+
its(:dmg) { should be_an_instance_of(Hash) }
|
67
|
+
its(:working_dir) { should eq(@config_filename.dirname) }
|
68
|
+
it { is_expected.to be_valid }
|
69
|
+
|
70
|
+
describe "#icons" do
|
71
|
+
it 'has osx' do
|
72
|
+
expect(subject.icons[:osx]).to eq('img/boots.icns')
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'has gtk' do
|
76
|
+
expect(subject.icons[:gtk]).to eq('img/boots_512x512x32.png')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'has win32' do
|
80
|
+
expect(subject.icons[:win32]).to eq('img/boots.ico')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#dmg" do
|
85
|
+
it "has ds_store" do
|
86
|
+
expect(subject.dmg[:ds_store]).to eq('path/to/custom/.DS_Store')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "has background" do
|
90
|
+
expect(subject.dmg[:background]).to eq('path/to/custom/background.png')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "incorporates custom features" do
|
95
|
+
expect(subject.custom).to eq('my custom feature')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "round-trips" do
|
99
|
+
expect(Shoes::Package::Configuration.create(subject.to_hash)).to eq(subject)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "with name, but without explicit shortname" do
|
104
|
+
let(:options) { {:name => "Sugar Clouds"} }
|
105
|
+
subject { Shoes::Package::Configuration.create options }
|
106
|
+
|
107
|
+
its(:name) { should eq("Sugar Clouds") }
|
108
|
+
its(:shortname) { should eq("sugarclouds") }
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when the file to run doens't exist" do
|
112
|
+
let(:options) { {:run => "path/to/non-existent/file"} }
|
113
|
+
subject { Shoes::Package::Configuration.create options }
|
114
|
+
|
115
|
+
it { is_expected.not_to be_valid }
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when osx icon is not specified" do
|
119
|
+
include_context 'config'
|
120
|
+
let(:valid_config) { Shoes::Package::Configuration.load(@config_filename) }
|
121
|
+
let(:options) { valid_config.to_hash.merge(:icons => {}) }
|
122
|
+
subject { Shoes::Package::Configuration.create(options) }
|
123
|
+
|
124
|
+
it "sets osx icon path to nil" do
|
125
|
+
expect(subject.icons[:osx]).to be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
it "is valid" do
|
129
|
+
expect(subject).to be_valid
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when osx icon is specified, but doesn't exist" do
|
134
|
+
let(:options) { ({:icons => {:osx => "path/to/non-existent/file"}}) }
|
135
|
+
subject { Shoes::Package::Configuration.create options }
|
136
|
+
|
137
|
+
it "sets osx icon path" do
|
138
|
+
expect(subject.icons[:osx]).to eq("path/to/non-existent/file")
|
139
|
+
end
|
140
|
+
|
141
|
+
it { is_expected.not_to be_valid }
|
142
|
+
end
|
143
|
+
|
144
|
+
context "auto-loading" do
|
145
|
+
include_context 'config'
|
146
|
+
|
147
|
+
shared_examples "config with path" do
|
148
|
+
it "finds the config" do
|
149
|
+
Dir.chdir File.dirname(__FILE__) do
|
150
|
+
config = Shoes::Package::Configuration.load(path)
|
151
|
+
expect(config.shortname).to eq('sweet-nebulae')
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "with an 'app.yaml'" do
|
157
|
+
let(:path) { @config_filename }
|
158
|
+
it_behaves_like "config with path"
|
159
|
+
end
|
160
|
+
|
161
|
+
context "with a path to a directory containing an 'app.yaml'" do
|
162
|
+
let(:path) { @config_filename.parent }
|
163
|
+
it_behaves_like "config with path"
|
164
|
+
end
|
165
|
+
|
166
|
+
context "with a path to a file that is siblings with an 'app.yaml'" do
|
167
|
+
let(:path) { @config_filename.parent.join('sibling.rb') }
|
168
|
+
it_behaves_like "config with path"
|
169
|
+
end
|
170
|
+
|
171
|
+
context "with a path that exists, but no 'app.yaml'" do
|
172
|
+
let(:path) { @config_filename.parent.join('bin/hello_world') }
|
173
|
+
subject { Shoes::Package::Configuration.load(path) }
|
174
|
+
|
175
|
+
its(:name) { should eq('hello_world') }
|
176
|
+
its(:shortname) { should eq('hello_world') }
|
177
|
+
end
|
178
|
+
|
179
|
+
context "when the file doesn't exist" do
|
180
|
+
it "blows up" do
|
181
|
+
expect { Shoes::Package::Configuration.load('some/bogus/path') }.to raise_error
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "with additional gems" do
|
186
|
+
let(:path) { @config_filename }
|
187
|
+
let(:additional_config) { { gems: gems } }
|
188
|
+
let(:config) { config = Shoes::Package::Configuration.load(path, additional_config) }
|
189
|
+
|
190
|
+
context "one gem" do
|
191
|
+
let(:gems) { 'shoes-swt' }
|
192
|
+
|
193
|
+
it "adds one additional" do
|
194
|
+
expect(config.gems).to include('shoes-swt')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "multiple gems" do
|
199
|
+
let(:gems) { ['shoes-swt', 'shoes-extras'] }
|
200
|
+
|
201
|
+
it "adds multiple additional gems" do
|
202
|
+
expect(config.gems & gems).to eq(gems)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Packaging caches files in $HOME/.furoshiki/cache by default.
|
2
|
+
# For testing, we override $HOME using $FUROSHIKI_HOME
|
3
|
+
FUROSHIKI_SPEC_DIR = Pathname.new(__FILE__).dirname.expand_path.to_s
|
4
|
+
ENV['FUROSHIKI_HOME'] = FUROSHIKI_SPEC_DIR
|
5
|
+
|
6
|
+
SHOES_PACKAGE_SPEC_ROOT= File.expand_path('..', __FILE__)
|
7
|
+
|
8
|
+
$LOAD_PATH << File.expand_path(SHOES_PACKAGE_SPEC_ROOT)
|
9
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
10
|
+
$LOAD_PATH << File.expand_path('../../../shoes-core/lib', __FILE__)
|
11
|
+
|
12
|
+
require 'pathname'
|
13
|
+
require 'rspec/its'
|
14
|
+
|
15
|
+
Dir["#{SHOES_PACKAGE_SPEC_ROOT}/support/**/*.rb"].each {|f| require f}
|
16
|
+
|
17
|
+
module PackageHelpers
|
18
|
+
# need these values from a context block, so let doesn't work
|
19
|
+
def spec_dir
|
20
|
+
Pathname.new(__FILE__).join('..').cleanpath
|
21
|
+
end
|
22
|
+
|
23
|
+
def input_dir
|
24
|
+
spec_dir.join 'support', 'zip'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Guards for running or not running specs. Specs in the guarded block only
|
29
|
+
# run if the guard conditions are met.
|
30
|
+
#
|
31
|
+
module Guard
|
32
|
+
# Runs specs only if platform matches
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# platform_is :windows do
|
36
|
+
# it "does something only on windows" do
|
37
|
+
# # specification
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
def platform_is(platform)
|
41
|
+
yield if self.send "platform_is_#{platform.to_s}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Runs specs only if platform does not match
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# platform_is_not :windows do
|
48
|
+
# it "does something only on posix systems" do
|
49
|
+
# # specification
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
def platform_is_not(platform)
|
53
|
+
yield unless self.send "platform_is_#{platform.to_s}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def platform_is_windows
|
57
|
+
return RbConfig::CONFIG['host_os'] =~ /windows|mswin/i
|
58
|
+
end
|
59
|
+
|
60
|
+
def platform_is_linux
|
61
|
+
return RbConfig::CONFIG['host_os'] =~ /linux/i
|
62
|
+
end
|
63
|
+
|
64
|
+
def platform_is_osx
|
65
|
+
return RbConfig::CONFIG['host_os'] =~ /darwin/i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
include Guard
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
shared_context 'config' do
|
5
|
+
before :all do
|
6
|
+
@config_filename = Pathname.new(__FILE__).join('../../test_app/app.yaml').cleanpath
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
shared_context 'package' do
|
11
|
+
before :all do
|
12
|
+
@app_dir = Pathname.new(__FILE__).join '../../test_app'
|
13
|
+
@output_dir = @app_dir.join 'pkg'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'shoes/package/configuration'
|
3
|
+
require 'shoes/package/jar_app'
|
4
|
+
|
5
|
+
include PackageHelpers
|
6
|
+
|
7
|
+
describe Shoes::Package::JarApp do
|
8
|
+
include_context 'config'
|
9
|
+
include_context 'package'
|
10
|
+
|
11
|
+
let(:config) { Shoes::Package::Configuration.load @config_filename }
|
12
|
+
subject { Shoes::Package::JarApp.new config }
|
13
|
+
|
14
|
+
let(:launcher) { @output_file.join('Contents/MacOS/JavaAppLauncher') }
|
15
|
+
let(:icon) { @output_file.join('Contents/Resources/boots.icns') }
|
16
|
+
let(:jar) { @output_file.join('Contents/Java/sweet-nebulae.jar') }
|
17
|
+
|
18
|
+
# $FUROSHIKI_HOME is set in spec_helper.rb for testing purposes,
|
19
|
+
# but should default to $HOME
|
20
|
+
context "when not setting $FUROSHIKI_HOME" do
|
21
|
+
before do
|
22
|
+
@old_furoshiki_home = ENV['FUROSHIKI_HOME']
|
23
|
+
ENV['FUROSHIKI_HOME'] = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
its(:cache_dir) { should eq(Pathname.new(Dir.home).join('.furoshiki', 'cache')) }
|
28
|
+
|
29
|
+
after do
|
30
|
+
ENV['FUROSHIKI_HOME'] = @old_furoshiki_home
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "default" do
|
35
|
+
let(:cache_dir) { Pathname.new(SHOES_PACKAGE_SPEC_ROOT).join('.furoshiki', 'cache') }
|
36
|
+
its(:cache_dir) { should eq(cache_dir) }
|
37
|
+
|
38
|
+
it "sets package dir to {pwd}/pkg" do
|
39
|
+
Dir.chdir @app_dir do
|
40
|
+
expect(subject.default_package_dir).to eq(@app_dir.join 'pkg')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
its(:template_path) { should eq(cache_dir.join('shoes-app-template.zip')) }
|
45
|
+
its(:remote_template_url) { should eq(Shoes::Package::Configuration::JAR_APP_TEMPLATE_URL) }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when creating a .app" do
|
49
|
+
before :all do
|
50
|
+
@output_dir.rmtree if @output_dir.exist?
|
51
|
+
@output_dir.mkpath
|
52
|
+
app_name = 'Sugar Clouds.app'
|
53
|
+
@output_file = @output_dir.join app_name
|
54
|
+
config = Shoes::Package::Configuration.load @config_filename
|
55
|
+
@subject = Shoes::Package::JarApp.new config
|
56
|
+
@subject.package
|
57
|
+
end
|
58
|
+
|
59
|
+
subject { @subject }
|
60
|
+
|
61
|
+
its(:template_path) { should exist }
|
62
|
+
|
63
|
+
it "creates a .app" do
|
64
|
+
expect(@output_file).to exist
|
65
|
+
end
|
66
|
+
|
67
|
+
it "includes launcher" do
|
68
|
+
expect(launcher).to exist
|
69
|
+
end
|
70
|
+
|
71
|
+
# Windows can't test this
|
72
|
+
platform_is_not :windows do
|
73
|
+
it "makes launcher executable" do
|
74
|
+
expect(launcher).to be_executable
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "deletes generic icon" do
|
79
|
+
expect(icon.parent.join('GenericApp.icns')).not_to exist
|
80
|
+
end
|
81
|
+
|
82
|
+
it "injects icon" do
|
83
|
+
expect(icon).to exist
|
84
|
+
end
|
85
|
+
|
86
|
+
it "injects jar" do
|
87
|
+
expect(jar).to exist
|
88
|
+
end
|
89
|
+
|
90
|
+
it "removes any extraneous jars" do
|
91
|
+
jar_dir_contents = @output_file.join("Contents/Java").children
|
92
|
+
expect(jar_dir_contents.reject {|f| f == jar }).to be_empty
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "Info.plist" do
|
96
|
+
require 'plist'
|
97
|
+
before :all do
|
98
|
+
@plist = Plist.parse_xml(@output_file.join 'Contents/Info.plist')
|
99
|
+
end
|
100
|
+
|
101
|
+
it "sets identifier" do
|
102
|
+
expect(@plist['CFBundleIdentifier']).to eq('com.hackety.shoes.sweet-nebulae')
|
103
|
+
end
|
104
|
+
|
105
|
+
it "sets display name" do
|
106
|
+
expect(@plist['CFBundleDisplayName']).to eq('Sugar Clouds')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "sets bundle name" do
|
110
|
+
expect(@plist['CFBundleName']).to eq('Sugar Clouds')
|
111
|
+
end
|
112
|
+
|
113
|
+
it "sets icon" do
|
114
|
+
expect(@plist['CFBundleIconFile']).to eq('boots.icns')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "sets version" do
|
118
|
+
expect(@plist['CFBundleVersion']).to eq('0.0.1')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "with an invalid configuration" do
|
124
|
+
let(:config) { Shoes::Package::Configuration.create }
|
125
|
+
subject { Shoes::Package::JarApp.new config }
|
126
|
+
|
127
|
+
it "fails to initialize" do
|
128
|
+
expect { subject }.to raise_error(Furoshiki::ConfigurationError)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'shoes/package/configuration'
|
3
|
+
require 'shoes/package/jar'
|
4
|
+
|
5
|
+
include PackageHelpers
|
6
|
+
|
7
|
+
describe Furoshiki::Jar do
|
8
|
+
include_context 'config'
|
9
|
+
include_context 'package'
|
10
|
+
|
11
|
+
context "when creating a .jar" do
|
12
|
+
before :all do
|
13
|
+
@output_dir.rmtree if @output_dir.exist?
|
14
|
+
@output_dir.mkpath
|
15
|
+
config = Shoes::Package::Configuration.load(@config_filename)
|
16
|
+
@subject = Shoes::Package::Jar.new(config)
|
17
|
+
@jar_path = @subject.package
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:jar_name) { 'sweet-nebulae.jar' }
|
21
|
+
let(:output_file) { Pathname.new(@output_dir.join jar_name) }
|
22
|
+
subject { @subject }
|
23
|
+
|
24
|
+
it "creates a .jar" do
|
25
|
+
expect(output_file).to exist
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns path to .jar" do
|
29
|
+
expect(@jar_path).to eq(output_file.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "creates .jar smaller than 60MB" do
|
33
|
+
expect(File.size(output_file)).to be < 60 * 1024 * 1024
|
34
|
+
end
|
35
|
+
|
36
|
+
context "inspecting contents" do
|
37
|
+
let (:jar) { Zip::File.new(output_file) }
|
38
|
+
|
39
|
+
it "includes shoes-core" do
|
40
|
+
shoes_core = jar.glob "gems/shoes-core*"
|
41
|
+
expect(shoes_core.length).to equal(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "includes shoes-swt" do
|
45
|
+
shoes_swt = jar.glob "gems/shoes-swt*"
|
46
|
+
expect(shoes_swt.length).to equal(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "excludes directories recursively" do
|
50
|
+
expect(jar.entries).not_to include("dir_to_ignore/file_to_ignore")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
its(:default_dir) { should eq(@output_dir) }
|
55
|
+
its(:filename) { should eq(jar_name) }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "with an invalid configuration" do
|
59
|
+
let(:config) { Shoes::Package::Configuration.create}
|
60
|
+
subject { Shoes::Package::Jar.new(config) }
|
61
|
+
|
62
|
+
it "fails to initialize" do
|
63
|
+
expect { subject }.to raise_error(Furoshiki::ConfigurationError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: Sugar Clouds
|
2
|
+
shortname: sweet-nebulae
|
3
|
+
ignore:
|
4
|
+
- pkg
|
5
|
+
- dir_to_ignore
|
6
|
+
run: bin/hello_world
|
7
|
+
gems: rspec
|
8
|
+
version: 0.0.1
|
9
|
+
release: Mindfully
|
10
|
+
icons:
|
11
|
+
osx: img/boots.icns
|
12
|
+
gtk: img/boots_512x512x32.png
|
13
|
+
win32: img/boots.ico
|
14
|
+
dmg:
|
15
|
+
ds_store: path/to/custom/.DS_Store
|
16
|
+
background: path/to/custom/background.png
|
17
|
+
custom: my custom feature
|
@@ -0,0 +1 @@
|
|
1
|
+
should be ignored
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
# Just a sibling of 'app.yaml'
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shoes-package
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 4.0.0.pre3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Team Shoes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - '='
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 4.0.0.pre3
|
19
|
+
name: shoes-core
|
20
|
+
prerelease: false
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0.pre3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.3.0
|
33
|
+
name: furoshiki
|
34
|
+
prerelease: false
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.3.0
|
41
|
+
description: Application packaging for Shoes, the best little GUI toolkit for Ruby. Shoes makes building for Mac, Windows, and Linux super simple.
|
42
|
+
email:
|
43
|
+
- shoes@librelist.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Rakefile
|
49
|
+
- lib/shoes/package/configuration.rb
|
50
|
+
- lib/shoes/package/jar.rb
|
51
|
+
- lib/shoes/package/jar_app.rb
|
52
|
+
- lib/shoes/package/version.rb
|
53
|
+
- lib/warbler/traits/shoes.rb
|
54
|
+
- shoes-package.gemspec
|
55
|
+
- spec/.furoshiki/cache/shoes-app-template.zip
|
56
|
+
- spec/configuration_spec.rb
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
- spec/support/shared_config.rb
|
59
|
+
- spec/swt_app_spec.rb
|
60
|
+
- spec/swt_jar_spec.rb
|
61
|
+
- spec/test_app/app.yaml
|
62
|
+
- spec/test_app/bin/hello_world
|
63
|
+
- spec/test_app/dir_to_ignore/file_to_ignore.txt
|
64
|
+
- spec/test_app/img/boots.icns
|
65
|
+
- spec/test_app/img/boots.ico
|
66
|
+
- spec/test_app/img/boots_512x512x32.png
|
67
|
+
- spec/test_app/sibling.rb
|
68
|
+
homepage: https://github.com/shoes/shoes4
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>'
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.3.1
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.4.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Application packaging for Shoes, the best little GUI toolkit for Ruby.
|
92
|
+
test_files:
|
93
|
+
- spec/.furoshiki/cache/shoes-app-template.zip
|
94
|
+
- spec/configuration_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
- spec/support/shared_config.rb
|
97
|
+
- spec/swt_app_spec.rb
|
98
|
+
- spec/swt_jar_spec.rb
|
99
|
+
- spec/test_app/app.yaml
|
100
|
+
- spec/test_app/bin/hello_world
|
101
|
+
- spec/test_app/dir_to_ignore/file_to_ignore.txt
|
102
|
+
- spec/test_app/img/boots.icns
|
103
|
+
- spec/test_app/img/boots.ico
|
104
|
+
- spec/test_app/img/boots_512x512x32.png
|
105
|
+
- spec/test_app/sibling.rb
|
106
|
+
has_rdoc:
|