Module: CascadingClasses
- Defined in:
- lib/cascading_classes.rb,
lib/cascading_classes/dsl.rb,
lib/cascading_classes/types.rb,
lib/cascading_classes/parse_options.rb,
lib/cascading_classes/cascading_classes.rb
Overview
todo: support regexp’s natively??
Defined Under Namespace
Classes: BlankSlate, DSL
Class Method Summary collapse
-
.apply(klass, instances_too) ⇒ Object
klass is the class that included/excluded CascadingClasses.
- .basic_types ⇒ Object
- .block_inst_exec=(val) ⇒ Object
-
.block_inst_exec? ⇒ Boolean
global default: whether blocks are yielded to or instance_exec’d global default defaults to true.
-
.cascade_intern(property, settings) ⇒ Object
def self.cascade_intern(klass, property, settings).
- .cascade_parse_options(arg1, arg2, opts, settings) ⇒ Object
- .class_methods ⇒ Object
-
.const_missing(name) ⇒ Object
note, VERSION will be undetected unless called directly.
-
.default_blank(klass) ⇒ Object
returns a proc that takes an argument, evaluating whether it is blank.
-
.default_ensure_type(klass, is_blank, new_proc) ⇒ Object
naive transformation to be applied for each type to ensure that it stays that type of course it would be better (wouldn’t it??) to react to an object rather than a ‘type’ but one of the features we support is to specify the type before any default data has been given procs take any value and should attempt to convert to specified type is_blank: proc that determines whether a value of that type is blank new_proc: proc that creates new objects of that type.
-
.default_new(klass) ⇒ Object
subclass’ initialition of own version of property.
- .extended(klass) ⇒ Object
-
.get_type(default = nil, ensure_type = nil, ensure_val = nil) ⇒ Object
typically you will be given just a default value, from that we need to determine the type: default.class.to_s.to_sym in this case ensure_type will be nil and ensure_val will be ignored.
-
.illegal_names ⇒ Object
only applies to desired getter and setter names, not the property itself todo: allow one of [:cascade, :all!] to be overwritten todo: add :unset_property.
-
.included(klass) ⇒ Object
self.apply.
- .is_array_like?(obj, blk = nil) ⇒ Boolean
- .is_container?(obj, blk = nil) ⇒ Boolean
- .is_hash_like?(obj, blk = nil) ⇒ Boolean
- .is_of_type?(obj, klass) ⇒ Boolean
-
.parse_options(apply_to_instances, *properties, &block) ⇒ Object
returns hash: {…, attr => [default, inst_too], .. } todo: consider removing support for custom getters/setters in array syntax.
- .proc_inst_exec=(val) ⇒ Object
-
.proc_inst_exec? ⇒ Boolean
global default: whether proc values are evaluated or instance_exec’d global default defaults to false.
- .respond_to?(const) ⇒ Boolean
- .undefined_type ⇒ Object
- .undefined_type?(type) ⇒ Boolean
-
.unsymbolize(obj) ⇒ Object
turns :Hash into Hash, :Array into Array, …
Class Method Details
.apply(klass, instances_too) ⇒ Object
klass is the class that included/excluded CascadingClasses
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 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 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/cascading_classes.rb', line 63 def self.apply(klass, instances_too) # cascade method klass.singleton_class.instance_eval do define_method :cascade do |*props, &block| illegal_names = CascadingClasses.illegal_names props = CascadingClasses.(instances_too, *props, &block) props.each{|prop, v| v[:getters].each{|getter| raise NameError, "Illegal property getter: #{getter} for #{prop}" if illegal_names.include? getter } } props.each do |prop, settings| raise "Hell" unless Symbol === prop # sanity getters, setters = settings[:getters], settings[:setters] getter, setter = getters.first, setters.first # set default value on top level parent me = settings[:ensure_type].call(settings[:default]) instance_variable_set("@#{prop}", me) slf = self # property metadata saved in singleton class singleton_class.instance_eval do @properties ||= {} @properties.delete prop # in case it's already there @properties[prop] = settings @properties[prop][:class] = slf end ## mod = CascadingClasses.cascade_intern(self, prop, settings) mod = CascadingClasses.cascade_intern(prop, settings) self.extend mod singleton_class.instance_eval do getters[1..-1].each{|g| alias_method g, getter} unless getter.nil? setters[1..-1].each{|s| alias_method s, setter} unless setter.nil? end if settings[:instances_too] send(:include, mod) instance_eval do getters[1..-1].each{|g| alias_method g, getter} unless getter.nil? setters[1..-1].each{|s| alias_method s, setter} unless setter.nil? end end end end alias_method(:all!, :cascade) unless instance_methods.include?(:all!) end klass_methods = Module.new do # whether property was originally defined on self # uses the fact that 'properties' hash can be defined multiple times # for the same property on the class tree # name: property name # instance: (boolean) whether callee is an instance define_method :top_level_parent_for? do |name, instance=false| return false if instance name = name.to_sym props = singleton_class.properties raise "neither #{self} nor its ancestors have #{name}" unless props and props[name] props2 = superclass.singleton_class.properties rescue nil (props2 and props2[name]) ? false : true end # returns array of parents starting (excludes self) # for property:'name' ### will include self if self is a class # never includes self, even if a class # name: property name # instance: (boolean) whether 'callee' is an instance # always called by a class, not an instance define_method :parents_for do |name, instance=false| return [] if top_level_parent_for?(name, instance) s = instance ? self : self.superclass res = [s] until s.top_level_parent_for?(name) raise "Hell" if s.nil? s = s.superclass res << s end res end # differs from 'parents_for' in that it is not # property specific. a property defined on a # descendent of the top-most class will only # go as high as that descendent in the 'parents_for'. # hence any call to 'parents_for' will be a sub array # of a call to 'ancestor_chain' # # never includes self define_method :ancestor_chain do |instance=false| top_parent = oldest_cascading_class return [] unless top_parent # todo: raise "Hell" instead ## return [self] if self == top_parent return [] if self == top_parent res = [] up = self while up != top_parent res << up unless up == self and instance == false up = up.superclass end res << top_parent res end # slightly hackish # todo: propose adding marker variable to top-level class define_method :oldest_cascading_class do res = singleton_class.instance_variable_get("@properties").nil? ? nil : self tmp = self while tmp = tmp.superclass if s = tmp.singleton_class.instance_variable_get("@properties") res = tmp if Hash === s end end res end # calculates number of generations to ancestor # *eval* todo: resolve # note: a class is considered one generations away from its instances # todo: explain why not using self.ancestors # ancestor: class name to take generations from # instance: (boolean) whether 'callee' is an instance # always called by a class, not an instance define_method :generations_from do |ancestor, instance=false| ancestor = eval(ancestor.to_s) if Symbol === ancestor # *eval* s = self num = instance ? 1 : 0 while s != ancestor s = s.superclass raise "NoAncestor" if s.nil? num += 1 end num end # todo: instances # collects properties and their values # blanks: whether to include values that are blank define_method :to_hash do |blanks=true| s = self.class == Class ? self : self.class ## props = s.singleton_class.properties rescue {} ## return props if props.empty? props = s.singleton_class.properties getters = Hash[ props.map{|name, v| [name, v[:getters].first]} ] res = if blanks props.map{|name, v| [name, s.send(getters[name])] } else prop_names = props.keys.delete_if{|name| send("#{name}_is_blank?") } prop_names.map{|name| [name, s.send(getters[name])] } end Hash[res] end # closest ancestor with a non-blank property # returns nil if there aren't any # name: property name # instance: (boolean) whether 'callee' is an instance # always called by a class, not an instance define_method :youngest_valid_ancestor_for do |name, instance=false| parents = parents_for(name, instance) return nil if parents.empty? parents.each{|ancestor| return ancestor unless ancestor.send("#{name}_is_blank?") } nil end =begin define_method :youngest_valid_ancestor_for_OLD do |name, instance=false| parents = parents_for(name, instance) return self if parents.empty? parents.each{|ancestor| return ancestor unless ancestor.send("#{name}_is_blank?") } self end =end # name: property name # instance: (boolean) whether 'callee' is an instance # always called by a class, not an instance define_method :oldest_valid_ancestor_for do |name, instance=false| ## s = self.class == Class ? self : self.class ## props = s.singleton_class.properties props = singleton_class.properties raise "No ancestor of #{self} has #{name}" unless props and props[name.to_sym] s = self begin tmp = s.superclass while props2 = tmp.singleton_class.properties and props2[name.to_sym] s = tmp tmp = s.superclass end rescue NoMethodError end s end end klass.extend klass_methods ## klass.send(:include, klass_and_inst_methods) if instances_too klass.singleton_class.instance_eval do @properties ||= {} singleton_class.instance_eval do define_method :properties do res = instance_variable_get("@properties") || {} return res if self.superclass == Object.singleton_class up = superclass.send(:properties) rescue {} up.merge(res) end define_method :children do @children ||= [] end end define_method :inherited do |subklass| c = self.singleton_class.send(:children) c.delete subklass # if present c << subklass end end end |
.basic_types ⇒ Object
10 11 12 13 |
# File 'lib/cascading_classes/types.rb', line 10 def self.basic_types [:String, :Fixnum, :Float, :bool, :boolean, :Symbol, :Hash, :Array, :Set, :Proc, :FalseClass, :TrueClass] end |
.block_inst_exec=(val) ⇒ Object
58 59 60 |
# File 'lib/cascading_classes.rb', line 58 def self.block_inst_exec=(val) @block_inst_exec = !!val end |
.block_inst_exec? ⇒ Boolean
global default: whether blocks are yielded to or instance_exec’d global default defaults to true
54 55 56 |
# File 'lib/cascading_classes.rb', line 54 def self.block_inst_exec? @block_inst_exec.nil? ? @block_inst_exec = true : @block_inst_exec end |
.cascade_intern(property, settings) ⇒ Object
def self.cascade_intern(klass, property, settings)
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/cascading_classes/cascading_classes.rb', line 73 def self.cascade_intern(property, settings) property = property.to_sym Module.new do getter, setter = settings[:getters].first, settings[:setters].first raise "Hell" unless getter # opts: # proc_context: class to evaluate any proc in # block_inst_exec: whether to call block (false) or # to send block to instance_exec (true) # proc_inst_exec: whether to call proc (false) or # to convert to a block and send to instance_exec (true) # inherit: whether property (if blank) should inherit its value from above # --->top_most_parent_overall vs top_most_parent_property define_method getter do |arg1=:undef, arg2=:undef, opts={}, &block| is_instance = (self.class == Class) ? false : true klass = is_instance ? self.class : self # todo: allow an option to decide which chain to use chain = klass.parents_for(property, is_instance) ## chain = klass.ancestor_chain # incorrect value for A.new.prop{|val, parents| parents} if send("#{property}_is_unset?") and not CascadingClasses.undefined_type?(settings[:type]) instance_variable_set "@#{property}", settings[:new].call end # parse options r = CascadingClasses.(arg1, arg2, opts, settings) proc_inst_exec, block_inst_exec = r[:proc_inst_exec], r[:block_inst_exec] inherit_age = r[:inherit_age] || -1 if r[:default] val, is_blank = settings[:default], false else val = instance_variable_get "@#{property}" is_blank = send("#{property}_is_blank?") end raise "Hell" if val and CascadingClasses.undefined_type?(settings[:type]) # sanity return nil if CascadingClasses.undefined_type?(settings[:type]) # corner case ## cb = settings[:ensure_type] # callback cb = (settings[:type] == :Proc) ? Proc.new{|v| v} : settings[:ensure_type] if settings[:after_get] cb = Proc.new{|v| cb.call(settings[:after_get].call(v))} end first_valid_ancestor = is_blank ? klass.youngest_valid_ancestor_for(property, is_instance) : self # todo: double-check unless first_valid_ancestor # there are no valid ancestors (from self) for property raise "Hell" unless klass.top_level_parent_for?(property, is_instance) or is_blank me = cb.call(val) return block ? (block_inst_exec ? instance_exec(self, chain, &block) : block.call(me, chain)) : me end ancestor_age = (self == first_valid_ancestor) ? 0 : klass.generations_from(first_valid_ancestor, is_instance) inherit_age = (inherit_age < 0) ? ancestor_age : inherit_age if is_blank if ancestor_age > 0 and inherit_age >= ancestor_age if settings[:type] == :Proc and not opts.include?(:proc_context) opts.merge!({:proc_context => self}) end # fix: hackish up_val = first_valid_ancestor.instance_variable_get "@#{property}" val = (settings[:type] == :Proc or Proc === up_val) ? up_val : first_valid_ancestor.send(getter, :undef, :undef, opts) end end if is_blank and inherit_age.zero? and settings[:type] == :Proc val = settings[:default] end if Proc === val val = if opts[:proc_context] opts[:proc_context].instance_exec(self, chain, &val) else opts[:proc_inst_exec] ? instance_exec(self, chain, &val) : val.call(self, chain) end end me = cb.call(val) block ? (block_inst_exec ? instance_exec(me, chain, &block) : block.call(me, chain)) : me end if setter define_method setter do |v| raise "Hell" if settings[:type] == :NilClass # sanity if CascadingClasses.undefined_type?(settings[:type]) # or ## settings[:type] == :NilClass if v and v.class.to_s.to_sym != settings[:type] type = settings[:type] = v.class.to_s.to_sym unless settings[:options_given].include?(:blank) settings[:blank] = CascadingClasses.default_blank(type) end unless settings[:options_given].include?(:new) settings[:new] = CascadingClasses.default_new(type) end unless settings[:options_given].include?(:ensure_type) settings[:ensure_type] = CascadingClasses.default_ensure_type type, settings[:blank], settings[:new] end unless settings[:options_given].include?(:inherit) settings[:inherit] = CascadingClasses.is_container?(type) ? false : true end end end v = settings[:ensure_type].call(v) unless Proc === v instance_variable_set("@#{property}", v) end end define_method "#{property}_is_unset?" do not instance_variables.include? "@#{property}".to_sym end define_method "#{property}_is_blank?" do return true if self.send "#{property}_is_unset?" val = instance_variable_get "@#{property}" Proc === val ? false : settings[:blank].call(val) end end end |
.cascade_parse_options(arg1, arg2, opts, settings) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/cascading_classes/cascading_classes.rb', line 9 def self.(arg1, arg2, opts, settings) r = {} # ':default' given? -> whether to ignore own value and # return top-level 'default' set in 'cascade' call r[:default] = if arg1 == :default and (arg2 or arg2 == :undef) true elsif Hash === arg1 and arg1.include?(:default) !!arg1[:default] else false end return r if r[:default] r[:proc_inst_exec] = opts.include?(:proc_inst_exec) ? !!opts[:proc_inst_exec] : settings[:proc_inst_exec] r[:block_inst_exec] = opts.include?(:block_inst_exec) ? !!opts[:block_inst_exec] : settings[:block_inst_exec] is_container = CascadingClasses.is_container?(settings[:type]) # inherit_age unless it's given manually r[:inherit_age] = if settings[:inherit] == :undef raise "Hell" unless CascadingClasses.undefined_type?(settings[:type]) # sanity -1 else settings[:inherit] end =begin r[:inherit_age] = settings[:inherit] if r[:inherit_age] == :undef and not CascadingClasses.undefined_type?(settings[:type]) # only happens in corner case of unknown type r[:inherit_age] = is_container ? 0 : -1 end =end r[:inherit_age] = if Hash === arg1 and arg1.include?(:inherit) arg1[:inherit] elsif arg1 == :inherit arg2 == :undef ? -1 : arg2 elsif opts.include?(:inherit) opts[:inherit] else (arg1 == :undef) ? r[:inherit_age] : arg1 end r[:inherit_age] = case r[:inherit_age] when Fixnum then r[:inherit_age] when true then -1 when false, nil then 0 else raise "inherit_age: #{r[:inherit_age]} must be one of true, false, nil, <fixnum>" end r end |
.class_methods ⇒ Object
30 31 32 |
# File 'lib/cascading_classes.rb', line 30 def self.class_methods [:to_hash, :nearest_parent_for, :top_level_parent_for?] end |
.const_missing(name) ⇒ Object
note, VERSION will be undetected unless called directly
19 20 21 22 23 24 |
# File 'lib/cascading_classes.rb', line 19 def self.const_missing(name) case name when :VERSION then "0.6.1" else super end end |
.default_blank(klass) ⇒ Object
returns a proc that takes an argument, evaluating
whether it is blank
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/cascading_classes/types.rb', line 55 def self.default_blank(klass) ## raise "Hell" unless basic_type.include klass # sanity =begin if is_container?(klass) return Proc.new{|v| v.respond_to?(:size) ? v.size.zero? : true} # *eval* end =end return Proc.new{|v| v.size.zero?} if is_container?(klass) # *eval* klass = klass.to_s.to_sym case klass ## when :Proc, :Object then Proc.new{|v| v.nil? } when :Proc then Proc.new{|v| v.nil? } when :String then Proc.new{|v| v == ''} when :Fixnum, :Float then Proc.new{|v| v.nil?} ## Proc.new{|v| v.zero? } when :Symbol then Proc.new{|v| v.nil? or v.size.zero? } when :FalseClass, :TrueClass then Proc.new{|v| v.nil? } else self.undefined_type?(klass) ? Proc.new{|v| v.nil? } : Proc.new{|v| !!v} end end |
.default_ensure_type(klass, is_blank, new_proc) ⇒ Object
naive transformation to be applied for each type to ensure
that it stays that type
of course it would be better (wouldn’t it??) to react
to an object rather than a 'type'
but one of the features we support is to specify the
type before any default data has been given
procs take any value and should attempt to convert to specified type is_blank: proc that determines whether a value of that type is blank new_proc: proc that creates new objects of that type
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/cascading_classes/types.rb', line 116 def self.default_ensure_type(klass, is_blank, new_proc) ## raise "Hell" unless basic_type.include klass # sanity klass = klass.to_s.to_sym pre = lambda{|v| is_blank.call(v) ? v : (v || new_proc.call) } case klass when :Hash lambda do |v| return v if is_blank.call(v) v = new_proc.call if v.nil? return v if CascadingClasses.is_of_type?(v, klass) v = v.to_hash if v.respond_to?(:to_hash) Hash === v ? v : {v => nil} end when :Array lambda do |v| return v if is_blank.call(v) v = new_proc.call if v.nil? return v if CascadingClasses.is_of_type?(v, klass) v = v.to_a if v.respond_to?(:to_a) Array === v ? v : [*v] end when :Set lambda do |v| return v if is_blank.call(v) v = new_proc.call if v.nil? return v if CascadingClasses.is_of_type?(v, klass) if v.respond_to? :to_set v.to_set elsif v.respond_to? :to_a Set.new(v.to_a) else Set.new([*v]) end end ## when :Proc then Proc.new{|v| v} when :Proc Proc.new do |v| if v.nil? then nil elsif Proc === v then v else Proc.new{ v } end end when :Fixnum lambda{|v| return v if is_blank.call(v) v.to_i } when :Float lambda{|v| return v if is_blank.call(v) v.to_f } when :String lambda{|v| return v if is_blank.call(v) v.to_s } when :Symbol lambda{|v| return v if is_blank.call(v) v.respond_to?(:to_sym) ? v.to_sym : "#{v}".to_sym } when :FalseClass, :TrueClass lambda{|v| return v if is_blank.call(v) v.nil? ? nil : !!(v) } when :NilClass then Proc.new{|v| nil} ## when :Object then Proc.new{|v| v} # unknown type ## Proc.new{|v| raise "hell"} # sanity else if self.undefined_type?(klass) then Proc.new{|v| v} else lambda do |v| return v if is_blank.call(v) v = new_proc.call if v.nil? return v if CascadingClasses.is_of_type?(v, klass) raise "error: don't know how to convert #{v} to object of type #{klass}" end end end end |
.default_new(klass) ⇒ Object
subclass’ initialition of own version of property
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/cascading_classes/types.rb', line 82 def self.default_new(klass) ## raise "Hell" unless basic_type.include klass # sanity case klass when :String then Proc.new{ String.new } when :Fixnum then Proc.new{ nil } ## Proc.new{ 0 } when :Float then Proc.new{ nil } ## Proc.new{ 0.0 } when :Hash then Proc.new{ Hash.new } when :Array then Proc.new{ Array.new } when :Proc, :Symbol then Proc.new{ nil } when :FalseClass, :TrueClass, :NilClass then Proc.new{ nil } else if self.undefined_type?(klass) then Proc.new{ nil } else s = (klass.class == Class) ? klass : unsymbolize(klass) # *eval* if s.class == Class and s.respond_to?(:new) Proc.new{ s.new } else raise "don't know how to create new instance for #{klass}" end end end end |
.extended(klass) ⇒ Object
308 309 310 |
# File 'lib/cascading_classes.rb', line 308 def self.extended(klass) apply(klass, false) end |
.get_type(default = nil, ensure_type = nil, ensure_val = nil) ⇒ Object
typically you will be given just a default value, from that
we need to determine the type: default.class.to_s.to_sym
in this case ensure_type will be nil and ensure_val will be ignored
but a type can also be specified explicitly:
A.cascade do
name :hash => true
end
in this case ensure_type will be :Hash and ensure_val will be true
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/cascading_classes/types.rb', line 32 def self.get_type(default=nil, ensure_type=nil, ensure_val=nil) raise "type: #{ensure_type} must be set to true or false, not #{ensure_val}" unless ensure_val == true or ensure_val == false if ensure_type type = default.class.to_s.to_sym type = self.undefined_type if type == :NilClass if ensure_type if ensure_val then ensure_type ## elsif ensure_type == type then :Object elsif ensure_type == type then self.undefined_type else type end else type end end |
.illegal_names ⇒ Object
only applies to desired getter and setter names, not the property itself todo: allow one of [:cascade, :all!] to be overwritten todo: add :unset_property
37 38 39 40 |
# File 'lib/cascading_classes.rb', line 37 def self.illegal_names [:cascade, :all!, :set_property, :instance_eval, :instance_exec, :class_eval].concat(class_methods) end |
.included(klass) ⇒ Object
self.apply
304 305 306 |
# File 'lib/cascading_classes.rb', line 304 def self.included(klass) apply(klass, true) end |
.is_array_like?(obj, blk = nil) ⇒ Boolean
219 220 221 222 223 224 225 226 227 |
# File 'lib/cascading_classes/types.rb', line 219 def self.is_array_like?(obj, blk=nil) obj = unsymbolize(obj) if Symbol === obj return blk.call(obj) if blk s = (obj.class == Class) ? obj : obj.class return true if s.ancestors.include?(Array) meths = s.instance_methods meths.include?(:index) and meths.include?(:at) and meths.include?(:include?) end |
.is_container?(obj, blk = nil) ⇒ Boolean
201 202 203 204 205 206 207 |
# File 'lib/cascading_classes/types.rb', line 201 def self.is_container?(obj, blk=nil) obj = unsymbolize(obj) if Symbol === obj return blk.call(obj) if blk s = (obj.class == Class) ? obj : obj.class s.ancestors.include?(Enumerable) or s.instance_methods.include?(:each) end |
.is_hash_like?(obj, blk = nil) ⇒ Boolean
209 210 211 212 213 214 215 216 217 |
# File 'lib/cascading_classes/types.rb', line 209 def self.is_hash_like?(obj, blk=nil) obj = unsymbolize(obj) if Symbol === obj return blk.call(obj) if blk s = (obj.class == Class) ? obj : obj.class return true if s.ancestors.include?(Hash) meths = s.instance_methods meths.include?(:keys) and meths.include?(:include?) and (meths.include?(:[]) or meths.include?(:fetch)) end |
.is_of_type?(obj, klass) ⇒ Boolean
49 50 51 |
# File 'lib/cascading_classes/types.rb', line 49 def self.is_of_type?(obj, klass) obj.class.to_s.to_sym == klass end |
.parse_options(apply_to_instances, *properties, &block) ⇒ Object
returns hash: {…, attr => [default, inst_too], .. } todo: consider removing support for custom getters/setters in array syntax
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/cascading_classes/parse_options.rb', line 7 def self.(apply_to_instances, *properties, &block) err = "syntax error: list properties or supply block, but not both" raise err if properties.size > 0 and block_given? res = {} properties.each do |prop| name, default, inst_too = prop, nil, apply_to_instances if Array === prop case prop.size when 0 then next when 1 then name = prop[0] when 2 then name, default = prop[0..1] else name, default, inst_too = prop[0..2] end end name = name.to_sym ## type = default.nil? ? :Object : default.class.name.to_sym type = default.nil? ? CascadingClasses.undefined_type() : default.class.name.to_sym new_proc = CascadingClasses.default_new(type) # proc creates new obj is_blank = CascadingClasses.default_blank(type) # proc verifies blank values res[name] = { :default => default, :inst_too => !!inst_too, :getters => [name], :setters => ["#{name}=".to_sym], :type => type, :after_get => nil, :blank => is_blank, :proc_inst_exec => CascadingClasses.proc_inst_exec?, :block_inst_exec => CascadingClasses.block_inst_exec?, :new => new_proc, :inherit => CascadingClasses.undefined_type?(type) ? :undef : (CascadingClasses.is_container?(type) ? false : true), :ensure_type => CascadingClasses.default_ensure_type(type, is_blank, new_proc), :options_given => [] } end if block_given? a = CascadingClasses::DSL.new(apply_to_instances, &block) names = a.instance_eval{ @names } pluck = proc{|v| a.instance_eval("@#{v}") } vars = [:default, :instances_too, :getters, :setters, :type, :after_get, :blank, :proc_inst_exec, :block_inst_exec, :new, :inherit, :ensure_type, :options_given] names.each{|name| res[name] = Hash[ vars.map{|v| [v, pluck.call(v)[name]] } ] } end # validation res.each do |name, settings| =begin -> why must new values be blank? ans: b/c if they don't new_var = settings[:new].call raise "proc to create new #{settings[:type]} is #{new_var.inspect}, not blank" unless settings[:blank].call(new_var) =end end res end |
.proc_inst_exec=(val) ⇒ Object
48 49 50 |
# File 'lib/cascading_classes.rb', line 48 def self.proc_inst_exec=(val) @proc_inst_exec = !!val end |
.proc_inst_exec? ⇒ Boolean
global default: whether proc values are evaluated or instance_exec’d global default defaults to false
44 45 46 |
# File 'lib/cascading_classes.rb', line 44 def self.proc_inst_exec? @proc_inst_exec.nil? ? @proc_inst_exec = false : @proc_inst_exec end |
.respond_to?(const) ⇒ Boolean
26 27 28 |
# File 'lib/cascading_classes.rb', line 26 def self.respond_to?(const) (const.to_s == 'VERSION') ? true : super end |
.undefined_type ⇒ Object
2 3 4 |
# File 'lib/cascading_classes/types.rb', line 2 def self.undefined_type :undefined_type end |
.undefined_type?(type) ⇒ Boolean
6 7 8 |
# File 'lib/cascading_classes/types.rb', line 6 def self.undefined_type?(type) type == self.undefined_type end |
.unsymbolize(obj) ⇒ Object
turns :Hash into Hash, :Array into Array, …
16 17 18 19 |
# File 'lib/cascading_classes/types.rb', line 16 def self.unsymbolize(obj) res = eval(obj.to_s) rescue nil (res.class == Class) ? res : obj end |