geoffrey 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg/*
3
3
  .bundle
4
4
  .yardoc
5
5
  doc
6
+ log
@@ -1,12 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- geoffrey (0.0.5)
4
+ geoffrey (0.1.0)
5
5
  plist4r
6
+ progressbar
7
+ thor
6
8
 
7
9
  GEM
8
10
  remote: http://rubygems.org/
9
11
  specs:
12
+ autotest (4.3.2)
10
13
  bluecloth (2.0.7)
11
14
  haml (3.0.18)
12
15
  libxml-ruby (1.1.4)
@@ -18,18 +21,23 @@ GEM
18
21
  haml
19
22
  libxml-ruby
20
23
  libxml4r
24
+ progressbar (0.9.0)
21
25
  rake (0.8.7)
22
26
  rspec (1.3.0)
27
+ thor (0.14.0)
23
28
  yard (0.6.0)
24
29
 
25
30
  PLATFORMS
26
31
  ruby
27
32
 
28
33
  DEPENDENCIES
34
+ autotest
29
35
  bluecloth (~> 2.0.7)
30
36
  bundler (~> 1.0.0.rc.5)
31
37
  geoffrey!
32
38
  mocha (~> 0.9.8)
33
39
  plist4r
40
+ progressbar
34
41
  rspec (~> 1.3.0)
42
+ thor
35
43
  yard (~> 0.6.0)
data/README.md CHANGED
@@ -1,12 +1,6 @@
1
1
  geoffrey
2
2
  ========
3
3
 
4
- ## ALERT!
5
-
6
- This is very early alpha code, use with precaution. It's a spinoff of myself willing to
7
- improve my [dotfiles project](http://github.com/mrsimo/dotfiles) a step further. All
8
- code examples in this README come from it.
9
-
10
4
  ## Links
11
5
 
12
6
  * [Documentation](http://rubydoc.info/gems/geoffrey/frames) (this README is better viewed there ;)
@@ -14,10 +8,13 @@ code examples in this README come from it.
14
8
  ## Introduction
15
9
 
16
10
  Geoffrey's your little helper, that who just wants to makes your life easier.
11
+ It helps you install applications into your OS X system.
12
+
13
+ It will help you in two ways, with a command line application, and an API to
14
+ let you create your own recipes.
17
15
 
18
- It aims to help you create little scripts to setup your OSX the way you like.
19
- If, like me, you use different computers at work, home, or even when travelling,
20
- you'll know it's a pain in the ass to keep everything up to date.
16
+ If, like me, you use different computers at work, home, or even when
17
+ travelling, you'll know it's a pain in the ass to keep everything up to date.
21
18
 
22
19
  Inspired in the numerous dotfiles projects, specially
23
20
  [holman's](http://github.com/holman/dotfiles),
@@ -27,8 +24,24 @@ the intention is to bring customization to a bigger extend.
27
24
 
28
25
  $ [sudo] gem install geoffrey
29
26
 
30
- Geoffrey's Talents
31
- ==================
27
+ Command line
28
+ ============
29
+
30
+ Type `geoffrey` to see help with the commands. Right now you can list the
31
+ provided recipes, and install them.
32
+
33
+ $ geoffrey list
34
+ adium growl skype unrarx
35
+
36
+ $ geoffrey install adium
37
+ >> Adium is a free instant messaging application for Mac OS X that can connect to AIM, MSN, Jabber, Yahoo, and more.
38
+ >> Downloading http://adiumx.cachefly.net/Adium_1.3.10.dmg
39
+ : 100% |oooooooooooooooooooooooo| 21.3MB 451.6KB/s ETA: 00:00:00
40
+ >> Moving file to /Applications
41
+ >> Install successful
42
+
43
+ API
44
+ ===
32
45
 
33
46
  Geoffrey has many talents, and thus you'll find he can help you in one of these areas:
34
47
 
@@ -67,10 +80,3 @@ up and running with Pro scheme, Menlo Font 14pt, and a size 520x100.
67
80
  end
68
81
 
69
82
  See {Geoffrey::plist} for extended documentation.
70
-
71
- ## A few words
72
-
73
- This is work done in my free hours, it's not yet finished. The code is dirty and mainly undocumented. It all started on #whyday,
74
- but I'm so lazy that I've needed a lot of time to do just this.
75
-
76
- Thanks!
data/Rakefile CHANGED
@@ -1,9 +1,11 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
+ require 'spec/rake/spectask'
4
+ require 'mocha'
3
5
 
4
6
  desc "Execute specs"
5
- task :spec do
6
- puts `bundle exec spec spec`
7
+ Spec::Rake::SpecTask.new('spec') do |t|
8
+ t.spec_files = FileList['spec/**/*.rb'].reject{|f| f =~ /templates/ }
7
9
  end
8
10
 
9
11
  desc "Generate docs"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ require 'geoffrey'
5
+ require 'geoffrey/package'
6
+ require 'geoffrey/cli'
7
+
8
+ Geoffrey::CLI.start
@@ -16,13 +16,16 @@ Gem::Specification.new do |s|
16
16
 
