firebrew 0.1.3 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +19 -1
- data/firebrew.gemspec +1 -3
- data/lib/firebrew.rb +11 -13
- data/lib/firebrew/amo_api/search.rb +33 -19
- data/lib/firebrew/command_line.rb +200 -68
- data/lib/firebrew/entity.rb +6 -0
- data/lib/firebrew/firefox/basic_extension.rb +0 -2
- data/lib/firebrew/firefox/command.rb +24 -6
- data/lib/firebrew/firefox/extension.rb +3 -3
- data/lib/firebrew/firefox/profile.rb +5 -5
- data/lib/firebrew/runner.rb +4 -3
- data/lib/firebrew/version.rb +1 -1
- data/spec/firebrew/amo_api/search_spec.rb +31 -14
- data/spec/firebrew/command_line_spec.rb +48 -6
- data/spec/firebrew/firefox/command_spec.rb +3 -3
- data/spec/firebrew/firefox/extension_spec.rb +1 -1
- data/spec/firebrew/firefox/profile_spec.rb +2 -2
- data/spec/firebrew/runner_spec.rb +14 -9
- metadata +11 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfb18b86ed200214495e26c35083760ee06adbfa
|
4
|
+
data.tar.gz: 5d06456d51c4d55ac9cc11355a38b6fe0de60599
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b466f350381f97a8ae2e178d3d0a08fa3379ccff2524dfec7f72b14d8945fc33333a787ee839c137b8b6d536f659fff2c452137b1661b3fb4e177085319f0e40
|
7
|
+
data.tar.gz: 54367c7cb733c9a08466ef91a6ef2142783fb7924d0a7e8336a7afea1153e8d5d95915df33b1e7ed0a2fcd87c14b71dae4fe720a1c3c440efe6cb46ae9970e78
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.2.0](https://github.com/mrk21/firebrew/tree/v0.2.0) - 2014-08-31
|
4
|
+
|
5
|
+
* Future [#35](https://github.com/mrk21/firebrew/issues/35): Add the subcommand help
|
6
|
+
* Future [#34](https://github.com/mrk21/firebrew/issues/34): Make the help message in detail
|
7
|
+
* Future [#33](https://github.com/mrk21/firebrew/issues/33): Change to 0 a return value of the help command and version command
|
8
|
+
* Bugfix [#32](https://github.com/mrk21/firebrew/issues/32): The `OptionParser` exceptions has not been handled enough
|
9
|
+
* Bugfix [#31](https://github.com/mrk21/firebrew/issues/31): Has not been throw the `SystemCall` exception when executed the command which is not existed
|
10
|
+
* Future [#30](https://github.com/mrk21/firebrew/issues/30): Add the detailed error messages
|
11
|
+
* Future [#29](https://github.com/mrk21/firebrew/issues/29): Change the command return value
|
12
|
+
* Future [#28](https://github.com/mrk21/firebrew/issues/28): Stop depending on `ActiveSupport`
|
13
|
+
* Future [#27](https://github.com/mrk21/firebrew/issues/27): Stop depending on `ActiveResource`
|
14
|
+
* Future [#26](https://github.com/mrk21/firebrew/issues/26): Stop depending on `ActiveModel`
|
15
|
+
* Future [#25](https://github.com/mrk21/firebrew/issues/25): Add the command of getting the profile information
|
16
|
+
* Future [#16](https://github.com/mrk21/firebrew/issues/16): The handling of when the network errors of the 404 Not Found HTTP error, etc occurs on the Amo::Search
|
17
|
+
|
3
18
|
## [0.1.3](https://github.com/mrk21/firebrew/tree/v0.1.3) - 2014-08-19
|
4
19
|
|
5
20
|
* Bugfix [#24](https://github.com/mrk21/firebrew/issues/24): When the `em:unpack` value of the install manifests was true, is unable to normally installing
|
data/README.md
CHANGED
@@ -71,7 +71,25 @@ Enumerate the installed extensions:
|
|
71
71
|
$ firebrew list
|
72
72
|
```
|
73
73
|
|
74
|
-
|
74
|
+
#### profile
|
75
|
+
|
76
|
+
Show the profile information:
|
77
|
+
|
78
|
+
```bash
|
79
|
+
$ firebrew profile [--attribute=<attr-name>]
|
80
|
+
```
|
81
|
+
|
82
|
+
##### options
|
83
|
+
|
84
|
+
###### --attribute
|
85
|
+
|
86
|
+
The name of the attribute which want to display:
|
87
|
+
|
88
|
+
```bash
|
89
|
+
-a <attr-name>, --attribute=<attr-name>
|
90
|
+
```
|
91
|
+
|
92
|
+
### global options
|
75
93
|
|
76
94
|
#### --base-dir
|
77
95
|
|
data/firebrew.gemspec
CHANGED
@@ -22,9 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec", "~> 3.0"
|
24
24
|
|
25
|
-
spec.add_dependency "activesupport", "~> 4.1"
|
26
|
-
spec.add_dependency "activeresource", "~> 4.0"
|
27
|
-
spec.add_dependency "activemodel", "~> 4.1"
|
28
25
|
spec.add_dependency "inifile", "~> 2.0"
|
29
26
|
spec.add_dependency "rubyzip", "~> 1.1"
|
27
|
+
spec.add_dependency "faraday", "~> 0.9"
|
30
28
|
end
|
data/lib/firebrew.rb
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
require "firebrew/version"
|
2
|
+
require 'rake/pathmap'
|
2
3
|
|
3
4
|
module Firebrew
|
4
5
|
class Error < StandardError; def status; 1 end end
|
5
|
-
class ProfilesFileNotFoundError < Error;
|
6
|
-
class ProfileNotFoundError < Error;
|
7
|
-
class ExtensionNotFoundError < Error;
|
8
|
-
class FirefoxCommandError < Error;
|
9
|
-
class CommandLineError < Error;
|
10
|
-
class
|
6
|
+
class ProfilesFileNotFoundError < Error; end
|
7
|
+
class ProfileNotFoundError < Error; end
|
8
|
+
class ExtensionNotFoundError < Error; end
|
9
|
+
class FirefoxCommandError < Error; end
|
10
|
+
class CommandLineError < Error; end
|
11
|
+
class NetworkError < Error; end
|
12
|
+
class OperationAlreadyCompletedError < Error; def status; 2 end end
|
11
13
|
end
|
12
14
|
|
13
|
-
|
14
|
-
require '
|
15
|
-
|
16
|
-
require 'firebrew/firefox/extension'
|
17
|
-
require 'firebrew/firefox/command'
|
18
|
-
require 'firebrew/runner'
|
19
|
-
require 'firebrew/command_line'
|
15
|
+
Dir[__FILE__.pathmap('%X/*.rb')].each do |rb|
|
16
|
+
require rb.pathmap('%-1d/%n')
|
17
|
+
end
|
@@ -1,20 +1,17 @@
|
|
1
|
-
require 'active_resource'
|
2
1
|
require 'uri'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'faraday'
|
3
4
|
require 'firebrew/firefox/basic_extension'
|
4
5
|
|
5
6
|
module Firebrew::AmoApi
|
6
|
-
class Search <
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def decode(xml)
|
11
|
-
results = super(xml)['addon'] || []
|
12
|
-
results.instance_of?(Array) ? results : [results]
|
13
|
-
end
|
7
|
+
class Search < Firebrew::Firefox::BasicExtension
|
8
|
+
def self.connection=(val)
|
9
|
+
@connection = val
|
14
10
|
end
|
15
11
|
|
16
|
-
self.
|
17
|
-
|
12
|
+
def self.connection
|
13
|
+
@connection ||= Faraday.new(url: 'https://services.addons.mozilla.org')
|
14
|
+
end
|
18
15
|
|
19
16
|
def self.path(params={})
|
20
17
|
path_source = '/ja/firefox/api/%{api_version}/search/%{term}/%{type}/%{max}/%{os}/%{version}'
|
@@ -29,21 +26,38 @@ module Firebrew::AmoApi
|
|
29
26
|
end
|
30
27
|
|
31
28
|
def self.fetch(params={})
|
32
|
-
self.
|
29
|
+
response = self.connection.get(self.path params)
|
30
|
+
raise Firebrew::NetworkError, "Invalid HTTP status: #{response.status}" unless response.status == 200
|
31
|
+
dom = REXML::Document.new response.body
|
32
|
+
addons = REXML::XPath.match(dom, '/searchresults/addon')
|
33
|
+
addons.map{|v| Search.new v}
|
34
|
+
|
35
|
+
rescue Faraday::Error => e
|
36
|
+
m = e.message
|
37
|
+
m[0] = m[0].upcase
|
38
|
+
raise Firebrew::NetworkError, m
|
33
39
|
end
|
34
40
|
|
35
41
|
def self.fetch!(params={})
|
36
42
|
results = self.fetch(params)
|
37
|
-
raise Firebrew::ExtensionNotFoundError if results.empty?
|
43
|
+
raise Firebrew::ExtensionNotFoundError, %[Extension not found: like "#{params[:term]}"] if results.empty?
|
38
44
|
results
|
39
45
|
end
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
attr_reader :data
|
48
|
+
|
49
|
+
def initialize(data)
|
50
|
+
@data = data
|
51
|
+
|
52
|
+
val = lambda do |name|
|
53
|
+
REXML::XPath.match(self.data, "#{name}/text()").first.value.strip
|
54
|
+
end
|
55
|
+
|
56
|
+
super(
|
57
|
+
name: val[:name],
|
58
|
+
guid: val[:guid],
|
59
|
+
uri: val[:install],
|
60
|
+
version: val[:version]
|
47
61
|
)
|
48
62
|
end
|
49
63
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'erb'
|
3
|
+
require 'firebrew/runner'
|
2
4
|
|
3
5
|
module Firebrew
|
4
6
|
class CommandLine
|
5
|
-
|
7
|
+
attr_reader :arguments
|
6
8
|
|
7
9
|
def self.execute
|
8
10
|
begin
|
@@ -15,83 +17,137 @@ module Firebrew
|
|
15
17
|
$stderr.puts e.message
|
16
18
|
exit e.status
|
17
19
|
rescue SystemExit => e
|
18
|
-
exit
|
20
|
+
exit 0
|
19
21
|
rescue Exception => e
|
20
22
|
$stderr.puts e.inspect
|
21
23
|
$stderr.puts e.backtrace
|
22
|
-
exit
|
24
|
+
exit 1
|
23
25
|
else
|
24
26
|
exit 0
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
30
|
def initialize(args=[])
|
29
|
-
|
30
|
-
opt.version = Firebrew::VERSION
|
31
|
-
opt.banner = <<-USAGE.split(/\n/).map{|v| v.gsub(/^( ){4}/,'')}.join("\n")
|
32
|
-
Usage: firebrew [--help] [--version]
|
33
|
-
[--base-dir=<path>] [--profile=<name>] [--firefox=<path>]
|
34
|
-
<command> [<args>]
|
35
|
-
|
36
|
-
commands:
|
37
|
-
install:
|
38
|
-
firebrew install <extension-name>
|
39
|
-
|
40
|
-
uninstall:
|
41
|
-
firebrew uninstall <extension-name>
|
42
|
-
|
43
|
-
info:
|
44
|
-
firebrew info <extension-name>
|
45
|
-
|
46
|
-
search:
|
47
|
-
firebrew search <term>
|
48
|
-
|
49
|
-
list:
|
50
|
-
firebrew list
|
51
|
-
|
52
|
-
options:
|
53
|
-
USAGE
|
54
|
-
|
55
|
-
self.arguments = {
|
31
|
+
@arguments = {
|
56
32
|
command: nil,
|
57
33
|
params: {},
|
58
34
|
config: {}
|
59
35
|
}
|
36
|
+
global_parser = self.option_parser do |parser|
|
37
|
+
parser.banner = self.desc(<<-DESC)
|
38
|
+
Usage: firebrew [--help] [--version]
|
39
|
+
[--base-dir=<path>] [--profile=<name>] [--firefox=<path>]
|
40
|
+
<command> [<args>]
|
41
|
+
DESC
|
42
|
+
|
43
|
+
parser.separator ''
|
44
|
+
parser.separator 'commands:'
|
45
|
+
begin
|
46
|
+
pos = 11
|
47
|
+
|
48
|
+
self.summary(parser, :install, <<-DESC, pos)
|
49
|
+
Install the Firefox extension
|
50
|
+
DESC
|
51
|
+
|
52
|
+
self.summary(parser, :uninstall, <<-DESC, pos)
|
53
|
+
Uninstall the Firefox extension
|
54
|
+
DESC
|
55
|
+
|
56
|
+
self.summary(parser, :info, <<-DESC, pos)
|
57
|
+
Show detail information of the Firefox extension
|
58
|
+
DESC
|
59
|
+
|
60
|
+
self.summary(parser, :search, <<-DESC, pos)
|
61
|
+
Search Firefox extensions
|
62
|
+
DESC
|
63
|
+
|
64
|
+
self.summary(parser, :list, <<-DESC, pos)
|
65
|
+
Enumerate the installed Firefox extensions
|
66
|
+
DESC
|
67
|
+
|
68
|
+
self.summary(parser, :profile, <<-DESC, pos)
|
69
|
+
Show the profile information
|
70
|
+
DESC
|
71
|
+
end
|
72
|
+
end
|
60
73
|
|
61
|
-
|
62
|
-
|
74
|
+
global_parser.order!(args)
|
75
|
+
command = args.shift.to_s.intern
|
63
76
|
|
64
|
-
case
|
65
|
-
when
|
66
|
-
self.
|
67
|
-
|
68
|
-
|
77
|
+
case command
|
78
|
+
when :install, :uninstall, :info then
|
79
|
+
subcommand_parser = self.option_parser do |parser|
|
80
|
+
parser.banner = self.desc(<<-DESC)
|
81
|
+
Usage: firebrew [--help] [--version]
|
82
|
+
[--base-dir=<path>] [--profile=<name>] [--firefox=<path>]
|
83
|
+
#{command} <extension-name>
|
84
|
+
DESC
|
85
|
+
end
|
69
86
|
|
70
|
-
|
71
|
-
self.
|
72
|
-
self.arguments[:command] = :uninstall
|
87
|
+
subcommand_parser.permute!(args)
|
88
|
+
self.arguments[:command] = command
|
73
89
|
self.arguments[:params][:term] = args[0]
|
74
90
|
|
75
|
-
when
|
76
|
-
self.
|
77
|
-
|
78
|
-
|
91
|
+
when :search then
|
92
|
+
subcommand_parser = self.option_parser do |parser|
|
93
|
+
parser.banner = self.desc(<<-DESC)
|
94
|
+
Usage: firebrew [--help] [--version]
|
95
|
+
[--base-dir=<path>] [--profile=<name>] [--firefox=<path>]
|
96
|
+
#{command} <term>
|
97
|
+
DESC
|
98
|
+
end
|
79
99
|
|
80
|
-
|
81
|
-
self.
|
82
|
-
self.arguments[:command] = :search
|
100
|
+
subcommand_parser.permute!(args)
|
101
|
+
self.arguments[:command] = command
|
83
102
|
self.arguments[:params][:term] = args[0]
|
84
103
|
|
85
|
-
when
|
86
|
-
self.
|
87
|
-
|
104
|
+
when :list then
|
105
|
+
subcommand_parser = self.option_parser do |parser|
|
106
|
+
parser.banner = self.desc(<<-DESC)
|
107
|
+
Usage: firebrew [--help] [--version]
|
108
|
+
[--base-dir=<path>] [--profile=<name>] [--firefox=<path>]
|
109
|
+
#{command}
|
110
|
+
DESC
|
111
|
+
end
|
112
|
+
|
113
|
+
subcommand_parser.permute!(args)
|
114
|
+
self.arguments[:command] = command
|
115
|
+
|
116
|
+
when :profile then
|
117
|
+
subcommand_parser = self.option_parser do |parser|
|
118
|
+
parser.summary_width = 30
|
119
|
+
parser.banner = self.desc(<<-DESC)
|
120
|
+
Usage: firebrew [--help] [--version]
|
121
|
+
[--base-dir=<path>] [--profile=<name>] [--firefox=<path>]
|
122
|
+
#{command} [--attribute=<attr-name>]
|
123
|
+
DESC
|
124
|
+
|
125
|
+
parser.separator ''
|
126
|
+
parser.separator 'options:'
|
127
|
+
begin
|
128
|
+
chooses = %r[#{Firebrew::Firefox::Profile.attributes.join('|')}]
|
129
|
+
parser.on('-a <attr-name>','--attribute=<attr-name>', chooses, self.desc(<<-DESC)) do |v|
|
130
|
+
The name of the attribute which want to display
|
131
|
+
DESC
|
132
|
+
self.arguments[:params][:attribute] = v
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
subcommand_parser.permute!(args)
|
138
|
+
self.arguments[:command] = command
|
139
|
+
|
140
|
+
when :'' then
|
141
|
+
global_parser.permute(['--help'])
|
88
142
|
|
89
|
-
when nil then
|
90
|
-
self.class.opt_operation(opt, :permute, ['--help'])
|
91
|
-
|
92
143
|
else
|
93
|
-
raise Firebrew::CommandLineError
|
144
|
+
raise Firebrew::CommandLineError, "Invalid command: #{command}"
|
94
145
|
end
|
146
|
+
|
147
|
+
rescue OptionParser::ParseError => e
|
148
|
+
m = e.message
|
149
|
+
m[0] = m[0].upcase
|
150
|
+
raise Firebrew::CommandLineError, m
|
95
151
|
end
|
96
152
|
|
97
153
|
def execute
|
@@ -106,7 +162,23 @@ module Firebrew
|
|
106
162
|
|
107
163
|
when :info then
|
108
164
|
result = runner.send(self.arguments[:command], self.arguments[:params])
|
109
|
-
puts result.
|
165
|
+
puts result.data
|
166
|
+
|
167
|
+
when :profile then
|
168
|
+
r = runner.profile
|
169
|
+
attr = self.arguments[:params][:attribute]
|
170
|
+
if attr.nil? then
|
171
|
+
attrs = r.class.attributes
|
172
|
+
puts ERB.new(self.desc(<<-XML),nil,'-').result(binding)
|
173
|
+
<profile>
|
174
|
+
<% attrs.each do |attr| -%>
|
175
|
+
<<%= attr %>><%= r.send(attr) %></<%= attr %>>
|
176
|
+
<% end -%>
|
177
|
+
</profile>
|
178
|
+
XML
|
179
|
+
else
|
180
|
+
puts r.send(attr)
|
181
|
+
end
|
110
182
|
|
111
183
|
else
|
112
184
|
runner.send(self.arguments[:command], self.arguments[:params])
|
@@ -115,26 +187,86 @@ module Firebrew
|
|
115
187
|
|
116
188
|
protected
|
117
189
|
|
118
|
-
def
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
190
|
+
def desc(str)
|
191
|
+
lines = str.split(/\n/)
|
192
|
+
indent = lines.map{|v| v.match(/^ +/).to_a[0].to_s.length}.min
|
193
|
+
lines.map{|v| v[indent..-1].rstrip}.join("\n")
|
194
|
+
end
|
195
|
+
|
196
|
+
def summary(parser, name, description, pos)
|
197
|
+
result = ' '*100
|
198
|
+
result[0] = name.to_s
|
199
|
+
result[pos+1] = self.desc(description)
|
200
|
+
result = parser.summary_indent + result.rstrip
|
201
|
+
parser.separator result
|
124
202
|
end
|
125
203
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
204
|
+
def option_parser
|
205
|
+
parser = OptionParser.new
|
206
|
+
parser.version = Firebrew::VERSION
|
207
|
+
parser.summary_indent = ' '*3
|
208
|
+
parser.summary_width = 25
|
209
|
+
|
210
|
+
yield parser
|
211
|
+
|
212
|
+
parser.separator ''
|
213
|
+
parser.separator 'global options:'
|
214
|
+
begin
|
215
|
+
parser.on('-d <path>','--base-dir=<path>', String, self.desc(<<-DESC)) do |v|
|
216
|
+
Firefox profiles.ini directory
|
217
|
+
DESC
|
218
|
+
self.arguments[:config][:base_dir] = v
|
219
|
+
end
|
220
|
+
|
221
|
+
parser.on('-p <name>','--profile=<name>', String, self.desc(<<-DESC)) do |v|
|
222
|
+
Firefox profile name
|
223
|
+
DESC
|
224
|
+
self.arguments[:config][:profile] = v
|
225
|
+
end
|
226
|
+
|
227
|
+
parser.on('-f <path>','--firefox=<path>', String, self.desc(<<-DESC)) do |v|
|
228
|
+
Firefox command path
|
229
|
+
DESC
|
230
|
+
self.arguments[:config][:firefox] = v
|
231
|
+
end
|
129
232
|
end
|
130
233
|
|
131
|
-
|
132
|
-
|
234
|
+
parser.separator ''
|
235
|
+
begin
|
236
|
+
parser.on('-h', '--help', self.desc(<<-DESC)) do
|
237
|
+
Show this message
|
238
|
+
DESC
|
239
|
+
puts parser.help
|
240
|
+
exit
|
241
|
+
end
|
242
|
+
|
243
|
+
parser.on('-v', '--version', self.desc(<<-DESC)) do
|
244
|
+
Show version
|
245
|
+
DESC
|
246
|
+
puts parser.ver
|
247
|
+
exit
|
248
|
+
end
|
133
249
|
end
|
134
250
|
|
135
|
-
|
136
|
-
|
251
|
+
parser.separator ''
|
252
|
+
parser.separator 'return value:'
|
253
|
+
begin
|
254
|
+
pos = 3
|
255
|
+
|
256
|
+
self.summary(parser, '0', <<-DESC, pos)
|
257
|
+
Success
|
258
|
+
DESC
|
259
|
+
|
260
|
+
self.summary(parser, '1', <<-DESC, pos)
|
261
|
+
Error
|
262
|
+
DESC
|
263
|
+
|
264
|
+
self.summary(parser, '2', <<-DESC, pos)
|
265
|
+
No operation
|
266
|
+
DESC
|
137
267
|
end
|
268
|
+
|
269
|
+
return parser
|
138
270
|
end
|
139
271
|
end
|
140
272
|
end
|
data/lib/firebrew/entity.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
1
3
|
module Firebrew::Firefox
|
2
4
|
class Command
|
3
5
|
class Executer
|
@@ -10,18 +12,34 @@ module Firebrew::Firefox
|
|
10
12
|
@config = config
|
11
13
|
@executer = executer
|
12
14
|
begin
|
13
|
-
result =
|
14
|
-
|
15
|
-
|
15
|
+
result = if ENV['OS'].nil? then
|
16
|
+
@executer.exec('%{firefox} --version' % self.escaped_config)
|
17
|
+
else
|
18
|
+
@executer.exec('"%{firefox}" --version' % @config)
|
19
|
+
end
|
20
|
+
raise Firebrew::FirefoxCommandError, 'Command is not Firefox: %{firefox}' % @config unless result[0] =~ /Mozilla Firefox/
|
21
|
+
raise Firebrew::FirefoxCommandError, 'Command is not Firefox: %{firefox}' % @config unless result[1] == 0
|
16
22
|
rescue SystemCallError
|
17
|
-
raise Firebrew::FirefoxCommandError
|
23
|
+
raise Firebrew::FirefoxCommandError, 'Firefox command not found: %{firefox}' % @config
|
18
24
|
end
|
19
25
|
end
|
20
26
|
|
21
27
|
def version
|
22
|
-
return @version
|
23
|
-
result =
|
28
|
+
return @version unless @version.nil?
|
29
|
+
result = if ENV['OS'].nil? then
|
30
|
+
@executer.exec('%{firefox} --version' % self.escaped_config)[0]
|
31
|
+
else
|
32
|
+
@executer.exec('"%{firefox}" --version' % @config)[0]
|
33
|
+
end
|
24
34
|
@version = result.match(/[0-9.]+/)[0]
|
25
35
|
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def escaped_config
|
40
|
+
result = @config.clone
|
41
|
+
result[:firefox] = Shellwords.escape result[:firefox]
|
42
|
+
result
|
43
|
+
end
|
26
44
|
end
|
27
45
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'open-uri'
|
3
3
|
require 'json'
|
4
|
-
require 'zip'
|
5
4
|
require 'rexml/document'
|
5
|
+
require 'zip'
|
6
6
|
require 'firebrew/firefox/basic_extension'
|
7
7
|
|
8
8
|
module Firebrew::Firefox
|
@@ -35,7 +35,7 @@ module Firebrew::Firefox
|
|
35
35
|
|
36
36
|
def find!(name)
|
37
37
|
result = self.find(name)
|
38
|
-
raise Firebrew::ExtensionNotFoundError if result.nil?
|
38
|
+
raise Firebrew::ExtensionNotFoundError, "Extension not found: #{name}" if result.nil?
|
39
39
|
result
|
40
40
|
end
|
41
41
|
|
@@ -91,7 +91,7 @@ module Firebrew::Firefox
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def fetch
|
94
|
-
return @data
|
94
|
+
return @data unless @data.nil?
|
95
95
|
@data = JSON.load(File.read(self.data_path))
|
96
96
|
end
|
97
97
|
|
@@ -1,16 +1,16 @@
|
|
1
|
-
require 'active_model'
|
2
1
|
require 'inifile'
|
2
|
+
require 'firebrew/entity'
|
3
3
|
require 'firebrew/firefox/extension'
|
4
4
|
|
5
5
|
module Firebrew::Firefox
|
6
6
|
class Profile
|
7
|
-
include
|
7
|
+
include Firebrew::Entity
|
8
8
|
|
9
9
|
class Manager
|
10
10
|
def initialize(params={})
|
11
11
|
@base_dir = params[:base_dir]
|
12
12
|
@data_file = params[:data_file] || 'profiles.ini'
|
13
|
-
raise Firebrew::ProfilesFileNotFoundError unless File.exists? self.data_path
|
13
|
+
raise Firebrew::ProfilesFileNotFoundError, %["profiles.ini" not found: #{self.data_path}] unless File.exists? self.data_path
|
14
14
|
end
|
15
15
|
|
16
16
|
def all
|
@@ -31,7 +31,7 @@ module Firebrew::Firefox
|
|
31
31
|
|
32
32
|
def find!(name)
|
33
33
|
result = self.find(name)
|
34
|
-
raise Firebrew::ProfileNotFoundError if result.nil?
|
34
|
+
raise Firebrew::ProfileNotFoundError, "Profile not found: #{name}" if result.nil?
|
35
35
|
result
|
36
36
|
end
|
37
37
|
|
@@ -47,7 +47,7 @@ module Firebrew::Firefox
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
entity_attr :name, :path, :is_default
|
51
51
|
|
52
52
|
def extensions
|
53
53
|
Extension::Manager.new(profile: self)
|
data/lib/firebrew/runner.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'firebrew/amo_api/search'
|
2
2
|
require 'firebrew/firefox/profile'
|
3
3
|
require 'firebrew/firefox/extension'
|
4
|
+
require 'firebrew/firefox/command'
|
4
5
|
|
5
6
|
module Firebrew
|
6
7
|
class Runner
|
@@ -53,16 +54,16 @@ module Firebrew
|
|
53
54
|
|
54
55
|
def install(params={})
|
55
56
|
extension = self.profile.extensions.find(params[:term])
|
56
|
-
raise Firebrew::OperationAlreadyCompletedError
|
57
|
+
raise Firebrew::OperationAlreadyCompletedError, "Already installed: #{params[:term]}" unless extension.nil?
|
57
58
|
result = self.fetch_api(term: params[:term], max: 1).first
|
58
|
-
self.profile.extensions.install(result
|
59
|
+
self.profile.extensions.install(result)
|
59
60
|
end
|
60
61
|
|
61
62
|
def uninstall(params={})
|
62
63
|
begin
|
63
64
|
self.profile.extensions.find!(params[:term]).delete
|
64
65
|
rescue Firebrew::ExtensionNotFoundError
|
65
|
-
raise Firebrew::OperationAlreadyCompletedError
|
66
|
+
raise Firebrew::OperationAlreadyCompletedError, "Already uninstalled: #{params[:term]}"
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
data/lib/firebrew/version.rb
CHANGED
@@ -3,34 +3,35 @@ require 'spec_helper'
|
|
3
3
|
module Firebrew::AmoApi
|
4
4
|
describe Firebrew::AmoApi::Search do
|
5
5
|
before do
|
6
|
-
|
7
|
-
|
8
|
-
ActiveResource::HttpMock.respond_to do |mock|
|
9
|
-
mock.get Search.path(self.params), {}, response
|
10
|
-
end
|
6
|
+
Search.connection = double(:connection)
|
7
|
+
allow(Search.connection).to self.stub
|
11
8
|
end
|
12
9
|
|
13
10
|
after do
|
14
|
-
|
11
|
+
Search.connection = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:stub) do
|
15
|
+
response = File.read("spec/fixtures/amo_api/search/#{self.fixture}")
|
16
|
+
receive(:get).and_return(OpenStruct.new body: response, status: 200)
|
15
17
|
end
|
16
18
|
|
17
|
-
let(:params){{term: 'hoge'}}
|
18
19
|
let(:fixture){'base.xml'}
|
19
20
|
|
20
21
|
describe '::fetch(params)' do
|
21
|
-
subject{Search.fetch
|
22
|
+
subject{Search.fetch term: ''}
|
22
23
|
|
23
24
|
it { is_expected.to be_instance_of(Array) }
|
24
25
|
it { expect(subject.size).to eq(3) }
|
25
26
|
|
26
27
|
it 'should construct objects' do
|
27
|
-
expect(subject[0].
|
28
|
+
expect(subject[0].guid).to eq('hoge-ja@example.org')
|
28
29
|
expect(subject[0].name).to eq('hoge')
|
29
30
|
|
30
|
-
expect(subject[1].
|
31
|
+
expect(subject[1].guid).to eq('hoge-fuga-ja@example.org')
|
31
32
|
expect(subject[1].name).to eq('hoge_fuga')
|
32
33
|
|
33
|
-
expect(subject[2].
|
34
|
+
expect(subject[2].guid).to eq('hoge-hoge-ja@example.org')
|
34
35
|
expect(subject[2].name).to eq('hoge_hoge')
|
35
36
|
end
|
36
37
|
|
@@ -47,14 +48,30 @@ module Firebrew::AmoApi
|
|
47
48
|
it { expect(subject.size).to eq(1) }
|
48
49
|
|
49
50
|
it 'should construct objects' do
|
50
|
-
expect(subject[0].
|
51
|
+
expect(subject[0].guid).to eq('hoge-ja@example.org')
|
51
52
|
expect(subject[0].name).to eq('hoge')
|
52
53
|
end
|
53
54
|
end
|
55
|
+
|
56
|
+
context 'when occurred a network error' do
|
57
|
+
let(:stub) do
|
58
|
+
receive(:get).and_raise(Faraday::ConnectionFailed, 'message')
|
59
|
+
end
|
60
|
+
|
61
|
+
it { expect{subject}.to raise_error(Firebrew::NetworkError, 'Message') }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when a response HTTP status was not 200' do
|
65
|
+
let(:stub) do
|
66
|
+
receive(:get).and_return(OpenStruct.new body: '', status: 404)
|
67
|
+
end
|
68
|
+
|
69
|
+
it { expect{subject}.to raise_error(Firebrew::NetworkError, 'Invalid HTTP status: 404') }
|
70
|
+
end
|
54
71
|
end
|
55
72
|
|
56
73
|
describe '::fetch!(params)' do
|
57
|
-
subject {Search.fetch!
|
74
|
+
subject {Search.fetch! term: 'aa'}
|
58
75
|
|
59
76
|
it { is_expected.to be_instance_of(Array) }
|
60
77
|
it { expect(subject.size).to eq(3) }
|
@@ -62,7 +79,7 @@ module Firebrew::AmoApi
|
|
62
79
|
|
63
80
|
context 'when results were empty' do
|
64
81
|
let(:fixture){'empty.xml'}
|
65
|
-
it { expect{subject}.to raise_error(Firebrew::ExtensionNotFoundError) }
|
82
|
+
it { expect{subject}.to raise_error(Firebrew::ExtensionNotFoundError, 'Extension not found: like "aa"') }
|
66
83
|
end
|
67
84
|
end
|
68
85
|
end
|
@@ -10,12 +10,19 @@ module Firebrew
|
|
10
10
|
|
11
11
|
context 'when the command was invalid' do
|
12
12
|
let(:args){'invalid-command'}
|
13
|
-
it { expect{subject}.to raise_error(Firebrew::CommandLineError) }
|
13
|
+
it { expect{subject}.to raise_error(Firebrew::CommandLineError, 'Invalid command: invalid-command') }
|
14
14
|
end
|
15
15
|
|
16
16
|
context 'when the options was invalid' do
|
17
|
-
|
18
|
-
|
17
|
+
describe 'invalid option' do
|
18
|
+
let(:args){'install --invalid-option'}
|
19
|
+
it { expect{subject}.to raise_error(Firebrew::CommandLineError, 'Invalid option: --invalid-option') }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'MissingArgument' do
|
23
|
+
let(:args){'install --firefox'}
|
24
|
+
it { expect{subject}.to raise_error(Firebrew::CommandLineError, 'Missing argument: --firefox') }
|
25
|
+
end
|
19
26
|
end
|
20
27
|
|
21
28
|
describe '#arguments()' do
|
@@ -104,6 +111,35 @@ module Firebrew
|
|
104
111
|
)
|
105
112
|
end
|
106
113
|
end
|
114
|
+
|
115
|
+
describe 'profile command' do
|
116
|
+
let(:args){'profile'}
|
117
|
+
it 'should parse' do
|
118
|
+
is_expected.to eq(
|
119
|
+
command: :profile,
|
120
|
+
params: {},
|
121
|
+
config: {}
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with options' do
|
126
|
+
let(:args){'profile -a name --attribute=is_default'}
|
127
|
+
it 'should parse' do
|
128
|
+
is_expected.to eq(
|
129
|
+
command: :profile,
|
130
|
+
params: {
|
131
|
+
attribute: 'is_default'
|
132
|
+
},
|
133
|
+
config: {}
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'with invalid options' do
|
138
|
+
let(:args){'profile -a hoge'}
|
139
|
+
it { expect{subject}.to raise_error(Firebrew::CommandLineError, 'Invalid argument: -a hoge') }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
107
143
|
end
|
108
144
|
|
109
145
|
describe '::execute(&block)' do
|
@@ -130,19 +166,25 @@ module Firebrew
|
|
130
166
|
|
131
167
|
context 'when the `Firebrew::Error` was thrown' do
|
132
168
|
let(:exeption){raise Firebrew::CommandLineError, 'CommandLineError message'}
|
133
|
-
it { expect(subject[0]).to eq(
|
169
|
+
it { expect(subject[0]).to eq(1) }
|
134
170
|
it { expect(subject[1]).to eq('CommandLineError message') }
|
135
171
|
end
|
136
172
|
|
173
|
+
context 'when the `Firebrew::OperationAlreadyCompletedError` was thrown' do
|
174
|
+
let(:exeption){raise Firebrew::OperationAlreadyCompletedError, 'OperationAlreadyCompletedError message'}
|
175
|
+
it { expect(subject[0]).to eq(2) }
|
176
|
+
it { expect(subject[1]).to eq('OperationAlreadyCompletedError message') }
|
177
|
+
end
|
178
|
+
|
137
179
|
context 'when the `SystemExit` was thrown' do
|
138
180
|
let(:exeption){abort 'abort message'}
|
139
|
-
it { expect(subject[0]).to eq(
|
181
|
+
it { expect(subject[0]).to eq(0) }
|
140
182
|
it { expect(subject[1]).to eq('abort message') }
|
141
183
|
end
|
142
184
|
|
143
185
|
context 'when the unknown exception was thrown' do
|
144
186
|
let(:exeption){raise StandardError, 'StandardError message'}
|
145
|
-
it { expect(subject[0]).to eq(
|
187
|
+
it { expect(subject[0]).to eq(1) }
|
146
188
|
it { expect(subject[1]).to match(/^#<StandardError: StandardError message>/) }
|
147
189
|
end
|
148
190
|
end
|
@@ -11,20 +11,20 @@ module Firebrew::Firefox
|
|
11
11
|
|
12
12
|
context 'when the indicated firefox command by the `config[:firefox]` not existed' do
|
13
13
|
let(:config){super().merge firefox: 'firefox/not/existed/path'}
|
14
|
-
it { expect{subject}.to raise_error(Firebrew::FirefoxCommandError) }
|
14
|
+
it { expect{subject}.to raise_error(Firebrew::FirefoxCommandError, 'Firefox command not found: firefox/not/existed/path') }
|
15
15
|
end
|
16
16
|
|
17
17
|
context 'when the indicated command by the `config[:firefox]` was not firefox' do
|
18
18
|
let(:executer) do
|
19
19
|
double('executer', exec: ['Other program', 0])
|
20
20
|
end
|
21
|
-
it { expect{subject}.to raise_error(Firebrew::FirefoxCommandError) }
|
21
|
+
it { expect{subject}.to raise_error(Firebrew::FirefoxCommandError, 'Command is not Firefox: %{firefox}' % self.config) }
|
22
22
|
|
23
23
|
describe 'command status' do
|
24
24
|
let(:executer) do
|
25
25
|
double('executer', exec: ['Fake Mozilla Firefox', 1])
|
26
26
|
end
|
27
|
-
it { expect{subject}.to raise_error(Firebrew::FirefoxCommandError) }
|
27
|
+
it { expect{subject}.to raise_error(Firebrew::FirefoxCommandError, 'Command is not Firefox: %{firefox}' % self.config) }
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -84,7 +84,7 @@ module Firebrew::Firefox
|
|
84
84
|
|
85
85
|
context 'when the extension corresponding to the `name` not existed' do
|
86
86
|
let(:name){'hoge'}
|
87
|
-
it { expect{subject}.to raise_error(Firebrew::ExtensionNotFoundError) }
|
87
|
+
it { expect{subject}.to raise_error(Firebrew::ExtensionNotFoundError, 'Extension not found: hoge') }
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
@@ -16,7 +16,7 @@ module Firebrew::Firefox
|
|
16
16
|
context 'when "profiles.ini" not existed' do
|
17
17
|
let(:base_dir){'not/existed/directory'}
|
18
18
|
let(:data_file){'not_found.ini'}
|
19
|
-
it { expect{subject}.to raise_error(Firebrew::ProfilesFileNotFoundError) }
|
19
|
+
it { expect{subject}.to raise_error(Firebrew::ProfilesFileNotFoundError, %["profiles.ini" not found: #{File.expand_path File.join(self.base_dir, self.data_file)}]) }
|
20
20
|
end
|
21
21
|
|
22
22
|
describe '#all()' do
|
@@ -61,7 +61,7 @@ module Firebrew::Firefox
|
|
61
61
|
|
62
62
|
context 'when not existed the `name` in the profiles.' do
|
63
63
|
let(:name){'not_existed_profile_name'}
|
64
|
-
it { expect{subject}.to raise_error(Firebrew::ProfileNotFoundError) }
|
64
|
+
it { expect{subject}.to raise_error(Firebrew::ProfileNotFoundError, "Profile not found: #{self.name}") }
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -150,14 +150,13 @@ module Firebrew
|
|
150
150
|
before do
|
151
151
|
FileUtils.cp './spec/fixtures/firefox/profile/base.ini', './tmp/profiles.ini'
|
152
152
|
response = File.read("./spec/fixtures/amo_api/search/base.xml")
|
153
|
-
|
154
|
-
|
155
|
-
end
|
153
|
+
AmoApi::Search.connection = double(:connection)
|
154
|
+
allow(AmoApi::Search.connection).to receive(:get).and_return(OpenStruct.new body: response, status: 200)
|
156
155
|
end
|
157
156
|
|
158
157
|
after do
|
159
158
|
FileUtils.rm_f './tmp/*'
|
160
|
-
|
159
|
+
AmoApi::Search.connection = nil
|
161
160
|
end
|
162
161
|
|
163
162
|
describe '#install(params)' do
|
@@ -171,12 +170,14 @@ module Firebrew
|
|
171
170
|
|
172
171
|
context 'when the `params[:term]` existed' do
|
173
172
|
subject do
|
174
|
-
extensions_double = double('extensions', install: nil, find: Firefox::BasicExtension.new)
|
173
|
+
extensions_double = double('extensions', install: nil, find: Firefox::BasicExtension.new(name: self.extension_name))
|
175
174
|
self.instance.profile = double('profile', extensions: extensions_double)
|
176
175
|
self.instance.install(self.search_params)
|
177
176
|
end
|
178
177
|
|
179
|
-
|
178
|
+
let(:extension_name){'hoge'}
|
179
|
+
|
180
|
+
it { expect{subject}.to raise_error(Firebrew::OperationAlreadyCompletedError, "Already installed: #{self.extension_name}") }
|
180
181
|
end
|
181
182
|
end
|
182
183
|
|
@@ -184,9 +185,11 @@ module Firebrew
|
|
184
185
|
subject do
|
185
186
|
extensions_double = double('extensions', find!: double('ext', delete: nil))
|
186
187
|
self.instance.profile = double('profile', extensions: extensions_double)
|
187
|
-
self.instance.uninstall(term:
|
188
|
+
self.instance.uninstall(term: self.extension_name)
|
188
189
|
end
|
189
190
|
|
191
|
+
let(:extension_name){'existed-extension'}
|
192
|
+
|
190
193
|
it { expect{subject}.to_not raise_error }
|
191
194
|
|
192
195
|
context 'when the `params[:term]` not existed' do
|
@@ -194,10 +197,12 @@ module Firebrew
|
|
194
197
|
extensions_double = double('extensions')
|
195
198
|
allow(extensions_double).to receive(:find!).and_raise(Firebrew::ExtensionNotFoundError)
|
196
199
|
self.instance.profile = double('profile', extensions: extensions_double)
|
197
|
-
self.instance.uninstall(term:
|
200
|
+
self.instance.uninstall(term: self.extension_name)
|
198
201
|
end
|
199
202
|
|
200
|
-
|
203
|
+
let(:extension_name){'not-existed-extension'}
|
204
|
+
|
205
|
+
it { expect{subject}.to raise_error(Firebrew::OperationAlreadyCompletedError, "Already uninstalled: #{self.extension_name}") }
|
201
206
|
end
|
202
207
|
end
|
203
208
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firebrew
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuichi Murata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,75 +53,47 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '4.1'
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '4.1'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: activeresource
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '4.0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '4.0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: activemodel
|
56
|
+
name: inifile
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
86
58
|
requirements:
|
87
59
|
- - "~>"
|
88
60
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
61
|
+
version: '2.0'
|
90
62
|
type: :runtime
|
91
63
|
prerelease: false
|
92
64
|
version_requirements: !ruby/object:Gem::Requirement
|
93
65
|
requirements:
|
94
66
|
- - "~>"
|
95
67
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
68
|
+
version: '2.0'
|
97
69
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
70
|
+
name: rubyzip
|
99
71
|
requirement: !ruby/object:Gem::Requirement
|
100
72
|
requirements:
|
101
73
|
- - "~>"
|
102
74
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
75
|
+
version: '1.1'
|
104
76
|
type: :runtime
|
105
77
|
prerelease: false
|
106
78
|
version_requirements: !ruby/object:Gem::Requirement
|
107
79
|
requirements:
|
108
80
|
- - "~>"
|
109
81
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
82
|
+
version: '1.1'
|
111
83
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
84
|
+
name: faraday
|
113
85
|
requirement: !ruby/object:Gem::Requirement
|
114
86
|
requirements:
|
115
87
|
- - "~>"
|
116
88
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
89
|
+
version: '0.9'
|
118
90
|
type: :runtime
|
119
91
|
prerelease: false
|
120
92
|
version_requirements: !ruby/object:Gem::Requirement
|
121
93
|
requirements:
|
122
94
|
- - "~>"
|
123
95
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
96
|
+
version: '0.9'
|
125
97
|
description: Firefox add-ons manager for CUI.
|
126
98
|
email:
|
127
99
|
- mrk21info+rubygems@gmail.com
|