rucola 0.0.3 → 0.5.0

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.
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