rucola 0.0.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/ChangeLog +468 -0
  2. data/History.txt +25 -0
  3. data/License.txt +1 -1
  4. data/Manifest.txt +32 -0
  5. data/README.txt +9 -67
  6. data/Rakefile +39 -31
  7. data/TODO +10 -18
  8. data/app_generators/rucola/rucola_generator.rb +15 -8
  9. data/app_generators/rucola/templates/Rakefile.erb +35 -7
  10. data/app_generators/rucola/templates/config/boot.rb +0 -1
  11. data/app_generators/rucola/templates/config/dependencies.rb +31 -0
  12. data/app_generators/rucola/templates/config/environment.rb +0 -1
  13. data/app_generators/rucola/templates/config/environments/debug.rb +13 -1
  14. data/app_generators/rucola/templates/config/environments/release.rb +15 -1
  15. data/app_generators/rucola/templates/config/environments/test.rb +7 -1
  16. data/app_generators/rucola/templates/misc/rb_main.rb.erb +12 -1
  17. data/app_generators/rucola/templates/project.pbxproj.erb +4 -0
  18. data/app_generators/rucola/templates/script/console +10 -0
  19. data/app_generators/rucola/templates/test/controllers/test_application_controller.rb +22 -10
  20. data/app_generators/rucola/templates/test/test_helper.rb +1 -0
  21. data/bin/rucola +4 -2
  22. data/lib/rucola/dependencies.rb +241 -0
  23. data/lib/rucola/dependencies/exclusions.rb +20 -0
  24. data/lib/rucola/dependencies/override_require_and_gem.rb +30 -0
  25. data/lib/rucola/dependencies/resolver.rb +68 -0
  26. data/lib/rucola/fsevents.rb +108 -0
  27. data/lib/rucola/initializer.rb +149 -117
  28. data/lib/rucola/log.rb +61 -0
  29. data/lib/rucola/nib.rb +3 -3
  30. data/lib/rucola/reloader.rb +39 -0
  31. data/lib/rucola/ruby_debug.rb +27 -0
  32. data/lib/rucola/rucola_support.rb +1 -2
  33. data/lib/rucola/rucola_support/core_ext.rb +4 -2
  34. data/lib/rucola/rucola_support/core_ext/objc.rb +9 -4
  35. data/lib/rucola/rucola_support/core_ext/objc/nsimage.rb +22 -0
  36. data/lib/rucola/rucola_support/core_ext/ruby.rb +11 -4
  37. data/lib/rucola/rucola_support/core_ext/ruby/file.rb +11 -0
  38. data/lib/rucola/rucola_support/core_ext/ruby/kernel.rb +16 -0
  39. data/lib/rucola/rucola_support/core_ext/ruby/object.rb +47 -0
  40. data/lib/rucola/rucola_support/core_ext/ruby/string.rb +8 -0
  41. data/lib/rucola/rucola_support/notifications/notifications.rb +26 -28
  42. data/lib/rucola/rucola_support/rc_app.rb +18 -0
  43. data/lib/rucola/tasks/dependencies.rake +49 -0
  44. data/lib/rucola/tasks/deploy.rake +131 -0
  45. data/lib/rucola/tasks/main.rake +39 -6
  46. data/lib/rucola/tasks/xcode.rake +54 -11
  47. data/lib/rucola/test_case.rb +138 -0
  48. data/lib/rucola/test_helper.rb +11 -5
  49. data/lib/rucola/version.rb +2 -2
  50. data/lib/rucola/xcode.rb +39 -9
  51. data/rucola_generators/controller/templates/test_controller_template.rb.erb +19 -7
  52. data/rucola_generators/simple_model/USAGE +5 -0
  53. data/rucola_generators/simple_model/simple_model_generator.rb +54 -0
  54. data/rucola_generators/simple_model/templates/simple_model.rb.erb +2 -0
  55. data/rucola_generators/simple_model/templates/test_simple_model.rb.erb +11 -0
  56. data/rucola_generators/window_controller/templates/test_window_controller_template.rb.erb +24 -13
  57. data/test/fixtures/dependencies/foo.rb +2 -0
  58. data/test/fixtures/dependencies/foo/bar.rb +0 -0
  59. data/test/fixtures/dependencies/foo/baz.rb +0 -0
  60. data/test/fixtures/dependencies/requires_fileutils.rb +1 -0
  61. data/test/fixtures/some_reloadable_class.rb +4 -0
  62. data/test/test_core_ext.rb +80 -0
  63. data/test/test_dependencies.rb +205 -0
  64. data/test/test_fsevents.rb +152 -0
  65. data/test/test_helper.rb +30 -1
  66. data/test/test_initializer.rb +56 -23
  67. data/test/test_log.rb +44 -0
  68. data/test/test_objc_core_ext.rb +23 -0
  69. data/test/test_rc_app.rb +5 -0
  70. data/test/test_reloader.rb +28 -0
  71. data/test/test_rucola_generator.rb +7 -0
  72. data/test/test_simple_model_generator.rb +48 -0
  73. data/test/test_xcode.rb +85 -5
  74. data/website/index.html +17 -91
  75. data/website/index.txt +14 -81
  76. data/website/template.rhtml +1 -1
  77. metadata +120 -76
@@ -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
@@ -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,2 +1,4 @@
1
- require 'rucola_support/core_ext/ruby'
2
- require 'rucola_support/core_ext/objc'
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'core_ext/ruby'
4
+ require 'core_ext/objc'
@@ -1,4 +1,9 @@
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
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
- filename = File.basename(path)
3
- require "rucola_support/core_ext/ruby/#{filename}"
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,11 @@
1
+ class File
2
+ class << self
3
+ # Returns the constant version of the file that is referred to.
4
+ #
5
+ # File.to_const("/some/path/foo_bar_controller.rb") # => FooBarController
6
+ def to_const(file)
7
+ file.match /(\w+)\.\w*$/
8
+ $1.to_const
9
+ end
10
+ end
11
+ end
@@ -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
@@ -18,4 +18,12 @@ class String
18
18
  end
19
19
  end
20
20
  end
21
+
22
+ # Returns the constant that this string refers to.
23
+ #
24
+ # "FooBar".to_const # => FooBar
25
+ # "foo_bar".to_const # => FooBar
26
+ def to_const
27
+ Object.const_get(camel_case)
28
+ end
21
29
  end
@@ -1,34 +1,32 @@
1
1
  require 'osx/cocoa'
2
2
 
3
3
  module Rucola
4
- module Notifications
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
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