rucola 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +301 -0
- data/History.txt +51 -0
- data/Manifest.txt +12 -0
- data/README.txt +6 -7
- data/TODO +13 -8
- data/app_generators/rucola/rucola_generator.rb +1 -1
- data/app_generators/rucola/templates/app/controllers/application_controller.rb +2 -2
- data/app_generators/rucola/templates/test/controllers/test_application_controller.rb +22 -5
- data/app_generators/rucola/templates/test/test_helper.rb +3 -2
- data/config/hoe.rb +2 -2
- data/lib/autotest/discover.rb +9 -0
- data/lib/autotest/fail.png +0 -0
- data/lib/autotest/growl_images.rb +39 -0
- data/lib/autotest/pass.png +0 -0
- data/lib/autotest/rucola.rb +36 -0
- data/lib/autotest/sound.rb +52 -0
- data/lib/rucola/info_plist.rb +5 -0
- data/lib/rucola/initializer.rb +65 -113
- data/lib/rucola/nib.rb +2 -2
- data/lib/rucola/plugin.rb +57 -0
- data/lib/rucola/rucola_support/initialize_hooks.rb +7 -0
- data/lib/rucola/rucola_support/notifications/notifications.rb +67 -27
- data/lib/rucola/rucola_support/rc_app.rb +9 -0
- data/lib/rucola/tasks/main.rake +8 -4
- data/lib/rucola/tasks/xcode.rake +10 -6
- data/lib/rucola/test_helper.rb +14 -0
- data/lib/rucola/version.rb +1 -1
- data/lib/rucola/xcode.rb +11 -6
- data/rucola_generators/controller/controller_generator.rb +1 -1
- data/rucola_generators/controller/templates/test_controller_template.rb.erb +14 -5
- data/rucola_generators/document_model/document_model_generator.rb +3 -3
- data/rucola_generators/document_model/templates/test_document_model_template.rb.erb +18 -5
- data/rucola_generators/rucola_plugin/USAGE +6 -0
- data/rucola_generators/rucola_plugin/rucola_plugin_generator.rb +63 -0
- data/rucola_generators/rucola_plugin/templates/init.rb.erb +25 -0
- data/rucola_generators/window_controller/templates/test_window_controller_template.rb.erb +21 -5
- data/rucola_generators/window_controller/window_controller_generator.rb +1 -1
- data/test/test_document_model_generator.rb +8 -2
- data/test/test_info_plist.rb +4 -0
- data/test/test_initializer.rb +80 -0
- data/test/test_nib.rb +9 -0
- data/test/test_notifications.rb +38 -19
- data/test/test_plugin.rb +48 -0
- data/test/test_rc_app.rb +5 -0
- data/test/test_rucola_plugin_generator.rb +65 -0
- data/test/test_xcode.rb +3 -3
- data/website/index.html +49 -7
- data/website/index.txt +34 -4
- metadata +37 -2
data/lib/rucola/nib.rb
CHANGED
@@ -34,10 +34,10 @@ module Rucola
|
|
34
34
|
@data['IBClasses']
|
35
35
|
end
|
36
36
|
|
37
|
-
def add_class(class_name)
|
37
|
+
def add_class(class_name, superclass_name = 'NSObject')
|
38
38
|
classes.push({
|
39
39
|
'CLASS' => class_name,
|
40
|
-
'SUPERCLASS' =>
|
40
|
+
'SUPERCLASS' => superclass_name,
|
41
41
|
'LANGUAGE' => 'ObjC'
|
42
42
|
})
|
43
43
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'osx/cocoa'
|
2
|
+
|
3
|
+
module Rucola
|
4
|
+
# Abstract base class for Rucola plugins
|
5
|
+
# override the methods in your plugin to get code run at appropriate times
|
6
|
+
class Plugin
|
7
|
+
@@plugins = []
|
8
|
+
|
9
|
+
def self.plugins
|
10
|
+
@@plugins
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.inherited(subclass)
|
14
|
+
@@plugins << subclass.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.before_boot
|
18
|
+
plugins.each { |p| p.before_boot }
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.after_boot
|
22
|
+
plugins.each { |p| p.after_boot }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.before_process(initializer)
|
26
|
+
plugins.each {|p| p.before_process(initializer) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.after_process(initializer)
|
30
|
+
plugins.each {|p| p.after_process(initializer) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.after_launch
|
34
|
+
plugins.each {|p| p.after_launch }
|
35
|
+
end
|
36
|
+
|
37
|
+
def before_boot; end
|
38
|
+
def after_boot; end
|
39
|
+
def before_process(initializer); end
|
40
|
+
def after_process(initializer); end
|
41
|
+
def after_launch; end
|
42
|
+
end
|
43
|
+
|
44
|
+
# This class is used to be able to run hooks when the app has started.
|
45
|
+
class PluginRunner < OSX::NSObject
|
46
|
+
def initialize
|
47
|
+
center = OSX::NSNotificationCenter.defaultCenter
|
48
|
+
center.addObserver_selector_name_object(self, :after_launch, OSX::NSApplicationDidFinishLaunchingNotification, nil)
|
49
|
+
end
|
50
|
+
|
51
|
+
def after_launch(notification)
|
52
|
+
Rucola::Plugin.after_launch
|
53
|
+
end
|
54
|
+
|
55
|
+
@instance = self.alloc.init
|
56
|
+
end
|
57
|
+
end
|
@@ -8,10 +8,17 @@ module Rucola
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
# Calls all the hooks that have been added to the queue.
|
12
|
+
#
|
13
|
+
# It will also call the method +after_init+, but only if you have defined it.
|
14
|
+
# Create this method to do work that you would normally do in +initialize+ or +init+.
|
11
15
|
def initialize
|
12
16
|
# get the hooks, if they exist let them all do their after initialization work.
|
13
17
|
hooks = self.class.instance_variable_get(:@_rucola_initialize_hooks)
|
14
18
|
hooks.each { |hook| self.instance_eval(&hook) } unless hooks.nil?
|
19
|
+
|
20
|
+
# also call after_init for custom initialization code.
|
21
|
+
send :after_init if respond_to? :after_init # TODO: test
|
15
22
|
end
|
16
23
|
|
17
24
|
def self.included(base) # :nodoc
|
@@ -38,7 +38,7 @@ module Rucola
|
|
38
38
|
# # This will make sure that :win_ is expanded to :window_ in the notifications that you register.
|
39
39
|
# notification_prefix :win => :window
|
40
40
|
#
|
41
|
-
#
|
41
|
+
# when :win_did_become_key do |notification|
|
42
42
|
# # code
|
43
43
|
# end
|
44
44
|
# end
|
@@ -48,6 +48,13 @@ module Rucola
|
|
48
48
|
(@_notification_prefixes ||= {}).merge! prefixes
|
49
49
|
end
|
50
50
|
|
51
|
+
# Creates a notification and posts it to the reciever
|
52
|
+
def fire_notification(notification, obj)
|
53
|
+
notification_name = resolve_notification_name(notification)
|
54
|
+
OSX::NSNotificationCenter.defaultCenter.postNotificationName_object(notification_name, obj)
|
55
|
+
end
|
56
|
+
alias_method :post_notification, :fire_notification
|
57
|
+
|
51
58
|
# Registers the object for the given notification and executes the given block when the
|
52
59
|
# notification is posted to the OSX::NSNotificationCenter.defaultCenter.
|
53
60
|
#
|
@@ -68,33 +75,8 @@ module Rucola
|
|
68
75
|
#
|
69
76
|
# You can even register shortcut prefixes. See +notification_prefix+.
|
70
77
|
def notify_on(notification, &block)
|
71
|
-
notification_name = notification
|
72
|
-
|
73
|
-
if notification_name.is_a? Symbol
|
74
|
-
notification_name = notification_name.to_s
|
75
|
-
|
76
|
-
# first check if this notification_name uses a shortcut prefix
|
77
|
-
splitted_notification_name = notification_name.split('_')
|
78
|
-
prefix = splitted_notification_name.first.to_sym
|
79
|
-
if @_notification_prefixes and @_notification_prefixes.has_key? prefix
|
80
|
-
notification_name = @_notification_prefixes[prefix].to_s << '_' << splitted_notification_name[1..-1].join('_')
|
81
|
-
end
|
82
|
-
|
83
|
-
begin
|
84
|
-
# try with only Notification appended
|
85
|
-
notification_name = notification_name.camel_case << 'Notification'
|
86
|
-
OSX.const_get(notification_name)
|
87
|
-
rescue NameError
|
88
|
-
begin
|
89
|
-
# then try with NS prepended
|
90
|
-
notification_name = 'NS' << notification_name
|
91
|
-
OSX.const_get(notification_name)
|
92
|
-
rescue NameError
|
93
|
-
raise NameError, "Unable to find the notification corresponding to :#{notification}"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
78
|
|
79
|
+
notification_name = resolve_notification_name(notification)
|
98
80
|
method_name = "_handle_#{notification_name.snake_case}".to_sym
|
99
81
|
|
100
82
|
# define the handle method
|
@@ -106,6 +88,29 @@ module Rucola
|
|
106
88
|
@_registered_notifications[notification_name.to_s] = method_name
|
107
89
|
end
|
108
90
|
|
91
|
+
# Registers the object for the given notification and executes the given block when the
|
92
|
+
# notification is posted to the OSX::NSNotificationCenter.defaultCenter.
|
93
|
+
#
|
94
|
+
# class FooController < OSX::NSObject
|
95
|
+
#
|
96
|
+
# once OSX::NSApplicationDidFinishLaunchingNotification do |notification|
|
97
|
+
# puts "Application did finish launching."
|
98
|
+
# p notification
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# # code
|
102
|
+
#
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# You can also pass it a symbol as +notification+ in which case it will be exapnded.
|
106
|
+
# It will first check if the name + 'Notification' exists, if not it will prepend 'NS'.
|
107
|
+
# So :application_did_finish_launching becomes 'NSApplicationDidFinishLaunchingNotification'.
|
108
|
+
#
|
109
|
+
# You can even register shortcut prefixes. See +notification_prefix+.
|
110
|
+
#
|
111
|
+
# FIXME: Which is better +once+ or +notify_on+?
|
112
|
+
alias_method :once, :notify_on
|
113
|
+
|
109
114
|
# Register a callback when a notification is posted.
|
110
115
|
#
|
111
116
|
# class FooController < OSX::NSObject
|
@@ -121,6 +126,41 @@ module Rucola
|
|
121
126
|
@_registered_notifications[options[:when]] = method_to_notify
|
122
127
|
end
|
123
128
|
|
129
|
+
protected
|
130
|
+
|
131
|
+
# Given a symbol, attempt to map it to an NSNoficication, otherwise
|
132
|
+
# return the symbol if nothing is found.
|
133
|
+
#
|
134
|
+
# :app_finished_launching => NSApplicationFinishLaunchingNotification
|
135
|
+
def resolve_notification_name(name)
|
136
|
+
return name if name.is_a?(String)
|
137
|
+
notification_name = name.to_s
|
138
|
+
|
139
|
+
# first check if this notification_name uses a shortcut prefix
|
140
|
+
split_notification_name = notification_name.split('_')
|
141
|
+
prefix = split_notification_name.first.to_sym
|
142
|
+
if @_notification_prefixes and @_notification_prefixes.has_key? prefix
|
143
|
+
notification_name = @_notification_prefixes[prefix].to_s << '_' << split_notification_name[1..-1].join('_')
|
144
|
+
end
|
145
|
+
|
146
|
+
begin
|
147
|
+
# try with only Notification appended
|
148
|
+
notification_name = notification_name.camel_case << 'Notification'
|
149
|
+
OSX.const_get(notification_name)
|
150
|
+
return notification_name
|
151
|
+
rescue NameError
|
152
|
+
# then try with NS prepended
|
153
|
+
begin
|
154
|
+
notification_name = 'NS' << notification_name
|
155
|
+
OSX.const_get(notification_name)
|
156
|
+
return notification_name
|
157
|
+
rescue
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
return name
|
162
|
+
end
|
163
|
+
|
124
164
|
end
|
125
165
|
|
126
166
|
def self.included(base) # :nodoc
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rucola/info_plist'
|
2
|
+
|
1
3
|
module Rucola
|
2
4
|
module RCApp
|
3
5
|
# Returns the path to the current source root of the application.
|
@@ -97,5 +99,12 @@ module Rucola
|
|
97
99
|
end
|
98
100
|
module_function :path_for_asset
|
99
101
|
|
102
|
+
# Returns the name of the application as specified in the Info.plist file.
|
103
|
+
#
|
104
|
+
# Rucola::RCApp.app_name #=> 'MyApp'
|
105
|
+
def app_name
|
106
|
+
Rucola::InfoPlist.open((RUBYCOCOA_ROOT + 'config/Info.plist').to_s).app_name
|
107
|
+
end
|
108
|
+
module_function :app_name
|
100
109
|
end
|
101
110
|
end
|
data/lib/rucola/tasks/main.rake
CHANGED
@@ -6,8 +6,11 @@ require 'rucola/nib'
|
|
6
6
|
require 'rucola/rucola_support'
|
7
7
|
|
8
8
|
# set the env, default to debug if we are running a rake task.
|
9
|
-
|
10
|
-
|
9
|
+
ENV['RUBYCOCOA_ENV'] ||= 'debug'
|
10
|
+
ENV['RUBYCOCOA_ROOT'] ||= SOURCE_ROOT
|
11
|
+
|
12
|
+
require 'rucola/initializer'
|
13
|
+
|
11
14
|
puts "RUNNING IN MODE: #{RUBYCOCOA_ENV.upcase}\n\n"
|
12
15
|
|
13
16
|
# FIXME: We also need to check if the user uses a frozen rc framework
|
@@ -16,8 +19,8 @@ RUBYCOCOA_FRAMEWORK = OSX::NSBundle.bundleWithIdentifier('com.apple.rubycocoa').
|
|
16
19
|
# TASKS
|
17
20
|
|
18
21
|
# Get all the tasks
|
19
|
-
Dir["#{File.dirname(__FILE__)}/*.rake"].each {|file| load file unless File.basename(file)
|
20
|
-
|
22
|
+
Dir["#{File.dirname(__FILE__)}/*.rake"].each {|file| load file unless ['main.rake'].include? File.basename(file) }
|
23
|
+
Dir[(ENV['RUBYCOCOA_ROOT'] + '/vendor/plugins/*/tasks/*.rake').to_s].each { |r| load r }
|
21
24
|
task :default => 'xcode:build'
|
22
25
|
|
23
26
|
desc 'Runs all the clean tasks'
|
@@ -25,5 +28,6 @@ task :clean => 'xcode:clean'
|
|
25
28
|
|
26
29
|
Rake::TestTask.new do |t|
|
27
30
|
t.test_files = FileList['test/*/test_*.rb']
|
31
|
+
t.options = '-rr'
|
28
32
|
t.verbose = true
|
29
33
|
end
|
data/lib/rucola/tasks/xcode.rake
CHANGED
@@ -19,14 +19,18 @@ namespace :xcode do
|
|
19
19
|
task :build do
|
20
20
|
executable = "#{build_root}/Contents/MacOS/#{APPNAME}"
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
# For now let's do xcodebuild everytime.
|
23
|
+
# Otherwise nibs that are updated will not be updated in the bundle...
|
24
|
+
sh "xcodebuild -configuration #{config}"
|
25
|
+
|
26
|
+
# unless File.exists?(executable)
|
27
|
+
# sh "xcodebuild -configuration #{config}"
|
28
|
+
# else
|
29
|
+
# puts "Build already exists, skipping. (Use clean if you really really want a new build.)\n\n"
|
30
|
+
# end
|
27
31
|
|
28
32
|
# Make sure the app is brought to the front once launched.
|
29
|
-
Thread.new do
|
33
|
+
Thread.new(executable) do |executable|
|
30
34
|
sleep 0.025 until OSX::NSWorkspace.sharedWorkspace.launchedApplications.any? {|dict| dict['NSApplicationName'] == APPNAME }
|
31
35
|
`osascript -e 'tell application "#{executable}" to activate'`
|
32
36
|
end
|
data/lib/rucola/test_helper.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
require 'osx/cocoa'
|
2
2
|
|
3
|
+
class Object
|
4
|
+
# A mocha helper to get at an instance variable without having to use instance_variable_get.
|
5
|
+
#
|
6
|
+
# obj.ivar(:some_ivar).expects(:foo)
|
7
|
+
def ivar(name)
|
8
|
+
instance_variable_get("@#{name}".to_sym)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
3
12
|
module OSX
|
4
13
|
# Allows methods to be overriden with a different arity.
|
5
14
|
#
|
@@ -9,6 +18,11 @@ module OSX
|
|
9
18
|
def self._ignore_ns_override; true; end
|
10
19
|
|
11
20
|
class NSObject
|
21
|
+
# A mocha helper to get at an outlet (ivar) without having to use instance_variable_get.
|
22
|
+
#
|
23
|
+
# obj.ib_outlet(:some_text_view).expects(:string=).with('foo')
|
24
|
+
alias_method :ib_outlet, :ivar
|
25
|
+
|
12
26
|
# A Mocha helper method which allows to stub alloc.init and return a mock.
|
13
27
|
#
|
14
28
|
# it "should init and return an instance" do
|
data/lib/rucola/version.rb
CHANGED
data/lib/rucola/xcode.rb
CHANGED
@@ -13,7 +13,7 @@ module Rucola
|
|
13
13
|
@project_path = Pathname.new(project_path)
|
14
14
|
@project = @project_path.basename.to_s.sub(/\.xcodeproj$/, '')
|
15
15
|
@project_path_data = @project_path + 'project.pbxproj'
|
16
|
-
@project_data = OSX::
|
16
|
+
@project_data = OSX::NSMutableDictionary.dictionaryWithContentsOfFile(@project_path_data.to_s)
|
17
17
|
end
|
18
18
|
|
19
19
|
# Saves the project data atomically.
|
@@ -41,16 +41,20 @@ module Rucola
|
|
41
41
|
end
|
42
42
|
private :nil_if_empty
|
43
43
|
|
44
|
+
def objects
|
45
|
+
@project_data['objects'] = @project_data['objects'].to_ns
|
46
|
+
end
|
47
|
+
|
44
48
|
# Get's the id & values for a object which name is the one passed to this method.
|
45
49
|
# Returns an array: [id, values]
|
46
50
|
def object_for_name(name)
|
47
|
-
nil_if_empty
|
51
|
+
nil_if_empty objects.select { |object| object.last['name'] == name }.flatten
|
48
52
|
end
|
49
53
|
|
50
54
|
# Get's the id & values for a object which type and name is the one passed to this method.
|
51
55
|
# Returns an array: [id, values]
|
52
56
|
def object_for_type_and_name(type, name)
|
53
|
-
nil_if_empty
|
57
|
+
nil_if_empty objects.select { |object| object.last['isa'] == type and object.last['name'] == name }.flatten
|
54
58
|
end
|
55
59
|
|
56
60
|
# Returns the object that represents this projects target.
|
@@ -62,18 +66,19 @@ module Rucola
|
|
62
66
|
# Returns the object for a given name.
|
63
67
|
# Returns an array: [id, values]
|
64
68
|
def object_for_id(object_id)
|
65
|
-
|
69
|
+
objects[object_id].nil? ? nil : [object_id, objects[object_id]]
|
66
70
|
end
|
67
71
|
|
68
72
|
# Adds an object to the objects.
|
69
73
|
def add_object(object_id, object_values)
|
70
|
-
|
74
|
+
objects[object_id] = object_values
|
71
75
|
end
|
72
76
|
|
73
77
|
# Adds a build phase specified by +object_id+ to the build phases of the project target.
|
74
78
|
def add_build_phase_to_project_target(object_id)
|
75
79
|
# Add the new build phase to the main project target if it doesn't already exist
|
76
80
|
build_target_id, build_target_values = object_for_project_target
|
81
|
+
build_target_values['buildPhases'] = build_target_values['buildPhases'].to_ns
|
77
82
|
build_target_values['buildPhases'].push(object_id) unless build_target_values['buildPhases'].include?(object_id)
|
78
83
|
end
|
79
84
|
|
@@ -89,7 +94,7 @@ module Rucola
|
|
89
94
|
'dstPath' => '',
|
90
95
|
'dstSubfolderSpec' => 10, # TODO: is 10 the number for the location popup choice: Frameworks
|
91
96
|
'runOnlyForDeploymentPostprocessing' => 0,
|
92
|
-
'files' => []
|
97
|
+
'files' => [].to_ns
|
93
98
|
}]
|
94
99
|
# Creates a new framework copy build phase.
|
95
100
|
# It does not add it to the objects nor the build phases,
|
@@ -1,10 +1,19 @@
|
|
1
1
|
require File.expand_path('../../test_helper', __FILE__)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
describe '<%= name.camel_case %>Controller' do
|
4
|
+
before do
|
5
|
+
@controller = <%= name.camel_case %>Controller.alloc.init
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should initialize" do
|
9
|
+
@controller.should.be.an.instance_of <%= name.camel_case %>Controller
|
8
10
|
end
|
9
11
|
|
12
|
+
it "should do stuff at awakeFromNib" do
|
13
|
+
# Some example code of testing your #awakeFromNib.
|
14
|
+
#
|
15
|
+
# @controller.ib_outlet(:some_text_view).expects(:string=).with('foo')
|
16
|
+
|
17
|
+
@controller.awakeFromNib
|
18
|
+
end
|
10
19
|
end
|
@@ -9,7 +9,7 @@ class DocumentModelGenerator < RubiGen::Base
|
|
9
9
|
|
10
10
|
def initialize(runtime_args, runtime_options = {})
|
11
11
|
super
|
12
|
-
usage if args.empty?
|
12
|
+
usage if args.empty? || args.length == 1
|
13
13
|
@name = args.shift
|
14
14
|
@extension = args.shift
|
15
15
|
extract_options
|
@@ -41,9 +41,9 @@ class DocumentModelGenerator < RubiGen::Base
|
|
41
41
|
protected
|
42
42
|
def banner
|
43
43
|
<<-EOS
|
44
|
-
Creates a
|
44
|
+
Creates a model that inherits from NSDocument.
|
45
45
|
|
46
|
-
USAGE: #{$0} #{spec.name} name"
|
46
|
+
USAGE: #{$0} #{spec.name} name extension"
|
47
47
|
EOS
|
48
48
|
end
|
49
49
|
|