17
17
  s.add_development_dependency "bundler", "~> 1.0.0.rc.5"
18
18
  s.add_development_dependency "rspec", "~> 1.3.0"
19
+ s.add_development_dependency "autotest"
19
20
  s.add_development_dependency "mocha", "~> 0.9.8"
20
21
  s.add_development_dependency "yard", "~> 0.6.0"
21
22
  s.add_development_dependency "bluecloth", "~> 2.0.7"
22
23
 
23
24
  s.add_dependency 'plist4r'
25
+ s.add_dependency 'progressbar'
26
+ s.add_dependency 'thor'
24
27
 
25
28
  s.files = `git ls-files`.split("\n")
26
- s.executables = `git ls-files`.split("\n").select{|f| f =~ /^bin/}
29
+ s.executables = ['geoffrey']
27
30
  s.require_path = 'lib'
28
31
  end
@@ -5,6 +5,13 @@ module Geoffrey
5
5
 
6
6
  class << self
7
7
 
8
+ def environment=(value)
9
+ @@environment = value if value
10
+ end
11
+ def environment
12
+ @@environment ||= :production
13
+ end
14
+
8
15
  # Define a package installation from the URL specified. It will download
9
16
  # the file, and if it's compressed, it will decompress it.It currently
10
17
  # supports zip, tar.gz, .gz and .dmg files. Once extracted it will look for
@@ -18,6 +25,7 @@ module Geoffrey
18
25
  # * +:if+ inverse of inverse
19
26
  # * +:file+ what file has to be installed from all of the ones that came in
20
27
  # the compressed file.
28
+ # * +:filename+ set the name of the file you are downloading.
21
29
  #
22
30
  # If you don't want to install a .pkg but do something else, you can just
23
31
  # override the install method. See the examples
@@ -0,0 +1,58 @@
1
+ require 'thor'
2
+
3
+ module Geoffrey
4
+ class CLI < Thor
5
+
6
+ attr_accessor :package
7
+
8
+ desc "list", "list of all the available templates"
9
+ def list(filter = '')
10
+ names = if filter && filter != ''
11
+ list_of_template_names.select{ |name| name.downcase.include?(filter.downcase) }
12
+ else
13
+ list_of_template_names
14
+ end
15
+ puts names.join(' ')
16
+ end
17
+
18
+ desc "install [template]", "install the specified template"
19
+ method_options :verbose => :boolean
20
+ def install(name)
21
+ if file = file_for_template(name)
22
+ @package = Geoffrey::Package.new
23
+ @package.verbose(true) if options[:verbose]
24
+ @package.instance_eval File.read(file)
25
+ @package.download_and_decompress
26
+ @package.install
27
+ else
28
+ puts "Template not found"
29
+ exit 1
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ #--
36
+ # TODO Might be useful for the future to have multiple dirs for templates.
37
+ def templates_dir
38
+ File.dirname(__FILE__) + '/../../templates'
39
+ end
40
+
41
+ def list_of_templates
42
+ Dir[templates_dir + '/*']
43
+ end
44
+
45
+ def list_of_template_names
46
+ list_of_templates.map{ |file| name_from_file(file) }
47
+ end
48
+
49
+ def file_for_template(name)
50
+ list_of_templates.select{ |file| name_from_file(file) == name }.first
51
+ end
52
+
53
+ def name_from_file(file)
54
+ file.split("/").last.split(".").first
55
+ end
56
+
57
+ end
58
+ end
@@ -2,6 +2,8 @@ require 'tmpdir'
2
2
  require 'tempfile'
3
3
  require 'open-uri'
4
4
  require 'fileutils'
5
+ require 'progressbar'
6
+ require 'geoffrey/reporter'
5
7
 
6
8
  module Geoffrey
7
9
 
@@ -10,43 +12,49 @@ module Geoffrey
10
12
 
11
13
  # This class helps installing packages and other apps into your system.
12
14
  class Package
13
- attr_accessor :url, :options, :format, :filename, :dir, :file
15
+
16
+ include Geoffrey::Reporter
17
+
18
+ attr_accessor :url, :description, :options, :format, :filename, :dir, :file
14
19
 
15
20
  def initialize
16
21
  @options = {}
17
22
  end
18
23
 
19
- # Accepts anything parseable by URI
20
- def url(val = nil)
21
- @url = val if val
22
- @url
23
- end
24
-
25
- def options(val = nil)
26
- @options = val if val
27
- @options
24
+ [:url,:name,:description,:options].each do |attrib|
25
+ class_eval <<-GEOF
26
+ def #{attrib}(val=nil)
27
+ val.nil? ? @#{attrib} : @#{attrib} = val
28
+ end
29
+ GEOF
28
30
  end
29
31
 
30
32
  # Uses the url provided to download the file.
31
33
  # Checks the options first to see if it shouldn't be done.
