Class: Ditz::ModelObject
Defined Under Namespace
Classes: ModelError
Class Attribute Summary collapse
-
.fields ⇒ Object
readonly
Returns the value of attribute fields.
-
.serialized_values ⇒ Object
readonly
Returns the value of attribute serialized_values.
-
.values ⇒ Object
readonly
Returns the value of attribute values.
Class Method Summary collapse
- .changes_are_logged ⇒ Object
-
.create(generator_args, vals = {}) ⇒ Object
creates the object, filling in fields from ‘vals’, and throwing a ModelError when it can’t find all the requisite fields.
-
.create_interactively(opts = {}) ⇒ Object
creates the object, prompting the user when necessary.
-
.field(name, opts = {}) ⇒ Object
add a new field to a model object.
- .field_names ⇒ Object
- .from(fn) ⇒ Object
- .inherited(subclass) ⇒ Object
-
.yaml_domain ⇒ Object
yamlability.
- .yaml_other_thing ⇒ Object
Instance Method Summary collapse
- #changed! ⇒ Object
- #changed? ⇒ Boolean
-
#deserialized_form_of(field, value) ⇒ Object
override these two to model per-field transformations between disk and memory.
-
#each_modelobject ⇒ Object
depth-first search on all reachable ModelObjects.
-
#initialize ⇒ ModelObject
constructor
A new instance of ModelObject.
- #inspect ⇒ Object
- #log(what, who, comment) ⇒ Object
- #save!(fn) ⇒ Object
-
#serialized_form_of(field, value) ⇒ Object
convert memory form => disk form.
- #to_s ⇒ Object
- #to_yaml(opts = {}) ⇒ Object
- #to_yaml_type ⇒ Object
- #unchanged! ⇒ Object
Constructor Details
#initialize ⇒ ModelObject
Returns a new instance of ModelObject.
18 19 20 21 22 |
# File 'lib/model.rb', line 18 def initialize @values = {} @serialized_values = {} self.class.fields.map { |f, opts| @values[f] = [] if opts[:multi] } end |
Class Attribute Details
.fields ⇒ Object (readonly)
Returns the value of attribute fields.
105 106 107 |
# File 'lib/model.rb', line 105 def fields @fields end |
.serialized_values ⇒ Object (readonly)
Returns the value of attribute serialized_values.
105 106 107 |
# File 'lib/model.rb', line 105 def serialized_values @serialized_values end |
.values ⇒ Object (readonly)
Returns the value of attribute values.
105 106 107 |
# File 'lib/model.rb', line 105 def values @values end |
Class Method Details
.changes_are_logged ⇒ Object
108 109 110 111 |
# File 'lib/model.rb', line 108 def self.changes_are_logged define_method(:changes_are_logged?) { true } field :log_events, :multi => true, :ask => false end |
.create(generator_args, vals = {}) ⇒ Object
creates the object, filling in fields from ‘vals’, and throwing a ModelError when it can’t find all the requisite fields
222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/model.rb', line 222 def create generator_args, vals={} o = self.new @fields.each do |name, opts| val = if vals[name] vals[name] elsif(found, x = generate_field_value(o, opts, generator_args)) && found x else raise ModelError, "missing required field #{name}" end o.send "#{name}=", val end o end |
.create_interactively(opts = {}) ⇒ Object
creates the object, prompting the user when necessary. can take a :with => { hash } parameter for pre-filling model fields.
can also take a :defaults_from => obj parameter for pre-filling model fields from another object with (some of) those fields. kinda like a bizarre interactive copy constructor.
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 |
# File 'lib/model.rb', line 192 def create_interactively opts={} o = self.new generator_args = opts[:args] || [] @fields.each do |name, field_opts| val = if opts[:with] && opts[:with][name] opts[:with][name] elsif(found, x = generate_field_value(o, field_opts, generator_args)) && found x else q = field_opts[:prompt] || name.to_s.capitalize if field_opts[:multiline] ## multiline options currently aren't allowed to have a default ## value, so just ask. ask_multiline q else default = if opts[:defaults_from] && opts[:defaults_from].respond_to?(name) && (x = opts[:defaults_from].send(name)) x else default = generate_field_default o, field_opts, generator_args end ask q, :default => default end end o.send "#{name}=", val end o end |
.field(name, opts = {}) ⇒ Object
add a new field to a model object
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/model.rb', line 58 def self.field name, opts={} @fields ||= [] # can't use a hash because we need to preserve field order raise ModelError, "field with name #{name} already defined" if @fields.any? { |k, v| k == name } @fields << [name, opts] if opts[:multi] single_name = name.to_s.sub(/s$/, "") # oh yeah define_method "add_#{single_name}" do |obj| array = send(name) raise ModelError, "already has a #{single_name} with name #{obj.name.inspect}" if obj.respond_to?(:name) && array.any? { |o| o.name == obj.name } changed! @serialized_values.delete name array << obj end define_method "drop_#{single_name}" do |obj| return unless @values[name].delete obj @serialized_values.delete name changed! obj end end define_method "#{name}=" do |o| changed! @serialized_values.delete name @values[name] = o end define_method "__serialized_#{name}=" do |o| changed! @values.delete name @serialized_values[name] = o end define_method "__serialized_#{name}" do @serialized_values[name] end define_method name do return @values[name] if @values.member?(name) @values[name] = deserialized_form_of name, @serialized_values[name] end end |
.field_names ⇒ Object
103 |
# File 'lib/model.rb', line 103 def self.field_names; @fields.map { |name, opts| name } end |
.from(fn) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/model.rb', line 113 def self.from fn returning YAML::load_file(fn) do |o| raise ModelError, "error loading from yaml file #{fn.inspect}: expected a #{self}, got a #{o.class}" unless o.class == self o.pathname = fn if o.respond_to? :pathname= o.class.fields.each do |f, opts| m = "__serialized_#{f}" if opts[:multi] && o.send(m).nil? $stderr.puts "Warning: corrected nil multi-field #{f}" o.send "#{m}=", [] end end end end |
.inherited(subclass) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/model.rb', line 28 def self.inherited subclass YAML.add_domain_type(yaml_domain, subclass.yaml_other_thing) do |type, val| o = subclass.new val.each do |k, v| m = "__serialized_#{k}=" if o.respond_to? m o.send m, v else $stderr.puts "warning: unknown field #{k.inspect} in YAML for #{type}; ignoring" end end o.unchanged! o end end |
.yaml_domain ⇒ Object
yamlability
25 |
# File 'lib/model.rb', line 25 def self.yaml_domain; "ditz.rubyforge.org,2008-03-06" end |
.yaml_other_thing ⇒ Object
26 |
# File 'lib/model.rb', line 26 def self.yaml_other_thing; name.split('::').last.dcfirst end |
Instance Method Details
#changed! ⇒ Object
182 |
# File 'lib/model.rb', line 182 def changed!; @changed = true end |
#changed? ⇒ Boolean
181 |
# File 'lib/model.rb', line 181 def changed?; @changed ||= false end |
#deserialized_form_of(field, value) ⇒ Object
override these two to model per-field transformations between disk and memory.
convert disk form => memory form
48 49 50 |
# File 'lib/model.rb', line 48 def deserialized_form_of field, value @serialized_values[field] end |
#each_modelobject ⇒ Object
depth-first search on all reachable ModelObjects. fuck yeah.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/model.rb', line 135 def each_modelobject seen = {} to_see = [self] until to_see.empty? cur = to_see.pop seen[cur] = true yield cur cur.class.field_names.each do |f| val = cur.send(f) next if seen[val] if val.is_a?(ModelObject) to_see.push val elsif val.is_a?(Array) to_see += val.select { |v| v.is_a?(ModelObject) } end end end end |
#inspect ⇒ Object
132 |
# File 'lib/model.rb', line 132 def inspect; to_s end |
#log(what, who, comment) ⇒ Object
176 177 178 179 |
# File 'lib/model.rb', line 176 def log what, who, comment add_log_event([Time.now, who, what, comment || ""]) self end |
#save!(fn) ⇒ Object
154 155 156 157 158 |
# File 'lib/model.rb', line 154 def save! fn #FileUtils.mv fn, "#{fn}~", :force => true rescue nil File.open(fn, "w") { |f| f.puts to_yaml } self end |
#serialized_form_of(field, value) ⇒ Object
convert memory form => disk form
53 54 55 |
# File 'lib/model.rb', line 53 def serialized_form_of field, value @values[field] end |
#to_s ⇒ Object
128 129 130 |
# File 'lib/model.rb', line 128 def to_s "<#{self.class.name}: " + self.class.field_names.map { |f| "#{f}: " + (@values[f].to_s || @serialized_values[f]).inspect }.join(", ") + ">" end |
#to_yaml(opts = {}) ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/model.rb', line 160 def to_yaml opts={} YAML::quick_emit(object_id, opts) do |out| out.map(taguri, nil) do |map| self.class.fields.each do |f, fops| v = if @serialized_values.member?(f) @serialized_values[f] else @serialized_values[f] = serialized_form_of f, @values[f] end map.add f.to_s, v end end end end |
#to_yaml_type ⇒ Object
27 |
# File 'lib/model.rb', line 27 def to_yaml_type; "!#{self.class.yaml_domain}/#{self.class.yaml_other_thing}" end |
#unchanged! ⇒ Object
183 |
# File 'lib/model.rb', line 183 def unchanged!; @changed = false end |