rubyosa 0.3.0.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +3 -0
- data/bin/rdoc-osa +137 -78
- data/sample/Photoshop/new_doc.rb +13 -0
- data/sample/Photoshop/new_doc_with_text.rb +34 -0
- data/sample/iTunes/name_that_tune.rb +97 -0
- data/sample/iTunes/tag_genre_lastfm.rb +16 -4
- data/src/lib/rbosa.rb +808 -749
- data/src/lib/rbosa_properties.rb +7 -2
- data/src/rbosa.c +11 -1
- metadata +5 -2
data/AUTHORS
CHANGED
@@ -3,9 +3,12 @@ Laurent Sansonetti <lsansonetti@apple.com>
|
|
3
3
|
|
4
4
|
Contributors:
|
5
5
|
Aaron Patterson <aaron.patterson@gmail.com>
|
6
|
+
Carlos Villela <carlos.villela@gmail.com>
|
6
7
|
James MacAulay <jmacaulay@gmail.com>
|
8
|
+
Justin Palmer <encytemedia@gmail.com>
|
7
9
|
Michael Pruett <michael@68k.org>
|
8
10
|
Michail Pishchagin <mblsha@gmail.com>
|
11
|
+
Mike Naberezny <mike@maintainable.com>
|
9
12
|
Sebastian Delmont <sd@notso.net>
|
10
13
|
Stefan Saasen <s@juretta.com>
|
11
14
|
Terry Donoghue <donoghue@apple.com>
|
data/bin/rdoc-osa
CHANGED
@@ -28,146 +28,205 @@
|
|
28
28
|
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
29
|
# POSSIBILITY OF SUCH DAMAGE.
|
30
30
|
|
31
|
-
require 'rbosa'
|
32
31
|
require 'tmpdir'
|
33
32
|
require 'rbconfig'
|
33
|
+
require 'rbosa'
|
34
34
|
|
35
35
|
def usage
|
36
|
-
|
37
|
-
Usage: #{$0} [--name | --path | --bundle_id | --signature] <criterion> [rdoc-options...]
|
36
|
+
STDERR.puts <<-EOS
|
37
|
+
Usage: #{$0} [--addition] [--name | --path | --bundle_id | --signature] <criterion> [rdoc-options...]
|
38
38
|
Examples:
|
39
39
|
# Generate HTML documentation for iTunes:
|
40
40
|
#{$0} --name iTunes
|
41
41
|
# Generate RI documentation for iTunes:
|
42
42
|
#{$0} --name iTunes --ri
|
43
|
+
# Generate HTML documentation for the StandardAdditions scriptable addition:
|
44
|
+
#{$0} --addition --name StandardAdditions
|
43
45
|
See rdoc --help for additional options.
|
44
46
|
EOS
|
45
|
-
|
47
|
+
exit 1
|
46
48
|
end
|
47
49
|
|
48
50
|
def unique_tmp_path(base, extension='', dir=Dir.tmpdir)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
i = 0
|
52
|
+
loop do
|
53
|
+
p = File.join(dir, "#{base}-#{i}-#{Process.pid}" + extension)
|
54
|
+
return p unless File.exists?(p)
|
55
|
+
i += 1
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
usage unless ARGV.length >= 2
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
addition = key = criterion = nil
|
61
|
+
while arg = ARGV.shift
|
62
|
+
case arg
|
63
|
+
when '--addition'
|
64
|
+
addition = true
|
65
|
+
when '--name', '--path', '--bundle_id', '--signature'
|
66
|
+
if key
|
67
|
+
$stderr.puts "You cannot use --name, --path, --bundle_id or --signature more than once."
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
key = arg[2..-1].intern
|
71
|
+
criterion = ARGV.shift
|
72
|
+
usage if criterion.nil?
|
73
|
+
else
|
74
|
+
if key and criterion
|
75
|
+
ARGV.unshift(arg)
|
76
|
+
break
|
77
|
+
end
|
78
|
+
usage
|
79
|
+
end
|
70
80
|
end
|
71
81
|
|
72
82
|
DOC_NOT_AVAILABLE = 'Documentation not available.'
|
73
83
|
|
74
|
-
app =
|
84
|
+
app = app_name = nil
|
85
|
+
|
86
|
+
if addition
|
87
|
+
app_name = criterion if key == :name
|
88
|
+
mod = Module.new
|
89
|
+
OSA.const_set('TheApplication', mod)
|
90
|
+
klass = Class.new(OSA::Element)
|
91
|
+
mod.const_set('Application', klass)
|
92
|
+
klass.class_eval do
|
93
|
+
include OSA::EventDispatcher
|
94
|
+
METHODS_DESCRIPTION = []
|
95
|
+
DESCRIPTION = 'The application class.'
|
96
|
+
end
|
97
|
+
app = klass.new.merge(key => criterion)
|
98
|
+
else
|
99
|
+
app = OSA.app(key => criterion)
|
100
|
+
app_name = if app.respond_to?(:name)
|
101
|
+
app.name
|
102
|
+
else
|
103
|
+
if key != :name
|
104
|
+
STDERR.puts "Can't guess the application name, because the application doesn't have a #name method. Please use `--name' instead."
|
105
|
+
exit 1
|
106
|
+
else
|
107
|
+
criterion
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
75
112
|
mod = OSA.const_get(app.class.name.scan(/^OSA::(.+)::Application$/).to_s)
|
76
113
|
fake_ruby_src = mod.constants.map do |const_name|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
114
|
+
obj = mod.const_get(const_name)
|
115
|
+
case obj
|
116
|
+
when Class
|
117
|
+
# Class.
|
118
|
+
methods_desc = obj.const_get('METHODS_DESCRIPTION').map do |method|
|
119
|
+
args_doc, args_def, args_def_opt = '', '', ''
|
120
|
+
if method.args and !method.args.empty?
|
121
|
+
args_doc_ary, args_def_ary, args_def_opt_ary = [], [], []
|
122
|
+
method.args.each do |x|
|
123
|
+
arg = x.name
|
124
|
+
desc = x.description
|
125
|
+
desc = DOC_NOT_AVAILABLE if desc.empty?
|
126
|
+
args_doc_ary << " # #{arg}::\n # #{desc}" + (x.optional? ? ' Optional. Can be passed as a Hash key/value.' : '')
|
127
|
+
if x.optional?
|
128
|
+
args_def_ary << x.name + '=nil'
|
129
|
+
args_def_opt_ary << ':' + x.name + ' => nil'
|
130
|
+
else
|
131
|
+
args_def_ary << x.name
|
132
|
+
args_def_opt_ary << x.name
|
133
|
+
end
|
134
|
+
end
|
135
|
+
args_doc = args_doc_ary.join("\n")
|
136
|
+
args_def = '(' + args_def_ary.join(', ') + ')'
|
137
|
+
args_def_opt = '(' + args_def_opt_ary.join(', ') + ')'
|
138
|
+
end
|
139
|
+
if method.result
|
140
|
+
args_doc << "\n" unless args_doc.empty?
|
141
|
+
desc = method.result.description
|
142
|
+
desc = DOC_NOT_AVAILABLE if desc.empty?
|
143
|
+
args_doc << " # Returns::\n # #{desc}\n"
|
144
|
+
end
|
145
|
+
<<EOS
|
109
146
|
# call-seq:
|
110
|
-
#
|
111
|
-
#
|
147
|
+
# #{method.name + args_def}
|
148
|
+
# #{args_def_opt != args_def ? method.name + args_def_opt : ''}
|
112
149
|
#
|
113
150
|
# #{method.description}
|
114
151
|
#{args_doc}
|
115
152
|
def #{method.name}#{args_def}; end
|
116
153
|
EOS
|
117
|
-
|
118
|
-
|
154
|
+
end
|
155
|
+
<<EOS
|
119
156
|
# #{(obj.const_get('DESCRIPTION') || 'n/a')}
|
120
157
|
class #{obj.name} < #{obj.superclass}
|
121
158
|
#{methods_desc.join.rstrip}
|
122
159
|
end
|
123
160
|
|
124
161
|
EOS
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
162
|
+
when Module
|
163
|
+
# Enumeration group.
|
164
|
+
next unless obj.const_defined?(:DESCRIPTION)
|
165
|
+
enums_desc = obj.const_get(:DESCRIPTION).map do |item|
|
166
|
+
<<EOS
|
130
167
|
# #{item.description}
|
131
168
|
#{item.name} = '#{obj.const_get(item.name).code}'
|
132
169
|
EOS
|
133
|
-
|
134
|
-
|
170
|
+
end
|
171
|
+
<<EOS
|
135
172
|
module #{mod.name}::#{const_name}
|
136
173
|
#{enums_desc}
|
137
174
|
end
|
138
175
|
|
139
176
|
EOS
|
140
|
-
|
177
|
+
end
|
141
178
|
end.
|
142
179
|
join
|
143
180
|
|
144
|
-
|
145
|
-
|
181
|
+
header = if addition
|
182
|
+
<<EOS
|
183
|
+
# This documentation describes the RubyOSA API for the #{criterion} scriptable addition. It has been automatically generated.
|
184
|
+
#
|
185
|
+
# In order to use this API you have to merge the scriptable addition into an application object. For instance:
|
186
|
+
#
|
187
|
+
# OSA.app('iTunes').merge(#{app_name ? "'#{app_name}'" : ":#{key} => '#{criterion}'"})
|
188
|
+
#
|
189
|
+
# The module OSA::TheApplication is fake, everything inside will be defined in the module of the application you are controlling (for iTunes, in OSA::ITunes).
|
190
|
+
EOS
|
191
|
+
else
|
192
|
+
<<EOS
|
193
|
+
# This documentation describes the RubyOSA API for the #{app_name} application. It has been automatically generated.
|
146
194
|
#
|
147
195
|
# The main class is #{mod.name}::Application, of which an instance is created likewise:
|
148
196
|
#
|
149
|
-
# OSA.app('#{
|
197
|
+
# OSA.app('#{app_name}')
|
198
|
+
EOS
|
199
|
+
end
|
200
|
+
|
201
|
+
header << <<EOS
|
150
202
|
#
|
151
203
|
# For more information about RubyOSA, please visit the project homepage: http://rubyosa.rubyforge.org.
|
152
204
|
module OSA; end
|
153
|
-
# The #{
|
205
|
+
# The #{app_name} module.
|
154
206
|
module #{mod.name}; end
|
155
207
|
EOS
|
156
208
|
|
209
|
+
fake_ruby_src = header << fake_ruby_src
|
210
|
+
|
157
211
|
rdoc_flags = ''
|
158
|
-
|
212
|
+
datadir = if Config.respond_to?(:datadir)
|
213
|
+
Config.datadir('rubyosa')
|
214
|
+
else
|
215
|
+
File.join(Config::CONFIG['datadir'], 'rubyosa')
|
216
|
+
end
|
217
|
+
template = File.join(datadir, 'rdoc_html.rb')
|
159
218
|
if File.exists?(template)
|
160
|
-
|
219
|
+
rdoc_flags << " --template '#{template}' "
|
161
220
|
end
|
162
|
-
rdoc_flags << " --title '#{
|
221
|
+
rdoc_flags << " --title '#{app_name} RubyOSA API' "
|
163
222
|
rdoc_flags << ' --main OSA '
|
164
|
-
rdoc_flags << ARGV
|
223
|
+
rdoc_flags << ARGV.join(' ')
|
165
224
|
|
166
|
-
path = unique_tmp_path(
|
225
|
+
path = unique_tmp_path(app_name, '.rb')
|
167
226
|
File.open(path, 'w') { |io| io.puts fake_ruby_src }
|
168
227
|
line = "rdoc #{rdoc_flags} \"#{path}\""
|
169
228
|
unless system(line)
|
170
|
-
|
171
|
-
|
229
|
+
STDERR.puts "Error when executing `#{line}' : #{$?}"
|
230
|
+
exit 1
|
172
231
|
end
|
173
232
|
File.unlink(path)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Creates a new Photoshop document with a given title and size.
|
2
|
+
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
4
|
+
require 'rbosa'
|
5
|
+
|
6
|
+
app = OSA.app('Adobe Photoshop CS2')
|
7
|
+
app.settings.ruler_units = OSA::AdobePhotoshopCS2::E440::PIXEL_UNITS
|
8
|
+
|
9
|
+
app.make(OSA::AdobePhotoshopCS2::Document, nil, :with_properties => {
|
10
|
+
:name => 'Ruby Rocks',
|
11
|
+
:width => 500,
|
12
|
+
:height => 500
|
13
|
+
})
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Creates a new Photoshop document with a given title and size, and adds a text
|
2
|
+
# layer on it.
|
3
|
+
|
4
|
+
begin require 'rubygems'; rescue LoadError; end
|
5
|
+
require 'rbosa'
|
6
|
+
|
7
|
+
app = OSA.app('Adobe Photoshop CS2')
|
8
|
+
app.settings.ruler_units = OSA::AdobePhotoshopCS2::E440::PIXEL_UNITS
|
9
|
+
app.instance_eval do
|
10
|
+
def create_document(options = {})
|
11
|
+
make(OSA::AdobePhotoshopCS2::Document, nil, :with_properties => {
|
12
|
+
:name => 'Ruby Rocks',
|
13
|
+
:width => 500,
|
14
|
+
:height => 500
|
15
|
+
}.merge(options))
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_layer(name, kind)
|
19
|
+
kinds = %w(NORMAL GRADIENTFILL PATTERNFILL TEXT SOLIDFILL)
|
20
|
+
do_javascript %(
|
21
|
+
var doc = app.activeDocument;
|
22
|
+
var layer = doc.artLayers.add();
|
23
|
+
layer.name = "#{name || ''}";
|
24
|
+
layer.kind = LayerKind.#{kinds.detect {|k| k.downcase == kind} || 'NORMAL'};
|
25
|
+
)
|
26
|
+
current_document.art_layers[0]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
app.create_document(:name => 'Schweet')
|
31
|
+
layer = app.add_layer('A text layer', 'text')
|
32
|
+
texto = layer.text_object
|
33
|
+
texto.size = 40
|
34
|
+
texto.contents = "This is some text"
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Plays a track of your iTunes library at random and asks you to guess the name of the track.
|
2
|
+
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
4
|
+
require 'rbosa'
|
5
|
+
|
6
|
+
OSA.app('iTunes') # initialize the constants
|
7
|
+
|
8
|
+
class OSA::ITunes::Application
|
9
|
+
|
10
|
+
def library_source
|
11
|
+
sources.find {|s| s.kind == OSA::ITunes::ESRC::LIBRARY }
|
12
|
+
end
|
13
|
+
|
14
|
+
def library
|
15
|
+
library_source.playlists.find {|p| p.name == 'Library' }
|
16
|
+
end
|
17
|
+
|
18
|
+
def party_shuffle
|
19
|
+
library_source.playlists.find {|p| p.special_kind == OSA::ITunes::ESPK::PARTY_SHUFFLE }
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class OSA::ITunes::Playlist
|
25
|
+
|
26
|
+
def random_track
|
27
|
+
tracks[rand * tracks.size]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class OSA::ITunes::Track
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"#{artist} - #{name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class NameThatTune
|
42
|
+
attr_accessor :score
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
@itunes = OSA.app('iTunes')
|
46
|
+
end
|
47
|
+
|
48
|
+
def finish
|
49
|
+
puts "Thanks for playing! Score: #{score}"
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
|
53
|
+
def start
|
54
|
+
@score = 0
|
55
|
+
while true
|
56
|
+
@itunes.party_shuffle.play
|
57
|
+
@itunes.next_track
|
58
|
+
|
59
|
+
options = generate_options
|
60
|
+
options.each_with_index { |track, i| puts "#{i+1} - #{track}" }
|
61
|
+
|
62
|
+
selected = gets.to_i
|
63
|
+
|
64
|
+
finish if selected == 0
|
65
|
+
|
66
|
+
if correct?(options, selected)
|
67
|
+
points = calculate_points_for_correct_choice
|
68
|
+
puts "Correct! #{points} points"
|
69
|
+
self.score += points
|
70
|
+
else
|
71
|
+
puts "Sorry! That was #{@itunes.current_track}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def correct?(options, selected)
|
79
|
+
options[selected-1] == @itunes.current_track
|
80
|
+
end
|
81
|
+
|
82
|
+
def calculate_points_for_correct_choice
|
83
|
+
points = (@itunes.player_position > 10 ? 1 : 10 - @itunes.player_position) * 1000
|
84
|
+
points += (@itunes.current_track.played_count > 10 ? 1 : 10 - @itunes.current_track.played_count) * 100
|
85
|
+
points.to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_options(count = 5)
|
89
|
+
options = []
|
90
|
+
options << @itunes.current_track
|
91
|
+
(count - 1).times {|i| options << @itunes.library.random_track }
|
92
|
+
options = options.sort_by { rand }
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
NameThatTune.new.start
|
@@ -4,17 +4,29 @@ begin require 'rubygems'; rescue LoadError; end
|
|
4
4
|
require 'rbosa'
|
5
5
|
require 'net/http'
|
6
6
|
require 'cgi'
|
7
|
-
require 'rexml/document'
|
7
|
+
require 'rexml/document'
|
8
8
|
include REXML
|
9
9
|
|
10
10
|
itunes = OSA.app('iTunes')
|
11
|
+
|
11
12
|
selection = itunes.selection.get
|
12
13
|
if selection.empty?
|
13
14
|
$stderr.puts "Please select some tracks."
|
14
15
|
exit 1
|
15
16
|
end
|
17
|
+
|
18
|
+
first = selection.first.artist
|
19
|
+
feed = "http://ws.audioscrobbler.com/1.0/artist/#{CGI::escape(first)}/toptags.xml"
|
20
|
+
doc = Document.new(Net::HTTP.get(URI(feed)))
|
21
|
+
|
16
22
|
selection.each do |track|
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
if doc.root.attributes['artist'] == track.artist
|
24
|
+
genre = doc.root[1][1].text.capitalize
|
25
|
+
else
|
26
|
+
puts 'Querying Last.fm again...'
|
27
|
+
feed = "http://ws.audioscrobbler.com/1.0/artist/#{CGI::escape(track.artist)}/toptags.xml"
|
28
|
+
doc = Document.new(Net::HTTP.get(URI(feed)))
|
29
|
+
genre = doc.root[1][1].text.capitalize
|
30
|
+
end
|
31
|
+
track.genre = genre
|
20
32
|
end
|