32
- #--
33
- # TODO add some option to define the file name for weird url's
34
34
  def download_and_decompress
35
+ echo "URL not specified" unless @url && @url != ""
36
+
37
+ get_filename and debug("filename detected: #{@filename}")
38
+
35
39
  return unless should_install
36
40
 
41
+ echo @description if @description && @description != ""
42
+
37
43
  begin
38
- @file = Tempfile.new("geoffrey")
39
- @file.write open(@url).read
44
+ @file = Tempfile.new("geoffrey") and debug("downloading file into #{@file.path}")
45
+
46
+ read_with_progress_bar
40
47
  @file.close
41
48
 
42
- @filename = @url.split("/").last
43
- get_format
49
+ get_format and debug("format detected: #{@format}")
44
50
 
45
51
 
46
- @dir = "#{Dir.tmpdir}/#{filename}"
52
+ @dir = "#{Dir.tmpdir}/#{filename}" and debug("will put files into temporary dir: #{@dir}")
53
+
47
54
  FileUtils.rm_rf dir if File.exist?(dir)
48
55
  FileUtils.mkdir_p dir
49
- rescue
56
+ rescue Exception => e
57
+ echo "Can't find file #{@url}"
50
58
  raise NotFoundError
51
59
  end
52
60
 
@@ -64,10 +72,13 @@ module Geoffrey
64
72
 
65
73
  case File.extname file_to_install
66
74
  when ".pkg"
75
+ echo "Installing package"
67
76
  execute "installer -pkg #{file_to_install} -target /"
68
77
  when ".app"
69
- FileUtils.mv file_to_install, "/Appliactions"
78
+ echo "Moving file to /Applications"
79
+ FileUtils.mv file_to_install, "/Applications"
70
80
  end
81
+ echo "Install successful"
71
82
  end
72
83
 
73
84
  # If it was specified through options, then get that, otherwise
@@ -87,13 +98,19 @@ module Geoffrey
87
98
  private
88
99
 
89
100
  def should_install
90
- if @options[:unless] && @options[:unless].respond_to?(:call)
91
- !@options[:unless].call
92
- elsif @options[:if] && @options[:if].respond_to?(:call)
93
- @options[:if].call
101
+ ok_to_go = if @url && @url != ""
102
+ if @options[:unless] && @options[:unless].respond_to?(:call)
103
+ !@options[:unless].call
104
+ elsif @options[:if] && @options[:if].respond_to?(:call)
105
+ @options[:if].call
106
+ else
107
+ true
108
+ end
94
109
  else
95
- true
110
+ false
96
111
  end
112
+ echo "#{filename} is already installed." unless ok_to_go
113
+ ok_to_go
97
114
  end
98
115
 
99
116
  # Simple wrapper to execute things, using sudo or not as per defiend in the
@@ -103,12 +120,29 @@ module Geoffrey
103
120
  opts = @options.merge(opts)
104
121
  command = "sudo #{command}" if opts[:sudo]
105
122
  command = "#{command} > /dev/null 2>&1" unless opts[:verbose]
106
- `#{command}`
123
+ debug "-- executing: #{command}"
124
+ returned = `#{command}`
125
+ debug "-- returned:"
126
+ debug returned
127
+ returned
107
128
  end
108
129
  end
109
130
 
131
+ def read_with_progress_bar
132
+ pbar = nil
133
+ open(@url, :content_length_proc => lambda { |t|
134
+ if t && t > 0
135
+ echo "Downloading #{@url}"
136
+ pbar = ProgressBar.new("",t)
137
+ pbar.file_transfer_mode
138
+ end
139
+ }, :progress_proc => lambda { |s|
140
+ pbar.set s if pbar
141
+ }) { |f| @file.write f.read }
142
+ end
143
+
110
144
  def get_format
111
- @format = case File.extname @url
145
+ @format = case File.extname @filename
112
146
  when '.zip' then :zip
113
147
  when '.tar' then :tar
114
148
  when '.tgz' then :tgz
@@ -118,16 +152,23 @@ module Geoffrey
118
152
  end
119
153
  end
120
154
 
155
+ def get_filename
156
+ @filename = @options[:filename]
157
+ @filename ||= begin
158
+ escape_whitespaces(File.basename(@url).split("?").first) if @url && @url != ""
159
+ end
160
+ end
161
+
121
162
  # Actual decompressing of the file. Does so into a directory we created in
122
163
  # +#{Dir.tmpdir}/#{filename}+
123
164
  #--
124
- # TODO control errors. Maybe it's .zip but just doesn't work.
125
165
  # TODO better isolate each download inside the tmpdir? When debugging it's
126
166
  # misleading to have a folder with the file name and extension.
127
167
  # * Timestamp if clash?
128
168
  # * Random string?
129
169
  def decompress
130
- command = case format
170
+ debug "decompressing #{file.path}"
171
+ case format
131
172
  when :zip
132
173
  execute "unzip -o #{file.path} -d #{dir}", :sudo => false
133
174
  when :tar
