hotcocoa 0.5
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.
- data/History.txt +4 -0
- data/bin/hotcocoa +31 -0
- data/lib/hotcocoa/application_builder.rb +320 -0
- data/lib/hotcocoa/attributed_string.rb +143 -0
- data/lib/hotcocoa/behaviors.rb +7 -0
- data/lib/hotcocoa/data_sources/combo_box_data_source.rb +44 -0
- data/lib/hotcocoa/data_sources/table_data_source.rb +18 -0
- data/lib/hotcocoa/delegate_builder.rb +85 -0
- data/lib/hotcocoa/graphics/canvas.rb +836 -0
- data/lib/hotcocoa/graphics/color.rb +781 -0
- data/lib/hotcocoa/graphics/elements/particle.rb +75 -0
- data/lib/hotcocoa/graphics/elements/rope.rb +99 -0
- data/lib/hotcocoa/graphics/elements/sandpainter.rb +71 -0
- data/lib/hotcocoa/graphics/gradient.rb +63 -0
- data/lib/hotcocoa/graphics/image.rb +488 -0
- data/lib/hotcocoa/graphics/path.rb +325 -0
- data/lib/hotcocoa/graphics/pdf.rb +71 -0
- data/lib/hotcocoa/graphics.rb +161 -0
- data/lib/hotcocoa/kernel_ext.rb +14 -0
- data/lib/hotcocoa/kvo_accessors.rb +48 -0
- data/lib/hotcocoa/layout_view.rb +448 -0
- data/lib/hotcocoa/mapper.rb +227 -0
- data/lib/hotcocoa/mapping_methods.rb +40 -0
- data/lib/hotcocoa/mappings/alert.rb +25 -0
- data/lib/hotcocoa/mappings/application.rb +112 -0
- data/lib/hotcocoa/mappings/array_controller.rb +87 -0
- data/lib/hotcocoa/mappings/box.rb +39 -0
- data/lib/hotcocoa/mappings/button.rb +92 -0
- data/lib/hotcocoa/mappings/collection_view.rb +44 -0
- data/lib/hotcocoa/mappings/color.rb +28 -0
- data/lib/hotcocoa/mappings/column.rb +21 -0
- data/lib/hotcocoa/mappings/combo_box.rb +24 -0
- data/lib/hotcocoa/mappings/control.rb +33 -0
- data/lib/hotcocoa/mappings/font.rb +44 -0
- data/lib/hotcocoa/mappings/gradient.rb +15 -0
- data/lib/hotcocoa/mappings/image.rb +15 -0
- data/lib/hotcocoa/mappings/image_view.rb +43 -0
- data/lib/hotcocoa/mappings/label.rb +25 -0
- data/lib/hotcocoa/mappings/layout_view.rb +9 -0
- data/lib/hotcocoa/mappings/menu.rb +71 -0
- data/lib/hotcocoa/mappings/menu_item.rb +47 -0
- data/lib/hotcocoa/mappings/movie.rb +13 -0
- data/lib/hotcocoa/mappings/movie_view.rb +27 -0
- data/lib/hotcocoa/mappings/notification.rb +17 -0
- data/lib/hotcocoa/mappings/popup.rb +110 -0
- data/lib/hotcocoa/mappings/progress_indicator.rb +68 -0
- data/lib/hotcocoa/mappings/scroll_view.rb +29 -0
- data/lib/hotcocoa/mappings/search_field.rb +9 -0
- data/lib/hotcocoa/mappings/secure_text_field.rb +17 -0
- data/lib/hotcocoa/mappings/segmented_control.rb +97 -0
- data/lib/hotcocoa/mappings/slider.rb +25 -0
- data/lib/hotcocoa/mappings/sort_descriptor.rb +13 -0
- data/lib/hotcocoa/mappings/sound.rb +9 -0
- data/lib/hotcocoa/mappings/speech_synthesizer.rb +25 -0
- data/lib/hotcocoa/mappings/split_view.rb +21 -0
- data/lib/hotcocoa/mappings/status_bar.rb +7 -0
- data/lib/hotcocoa/mappings/status_item.rb +9 -0
- data/lib/hotcocoa/mappings/table_view.rb +110 -0
- data/lib/hotcocoa/mappings/text_field.rb +41 -0
- data/lib/hotcocoa/mappings/text_view.rb +13 -0
- data/lib/hotcocoa/mappings/timer.rb +25 -0
- data/lib/hotcocoa/mappings/toolbar.rb +97 -0
- data/lib/hotcocoa/mappings/toolbar_item.rb +36 -0
- data/lib/hotcocoa/mappings/view.rb +67 -0
- data/lib/hotcocoa/mappings/web_view.rb +22 -0
- data/lib/hotcocoa/mappings/window.rb +118 -0
- data/lib/hotcocoa/mappings/xml_parser.rb +41 -0
- data/lib/hotcocoa/mappings.rb +109 -0
- data/lib/hotcocoa/mvc.rb +175 -0
- data/lib/hotcocoa/notification_listener.rb +62 -0
- data/lib/hotcocoa/object_ext.rb +22 -0
- data/lib/hotcocoa/plist.rb +45 -0
- data/lib/hotcocoa/standard_rake_tasks.rb +17 -0
- data/lib/hotcocoa/template.rb +27 -0
- data/lib/hotcocoa/virtual_file_system.rb +172 -0
- data/lib/hotcocoa.rb +26 -0
- data/template/Rakefile +5 -0
- data/template/config/build.yml +8 -0
- data/template/lib/application.rb +45 -0
- data/template/lib/menu.rb +32 -0
- data/template/resources/HotCocoa.icns +0 -0
- data/test/test_helper.rb +3 -0
- data/test/test_hotcocoa.rb +11 -0
- metadata +137 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
module HotCocoa
|
2
|
+
module Mappings
|
3
|
+
|
4
|
+
def self.reload
|
5
|
+
Dir.glob(File.join(File.dirname(__FILE__), "mappings", "*.rb")).each do |mapping|
|
6
|
+
require mapping
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
DefaultEmptyRect = [0,0,0,0]
|
11
|
+
|
12
|
+
module TargetActionConvenience
|
13
|
+
def on_action=(behavior)
|
14
|
+
if target && (
|
15
|
+
target.instance_variable_get("@action_behavior") ||
|
16
|
+
target.instance_variable_get("@double_action_behavior"))
|
17
|
+
object.instance_variable_set("@action_behavior", behavior)
|
18
|
+
object = target
|
19
|
+
else
|
20
|
+
object = Object.new
|
21
|
+
object.instance_variable_set("@action_behavior", behavior)
|
22
|
+
setTarget(object)
|
23
|
+
end
|
24
|
+
def object.perform_action(sender)
|
25
|
+
@action_behavior.call(sender)
|
26
|
+
end
|
27
|
+
setAction("perform_action:")
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_action(&behavior)
|
31
|
+
self.on_action = behavior
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: Needs docs for all possible invocations and examples!
|
37
|
+
def self.map(options, &block)
|
38
|
+
framework = options.delete(:framework)
|
39
|
+
mapped_name = options.keys.first
|
40
|
+
mapped_value = options.values.first
|
41
|
+
args = [mapped_name, mapped_value]
|
42
|
+
|
43
|
+
if mapped_value.kind_of?(Class)
|
44
|
+
add_mapping(*args, &block)
|
45
|
+
else
|
46
|
+
if framework.nil? || loaded_framework?(framework)
|
47
|
+
add_constant_mapping(*args, &block)
|
48
|
+
else
|
49
|
+
on_framework(framework) do
|
50
|
+
add_constant_mapping(*args, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Registers +mapped_name+ as a Mapper#builder_method for the given
|
57
|
+
# +mapped_value+. The +block+ is used as the Mapper#builder_method's body.
|
58
|
+
def self.add_mapping(mapped_name, mapped_value, &block)
|
59
|
+
m = Mapper.map_instances_of(mapped_value, mapped_name, &block)
|
60
|
+
mappings[m.builder_method] = m
|
61
|
+
end
|
62
|
+
|
63
|
+
# Registers +mapped_name+ as a Mapper#builder_method for the given
|
64
|
+
# +constant+ string which will be looked up. The +block+ is used as the
|
65
|
+
# Mapper#builder_method's body.
|
66
|
+
def self.add_constant_mapping(mapped_name, constant, &block)
|
67
|
+
add_mapping(mapped_name, Object.full_const_get(constant), &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the Hash of mappings.
|
71
|
+
def self.mappings
|
72
|
+
@mappings ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Registers a callback for after the specified framework has been loaded.
|
76
|
+
def self.on_framework(name, &block)
|
77
|
+
(frameworks[name.to_s] ||= []) << block
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the Hash of mapped frameworks.
|
81
|
+
def self.frameworks
|
82
|
+
@frameworks ||= {}
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the Set of loaded frameworks.
|
86
|
+
def self.loaded_frameworks
|
87
|
+
@loaded_frameworks ||= Set.new
|
88
|
+
end
|
89
|
+
|
90
|
+
# Registers a given framework as being loaded.
|
91
|
+
def self.framework_loaded
|
92
|
+
frameworks.keys.each do |key|
|
93
|
+
if loaded_framework?(key)
|
94
|
+
frameworks[key].each do |mapper|
|
95
|
+
mapper.call
|
96
|
+
end
|
97
|
+
frameworks.delete(key)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns whether or not the framework has been loaded yet.
|
103
|
+
def self.loaded_framework?(name)
|
104
|
+
NSBundle.allFrameworks.map {|bundle| bundle.bundlePath.split("/").last}.select {|framework| framework.split(".")[1] == 'framework'}.map {|framework| framework.split(".")[0]}.include?(name.to_s)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
data/lib/hotcocoa/mvc.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'hotcocoa'
|
2
|
+
|
3
|
+
class HotCocoaApplication
|
4
|
+
attr_accessor :shared_application, :application_controller, :controllers
|
5
|
+
|
6
|
+
include HotCocoa
|
7
|
+
|
8
|
+
def self.instance=(instance)
|
9
|
+
@instance = instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.instance
|
13
|
+
@instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(application_file)
|
17
|
+
HotCocoaApplication.instance = self
|
18
|
+
@controllers = {}
|
19
|
+
load_controllers_and_views(directory_of(application_file))
|
20
|
+
@shared_application = application(ApplicationView.options[:application])
|
21
|
+
@shared_application.load_application_menu
|
22
|
+
@application_controller = controller(:application_controller)
|
23
|
+
shared_application.delegate_to(application_controller)
|
24
|
+
end
|
25
|
+
|
26
|
+
def start
|
27
|
+
@shared_application.run
|
28
|
+
end
|
29
|
+
|
30
|
+
def controller(controller_name)
|
31
|
+
controller_name_string = controller_name.to_s
|
32
|
+
controller_class = Object.const_get(controller_name_string !~ /_/ && controller_name_string =~ /[A-Z]+.*/ ? controller_name_string : controller_name_string.split('_').map{|e| e.capitalize}.join)
|
33
|
+
@controllers[controller_name] || create_controller_instance(controller_name, controller_class)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def create_controller_instance(controller_name, controller_class)
|
39
|
+
controller_instance = controller_class.new(self)
|
40
|
+
@controllers[controller_name] = controller_instance
|
41
|
+
controller_instance.application_window
|
42
|
+
controller_instance
|
43
|
+
end
|
44
|
+
|
45
|
+
def directory_of(application_file)
|
46
|
+
File.dirname(File.expand_path(application_file))
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_controllers_and_views(directory)
|
50
|
+
Dir.glob(File.join(directory, 'controllers', '**', '*.rb')).each do |controller_file|
|
51
|
+
load(controller_file)
|
52
|
+
end
|
53
|
+
Dir.glob(File.join(directory, 'views', '**', '*.rb')).each do |view_file|
|
54
|
+
load(view_file)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class HotCocoaController
|
61
|
+
|
62
|
+
def self.view_instances
|
63
|
+
@view_instances ||= {}
|
64
|
+
end
|
65
|
+
|
66
|
+
attr_reader :application
|
67
|
+
|
68
|
+
def initialize(application)
|
69
|
+
@application = application
|
70
|
+
end
|
71
|
+
|
72
|
+
def application_window
|
73
|
+
@application.application_controller.application_window
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
class HotCocoaApplicationController < HotCocoaController
|
79
|
+
|
80
|
+
def initialize(application)
|
81
|
+
super(application)
|
82
|
+
end
|
83
|
+
|
84
|
+
def application_window
|
85
|
+
@application_window ||= ApplicationWindow.new(self).application_window
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class HotCocoaWindow
|
91
|
+
|
92
|
+
attr_reader :application_controller, :application_window
|
93
|
+
|
94
|
+
def initialize(application_controller)
|
95
|
+
@application_controller = application_controller
|
96
|
+
render
|
97
|
+
end
|
98
|
+
|
99
|
+
def render
|
100
|
+
@application_window = HotCocoa.window(ApplicationView.options[:window])
|
101
|
+
@application_window.delegate_to(application_controller)
|
102
|
+
@application_window.view << application_controller.application_view
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class HotCocoaView < HotCocoa::LayoutView
|
108
|
+
|
109
|
+
DefaultLayoutOptions = {:expand => [:width, :height]}
|
110
|
+
|
111
|
+
module ClassMethods
|
112
|
+
def controller(name=nil)
|
113
|
+
if name
|
114
|
+
@name = name
|
115
|
+
else
|
116
|
+
@name || :application_controller
|
117
|
+
end
|
118
|
+
end
|
119
|
+
def options(options=nil)
|
120
|
+
if options
|
121
|
+
@options = options
|
122
|
+
else
|
123
|
+
@options
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.inherited(klass)
|
129
|
+
klass.extend(ClassMethods)
|
130
|
+
klass.send(:include, HotCocoa::Behaviors)
|
131
|
+
class_name = klass.name.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
|
132
|
+
HotCocoaController.class_eval %{
|
133
|
+
def #{class_name}
|
134
|
+
unless HotCocoaController.view_instances[:#{class_name}]
|
135
|
+
HotCocoaController.view_instances[:#{class_name}] = #{klass.name}.alloc.initWithFrame([0,0,0,0])
|
136
|
+
HotCocoaController.view_instances[:#{class_name}].setup_view
|
137
|
+
end
|
138
|
+
HotCocoaController.view_instances[:#{class_name}]
|
139
|
+
end
|
140
|
+
}, __FILE__, __LINE__
|
141
|
+
end
|
142
|
+
|
143
|
+
attr_reader :controller
|
144
|
+
|
145
|
+
def setup_view
|
146
|
+
unless @controller
|
147
|
+
@controller = class_controller
|
148
|
+
self.layout = layout_options
|
149
|
+
render
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def class_controller
|
156
|
+
HotCocoaApplication.instance.controller(self.class.controller)
|
157
|
+
end
|
158
|
+
|
159
|
+
def layout_options
|
160
|
+
options = if self.class.options && self.class.options[:layout]
|
161
|
+
self.class.options[:layout]
|
162
|
+
else
|
163
|
+
DefaultLayoutOptions
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
class ApplicationWindow < HotCocoaWindow
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
class Application < HotCocoaApplication
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module HotCocoa
|
2
|
+
|
3
|
+
class NotificationListener
|
4
|
+
|
5
|
+
DistributedBehaviors = {
|
6
|
+
:drop => NSNotificationSuspensionBehaviorDrop,
|
7
|
+
:coalesce => NSNotificationSuspensionBehaviorCoalesce,
|
8
|
+
:hold => NSNotificationSuspensionBehaviorHold,
|
9
|
+
:deliver_immediately => NSNotificationSuspensionBehaviorDeliverImmediately
|
10
|
+
}
|
11
|
+
|
12
|
+
attr_reader :callback, :name, :sender, :suspension_behavior
|
13
|
+
|
14
|
+
def self.registered_listeners
|
15
|
+
@registered_listeners ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(options={}, &block)
|
19
|
+
@callback = block
|
20
|
+
@distributed = (options[:distributed] == true)
|
21
|
+
@suspension_behavior = DistributedBehaviors[options[:when_suspended] || :coalesce]
|
22
|
+
@name = options[:named]
|
23
|
+
@sender = options[:sent_by]
|
24
|
+
NotificationListener.registered_listeners << self
|
25
|
+
observe
|
26
|
+
end
|
27
|
+
|
28
|
+
def distributed?
|
29
|
+
@distributed
|
30
|
+
end
|
31
|
+
|
32
|
+
def receive(notification)
|
33
|
+
callback.call(notification)
|
34
|
+
end
|
35
|
+
|
36
|
+
def stop_notifications(options={})
|
37
|
+
if options.has_key?(:named) || options.has_key?(:sent_by)
|
38
|
+
notification_center.removeObserver(self, name:options[:named], object:options[:sender])
|
39
|
+
else
|
40
|
+
notification_center.removeObserver(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def observe
|
47
|
+
if distributed?
|
48
|
+
notification_center.addObserver self, selector:'receive:', name:name, object:sender, suspensionBehavior:suspension_behavior
|
49
|
+
else
|
50
|
+
notification_center.addObserver self, selector:'receive:', name:name, object:sender
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def notification_center
|
55
|
+
@notification_center ||= (distributed? ? NSDistributedNotificationCenter.defaultCenter : NSNotificationCenter.defaultCenter)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_notification(options={}, &block)
|
60
|
+
NotificationListener.new(options, &block)
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Object.full_const_get was taken from the ‘extlib’ project:
|
2
|
+
# http://github.com/sam/extlib which is released under a MIT License and
|
3
|
+
# copyrighted by Sam Smoot (2008).
|
4
|
+
|
5
|
+
class Object
|
6
|
+
|
7
|
+
# @param name<String> The name of the constant to get, e.g. "Merb::Router".
|
8
|
+
#
|
9
|
+
# @return <Object> The constant corresponding to the name.
|
10
|
+
def full_const_get(name)
|
11
|
+
list = name.split("::")
|
12
|
+
list.shift if list.first.empty?
|
13
|
+
obj = self
|
14
|
+
list.each do |x|
|
15
|
+
# This is required because const_get tries to look for constants in the
|
16
|
+
# ancestor chain, but we only want constants that are HERE
|
17
|
+
obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
|
18
|
+
end
|
19
|
+
obj
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module HotCocoa
|
2
|
+
def read_plist(data, mutability=:all)
|
3
|
+
mutability = case mutability
|
4
|
+
when :none
|
5
|
+
NSPropertyListImmutable
|
6
|
+
when :containers_only
|
7
|
+
NSPropertyListMutableContainers
|
8
|
+
when :all
|
9
|
+
NSPropertyListMutableContainersAndLeaves
|
10
|
+
else
|
11
|
+
raise ArgumentError, "invalid mutability `#{mutability}'"
|
12
|
+
end
|
13
|
+
if data.is_a?(String)
|
14
|
+
data = data.dataUsingEncoding(NSUTF8StringEncoding)
|
15
|
+
if data.nil?
|
16
|
+
raise ArgumentError, "cannot convert string `#{data}' to data"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# TODO retrieve error description and raise it if there is an error.
|
20
|
+
NSPropertyListSerialization.propertyListFromData(data,
|
21
|
+
mutabilityOption:mutability,
|
22
|
+
format:nil,
|
23
|
+
errorDescription:nil)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Object
|
28
|
+
def to_plist(format=:xml)
|
29
|
+
format = case format
|
30
|
+
when :xml
|
31
|
+
NSPropertyListXMLFormat_v1_0
|
32
|
+
when :binary
|
33
|
+
NSPropertyListBinaryFormat_v1_0
|
34
|
+
when :open_step
|
35
|
+
NSPropertyListOpenStepFormat
|
36
|
+
else
|
37
|
+
raise ArgumentError, "invalid format `#{format}'"
|
38
|
+
end
|
39
|
+
# TODO retrieve error description and raise it if there is an error.
|
40
|
+
data = NSPropertyListSerialization.dataFromPropertyList(self,
|
41
|
+
format:format,
|
42
|
+
errorDescription:nil)
|
43
|
+
NSMutableString.alloc.initWithData(data, encoding:NSUTF8StringEncoding)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
AppConfig = HotCocoa::ApplicationBuilder::Configuration.new("config/build.yml")
|
2
|
+
|
3
|
+
task :deploy => [:clean] do
|
4
|
+
HotCocoa::ApplicationBuilder.build(AppConfig, :deploy => true)
|
5
|
+
end
|
6
|
+
|
7
|
+
task :build do
|
8
|
+
HotCocoa::ApplicationBuilder.build(AppConfig)
|
9
|
+
end
|
10
|
+
|
11
|
+
task :run => [:build] do
|
12
|
+
`"./#{AppConfig.name}.app/Contents/MacOS/#{AppConfig.name.gsub(/ /, '')}"`
|
13
|
+
end
|
14
|
+
|
15
|
+
task :clean do
|
16
|
+
`/bin/rm -rf "#{AppConfig.name}.app"`
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
module HotCocoa
|
5
|
+
class Template
|
6
|
+
|
7
|
+
def self.source_directory
|
8
|
+
File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.copy_to(directory, app_name)
|
12
|
+
FileUtils.mkdir_p(directory)
|
13
|
+
Dir.glob(File.join(source_directory, "template", "**/*")).each do |file|
|
14
|
+
short_name = file[(source_directory.length+10)..-1]
|
15
|
+
if File.directory?(file)
|
16
|
+
FileUtils.mkdir_p File.join(directory, short_name)
|
17
|
+
else
|
18
|
+
File.open(File.join(directory, short_name), "w") do |out|
|
19
|
+
input = File.read(file)
|
20
|
+
input.gsub!(/__APPLICATION_NAME__/, app_name)
|
21
|
+
out.write input
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
class VirtualFileSystem
|
2
|
+
|
3
|
+
def self.code_to_load(main_file, password = "\320\302\354\024\215\360P\235\2244\261\214\276:'\274%\270<\350t\212\003\340\t'\004\345\334\256\315\277")
|
4
|
+
<<-VFS
|
5
|
+
module DatabaseRuntime
|
6
|
+
|
7
|
+
class VirtualFilesystem
|
8
|
+
|
9
|
+
attr_reader :password, :encryption_method
|
10
|
+
|
11
|
+
def initialize(password, encryption_method="aes256")
|
12
|
+
@password = password
|
13
|
+
@encryption_method = encryption_method
|
14
|
+
end
|
15
|
+
|
16
|
+
def open_database(main_database)
|
17
|
+
@main_database = main_database
|
18
|
+
begin
|
19
|
+
@db = Marshal.load(File.open(main_database, "rb") {|f| f.read})
|
20
|
+
rescue
|
21
|
+
puts $!
|
22
|
+
puts $!.backtrace.join("\\n")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_file_and_key(filename)
|
27
|
+
data, key = read_file_using_path(filename)
|
28
|
+
if data.nil? && filename[0,1] != "/"
|
29
|
+
$:.each do |loadpath|
|
30
|
+
data, key = read_file_using_path(filename, expand_path(loadpath))
|
31
|
+
break if data
|
32
|
+
end
|
33
|
+
end
|
34
|
+
[data, key]
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_file_using_path(filename, pathname="/")
|
38
|
+
path = expand_path(File.join(pathname, filename))
|
39
|
+
rb_ext = false
|
40
|
+
if has_key?(path)
|
41
|
+
key = filename
|
42
|
+
elsif self.has_key?(path+".rb")
|
43
|
+
key = filename+".rb"
|
44
|
+
rb_ext = true
|
45
|
+
end
|
46
|
+
if key
|
47
|
+
result = rb_ext ? self[path+".rb"] : self[path]
|
48
|
+
if result
|
49
|
+
result.gsub!(/__FILE__/, "'\#{path + (rb_ext ? ".rb" : '')}'")
|
50
|
+
return result, key
|
51
|
+
end
|
52
|
+
end
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def has_key?(key)
|
57
|
+
@db.has_key?(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete(key)
|
61
|
+
end
|
62
|
+
|
63
|
+
def [](key)
|
64
|
+
read(@db, key)
|
65
|
+
end
|
66
|
+
|
67
|
+
def close
|
68
|
+
end
|
69
|
+
|
70
|
+
def keys
|
71
|
+
@db.keys
|
72
|
+
end
|
73
|
+
|
74
|
+
def []=(key, value)
|
75
|
+
end
|
76
|
+
|
77
|
+
def normalize(filename)
|
78
|
+
f = filename.split(File::Separator)
|
79
|
+
f.delete(".")
|
80
|
+
while f.include?("..")
|
81
|
+
f[f.index("..")-1,2] = []
|
82
|
+
end
|
83
|
+
f.join(File::Separator)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def expand_path(filename, dirstring=nil)
|
89
|
+
filename = File.join(dirstring, filename) if dirstring
|
90
|
+
f = filename.split(File::Separator)
|
91
|
+
f.delete(".")
|
92
|
+
f.delete("")
|
93
|
+
while f.include?("..")
|
94
|
+
f[f.index("..")-1,2] = []
|
95
|
+
end
|
96
|
+
"/"+f.join(File::Separator)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def read(db, key)
|
101
|
+
if db.has_key?(key)
|
102
|
+
#decrypt = OpenSSL::Cipher::Cipher.new(encryption_method)
|
103
|
+
#decrypt.decrypt
|
104
|
+
#decrypt.key = password
|
105
|
+
#data = decrypt.update(db[key])
|
106
|
+
#data << decrypt.final
|
107
|
+
#::Zlib::Inflate.inflate(data)
|
108
|
+
db[key]
|
109
|
+
else
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
$ROOT_BINDING = binding
|
119
|
+
|
120
|
+
module Kernel
|
121
|
+
|
122
|
+
def virtual_filesystems(options)
|
123
|
+
vfs = DatabaseRuntime::VirtualFilesystem.new(options["password"])
|
124
|
+
vfs.open_database(options["database"])
|
125
|
+
Kernel.const_set("VIRTUAL_FILESYSTEM", vfs)
|
126
|
+
vfs
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :original_require, :require
|
130
|
+
|
131
|
+
def require(path)
|
132
|
+
path = VIRTUAL_FILESYSTEM.normalize(path)
|
133
|
+
path_loaded = $".include?(path) || $".include?(path+".rb") || $".include?(path+".so")
|
134
|
+
unless path_loaded
|
135
|
+
data, key = VIRTUAL_FILESYSTEM.read_file_and_key(path)
|
136
|
+
if data
|
137
|
+
$" << key
|
138
|
+
eval(data, $ROOT_BINDING, key, 0)
|
139
|
+
return true
|
140
|
+
else
|
141
|
+
original_require(path)
|
142
|
+
end
|
143
|
+
else
|
144
|
+
return false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
alias_method :original_load, :load
|
149
|
+
|
150
|
+
def load(path)
|
151
|
+
path = VIRTUAL_FILESYSTEM.normalize(path)
|
152
|
+
data, key = VIRTUAL_FILESYSTEM.read_file_and_key(path)
|
153
|
+
if data
|
154
|
+
eval(data, $ROOT_BINDING, key, 0)
|
155
|
+
return true
|
156
|
+
else
|
157
|
+
begin
|
158
|
+
original_load(path)
|
159
|
+
rescue LoadError => e
|
160
|
+
raise e
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
virtual_filesystems("database"=>File.join(NSBundle.mainBundle.resourcePath.fileSystemRepresentation, 'vfs.db'), "password"=>#{password.inspect})
|
168
|
+
$:.unshift "/lib"
|
169
|
+
load '#{main_file}'
|
170
|
+
VFS
|
171
|
+
end
|
172
|
+
end
|
data/lib/hotcocoa.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
framework 'Cocoa'
|
5
|
+
|
6
|
+
module Hotcocoa
|
7
|
+
VERSION = '0.0.1'
|
8
|
+
Views = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'hotcocoa/object_ext'
|
12
|
+
require 'hotcocoa/kernel_ext'
|
13
|
+
require 'hotcocoa/mappings'
|
14
|
+
require 'hotcocoa/behaviors'
|
15
|
+
require 'hotcocoa/mapping_methods'
|
16
|
+
require 'hotcocoa/mapper'
|
17
|
+
require 'hotcocoa/layout_view'
|
18
|
+
require 'hotcocoa/delegate_builder'
|
19
|
+
require 'hotcocoa/notification_listener'
|
20
|
+
require 'hotcocoa/data_sources/table_data_source'
|
21
|
+
require 'hotcocoa/data_sources/combo_box_data_source'
|
22
|
+
require 'hotcocoa/plist'
|
23
|
+
require 'hotcocoa/kvo_accessors'
|
24
|
+
require 'hotcocoa/attributed_string'
|
25
|
+
|
26
|
+
HotCocoa::Mappings.reload
|
data/template/Rakefile
ADDED