rucola 0.0.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +468 -0
- data/History.txt +25 -0
- data/License.txt +1 -1
- data/Manifest.txt +32 -0
- data/README.txt +9 -67
- data/Rakefile +39 -31
- data/TODO +10 -18
- data/app_generators/rucola/rucola_generator.rb +15 -8
- data/app_generators/rucola/templates/Rakefile.erb +35 -7
- data/app_generators/rucola/templates/config/boot.rb +0 -1
- data/app_generators/rucola/templates/config/dependencies.rb +31 -0
- data/app_generators/rucola/templates/config/environment.rb +0 -1
- data/app_generators/rucola/templates/config/environments/debug.rb +13 -1
- data/app_generators/rucola/templates/config/environments/release.rb +15 -1
- data/app_generators/rucola/templates/config/environments/test.rb +7 -1
- data/app_generators/rucola/templates/misc/rb_main.rb.erb +12 -1
- data/app_generators/rucola/templates/project.pbxproj.erb +4 -0
- data/app_generators/rucola/templates/script/console +10 -0
- data/app_generators/rucola/templates/test/controllers/test_application_controller.rb +22 -10
- data/app_generators/rucola/templates/test/test_helper.rb +1 -0
- data/bin/rucola +4 -2
- data/lib/rucola/dependencies.rb +241 -0
- data/lib/rucola/dependencies/exclusions.rb +20 -0
- data/lib/rucola/dependencies/override_require_and_gem.rb +30 -0
- data/lib/rucola/dependencies/resolver.rb +68 -0
- data/lib/rucola/fsevents.rb +108 -0
- data/lib/rucola/initializer.rb +149 -117
- data/lib/rucola/log.rb +61 -0
- data/lib/rucola/nib.rb +3 -3
- data/lib/rucola/reloader.rb +39 -0
- data/lib/rucola/ruby_debug.rb +27 -0
- data/lib/rucola/rucola_support.rb +1 -2
- data/lib/rucola/rucola_support/core_ext.rb +4 -2
- data/lib/rucola/rucola_support/core_ext/objc.rb +9 -4
- data/lib/rucola/rucola_support/core_ext/objc/nsimage.rb +22 -0
- data/lib/rucola/rucola_support/core_ext/ruby.rb +11 -4
- data/lib/rucola/rucola_support/core_ext/ruby/file.rb +11 -0
- data/lib/rucola/rucola_support/core_ext/ruby/kernel.rb +16 -0
- data/lib/rucola/rucola_support/core_ext/ruby/object.rb +47 -0
- data/lib/rucola/rucola_support/core_ext/ruby/string.rb +8 -0
- data/lib/rucola/rucola_support/notifications/notifications.rb +26 -28
- data/lib/rucola/rucola_support/rc_app.rb +18 -0
- data/lib/rucola/tasks/dependencies.rake +49 -0
- data/lib/rucola/tasks/deploy.rake +131 -0
- data/lib/rucola/tasks/main.rake +39 -6
- data/lib/rucola/tasks/xcode.rake +54 -11
- data/lib/rucola/test_case.rb +138 -0
- data/lib/rucola/test_helper.rb +11 -5
- data/lib/rucola/version.rb +2 -2
- data/lib/rucola/xcode.rb +39 -9
- data/rucola_generators/controller/templates/test_controller_template.rb.erb +19 -7
- data/rucola_generators/simple_model/USAGE +5 -0
- data/rucola_generators/simple_model/simple_model_generator.rb +54 -0
- data/rucola_generators/simple_model/templates/simple_model.rb.erb +2 -0
- data/rucola_generators/simple_model/templates/test_simple_model.rb.erb +11 -0
- data/rucola_generators/window_controller/templates/test_window_controller_template.rb.erb +24 -13
- data/test/fixtures/dependencies/foo.rb +2 -0
- data/test/fixtures/dependencies/foo/bar.rb +0 -0
- data/test/fixtures/dependencies/foo/baz.rb +0 -0
- data/test/fixtures/dependencies/requires_fileutils.rb +1 -0
- data/test/fixtures/some_reloadable_class.rb +4 -0
- data/test/test_core_ext.rb +80 -0
- data/test/test_dependencies.rb +205 -0
- data/test/test_fsevents.rb +152 -0
- data/test/test_helper.rb +30 -1
- data/test/test_initializer.rb +56 -23
- data/test/test_log.rb +44 -0
- data/test/test_objc_core_ext.rb +23 -0
- data/test/test_rc_app.rb +5 -0
- data/test/test_reloader.rb +28 -0
- data/test/test_rucola_generator.rb +7 -0
- data/test/test_simple_model_generator.rb +48 -0
- data/test/test_xcode.rb +85 -5
- data/website/index.html +17 -91
- data/website/index.txt +14 -81
- data/website/template.rhtml +1 -1
- metadata +120 -76
data/lib/rucola/log.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Rucola
|
4
|
+
# The Log class is basically a wrapper around NSLog. It is a singleton class so you should get an instance using the instance
|
5
|
+
# class method instead of new.
|
6
|
+
#
|
7
|
+
# Rucola::Log.instance.fatal("Couldn't initialize application.")
|
8
|
+
#
|
9
|
+
# The Log class is generally accessed through the log method on Kernel.
|
10
|
+
#
|
11
|
+
# log.debug("%d exceptions caught, giving up", exceptions.length)
|
12
|
+
class Log
|
13
|
+
DEBUG = 0
|
14
|
+
INFO = 1
|
15
|
+
WARN = 2
|
16
|
+
ERROR = 3
|
17
|
+
FATAL = 4
|
18
|
+
UNKNOWN = 5
|
19
|
+
SILENT = 9
|
20
|
+
|
21
|
+
include Singleton
|
22
|
+
|
23
|
+
# Holds the current log level
|
24
|
+
attr_accessor :level
|
25
|
+
|
26
|
+
# Creates a new Log instance. Don't call this directly, call instance instead.
|
27
|
+
#
|
28
|
+
# log.instance
|
29
|
+
def initialize
|
30
|
+
@level = level_for_env
|
31
|
+
end
|
32
|
+
|
33
|
+
def debug(*args); log(DEBUG, *args); end
|
34
|
+
def info(*args); log(INFO, *args); end
|
35
|
+
def warn(*args); log(WARN, *args); end
|
36
|
+
def error(*args); log(ERROR, *args); end
|
37
|
+
def fatal(*args); log(FATAL, *args); end
|
38
|
+
def unknown(*args); log(UNKNOWN, *args); end
|
39
|
+
|
40
|
+
# Returns default log level for the application environment.
|
41
|
+
#
|
42
|
+
# log.level_for_env #=> Log::ERROR
|
43
|
+
def level_for_env
|
44
|
+
case RCApp.env
|
45
|
+
when 'test'
|
46
|
+
SILENT
|
47
|
+
when 'debug'
|
48
|
+
DEBUG
|
49
|
+
when 'release'
|
50
|
+
ERROR
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Writes a message to the log is the current loglevel is equal or greater than the message_level.
|
55
|
+
#
|
56
|
+
# log.log(Log::DEBUG, "This is a debug message")
|
57
|
+
def log(message_level, *args)
|
58
|
+
OSX.NSLog(*args) if message_level >= level
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/rucola/nib.rb
CHANGED
@@ -2,7 +2,7 @@ require 'osx/cocoa'
|
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
4
|
module Rucola
|
5
|
-
module Nib
|
5
|
+
module Nib #:nodoc:
|
6
6
|
|
7
7
|
def self.backup(path)
|
8
8
|
nib = File.dirname(path)
|
@@ -18,7 +18,7 @@ module Rucola
|
|
18
18
|
FileUtils.cp_r(nib, backup)
|
19
19
|
end
|
20
20
|
|
21
|
-
class Classes
|
21
|
+
class Classes #:nodoc:
|
22
22
|
attr_reader :data
|
23
23
|
|
24
24
|
def self.open(classes_nib_path)
|
@@ -53,7 +53,7 @@ module Rucola
|
|
53
53
|
|
54
54
|
end
|
55
55
|
|
56
|
-
class KeyedObjects
|
56
|
+
class KeyedObjects #:nodoc:
|
57
57
|
attr_reader :data
|
58
58
|
|
59
59
|
def self.open(path)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rucola/fsevents'
|
2
|
+
require 'rubynode'
|
3
|
+
|
4
|
+
module Rucola
|
5
|
+
# The +Reloader+ watches the app/controllers path and reloads
|
6
|
+
# a class if it notices that a file has been changed.
|
7
|
+
module Reloader
|
8
|
+
class << self
|
9
|
+
# Start watching app/controllers for a file modification and reload that class.
|
10
|
+
def start!
|
11
|
+
Rucola::FSEvents.start_watching(Rucola::RCApp.controllers_path, Rucola::RCApp.models_path) do |events|
|
12
|
+
events.each { |event| reload(event.last_modified_file) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Reload a file (class).
|
17
|
+
def reload(file)
|
18
|
+
klass = File.to_const(file)
|
19
|
+
begin
|
20
|
+
File.read(file).parse_to_nodes
|
21
|
+
|
22
|
+
log.debug "Reloading class #{klass.name}:"
|
23
|
+
|
24
|
+
i_methods = klass.instance_methods(false)
|
25
|
+
log.debug "- Undefining instance methods: #{i_methods.inspect}"
|
26
|
+
i_methods.each { |mname| klass.send(:undef_method, mname) }
|
27
|
+
|
28
|
+
c_methods = klass.original_class_methods
|
29
|
+
log.debug "- Undefining class methods: #{c_methods.inspect}"
|
30
|
+
c_methods.each { |mname| klass.metaclass.send(:undef_method, mname) }
|
31
|
+
|
32
|
+
Kernel.load(file)
|
33
|
+
rescue SyntaxError => e
|
34
|
+
log.error "WARNING: Reloading the class #{klass.name} would have caused a parse error:\n#{e.message}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Kernel
|
2
|
+
def debugger(steps = 1); end
|
3
|
+
end
|
4
|
+
|
5
|
+
module Rucola
|
6
|
+
module Debugger
|
7
|
+
def self.use! #:nodoc:
|
8
|
+
Kernel.module_eval do
|
9
|
+
# If enabled in the Configuration (default in `debug` environment is on), calling #debugger will try to load the ruby-debug gem.
|
10
|
+
# In other modes however any call to #debugger will be ignored.
|
11
|
+
# However, for performance reasons you still might want to take out any calls in a release build.
|
12
|
+
def debugger(steps = 1)
|
13
|
+
rucola_load_ruby_debug(steps)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def rucola_load_ruby_debug(steps) #:nodoc:
|
19
|
+
require 'ruby-debug'
|
20
|
+
debugger(steps)
|
21
|
+
rescue LoadError
|
22
|
+
log.error "The ruby-debug gem is needed to be able to use the debugger, but it wasn't found."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
-
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
1
|
+
$:.unshift(File.dirname(__FILE__)) #unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
2
|
|
4
3
|
require 'rucola_support/rc_app'
|
5
4
|
require 'rucola_support/initialize_hooks'
|
@@ -1,4 +1,9 @@
|
|
1
|
-
Dir[File.dirname(__FILE__) + "/objc/*.rb"].sort.each do |path|
|
2
|
-
|
3
|
-
|
4
|
-
end
|
1
|
+
# Dir[File.dirname(__FILE__) + "/objc/*.rb"].sort.each do |path|
|
2
|
+
# filename = File.basename(path)
|
3
|
+
# require "rucola_support/core_ext/objc/#{filename}"
|
4
|
+
# end
|
5
|
+
|
6
|
+
$:.unshift(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require 'objc/nsimage'
|
9
|
+
require 'objc/nsobject'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class OSX::NSImage
|
2
|
+
class << self
|
3
|
+
# This implementation adds app/assets to the search domain.
|
4
|
+
# So if for instance you have an image app/assets/some_img.png,
|
5
|
+
# you can then use OSX::NSImage.imageNamed('some_img') and it will be found.
|
6
|
+
def imageNamed(name)
|
7
|
+
if @assets_files.nil?
|
8
|
+
@assets_files = {}
|
9
|
+
Dir.glob("#{Rucola::RCApp.assets_path}/*.*").each do |file|
|
10
|
+
basename = File.basename(file).gsub(/\..*/, '')
|
11
|
+
@assets_files[basename] = file
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if image_file = (@assets_files[name.to_s] || @assets_files[name.to_s.gsub(/\..*/, '')])
|
16
|
+
alloc.initWithContentsOfFile image_file
|
17
|
+
else
|
18
|
+
super_imageNamed(name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,4 +1,11 @@
|
|
1
|
-
Dir[File.dirname(__FILE__) + "/ruby/*.rb"].sort.each do |path|
|
2
|
-
|
3
|
-
|
4
|
-
end
|
1
|
+
# Dir[File.dirname(__FILE__) + "/ruby/*.rb"].sort.each do |path|
|
2
|
+
# filename = File.basename(path)
|
3
|
+
# require "rucola_support/core_ext/ruby/#{filename}"
|
4
|
+
# end
|
5
|
+
|
6
|
+
$:.unshift(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
require 'ruby/file'
|
9
|
+
require 'ruby/object'
|
10
|
+
require 'ruby/string'
|
11
|
+
require 'ruby/kernel'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rucola/log'
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
# Returns a logger instance
|
5
|
+
#
|
6
|
+
# Examples:
|
7
|
+
#
|
8
|
+
# log.level = Rucola::Log::DEBUG
|
9
|
+
# log.info "Couldn't load preferences, using defaults"
|
10
|
+
# log.debug "Initiating primary foton drive…"
|
11
|
+
#
|
12
|
+
# For more information see the Rucola::Log class.
|
13
|
+
def log
|
14
|
+
Rucola::Log.instance
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Object
|
2
|
+
# Returns a class's metaclass.
|
3
|
+
#
|
4
|
+
# class FooBar; end
|
5
|
+
# p FooBar.metaclass # => #<Class:FooBar>
|
6
|
+
def self.metaclass
|
7
|
+
class << self; self; end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns an array of all the class methods that were added by extending the class.
|
11
|
+
#
|
12
|
+
# class FooBar; end
|
13
|
+
#
|
14
|
+
# module Baz
|
15
|
+
# def a_new_class_method; end
|
16
|
+
# end
|
17
|
+
# FooBar.extend(Baz)
|
18
|
+
#
|
19
|
+
# FooBar.extended_class_methods # => ['a_new_class_method']
|
20
|
+
def self.extended_class_methods
|
21
|
+
metaclass.included_modules.map { |mod| mod.instance_methods }.flatten.uniq
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns an array of all the class methods that were defined in this class
|
25
|
+
# without the ones that were defined in it's superclasses.
|
26
|
+
#
|
27
|
+
# class FooBar
|
28
|
+
# def self.a_original_class_method
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# class FooBarSubclass < FooBar
|
33
|
+
# def self.a_original_class_method_in_a_subclass
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# FooBarSubclass.own_class_methods # => ['a_original_class_method_in_a_subclass']
|
38
|
+
def self.own_class_methods
|
39
|
+
metaclass.instance_methods - superclass.metaclass.instance_methods
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns an array of all the class methods that were defined in only this class,
|
43
|
+
# so without class methods from any of it's superclasses or from extending it.
|
44
|
+
def self.original_class_methods
|
45
|
+
own_class_methods - extended_class_methods
|
46
|
+
end
|
47
|
+
end
|
@@ -1,34 +1,32 @@
|
|
1
1
|
require 'osx/cocoa'
|
2
2
|
|
3
3
|
module Rucola
|
4
|
-
module Notifications
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
4
|
+
module Notifications #:nodoc:
|
5
|
+
# This Notifications module will add a class method called +notify_on+, which registers
|
6
|
+
# your object for the given notification and executes the given block when the
|
7
|
+
# notification is posted to the OSX::NSNotificationCenter.defaultCenter.
|
8
|
+
#
|
9
|
+
# class FooController < OSX::NSObject
|
10
|
+
#
|
11
|
+
# notify_on OSX::NSApplicationDidFinishLaunchingNotification do |notification|
|
12
|
+
# puts "Application did finish launching."
|
13
|
+
# p notification
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # code
|
17
|
+
#
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# In addition to notify_on, you also get a method called notify which allows you to specify methods
|
21
|
+
# to be invoked when a notification is posted.
|
22
|
+
#
|
23
|
+
# class FooController < OSX::NSObject
|
24
|
+
# notify :some_method, :when => :application_did_finish_launching
|
25
|
+
#
|
26
|
+
# def some_method(notification)
|
27
|
+
# puts "Application finished launching"
|
28
|
+
# end
|
29
|
+
# end
|
32
30
|
module ClassMethods
|
33
31
|
# Add prefix shortcuts as a hash.
|
34
32
|
#
|
@@ -2,6 +2,16 @@ require 'rucola/info_plist'
|
|
2
2
|
|
3
3
|
module Rucola
|
4
4
|
module RCApp
|
5
|
+
# Returns the current RUBYCOCOA_ENV, which normally is 'debug' during development, test in the tests and 'release' in a release.
|
6
|
+
def env; RUBYCOCOA_ENV; end
|
7
|
+
module_function :env
|
8
|
+
def test?; env == 'test'; end
|
9
|
+
module_function :test?
|
10
|
+
def debug?; env == 'debug'; end
|
11
|
+
module_function :debug?
|
12
|
+
def release?; env == 'release'; end
|
13
|
+
module_function :release?
|
14
|
+
|
5
15
|
# Returns the path to the current source root of the application.
|
6
16
|
#
|
7
17
|
# So in debug & test mode this will point to your development source root.
|
@@ -106,5 +116,13 @@ module Rucola
|
|
106
116
|
Rucola::InfoPlist.open((RUBYCOCOA_ROOT + 'config/Info.plist').to_s).app_name
|
107
117
|
end
|
108
118
|
module_function :app_name
|
119
|
+
|
120
|
+
# Returns the path to the application support directory for this application.
|
121
|
+
#
|
122
|
+
# Rucola::RCApp.application_support_path #=> '/Users/eddy/Library/Application Support/MyApp/'
|
123
|
+
def application_support_path
|
124
|
+
File.join File.expand_path('~/Library/Application Support'), app_name
|
125
|
+
end
|
126
|
+
module_function :application_support_path
|
109
127
|
end
|
110
128
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rucola/dependencies'
|
2
|
+
|
3
|
+
namespace :dependencies do
|
4
|
+
THIRD_PARTY_DIR = (SOURCE_ROOT + '/vendor/third_party/').to_s
|
5
|
+
|
6
|
+
def dependencies_holder
|
7
|
+
deps = Rucola::Dependencies.load((SOURCE_ROOT + '/config/dependencies.rb').to_s)
|
8
|
+
deps.resolve!
|
9
|
+
deps
|
10
|
+
end
|
11
|
+
|
12
|
+
def file_types
|
13
|
+
if ENV['FILE_TYPES']
|
14
|
+
ENV['FILE_TYPES'].split(',').map {|t| t.strip.to_sym }
|
15
|
+
else
|
16
|
+
CONFIGURATION.dependency_types
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Lists all the dependencies and their required files. Use the FILE_TYPES env var to specify which files you'd like to include."
|
21
|
+
task :list do
|
22
|
+
Rucola::Dependencies.verbose = false
|
23
|
+
|
24
|
+
str = ''
|
25
|
+
dependencies_holder.dependencies.each do |dep|
|
26
|
+
str += "\nDependency '#{dep.pretty_print_name}' requires the following files:\n\n"
|
27
|
+
dep.required_files_of_types(file_types).each { |file| str += " #{file.full_path}\n" }
|
28
|
+
end
|
29
|
+
puts str
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Copies all the required files to 'vendor/third_party/'."
|
33
|
+
task :copy do
|
34
|
+
# Check if we have enough info to do a copy
|
35
|
+
if file_types.is_a?(Symbol) or !file_types.empty?
|
36
|
+
FileUtils.mkdir_p(THIRD_PARTY_DIR) unless File.exist?(THIRD_PARTY_DIR)
|
37
|
+
$VERBOSE = nil # we don't want all the warnings about constant being redefined.
|
38
|
+
dependencies_holder.copy_to(THIRD_PARTY_DIR, :types => file_types)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "Removes the 'vendor/third_party/' directory."
|
43
|
+
task :clean do
|
44
|
+
if File.exist? THIRD_PARTY_DIR
|
45
|
+
puts "Removing #{THIRD_PARTY_DIR}"
|
46
|
+
FileUtils.rm_rf(THIRD_PARTY_DIR)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|