@@ -144,21 +185,41 @@ module Geoffrey
144
185
 
145
186
  # Might be that the decompression failed
146
187
  raise DecompressionError unless $?.success?
188
+
189
+ debug "Files after decompression:"
190
+ debug `ls -la #{dir}`
147
191
  end
148
192
 
149
193
  # Mount the dmg file, copy contents to tmpdir, unmount, remove dmg
194
+ #--
195
+ # TODO change system calls with FileUtils maybe?
150
196
  def extract_from_dmg
151
197
  execute("mv #{file.path} #{dir}.dmg")
152
198
  str = execute("hdiutil attach #{dir}.dmg 2> /dev/null", :verbose => true)
153
199
  if $?.success?
154
200
  info = str.split("\t")
155
- dmg_dir = info.last.chomp
201
+
202
+ dmg_dir = escape_whitespaces(info.last.chomp)
203
+ debug "the dmg has been mounted here: #{dmg_dir}"
204
+
156
205
  device = info.first.strip.split("/").last
157
- execute("cp -r #{dmg_dir}/** #{dir}")
206
+ debug "the attached device is: #{device}"
207
+
208
+ debug "These are the files inside the dmg:"
209
+ debug `ls -la #{dmg_dir}`
210
+
211
+ execute("cp -r #{escape_whitespaces(dmg_dir)}/** #{dir}")
212
+
213
+ debug "Now these in the target"
214
+ debug `ls -la #{dir}`
215
+
158
216
  execute("hdiutil detach #{device}")
159
217
  execute("rm -f #{dir}.dmg")
160
218
  end
161
219
  end
162
220
 
221
+ def escape_whitespaces(path)
222
+ path.gsub(' ','\ ')
223
+ end
163
224
  end
164
225
  end
@@ -10,7 +10,11 @@ module Geoffrey
10
10
  :terminal => "#{ENV["HOME"]}/Library/Preferences/com.apple.Terminal.plist"
11
11
  }
12
12
 
13
- attr_accessor :data, :file, :process
13
+ attr_accessor :data, :file, :process, :changes
14
+
15
+ def initialize
16
+ @changes = {}
17
+ end
14
18
 
15
19
  # Set the file you'll edit. if you pass a symbol then it'll use the file
16
20
  # defined in the FILES hash.
@@ -30,7 +34,7 @@ module Geoffrey
30
34
  end
31
35
 
32
36
  def options
33
- data
37
+ self
34
38
  end
35
39
 
36
40
  def [](key)
@@ -38,12 +42,12 @@ module Geoffrey
38
42
  end
39
43
 
40
44
  def []=(key,value)
45
+ old_value = data[key]
41
46
  data[key] = value
47
+ @changes[key] = value if old_value != value
42
48
  end
43
49
 
44
50
  # Saves the changes defined into the file.
45
- #--
46
- # TODO don't reopen the process if it wasn't opened to begin with
47
51
  def save
48
52
  kill
49
53
  data.save
@@ -53,11 +57,21 @@ module Geoffrey
53
57
  private
54
58
 
55
59
  def kill
56
- system "killall '#{@process}'" unless @process.nil?
60
+ unless @process.nil? || @changes.keys.size.zero?
61
+ `ps aux | grep #{@process}`.split("\n").each do |row|
62
+ unless row.include?("grep #{@process}")
63
+ columns = row.split(" ")
64
+ @pid = columns[1].to_i
65
+ Process.kill("TERM", @pid) rescue nil
66
+ end
67
+ end
68
+ end
57
69
  end
58
70
 
59
71
  def open
60
- system "open '#{@process}'" unless @process.nil?
72
+ unless @process.nil? || @changes.keys.size.zero? || @pid.nil?
73
+ system "open '#{@process}'"
74
+ end
61
75
  end
62
76
 
63
77
  end
@@ -0,0 +1,42 @@
1
+ require 'logger'
2
+
3
+ #--
4
+ # H4X!
5
+ # Changing logger format, didn't like the other one
6
+ class Logger
7
+ def format_message(level, time, progname, msg)
8
+ ">> #{msg}\n"
9
+ end
10
+ end
11
+ # /H4X
12
+
13
+ module Geoffrey
14
+
15
+ # This module is intended to be used in real action classes so that they can
16
+ # report what they're doing. The idea is to abstract the functionality in
17
+ # order to test it better and help the future children.
18
+ module Reporter
19
+
20
+ def logger
21
+ unless @logger
22
+ @logger = Logger.new($stdout)
23
+ @logger.level = Logger::INFO
24
+ end
25
+ @logger
26
+ end
27
+
28
+ def echo(msg)
29
+ logger.info msg
30
+ end
31
+
32
+ def debug(msg)
33
+ logger.debug msg
34
+ end
35
+
36
+ def verbose(mode)
37
+ @logger = Logger.new($stdout)
38
+ @logger.level = (mode ? Logger::DEBUG : Logger::INFO)
39
+ end
40
+
41
+ end
42
+ end
@@ -1,3 +1,3 @@
1
1
  module Geoffrey
