launchdr 3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ lib/launchdr/launchd.rb
2
+ lib/launchdr/property_list.rb
3
+ lib/launchdr/task.rb
4
+ lib/launchdr.rb
5
+ Rakefile.rb
6
+ README.markdown
7
+ .manifest
@@ -0,0 +1,66 @@
1
+ LaunchDoctor
2
+ ============
3
+ I've got a PhD in [launchd][], and I'm not afraid to use it!
4
+
5
+ [launchd]: <http://en.wikipedia.org/wiki/Launchd> "launchd on Wikipedia"
6
+
7
+ Usage
8
+ -----
9
+ LaunchDoctor is really simple. It is essentially an easy interface to
10
+ [launchd][]'s [property list][plist] files.
11
+
12
+ There are three ways to use LaunchDoctor - the simplest being directly
13
+ creating property lists and treating them as hashes:
14
+
15
+ require 'launchdr'
16
+ plist = LaunchDr::Launchd.new "name.elliottcable.launchdr.test"
17
+ plist[:program_arguments] = ['/Applications/Calculator.app/Contents/MacOS/Calculator']
18
+ plist.dump LaunchDr::Launchd::Paths[:user_agent]
19
+
20
+ The second is to use the common block idiom, provided by the `LaunchDr()`
21
+ method:
22
+
23
+ require 'launchdr'
24
+ LaunchDr "name.elliottcable.launchdr.test" do |plist|
25
+ plist[:program_arguments] = ['/Applications/Calculator.app/Contents/MacOS/Calculator']
26
+ end
27
+
28
+ LaunchDoctor will automatically add the property list to `launchctl`, and then
29
+ start it running. Once you run the above snippet, the target will immediately
30
+ be launched for the first time.
31
+
32
+ LaunchDoctor can write (`dump`) the property lists to any place on your disk,
33
+ but the idiom method assumes you're going to want to use one of the
34
+ directories that launchd checks for property lists. These are stored in the
35
+ `Launchd::Paths` array. The default is to place it in the user-owned agents
36
+ directory at `~/Library/LaunchAgents`.
37
+
38
+ LaunchDoctor also preforms some 'prettification' on the keys provided by
39
+ launchd's property list structure. All of the keys on the
40
+ [`launchd.plist` manpage][manpage] are available, but they can also be used as
41
+ true 'Ruby-ish' symbol keys. All of the following are legal:
42
+
43
+ plist["UserName"] = "elliottcable"
44
+ plist[:UserName] = "elliottcable"
45
+ plist[:user_name] = "elliottcable"
46
+
47
+ Finally, you can use the Rake task interface to the last method. It simply
48
+ wraps the last method inside a rake task. This method is really great if you
49
+ want to provide a way to let users make your gem's binary run all the time:
50
+
51
+ require 'launchdr/task'
52
+ LaunchDr::Task.new :launchd, :bin => 'jello', :arguments => ['-D', 'shortener', 'grabup']
53
+
54
+ This isn't very flexible, but it's not very complicated either. If you need
55
+ more control over the plist, just use the second method inside a `task` block.
56
+ This method defaults to making your gem's binary `run_at_load` and be
57
+ `keep_alive` as well, so it won't die.
58
+
59
+ [plist]: <http://en.wikipedia.org/wiki/Property_list> "Property list on Wikipedia"
60
+ [manpage]: <http://developer.apple.com/DOCUMENTATION/DARWIN/Reference/ManPages/man5/launchd.plist.5.html> "Mac OS X Manual Page for launchd.plist(5)"
61
+ [rake]: <http://rake.rubyforge.org/> "Rake's RDocs"
62
+
63
+ Requirements
64
+ ------------
65
+ - Plist - `gem install plist`
66
+ - Ruby Facets - `gem install facets`
@@ -0,0 +1,40 @@
1
+ ($:.unshift File.expand_path(File.join( File.dirname(__FILE__), 'lib' ))).uniq!
2
+ require 'launchdr'
3
+
4
+ # =======================
5
+ # = Gem packaging tasks =
6
+ # =======================
7
+ begin
8
+ require 'echoe'
9
+
10
+ task :package => :'package:package'
11
+ task :install => :'package:install'
12
+ task :manifest => :'package:manifest'
13
+ task :clobber => :'package:clobber'
14
+ namespace :package do
15
+ Echoe.new('launchdr', LaunchDr::Version) do |g|
16
+ g.author = ['elliottcable']
17
+ g.email = ['launchdr@elliottcable.com']
18
+ g.summary = "One stop shop for launchd property list creation. The doctor is *in*!"
19
+ g.url = 'http://github.com/elliottcable/launchdr'
20
+ g.dependencies = ['plist', 'facets', 'uuid']
21
+ g.development_dependencies = ['echoe >= 3.0.2']
22
+ g.manifest_name = '.manifest' # I don't want this showing up <,<
23
+ g.retain_gemspec = true # perfect for GitHub
24
+ g.rakefile_name = 'Rakefile.rb' # It's a Ruby file, why not have .rb?
25
+ g.ignore_pattern = /^\.git\/|\.gemspec/
26
+ end
27
+
28
+ desc 'tests packaged files to ensure they are all present'
29
+ task :verify => :package do
30
+ # An error message will be displayed if files are missing
31
+ if system %(ruby -e "require 'rubygems'; require 'pkg/launchdr-#{LaunchDr::Version}/lib/launchdr'")
32
+ puts "\nThe library files are present"
33
+ end
34
+ end
35
+ end
36
+
37
+ rescue LoadError
38
+ desc 'You need the `echoe` gem to package LaunchDr'
39
+ task :package
40
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{launchdr}
5
+ s.version = "3"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["elliottcable"]
9
+ s.date = %q{2009-02-11}
10
+ s.description = %q{One stop shop for launchd property list creation. The doctor is *in*!}
11
+ s.email = ["launchdr@elliottcable.com"]
12
+ s.extra_rdoc_files = ["lib/launchdr/launchd.rb", "lib/launchdr/property_list.rb", "lib/launchdr/task.rb", "lib/launchdr.rb", "README.markdown"]
13
+ s.files = ["lib/launchdr/launchd.rb", "lib/launchdr/property_list.rb", "lib/launchdr/task.rb", "lib/launchdr.rb", "Rakefile.rb", "README.markdown", ".manifest", "launchdr.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/elliottcable/launchdr}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Launchdr", "--main", "README.markdown"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{launchdr}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{One stop shop for launchd property list creation. The doctor is *in*!}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<plist>, [">= 0"])
28
+ s.add_runtime_dependency(%q<facets>, [">= 0"])
29
+ s.add_runtime_dependency(%q<uuid>, [">= 0"])
30
+ s.add_development_dependency(%q<echoe>, [">= 0", "= 3.0.2"])
31
+ else
32
+ s.add_dependency(%q<plist>, [">= 0"])
33
+ s.add_dependency(%q<facets>, [">= 0"])
34
+ s.add_dependency(%q<uuid>, [">= 0"])
35
+ s.add_dependency(%q<echoe>, [">= 0", "= 3.0.2"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<plist>, [">= 0"])
39
+ s.add_dependency(%q<facets>, [">= 0"])
40
+ s.add_dependency(%q<uuid>, [">= 0"])
41
+ s.add_dependency(%q<echoe>, [">= 0", "= 3.0.2"])
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ require 'launchdr/property_list'
2
+ require 'launchdr/launchd'
3
+
4
+
5
+ def LaunchDr label, opts = {}
6
+ LaunchDr.create label, opts, &Proc.new
7
+ end
8
+ module LaunchDr
9
+ Version = 3
10
+ Options = Hash.new
11
+
12
+ def self.create label, opts = {}
13
+ plist = Launchd.new label
14
+
15
+ yield plist if block_given?
16
+
17
+ raise "You must run as a root user and allow_system_agents! if you wish to preform the dangerous act of writing to #{Launchd::Paths[:system_agent]}!" unless !(opts[:type] == :system_agent) or Options[:system_agents_allowed]
18
+ raise "You must run as a root user, allow_system_agents!, *and* allow_system_daemons! if you wish to preform the very dangerous act of writing to #{Launchd::Paths[:system_daemon]}!" unless !(opts[:type] == :system_daemon) or Options[:system_daemon_allowed]
19
+ path = Launchd::Paths[opts[:type] || :user_agent]
20
+ raise "Type error! Must be one of #{Launchd::Paths.keys.join(", ")}" unless path
21
+
22
+ plist.dump File.expand_path(path)
23
+ plist.load!
24
+ end
25
+
26
+ def self.allow_system_agents!
27
+ Options[:system_agents_allowed] = true
28
+ end
29
+
30
+ def self.allow_system_daemons!
31
+ raise "You must allow System-owned agents to allow System-owned daemons!" unless Options[:system_agents_allowed]
32
+ Options[:system_daemon_allowed] = true
33
+ end
34
+
35
+
36
+ end
@@ -0,0 +1,43 @@
1
+ require 'facets/string'
2
+
3
+ module LaunchDr
4
+
5
+ class Launchd < PropertyList
6
+ Paths = {
7
+ :user_agent => "~/Library/LaunchAgents",
8
+ :agent => "/Library/LaunchAgents",
9
+ :daemon => "/Library/LaunchDaemons",
10
+ :system_agent => "/System/Library/LaunchAgents",
11
+ :system_daemon => "/System/Library/LaunchDaemons"
12
+ }
13
+
14
+ def [] key
15
+ super(key.to_s.titlecase.camelcase)
16
+ end
17
+
18
+ def []= key, value
19
+ super(key.to_s.titlecase.camelcase, value)
20
+ end
21
+
22
+ # Adds the property list to `launchctl`'s indexes, and starts it (unless
23
+ # disabled).
24
+ def load!
25
+ system "launchctl load #{file}"
26
+ end
27
+
28
+ # Removes the propertly list from `launchctl`'s indexes.
29
+ def unload!
30
+ system "launchctl unload #{file}"
31
+ end
32
+
33
+ # Finds the plist file, if it's been created
34
+ def file
35
+ Paths.values.each do |dir|
36
+ file = File.expand_path(File.join(dir, self[:label] + '.plist'))
37
+ return file if File.file? file
38
+ end
39
+ raise "#{self[:label]} doesn't exist in any of the default locations. Did you #dump it?"
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,38 @@
1
+ require 'plist'
2
+ require 'uuid'
3
+
4
+ module LaunchDr
5
+
6
+ class PropertyList
7
+ attr_reader :elements
8
+
9
+ def initialize label = nil, elements = {}
10
+ @elements = elements
11
+ @elements['Label'] = label || "rb.launchdr.#{UUID.new.generate}"
12
+ end
13
+
14
+ def [] key
15
+ @elements[key.to_s]
16
+ end
17
+
18
+ def []= key, value
19
+ @elements[key] = value
20
+ end
21
+
22
+ def to_plist
23
+ @elements.to_plist
24
+ end
25
+
26
+ def dump path
27
+ out = File.new(File.expand_path(File.join(path, @elements['Label'] + '.plist')), 'w+')
28
+ out.puts Plist::Emit.dump @elements
29
+ out.close
30
+ end
31
+
32
+ def self.load filename
33
+ elements = Plist::parse_xml File.new(File.expand_path filename, 'r')
34
+ new elements[:label], elements
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,33 @@
1
+ require 'launchdr'
2
+
3
+ module LaunchDr
4
+
5
+ module Task
6
+ def self.new name = :launchd, opts = {}
7
+ raise "`LaunchDr.task`'s options must include the name of the binary for your gem!" unless opts[:bin]
8
+ opts = {:desc => 'Creates a launchd property list for this gem', :arguments => []}.merge opts
9
+
10
+ desc opts[:desc] unless opts[:no_desc]
11
+ task name do
12
+ path = File.expand_path File.join('', opts[:bin])
13
+ unless File.file? path
14
+ path = File.expand_path File.join(Gem::default_bindir, opts[:bin])
15
+ unless File.file? path
16
+ path = File.expand_path %x[which "#{opts[:bin]}"].chomp
17
+ end
18
+ end
19
+ unless File.file? path
20
+ raise "** Unable to locate binary #{opts[:bin]}"
21
+ end
22
+ LaunchDr "rb.launchdr.#{opts[:bin]}" do |plist|
23
+ plist[:program_arguments] = [path, opts[:arguments]].flatten
24
+ plist[:keep_alive] = true
25
+ plist[:run_at_load] = true
26
+ end
27
+ puts "** `#{[path, opts[:arguments]].flatten.join(' ')}` will now be run on startup!"
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: launchdr
3
+ version: !ruby/object:Gem::Version
4
+ hash: -124656629444251359
5
+ prerelease: false
6
+ segments:
7
+ - 3
8
+ version: "3"
9
+ platform: ruby
10
+ authors:
11
+ - elliottcable
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2009-02-11 00:00:00 -09:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: plist
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 134023568094289244
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: facets
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 134023568094289244
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :runtime
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: uuid
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 134023568094289244
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :runtime
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: echoe
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 134023568094289244
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ - - "="
74
+ - !ruby/object:Gem::Version
75
+ hash: 1556383981505405372
76
+ segments:
77
+ - 3
78
+ - 0
79
+ - 2
80
+ version: 3.0.2
81
+ type: :development
82
+ version_requirements: *id004
83
+ description: One stop shop for launchd property list creation. The doctor is *in*!
84
+ email:
85
+ - launchdr@elliottcable.com
86
+ executables: []
87
+
88
+ extensions: []
89
+
90
+ extra_rdoc_files:
91
+ - lib/launchdr/launchd.rb
92
+ - lib/launchdr/property_list.rb
93
+ - lib/launchdr/task.rb
94
+ - lib/launchdr.rb
95
+ - README.markdown
96
+ files:
97
+ - lib/launchdr/launchd.rb
98
+ - lib/launchdr/property_list.rb
99
+ - lib/launchdr/task.rb
100
+ - lib/launchdr.rb
101
+ - Rakefile.rb
102
+ - README.markdown
103
+ - .manifest
104
+ - launchdr.gemspec
105
+ has_rdoc: true
106
+ homepage: http://github.com/elliottcable/launchdr
107
+ licenses: []
108
+
109
+ post_install_message:
110
+ rdoc_options:
111
+ - --line-numbers
112
+ - --inline-source
113
+ - --title
114
+ - Launchdr
115
+ - --main
116
+ - README.markdown
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 134023568094289244
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ hash: 1922007725579590311
134
+ segments:
135
+ - 1
136
+ - 2
137
+ version: "1.2"
138
+ requirements: []
139
+
140
+ rubyforge_project: launchdr
141
+ rubygems_version: 1.3.7
142
+ signing_key:
143
+ specification_version: 2
144
+ summary: One stop shop for launchd property list creation. The doctor is *in*!
145
+ test_files: []
146
+