Class: HotCocoa::Mappings::Mapper
Overview
Does most of the heavy lifiting when it comes to HotCocoa mappings.
Constant Summary collapse
- SET =
Performance hack. Put mutable objects that are constant into a constant to avoid having to
#dup
. 'set'
Class Attribute Summary collapse
-
.bindings_modules ⇒ Hash{Symbol=>Module}
readonly
Cached bindings modules.
-
.delegate_modules ⇒ Hash{Symbol=>Module}
readonly
Cached delegate modules.
Instance Attribute Summary collapse
-
#builder_method ⇒ Symbol
readonly
The name which the mapping goes by (e.g. :window for NSWindow).
- #control_class ⇒ Class readonly
-
#control_module ⇒ Class
readonly
Singleton class for the mapper instance.
-
#map_bindings ⇒ Boolean
Whether or not bindings should be mapped for an instance of the mapped class.
Class Method Summary collapse
-
.map_class(klass) ⇒ nil
Add mappings to a class so instances of the class can benefit from HotCocoa features.
-
.map_instances_of(klass, builder_method, &block) ⇒ HotCocoa::Mappings::Mapper
Create a mapper for the given
klass
and assign it to the givenbuilder_method
.
Instance Method Summary collapse
-
#bindings_module_for_control(control) ⇒ Module
Create a module to hold all bindings setters.
-
#customize(control) ⇒ Object
Apply customizations to defined in a mapping to the control.
-
#decorate_with_bindings_methods(control) ⇒ nil
Do not count on a return value.
-
#decorate_with_delegate_methods(control) ⇒ Object
Add the delegate method hooks.
-
#delegate_module_for_control_class ⇒ Module
Create a module to hold the delegate object.
-
#include_in_class ⇒ Object
Add HotCocoa features to a class.
-
#inherited_constants ⇒ Hash{Hash}
Returns a hash of constant hashes that were inherited from ancestors that have also been mapped.
-
#inherited_custom_methods ⇒ Array<Module>
Return the
custom_methods
module for the class we are instantiating, as well as all of its ancestors. - #inherited_delegate_methods ⇒ Object
-
#initialize(klass) ⇒ Mapper
constructor
A new instance of Mapper.
-
#map_method(builder_method) { ... } ⇒ HotCocoa::Mappings::Mapper
Create the constructor named
builder_method
for the HotCocoa module. -
#remap_constants(tags) ⇒ Hash
Takes a hash and processes symbols, if the symbol is a mapped constant then it will be swapped with the value of the constant.
Constructor Details
#initialize(klass) ⇒ Mapper
Returns a new instance of Mapper.
81 82 83 |
# File 'lib/hotcocoa/mapper.rb', line 81 def initialize klass @control_class = klass end |
Class Attribute Details
.bindings_modules ⇒ Hash{Symbol=>Module} (readonly)
Cached bindings modules.
35 36 37 |
# File 'lib/hotcocoa/mapper.rb', line 35 def bindings_modules @bindings_modules end |
.delegate_modules ⇒ Hash{Symbol=>Module} (readonly)
Cached delegate modules.
41 42 43 |
# File 'lib/hotcocoa/mapper.rb', line 41 def delegate_modules @delegate_modules end |
Instance Attribute Details
#builder_method ⇒ Symbol (readonly)
We do not use the cached builder_method
attribute unless
you count tests. So maybe we should get rid of it?
The name which the mapping goes by (e.g. :window for NSWindow)
65 66 67 |
# File 'lib/hotcocoa/mapper.rb', line 65 def builder_method @builder_method end |
#control_class ⇒ Class (readonly)
56 57 58 |
# File 'lib/hotcocoa/mapper.rb', line 56 def control_class @control_class end |
#control_module ⇒ Class (readonly)
Singleton class for the mapper instance
71 72 73 |
# File 'lib/hotcocoa/mapper.rb', line 71 def control_module @control_module end |
#map_bindings ⇒ Boolean
Whether or not bindings should be mapped for an instance of the mapped class.
78 79 80 |
# File 'lib/hotcocoa/mapper.rb', line 78 def map_bindings @map_bindings end |
Class Method Details
.map_class(klass) ⇒ nil
Add mappings to a class so instances of the class can benefit from HotCocoa features. Usually called by Behaviors.included.
16 17 18 |
# File 'lib/hotcocoa/mapper.rb', line 16 def map_class klass new(klass).include_in_class end |
.map_instances_of(klass, builder_method, &block) ⇒ HotCocoa::Mappings::Mapper
Create a mapper for the given klass
and assign it to the
given builder_method
.
27 28 29 |
# File 'lib/hotcocoa/mapper.rb', line 27 def map_instances_of klass, builder_method, &block new(klass).map_method(builder_method, &block) end |
Instance Method Details
#bindings_module_for_control(control) ⇒ Module
Create a module to hold all bindings setters. The bindings module is meant to assist with setting up Cocoa Bindings by providing a simplified and more Ruby-ish interface.
Read more about Key-Value Binding.
If the control has no exposed bindings, then an empty module will be generated.
In either case, once a module is generated, it is cached for later use.
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/hotcocoa/mapper.rb', line 295 def bindings_module_for_control control bindings_module = HotCocoa::Mappings::Mapper.bindings_modules[control_class] return bindings_module if bindings_module instance = if control == control_class control_class.alloc.init else control end bindings_module = Module.new instance.exposedBindings.each do |exposed_binding| p = Proc.new do |value| if value.kind_of? Hash = value.delete :options bind "#{exposed_binding}", toObject: value.keys.first, withKeyPath: value.values.first, options: else send "set#{exposed_binding.camel_case}", value end end bindings_module.send :define_method, "#{exposed_binding.underscore}=", p end HotCocoa::Mappings::Mapper.bindings_modules[control_class] = bindings_module end |
#customize(control) ⇒ Object
Apply customizations to defined in a mapping to the control. The control is either an instance of the class or the class itself, depending on how things were setup.
211 212 213 214 215 216 217 |
# File 'lib/hotcocoa/mapper.rb', line 211 def customize control inherited_custom_methods.each do |custom_methods| control.send @extension_method, custom_methods end decorate_with_delegate_methods control decorate_with_bindings_methods control end |
#decorate_with_bindings_methods(control) ⇒ nil
Returns do not count on a return value.
275 276 277 278 279 280 |
# File 'lib/hotcocoa/mapper.rb', line 275 def decorate_with_bindings_methods control return if control_class == NSApplication if @map_bindings control.send @extension_method, bindings_module_for_control(control) end end |
#decorate_with_delegate_methods(control) ⇒ Object
Add the delegate method hooks. For #include they become instance methods and for #extend they become singleton methods.
222 223 224 |
# File 'lib/hotcocoa/mapper.rb', line 222 def decorate_with_delegate_methods control control.send @extension_method, delegate_module_for_control_class end |
#delegate_module_for_control_class ⇒ Module
Create a module to hold the delegate object. The module can then be mixed in so that a control instance can use HotCocoa style delegation.
The style of delegation that HotCocoa supports works by creating an Object instance and then defining delegate methods as singleton methods on that object. Then the object is set to be the delegate of the control.
The generated module is cached for later reuse.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/hotcocoa/mapper.rb', line 238 def delegate_module_for_control_class delegate_module = HotCocoa::Mappings::Mapper.delegate_modules[control_class] return delegate_module if delegate_module delegate_module = Module.new required_methods = [] delegate_methods = inherited_delegate_methods if delegate_methods.size > 0 delegate_methods.each do |delegate_method, mapping| required_methods << delegate_method if mapping[:required] end delegate_methods.each do |delegate_method, mapping| parameters = mapping[:parameters] ? mapping[:parameters] : [] # kind of a hack, giving a block directly to define_method is not working # for some odd reason, possibly a bug in MacRuby callback = Proc.new do |&block| raise 'Must pass in a block to use this delegate method' unless block @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, required_methods) @_delegate_builder.add_delegated_method(block, delegate_method, *parameters) end delegate_module.send :define_method, mapping[:to], callback end delegate_module.send :define_method, :delegate_to do |object| @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, required_methods) @_delegate_builder.delegate_to(object, *delegate_methods.values.map { |method| method[:to].to_sym }) end end HotCocoa::Mappings::Mapper.delegate_modules[control_class] = delegate_module end |
#include_in_class ⇒ Object
Add HotCocoa features to a class. The control_class
that the mapper
was initialized with will receive features for all ancestors that
have mappings.
89 90 91 92 |
# File 'lib/hotcocoa/mapper.rb', line 89 def include_in_class @extension_method = :include customize @control_class end |
#inherited_constants ⇒ Hash{Hash}
Returns a hash of constant hashes that were inherited from ancestors that have also been mapped.
176 177 178 179 180 181 182 |
# File 'lib/hotcocoa/mapper.rb', line 176 def inherited_constants constants = {} control_class.hotcocoa_mappers.each do |ancestor| constants.merge! ancestor.control_module.constants_map end constants end |
#inherited_custom_methods ⇒ Array<Module>
Return the custom_methods
module for the class we are instantiating,
as well as all of its ancestors.
197 198 199 200 201 202 203 |
# File 'lib/hotcocoa/mapper.rb', line 197 def inherited_custom_methods control_class.hotcocoa_mappers.map! { |ancestor| if ancestor.control_module.custom_methods ancestor.control_module.custom_methods end }.compact end |
#inherited_delegate_methods ⇒ Object
184 185 186 187 188 189 190 |
# File 'lib/hotcocoa/mapper.rb', line 184 def inherited_delegate_methods delegate_methods = {} control_class.hotcocoa_mappers.each do |ancestor| delegate_methods.merge! ancestor.control_module.delegate_map end delegate_methods end |
#map_method(builder_method) { ... } ⇒ HotCocoa::Mappings::Mapper
Create the constructor named builder_method
for the HotCocoa
module.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/hotcocoa/mapper.rb', line 101 def map_method builder_method, &block @extension_method = :extend @builder_method = builder_method # @todo use self.singleton_class instead (not implemented in MacRuby yet) mod = (class << self; self; end) mod.extend HotCocoa::MappingMethods mod.module_eval &block @control_module = mod # put self in a variable, because context of self changes inside the define_method block inst = self HotCocoa.send :define_method, builder_method do |args = {}, &control_block| map = inst.remap_constants args inst.map_bindings = map.delete :map_bindings default_empty_rect_used = (CGRectZero == map[:frame]) control = if inst.respond_to? :init_with_options inst.(inst.control_class.alloc, map) else inst.(map) end inst.customize control map.each do |key, value| if control.respond_to? "#{key}=" control.send "#{key}=", value elsif control.respond_to? key new_key = (key.start_with?(SET) ? key : "set#{key[0].capitalize}#{key[1..-1]}") if control.respond_to? new_key control.send new_key, value else control.send key end elsif control.respond_to? "set#{key.camel_case}" control.send "set#{key.camel_case}", value else NSLog("Unable to map #{key} as a method") end end if default_empty_rect_used control.sizeToFit if control.respondsToSelector :sizeToFit end if control_block if inst.respond_to? :handle_block inst.handle_block control, &control_block else control_block.call control end end control end # make the function callable using HotCocoa.xxxx HotCocoa.send :module_function, builder_method # module_function makes the instance method private, but we want it to stay public HotCocoa.send :public, builder_method self end |
#remap_constants(tags) ⇒ Hash
Takes a hash and processes symbols, if the symbol is a mapped constant then it will be swapped with the value of the constant.
This is how constant mappings are used in Hot Cocoa.
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/hotcocoa/mapper.rb', line 331 def remap_constants constants = inherited_constants if control_module.defaults control_module.defaults.each do |key, value| [key] = value unless .has_key? key end end result = {} .each do |tag, value| if constants[tag] result[tag] = value.kind_of?(Array) ? value.inject(0) { |a, i| a|constants[tag][i] } : constants[tag][value] else result[tag] = value end end result end |