2
- VERSION = "0.0.6"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ require 'geoffrey/cli'
4
+
5
+ describe Geoffrey::CLI do
6
+
7
+ before(:each) do
8
+ @app = Geoffrey::CLI.new
9
+ @app.stubs(:templates_dir).returns(File.dirname(__FILE__) + '/templates')
10
+ end
11
+
12
+ context 'list' do
13
+
14
+ it "prints a list of templates" do
15
+ output = capturing_output { @app.list }
16
+ output.should include("simbl")
17
+ output.should include("growl")
18
+ end
19
+
20
+ it "filters through items in the list" do
21
+ output = capturing_output { @app.list("gr") }
22
+ output.should include("growl")
23
+ output.should_not include("simbl")
24
+ end
25
+
26
+ it "filters through items in the list ignoring case" do
27
+ output = capturing_output { @app.list("Growl") }
28
+ output.should include("growl")
29
+ output.should_not include("simbl")
30
+ end
31
+
32
+ end
33
+
34
+ context 'install' do
35
+
36
+ # Can't really test something's installed for real, so we just expect
37
+ # that it will call the proper methods on the Geoffrey::Package object.
38
+ it "knows what to install" do
39
+ Geoffrey::Package.any_instance.expects(:url).with("http://some.fic/tional/url.dmg").once
40
+ Geoffrey::Package.any_instance.expects(:description).with("This package should do awesome stuff").once
41
+ Geoffrey::Package.any_instance.expects(:options).with(:sudo => true).once.returns(nil)
42
+ Geoffrey::Package.any_instance.expects(:download_and_decompress).once.returns(nil)
43
+ Geoffrey::Package.any_instance.expects(:install).once
44
+ @app.install("growl")
45
+ end
46
+
47
+ end
48
+
49
+ #--
50
+ # This doesn't really have to be tested, right? It's not public api, we
51
+ # should test the public methods that depend on these. Or not?
52
+ context 'finding templates' do
53
+
54
+ it "knows about template files" do
55
+ @app.send(:list_of_template_names).should == ['growl','simbl']
56
+ @app.send(:list_of_templates).each do |file|
57
+ File.exists?(file).should be_true
58
+ end
59
+ end
60
+
61
+ it "knows how to find the file a template is defined it" do
62
+ @app.send(:file_for_template,'growl').should == File.dirname(__FILE__) + '/templates/growl.rb'
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'geoffrey'
3
+
4
+ describe "Geoffrey" do
5
+
6
+ context "environment" do
7
+
8
+ it "has an environment attribute" do
9
+ Geoffrey.should respond_to(:"environment=")
10
+ Geoffrey.should respond_to(:"environment")
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -3,10 +3,18 @@ require 'geoffrey/package'
3
3
 
4
4
  describe Geoffrey::Package do
5
5
 
6
- it "allows for the url to be specified" do
6
+ it "receives different options" do
7
+ [:url, :name, :description, :options].each do |opt|
8
+ package = Geoffrey::Package.new
9
+ package.send(opt, 'foo')
10
+ package.send(opt).should == 'foo'
11
+ end
12
+ end
13
+
14
+ it "allows for a description to be specified" do
7
15
  package = Geoffrey::Package.new
8
- package.url 'http://localhost/crappy.zip'
9
- package.url.should == 'http://localhost/crappy.zip'
16
+ package.description 'foo'
17
+ package.description.should == 'foo'
10
18
  end
11
19
 
12
20
  it "allows for some options to be specified" do
@@ -33,19 +41,23 @@ describe Geoffrey::Package do
33
41
  end
34
42
 
35
43
  it "doesnt install the thing if a :unless option is provided" do
36
- package = Geoffrey::Package.new
37
- package.url("whatever")
38
- package.options :unless => Proc.new { true }
39
- package.download_and_decompress.should == nil
40
- package.install.should == nil
44
+ capturing_output do
45
+ package = Geoffrey::Package.new
46
+ package.url("whatever")
47
+ package.options :unless => Proc.new { true }
48
+ package.download_and_decompress.should == nil
49
+ package.install.should == nil
50
+ end
41
51
  end
42
52
 
43
53
  it "inverse if passed a :if option" do
44
- package = Geoffrey::Package.new
45
- package.url("whatever")
46
- package.options :if => Proc.new { false }
47
- package.download_and_decompress.should == nil
48
- package.install.should == nil
54
+ capturing_output do
55
+ package = Geoffrey::Package.new
56
+ package.url("whatever")
57
+ package.options :if => Proc.new { false }
58
+ package.download_and_decompress.should == nil
59
+ package.install.should == nil
60
+ end
49
61
  end
50
62
 
51
63
  end
@@ -55,9 +67,11 @@ describe Geoffrey::Package do
55
67
  context 'package downloading/uncompressing' do
56
68
 
57
69
  it "raises an error if the file can't be found/read" do
