forked from ryan-allen/modelling
/
modelling.rb
87 lines (71 loc) · 2.28 KB
/
modelling.rb
1
2
3
4
5
6
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
require 'ostruct'
module Modelling
class << self
def append_features(receiver)
initialize_members_classvar(receiver)
bind_attributes_meta_method(receiver)
bind_maps_meta_method(receiver)
bind_collection_meta_method(receiver)
build_structs_meta_method(receiver)
bind_default_constructor(receiver)
end
def generate_accessors(receiver, names, initializer)
receiver.instance_eval do
names.each do |name|
attr_accessor name
@@members[self][name] = initializer
end
end
end
def generate_adhoc_accessors(receiver, names_to_class_or_proc)
receiver.instance_eval do
names_to_class_or_proc.each do |name, class_or_proc|
attr_accessor name
if class_or_proc.is_a?(Proc)
@@members[self][name] = class_or_proc
else
@@members[self][name] = Proc.new { class_or_proc.new }
end
end
end
end
private
def initialize_members_classvar(receiver)
receiver.instance_eval do
@@members ||= {}; @@members[self] ||= {}
end
end
def bind_meta_method(receiver, name, initializer)
receiver.instance_eval { class << self; self; end }.instance_eval do
define_method name do |*args|
if args.last.is_a?(Hash)
Modelling.generate_adhoc_accessors(self, args.pop)
end
Modelling.generate_accessors(self, args, initializer)
end
end
end
def bind_attributes_meta_method(receiver)
bind_meta_method(receiver, :attributes, Proc.new { nil })
end
def bind_collection_meta_method(receiver)
bind_meta_method(receiver, :collections, Proc.new { Array.new })
end
def bind_maps_meta_method(receiver)
bind_meta_method(receiver, :maps, Proc.new { Hash.new })
end
def build_structs_meta_method(receiver)
bind_meta_method(receiver, :structs, Proc.new { OpenStruct.new })
end
def bind_default_constructor(receiver)
receiver.class_eval do
def initialize(args = {})
@@members[self.class].each do |accessor, initializer|
send "#{accessor}=", initializer.call(self)
end
args.each { |name, value| send "#{name}=", value }
end
end
end
end
end