hotcocoa 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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