58
- package = Geoffrey::Package.new
59
- package.url 'really/impossible/to/exist.dmg'
60
- lambda { package.download_and_decompress}.should raise_exception(Geoffrey::NotFoundError)
70
+ capturing_output do
71
+ package = Geoffrey::Package.new
72
+ package.url 'really/impossible/to/exist.dmg'
73
+ lambda { package.download_and_decompress }.should raise_exception(Geoffrey::NotFoundError)
74
+ end
61
75
  end
62
76
 
63
77
  it "raises an error if the file has bad format" do
@@ -75,6 +89,13 @@ describe Geoffrey::Package do
75
89
  File.read(package.file_to_install).chomp.should == "foo"
76
90
  end
77
91
 
92
+ it "removes dirty from url's (but only from http/ftp" do
93
+ package = Geoffrey::Package.new
94
+ package.url(File.dirname(__FILE__) + "/static/test.txt?foo=bar&baz=jar")
95
+ package.send(:get_filename)
96
+ package.filename.should == "test.txt"
97
+ end
98
+
78
99
  it "smartly unzips the file if it's a zip" do
79
100
  package = Geoffrey::Package.new
80
101
  package.url(File.dirname(__FILE__) + "/static/test.txt.zip")
@@ -96,9 +117,25 @@ describe Geoffrey::Package do
96
117
  File.read(package.file_to_install).chomp.should == "foo"
97
118
  end
98
119
 
120
+ # Testing with a dmg that has a whitespace as a name because testing dmg's
121
+ # is too slow, so we do it all in one.
122
+ #--
123
+ # TODO this test fails :( but it does work in real examples.
99
124
  it "also supports .dmg files" do
100
125
  package = Geoffrey::Package.new
101
- package.url(File.dirname(__FILE__) + "/static/test.dmg")
126
+ package.url(File.dirname(__FILE__) + "/static/test 1.dmg")
127
+ package.download_and_decompress
128
+ File.read(package.file_to_install).chomp.should == "foo"
129
+ end
130
+
131
+ # test.txt.gz.123 is test.txt.gz copied and renamed. This way geoffrey
132
+ # knows what file format to use.
133
+ # TODO maybe would be better to just set the strategy to use when
134
+ # extracting
135
+ it "allows for filename to be configured" do
136
+ package = Geoffrey::Package.new
137
+ package.url(File.dirname(__FILE__) + "/static/test.txt.gz.123")
138
+ package.options :filename => 'test.txt.gz'
102
139
  package.download_and_decompress
103
140
  File.read(package.file_to_install).chomp.should == "foo"
104
141
  end
@@ -17,7 +17,7 @@ describe Geoffrey::Plist do
17
17
  it "also accepts symbol values for already known values" do
18
18
  plist = Geoffrey::Plist.new
19
19
  plist.file :terminal
20
- plist.file.should include("com.apple.Terminal.plist")
20
+ plist.file.should include("com.apple.Terminal.plist")
21
21
  end
22
22
 
23
23
  it "parses correct plist items" do
@@ -26,18 +26,16 @@ describe Geoffrey::Plist do
26
26
  plist.data.class.should == Plist4r::Plist
27
27
  end
28
28
 
29
- it "knows that it has to close some appication before applying changes" do
29
+ it "doesn't restart applications if no changes where really made" do
30
30
  plist = Geoffrey::Plist.new
31
31
  plist.file(@plist_file.path)
32
32
  plist.affects "/some/process"
33
-
34
- # Since I don't know how to test this without starting and shutting down processes
35
- # I'll just suppose that some internal functions are called
36
- plist.expects(:system).twice
33
+ plist.expects(:system).never
37
34
  plist.save
38
35
  end
39
36
 
40
37
  context "plist modification" do
38
+
41
39
  it "allows adding new plist item" do
42
40
  plist = Geoffrey::Plist.new
43
41
  plist.file(@plist_file.path)
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ require 'geoffrey'
4
+
5
+
6
+ class Foo
7
+ include Geoffrey::Reporter
8
+ end
9
+
10
+ describe Geoffrey::Reporter do
11
+
12
+ it "gives classes the ability to echo" do
13
+ foo = Foo.new
14
+ capturing_output { foo.echo "Hola" }.should include("Hola")
15
+ end
16
+
17
+ it "gives classes the ability to print debug information" do
18
+ foo = Foo.new
19
+ capturing_output { foo.debug "useful" }.should_not include("useful")
20
+ capturing_output { foo.verbose(true); foo.debug "useful" }.should include("useful")
21
+ capturing_output { foo.verbose(false); foo.debug "useful" }.should_not include("useful")
22
+ end
23
+
24
+ end
25
+
26
+ describe Geoffrey::Package do
27
+
28
+ before(:each) do
29
+ @package = Geoffrey::Package.new
30
+ end
31
+
32
+ it "prints proper error when no url specified" do
33
+ capturing_output { @package.download_and_decompress }.should include("URL not specified")
34
+ end
35
+
36
+ it "prints proper error when file can't be downloaded for some reason" do
37
+ capturing_output do
38
+ @package.url("/nothing/to/see/here.txt")
39
+ lambda { @package.download_and_decompress }.should raise_error
40
+ end.should include("Can't find file /nothing/to/see/here.txt")
41
+ end
42
+
43
+ it "prints the description when given" do
44
+ capturing_output do
45
+ @package.url(File.dirname(__FILE__) + "/static/test.txt")
46
+ @package.description 'Something useful'
47
+ @package.download_and_decompress
48
+ end.should include("Something useful")
49
+ end
50
+
51
+ it "prints proper message when the conditions don't apply" do
52
+ capturing_output do
53
+ @package.url(File.dirname(__FILE__) + "/static/test.txt")
54
+ @package.options :if => Proc.new { false }
55
+ @package.download_and_decompress
56
+ end.strip.should include("test.txt is already installed.")
57
+ end
58
+
59
+ end
60
+
61
+ describe Geoffrey::Plist do
62
+
63
+ it "prints proper error when file is not found or has bad format" do
64
+ pending
65
+ end
66
+
67
+ it "prints amount of changes done" do
68
+ pending
69
+ end
70
+
71
+ it "prints nothing if no values where changed" do
72
+ pending
73
+ end
74
+
75
+ it "prints information about process being restarted" do
76
+ pending
77
+ end
78
+
79
+ end
@@ -1,8 +1,11 @@
1
- require 'bundler'
2
- Bundler.setup(:default,:test)
3
- require 'geoffrey'
4
1
  require 'tempfile'
2
+ require 'stringio'
3
+ require 'geoffrey'
4
+ require 'support/helpers'
5
+
6
+ Geoffrey.environment = :test
5
7
 
6
8
  Spec::Runner.configure do |config|
7
9
  config.mock_with :mocha
10
+ config.include Helpers
8
11
  end
@@ -0,0 +1,17 @@
1
+ module Helpers
2
+
3
+ # Small helper to capture output oft hings happening inside a block
4
+ # @example
5
+ # out = capturing_output do
6
+ # puts "Hello!"
7
+ # end
8
+ # out # => "Hello\n"
9
+ def capturing_output(&block)
10
+ $stdout = StringIO.new
11
+ yield
12
+ out = $stdout.string
13
+ $stdout = STDOUT
14
+ return out
15
+ end
16
+
17
+ end
@@ -0,0 +1,3 @@
1
+ url 'http://some.fic/tional/url.dmg'
2
+ description "This package should do awesome stuff"
3
+ options :sudo => true
File without changes
@@ -0,0 +1,4 @@
1
+ name 'Adium'
2
+ description "Adium is a free instant messaging application for Mac OS X that can connect to AIM, MSN, Jabber, Yahoo, and more."
3
+ url 'http://adiumx.cachefly.net/Adium_1.3.10.dmg'
4
+ options :unless => Proc.new { File.exists?("/Applications/Adium.app") }
@@ -0,0 +1,4 @@
1
+ name "Growl"
2
+ description "Growl lets Mac OS X applications unintrusively tell you when things happen."
3
+ url 'http://growl.cachefly.net/Growl-1.2.1.dmg'
4
+ options :sudo => true, :unless => Proc.new { File.exists?("/Library/PreferencePanes/Growl.prefPane") }
@@ -0,0 +1,4 @@
1
+ name "Skype"
2
+ description "Call landlines and mobiles worldwide"
3
+ url 'http://www.skype.com/go/getskype-macosx.dmg'
4
+ options :unless => Proc.new { File.exists?("/Applications/Skype.app") }
@@ -0,0 +1,4 @@
1
+ name "UnRarX"
2
+ description "UnRarX - Mac OS X RAR Extraction Utility"
3
+ url 'http://www.unrarx.com/files/UnRarX_2.2.zip'
4
+ options :unless => Proc.new { File.exists?("/Applications/UnRarX.app") }
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geoffrey
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
7
+ - 1
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ version: 0.1.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Albert Llop
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-08-31 00:00:00 +02:00
17
+ date: 2010-09-10 00:00:00 +02:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ~>
28
27
  - !ruby/object:Gem::Version
29
- hash: 15424063
30
28
  segments:
31
29
  - 1
32
30
  - 0
@@ -44,7 +42,6 @@ dependencies:
44
42
  requirements:
45
43
  - - ~>
46
44
  - !ruby/object:Gem::Version
47
- hash: 27
48
45
  segments:
49
46
  - 1
50
47
  - 3
@@ -53,72 +50,107 @@ dependencies:
53
50
  type: :development
54
51
  version_requirements: *id002
55
52
  - !ruby/object:Gem::Dependency
56
- name: mocha
53
+ name: autotest
57
54
  prerelease: false
58
55
  requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: mocha
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
59
69
  none: false
60
70
  requirements:
61
71
  - - ~>
62
72
  - !ruby/object:Gem::Version
63
- hash: 43
64
73
  segments:
65
74
  - 0
66
75
  - 9
67
76
  - 8
68
77
  version: 0.9.8
69
78
  type: :development
70
- version_requirements: *id003
79
+ version_requirements: *id004
71
80
  - !ruby/object:Gem::Dependency
72
81
  name: yard
73
82
  prerelease: false
74
- requirement: &id004 !ruby/object:Gem::Requirement
83
+ requirement: &id005 !ruby/object:Gem::Requirement
75
84
  none: false
76
85
  requirements:
77
86
  - - ~>
78
87
  - !ruby/object:Gem::Version
79
- hash: 7
80
88
  segments:
81
89
  - 0
82
90
  - 6
83
91
  - 0
84
92
  version: 0.6.0
85
93
  type: :development
86
- version_requirements: *id004
94
+ version_requirements: *id005
87
95
  - !ruby/object:Gem::Dependency
88
96
  name: bluecloth
89
97
  prerelease: false
90
- requirement: &id005 !ruby/object:Gem::Requirement
98
+ requirement: &id006 !ruby/object:Gem::Requirement
91
99
  none: false
92
100
  requirements:
93
101
  - - ~>
94
102
  - !ruby/object:Gem::Version
95
- hash: 1
96
103
  segments:
97
104
  - 2
98
105
  - 0
99
106
  - 7
100
107
  version: 2.0.7
101
108
  type: :development
102
- version_requirements: *id005
109
+ version_requirements: *id006
103
110
  - !ruby/object:Gem::Dependency
104
111
  name: plist4r
105
112
  prerelease: false
106
- requirement: &id006 !ruby/object:Gem::Requirement
113
+ requirement: &id007 !ruby/object:Gem::Requirement
107
114
  none: false
108
115
  requirements:
109
116
  - - ">="
110
117
  - !ruby/object:Gem::Version
111
- hash: 3
112
118
  segments:
113
119
  - 0
114
120
  version: "0"
115
121
  type: :runtime
116
- version_requirements: *id006
122
+ version_requirements: *id007
123
+ - !ruby/object:Gem::Dependency
124
+ name: progressbar
125
+ prerelease: false
126
+ requirement: &id008 !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ segments:
132
+ - 0
133
+ version: "0"
134
+ type: :runtime
135
+ version_requirements: *id008
136
+ - !ruby/object:Gem::Dependency
137
+ name: thor
138
+ prerelease: false
139
+ requirement: &id009 !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ type: :runtime
148
+ version_requirements: *id009
117
149
  description: Geoffrey is a little helper to aid in the installation of packages and configuration of various Mac OSX applications.
118
150
  email:
119
151
  - mrsimo@gmail.com
120
- executables: []
121
-
152
+ executables:
153
+ - geoffrey
122
154
  extensions: []
123
155
 
124
156
  extra_rdoc_files: []
@@ -129,17 +161,23 @@ files:
129
161
  - Gemfile.lock
130
162
  - README.md
131
163
  - Rakefile
164
+ - bin/geoffrey
132
165
  - geoffrey.gemspec
133
166
  - lib/geoffrey.rb
167
+ - lib/geoffrey/cli.rb
134
168
  - lib/geoffrey/package.rb
135
169
  - lib/geoffrey/plist.rb
170
+ - lib/geoffrey/reporter.rb
136
171
  - lib/geoffrey/version.rb
172
+ - spec/cli_spec.rb
173
+ - spec/geoffrey_spec.rb
137
174
  - spec/package_spec.rb
138
175
  - spec/plist_spec.rb
176
+ - spec/reporter_spec.rb
139
177
  - spec/spec.opts
140
178
  - spec/spec_helper.rb
141
179
  - spec/static/example.plist
142
- - spec/static/test.dmg
180
+ - spec/static/test 1.dmg
143
181
  - spec/static/test.error.dmg
144
182
  - spec/static/test.error.gz
145
183
  - spec/static/test.error.tar.gz
@@ -148,7 +186,15 @@ files:
148
186
  - spec/static/test.tgz
149
187
  - spec/static/test.txt
150
188
  - spec/static/test.txt.gz
189
+ - spec/static/test.txt.gz.123
151
190
  - spec/static/test.txt.zip
191
+ - spec/support/helpers.rb
192
+ - spec/templates/growl.rb
193
+ - spec/templates/simbl.rb
194
+ - templates/adium.rb
195
+ - templates/growl.rb
196
+ - templates/skype.rb
197
+ - templates/unrarx.rb
152
198
  has_rdoc: true
153
199
  homepage:
154
200
  licenses: []
@@ -163,7 +209,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
163
209
  requirements:
164
210
  - - ">="
165
211
  - !ruby/object:Gem::Version
166
- hash: 3
167
212
  segments:
168
213
  - 0
169
214
  version: "0"
@@ -172,7 +217,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
217
  requirements:
173
218
  - - ">="
174
219
  - !ruby/object:Gem::Version
175
- hash: 23
176
220
  segments:
177
221
  - 1
178
222
  - 3