gorillib 0.4.0pre → 0.4.1pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/CHANGELOG.md +36 -1
  2. data/Gemfile +23 -19
  3. data/Guardfile +1 -1
  4. data/Rakefile +31 -31
  5. data/TODO.md +2 -30
  6. data/VERSION +1 -1
  7. data/examples/builder/ironfan.rb +4 -4
  8. data/gorillib.gemspec +40 -25
  9. data/lib/gorillib/array/average.rb +13 -0
  10. data/lib/gorillib/array/sorted_median.rb +11 -0
  11. data/lib/gorillib/array/sorted_percentile.rb +11 -0
  12. data/lib/gorillib/array/sorted_sample.rb +12 -0
  13. data/lib/gorillib/builder.rb +8 -14
  14. data/lib/gorillib/collection/has_collection.rb +31 -31
  15. data/lib/gorillib/collection/list_collection.rb +58 -0
  16. data/lib/gorillib/collection/model_collection.rb +63 -0
  17. data/lib/gorillib/collection.rb +57 -85
  18. data/lib/gorillib/logger/log.rb +26 -22
  19. data/lib/gorillib/model/base.rb +52 -39
  20. data/lib/gorillib/model/doc_string.rb +15 -0
  21. data/lib/gorillib/model/factories.rb +56 -61
  22. data/lib/gorillib/model/lint.rb +24 -0
  23. data/lib/gorillib/model/serialization.rb +12 -2
  24. data/lib/gorillib/model/validate.rb +2 -2
  25. data/lib/gorillib/pathname.rb +21 -6
  26. data/lib/gorillib/some.rb +2 -0
  27. data/lib/gorillib/type/extended.rb +0 -2
  28. data/lib/gorillib/type/url.rb +9 -0
  29. data/lib/gorillib/utils/console.rb +4 -1
  30. data/notes/HOWTO.md +22 -0
  31. data/notes/bucket.md +155 -0
  32. data/notes/builder.md +170 -0
  33. data/notes/collection.md +81 -0
  34. data/notes/factories.md +86 -0
  35. data/notes/model-overlay.md +209 -0
  36. data/notes/model.md +135 -0
  37. data/notes/structured-data-classes.md +127 -0
  38. data/spec/array/average_spec.rb +24 -0
  39. data/spec/array/sorted_median_spec.rb +18 -0
  40. data/spec/array/sorted_percentile_spec.rb +24 -0
  41. data/spec/array/sorted_sample_spec.rb +28 -0
  42. data/spec/gorillib/builder_spec.rb +46 -28
  43. data/spec/gorillib/collection_spec.rb +195 -10
  44. data/spec/gorillib/model/lint_spec.rb +28 -0
  45. data/spec/gorillib/model/record/factories_spec.rb +27 -13
  46. data/spec/gorillib/model/serialization_spec.rb +3 -5
  47. data/spec/gorillib/model_spec.rb +86 -104
  48. data/spec/spec_helper.rb +2 -1
  49. data/spec/support/gorillib_test_helpers.rb +83 -7
  50. data/spec/support/model_test_helpers.rb +9 -28
  51. metadata +52 -44
  52. data/lib/gorillib/configurable.rb +0 -28
  53. data/spec/gorillib/configurable_spec.rb +0 -62
  54. data/spec/support/shared_examples/included_module.rb +0 -20
@@ -0,0 +1,58 @@
1
+ require 'gorillib/collection'
2
+
3
+ module Gorillib
4
+ class ListCollection < Gorillib::GenericCollection
5
+
6
+ def initialize
7
+ @clxn = Array.new
8
+ end
9
+
10
+ # common to all collections, delegable to array
11
+ delegate :to_a, :each, :to => :clxn
12
+
13
+ # Add the new items in-place; given items clobber existing items
14
+ # @param other [{Symbol => Object}, Array<Object>] a hash of key=>item pairs or a list of items
15
+ # @return [Gorillib::Collection] the collection
16
+ def receive!(other)
17
+ clxn.concat( convert_collection(other) ).uniq!
18
+ self
19
+ end
20
+
21
+ # @return [Array] an array holding the items
22
+ def values ; to_a ; end
23
+
24
+ # iterate over each value in the collection
25
+ def each_value(&block); each(&block) ; end
26
+
27
+ # # removed on purpose, pending us understanding what the
28
+ # # even-yet-simpler-still collection should be.
29
+ #
30
+ # delegate :[], :[]=, :fetch, :to => :clxn
31
+ #
32
+ # # Deletes the entry whose index is `idx`, returning the corresponding
33
+ # # value. If the index is out of bounds, returns nil. If the optional code
34
+ # # block is given and the index is out of bounds, pass it the index and
35
+ # # return the result of block.
36
+ # #
37
+ # # @return the now-delete value, if found; otherwise, the result of the
38
+ # # block, if given; otherwise nil.
39
+ # def delete(idx, &block)
40
+ # if idx < length
41
+ # clxn.delete_at(idx)
42
+ # elsif block_given?
43
+ # yield(idx)
44
+ # else
45
+ # nil
46
+ # end
47
+ # end
48
+
49
+ protected
50
+
51
+ # - if the given collection responds_to `to_hash`, it is received into the internal collection; each hash key *must* match the id of its value or results are undefined.
52
+ # - otherwise, it uses a hash generated from the id/value pairs of each object in the given collection.
53
+ def convert_collection(cc)
54
+ cc.respond_to?(:values) ? cc.values : cc.to_a
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,63 @@
1
+ module Gorillib
2
+ class ModelCollection < Gorillib::Collection
3
+ # [String, Symbol] Method invoked on a new item to generate its collection key; :to_key by default
4
+ attr_accessor :key_method
5
+ # The default `key_method` invoked on a new item to generate its collection key
6
+ DEFAULT_KEY_METHOD = :to_key
7
+
8
+ # [Class, #receive] Factory for generating a new collection item.
9
+ class_attribute :factory, :instance_writer => false
10
+ singleton_class.class_eval{ protected :factory= }
11
+
12
+ def initialize(key_meth=nil, obj_factory=nil)
13
+ @factory = Gorillib::Factory(obj_factory) if obj_factory
14
+ @clxn = Hash.new
15
+ @key_method = key_meth || DEFAULT_KEY_METHOD
16
+ end
17
+
18
+ # Adds an item in-place
19
+ # @return [Gorillib::Collection] the collection
20
+ def <<(val)
21
+ receive! [val]
22
+ self
23
+ end
24
+
25
+ def create(*args, &block)
26
+ item = factory.receive(*args)
27
+ self << item
28
+ item
29
+ end
30
+
31
+ def update_or_create(key, *args, &block)
32
+ if include?(key)
33
+ obj = fetch(key)
34
+ obj.receive!(*args, &block)
35
+ obj
36
+ else
37
+ attrs = args.extract_options!.merge(key_method => key)
38
+ create(*args, attrs, &block)
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def convert_value(val)
45
+ return val unless factory
46
+ return nil if val.nil?
47
+ factory.receive(val)
48
+ end
49
+
50
+ # - if the given collection responds_to `to_hash`, it is received into the internal collection; each hash key *must* match the id of its value or results are undefined.
51
+ # - otherwise, it receives a hash generates from the id/value pairs of each object in the given collection.
52
+ def convert_collection(cc)
53
+ return cc.to_hash if cc.respond_to?(:to_hash)
54
+ cc.inject({}) do |acc, val|
55
+ val = convert_value(val)
56
+ key = val.public_send(key_method)
57
+ acc[key] = val
58
+ acc
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -2,51 +2,36 @@ require 'gorillib/metaprogramming/delegation'
2
2
  require 'gorillib/metaprogramming/class_attribute'
3
3
 
4
4
  module Gorillib
5
- class Collection
6
- # [String, Symbol] Method invoked on a new item to generate its collection key; :to_key by default
7
- attr_accessor :key_method
8
- # The default `key_method` invoked on a new item to generate its collection key
9
- DEFAULT_KEY_METHOD = :to_key
10
-
11
- # [Class, #receive] Factory for generating a new collection item.
12
- class_attribute :factory, :instance_writer => false
13
- singleton_class.class_eval{ protected :factory= }
14
5
 
6
+ #
7
+ # A generic collection stores objects uniquely, in the order added. It responds to:
8
+ # - receive!, values, to_a, each and each_value;
9
+ # - length, size, empty?, blank?
10
+ #
11
+ # A Collection additionally lets you store and retrieve things by label:
12
+ # - [], []=, include?, fetch, delete, each_pair, to_hash.
13
+ #
14
+ # A ModelCollection adds:
15
+ # - `key_method`: called on objects to get their key; `to_key` by default.
16
+ # - `factory`: generates new objects, converts received objects
17
+ # - `<<`: adds object under its `key_method` key
18
+ # - `receive!`s an array by auto-keying the elements, or a hash by trusting what you give it
19
+ # - `update_or_create: if absent, creates object with given attributes and
20
+ # `key_method => key`; if present, updates with given attributes.
21
+ #
22
+ class GenericCollection
15
23
  # [{Symbol => Object}] The actual store of items, but not for you to mess with
16
24
  attr_reader :clxn
17
25
  protected :clxn
18
26
 
19
- delegate :[], :[]=, :delete, :fetch, :to => :clxn
20
- delegate :keys, :values, :each_pair, :each_value, :to => :clxn
21
- delegate :has_key?, :include?, :length, :size, :empty?, :blank?, :to => :clxn
22
-
23
- def initialize(factory=nil, key_method=DEFAULT_KEY_METHOD)
24
- @key_method = key_method
25
- @factory = factory unless factory.nil?
26
- @clxn = {}
27
- end
28
-
29
- # @return [Array] an array holding the items
30
- def to_a ; values ; end
31
- # @return [{Symbol => Object}] a hash of key=>item pairs
32
- def to_hash ; clxn.dup ; end
33
-
34
- # Merge the new items in-place; given items clobber existing items
35
- # @param other [{Symbol => Object}, Array<Object>] a hash of key=>item pairs or a list of items
36
- # @return [Gorillib::Collection] the collection
37
- def merge!(other)
38
- clxn.merge!( convert_collection(other) )
39
- self
40
- end
41
- alias_method :concat, :merge!
42
- alias_method :receive!, :merge!
43
-
44
27
  def self.receive(items, *args)
45
28
  coll = new(*args)
46
29
  coll.receive!(items)
47
30
  coll
48
31
  end
49
32
 
33
+ delegate :length, :size, :empty?, :blank?, :to => :clxn
34
+
50
35
  # Two collections are equal if they have the same class and their contents are equal
51
36
  #
52
37
  # @param [Gorillib::Collection, Object] other The other collection to compare
@@ -56,74 +41,61 @@ module Gorillib
56
41
  clxn == other.send(:clxn)
57
42
  end
58
43
 
59
- # Merge the new items into a new collection; given items clobber existing items
60
- # @param other [{Symbol => Object}, Array<Object>] a hash of key=>item pairs or a list of items
61
- # @return [Gorillib::Collection] a new merged collection
62
- def merge(other)
63
- dup.merge!(other)
64
- end
65
-
66
- def create(*args)
67
- item = factory.receive(*args)
68
- self << item
69
- item
70
- end
71
-
72
- def find_or_create(key)
73
- fetch(key){ create(key_method => key) }
74
- end
75
-
76
- # Adds an item in-place
77
- # @return [Gorillib::Collection] the collection
78
- def <<(val)
79
- merge! [val]
80
- self
81
- end
44
+ public
82
45
 
83
46
  # @return [String] string describing the collection's array representation
84
47
  def to_s ; to_a.to_s ; end
85
48
  # @return [String] string describing the collection's array representation
86
49
  def inspect(detailed=true)
87
- str = "c{ "
88
- if detailed
89
- str << clxn.map do |key, val|
90
- "%-15s %s" % ["#{key}:", val.inspect]
91
- end.join(",\n ")
92
- else
93
- str << keys.join(", ")
94
- end
95
- str << " }"
50
+ if detailed then guts = clxn.map{|key, val| "%-15s %s" % ["#{key}:", val.inspect] }.join(",\n ")
51
+ else guts = keys.join(", ") ; end
52
+ ["c{ ", guts, " }"].join
96
53
  end
97
54
  # @return [Array] serializable array representation of the collection
98
- def to_wire(options)
55
+ def to_wire(options={})
99
56
  to_a.map{|el| el.respond_to?(:to_wire) ? el.to_wire(options) : el }
100
57
  end
101
- alias_method(:as_json, :to_wire)
58
+ # same as #to_wire
59
+ def as_json(*args) to_wire(*args) ; end
102
60
  # @return [String] JSON serialization of the collection's array representation
103
- def to_json(*args)
104
- p [self, options]
105
- to_wire(*args).to_json(*args)
61
+ def to_json(*args) to_wire(*args).to_json(*args) ; end
62
+
63
+ end
64
+
65
+ class Collection < Gorillib::GenericCollection
66
+ def initialize
67
+ @clxn = Hash.new
106
68
  end
107
69
 
108
- protected
70
+ delegate :[], :[]=, :fetch, :delete, :to => :clxn
71
+ delegate :values, :to => :clxn
72
+ delegate :keys, :each_pair, :each_value, :to => :clxn
73
+ delegate :include?, :to => :clxn
74
+
75
+ # @return [Array] an array holding the items
76
+ def to_a ; values ; end
77
+ # @return [{Symbol => Object}] a hash of key=>item pairs
78
+ def to_hash ; clxn.dup ; end
79
+
80
+ # iterate over each value in the collection
81
+ def each(&block); each_value(&block) ; end
109
82
 
110
- def convert_value(val)
111
- return val unless factory
112
- return nil if val.nil?
113
- factory.receive(val)
83
+ # Add the new items in-place; given items clobber existing items
84
+ # @param other [{Symbol => Object}, Array<Object>] a hash of key=>item pairs or a list of items
85
+ # @return [Gorillib::Collection] the collection
86
+ def receive!(other)
87
+ clxn.merge!( convert_collection(other) )
88
+ self
114
89
  end
115
90
 
116
- # - if the given collection responds_to `to_hash`, it is merged into the internal collection; each hash key *must* match the id of its value or results are undefined.
117
- # - otherwise, it merges a hash generates from the id/value pairs of each object in the given collection.
91
+ protected
92
+
93
+ # - if the given collection responds_to `to_hash`, it is received into the internal collection; each hash key *must* match the id of its value or results are undefined.
94
+ # - otherwise, it receives a hash generates from the id/value pairs of each object in the given collection.
118
95
  def convert_collection(cc)
119
96
  return cc.to_hash if cc.respond_to?(:to_hash)
120
- cc.inject({}) do |acc, val|
121
- val = convert_value(val)
122
- key = val.public_send(key_method).to_sym
123
- acc[key] = val
124
- acc
125
- end
97
+ raise "a #{self.class} can only receive a hash with explicitly-labelled contents."
126
98
  end
127
- end
128
99
 
100
+ end
129
101
  end
@@ -1,29 +1,12 @@
1
- require 'logger'
2
-
3
1
  #
4
2
  # A convenient logger.
5
3
  #
6
- # define Log yourself to prevent its creation
4
+ # to override its creation, simply define the top-level constant `::Log`
7
5
  #
8
- ::Log = Logger.new($stderr) unless defined?(::Log)
9
-
10
- # unless defined?(Log)
11
- # require 'log4r'
12
- # Log = Log4r::Logger.new('wukong')
13
- # Log.outputters = Log4r::Outputter.stderr
14
- # # require 'logger'
15
- # # Log = Logger.new(STDERR)
16
- # end
17
-
18
- # require 'log_buddy'; LogBuddy.init :log_to_stdout => false, :logger => Log
19
- # LogBuddy::Utils.module_eval do
20
- # def arg_and_blk_debug(arg, blk)
21
- # result = eval(arg, blk.binding)
22
- # result_str = obj_to_string(result, :quote_strings => true)
23
- # LogBuddy.debug(%[#{arg} = #{result_str}])
24
- # end
25
- # end
26
-
6
+ unless defined?(::Log)
7
+ require 'logger'
8
+ ::Log = Logger.new($stderr)
9
+ end
27
10
 
28
11
  def Log.dump *args
29
12
  self.debug([
@@ -31,3 +14,24 @@ def Log.dump *args
31
14
  caller.first
32
15
  ].join("\t"))
33
16
  end unless Log.respond_to?(:dump)
17
+
18
+
19
+
20
+ # TODO: allow swappable loggers more cleanly
21
+
22
+ # unless defined?(Log)
23
+ # require 'log4r'
24
+ # Log = Log4r::Logger.new('wukong')
25
+ # Log.outputters = Log4r::Outputter.stderr
26
+ # # require 'logger'
27
+ # # Log = Logger.new(STDERR)
28
+ # end
29
+
30
+ # require 'log_buddy'; LogBuddy.init :log_to_stdout => false, :logger => Log
31
+ # LogBuddy::Utils.module_eval do
32
+ # def arg_and_blk_debug(arg, blk)
33
+ # result = eval(arg, blk.binding)
34
+ # result_str = obj_to_string(result, :quote_strings => true)
35
+ # LogBuddy.debug(%[#{arg} = #{result_str}])
36
+ # end
37
+ # end
@@ -1,4 +1,3 @@
1
-
2
1
  module Gorillib
3
2
 
4
3
  # Provides a set of class methods for defining a field schema and instance
@@ -19,6 +18,16 @@ module Gorillib
19
18
  module Model
20
19
  extend Gorillib::Concern
21
20
 
21
+ def initialize(*args, &block)
22
+ attrs = args.extract_options!
23
+ if args.present?
24
+ fns = self.class.field_names
25
+ ArgumentError.check_arity!(args, 0..fns.length)
26
+ attrs = attrs.merge(Hash[ fns[0..(args.length-1)].zip(args) ])
27
+ end
28
+ receive!(attrs, &block)
29
+ end
30
+
22
31
  # Returns a Hash of all attributes
23
32
  #
24
33
  # @example Get attributes
@@ -32,6 +41,11 @@ module Gorillib
32
41
  end
33
42
  end
34
43
 
44
+ # @return [Array[Object]] all the attributes, in field order, with `nil` where unset
45
+ def attribute_values
46
+ self.class.field_names.map{|fn| read_attribute(fn) }
47
+ end
48
+
35
49
  # Returns a Hash of all attributes *that have been set*
36
50
  #
37
51
  # @example Get attributes (smurfette is unarmed)
@@ -57,14 +71,19 @@ module Gorillib
57
71
  # @param [{Symbol => Object}] hsh The values to receive
58
72
  # @return [Gorillib::Model] the object itself
59
73
  def receive!(hsh={})
60
- if hsh.respond_to?(:attributes) then hsh = hsh.attributes ; end
61
- Gorillib::Model::Validate.hashlike!("attributes hash for #{self.inspect}", hsh)
62
- hsh = hsh.symbolize_keys
63
- self.class.fields.each do |field_name, field|
64
- next unless hsh.has_key?(field_name)
65
- self.public_send(:"receive_#{field_name}", hsh[field_name])
74
+ if hsh.respond_to?(:attributes)
75
+ hsh = hsh.attributes
76
+ else
77
+ Gorillib::Model::Validate.hashlike!(hsh){ "attributes hash for #{self.inspect}" }
78
+ hsh = hsh.dup
66
79
  end
67
- handle_extra_attributes( hsh.reject{|field_name,val| self.class.has_field?(field_name) } )
80
+ self.class.field_names.each do |field_name|
81
+ if hsh.has_key?(field_name) then val = hsh.delete(field_name)
82
+ elsif hsh.has_key?(field_name.to_s) then val = hsh.delete(field_name.to_s)
83
+ else next ; end
84
+ self.send("receive_#{field_name}", val)
85
+ end
86
+ handle_extra_attributes(hsh)
68
87
  self
69
88
  end
70
89
 
@@ -83,12 +102,12 @@ module Gorillib
83
102
  # @return [Gorillib::Model] the object itself
84
103
  def update_attributes(hsh)
85
104
  if hsh.respond_to?(:attributes) then hsh = hsh.attributes ; end
86
- Gorillib::Model::Validate.hashlike!("attributes hash", hsh)
87
- self.class.fields.each do |attr, field|
88
- if hsh.has_key?(attr) then val = hsh[attr]
89
- elsif hsh.has_key?(attr.to_s) then val = hsh[attr.to_s]
105
+ Gorillib::Model::Validate.hashlike!(hsh){ "attributes hash for #{self.inspect}" }
106
+ self.class.field_names.each do |field_name|
107
+ if hsh.has_key?(field_name) then val = hsh[field_name]
108
+ elsif hsh.has_key?(field_name.to_s) then val = hsh[field_name.to_s]
90
109
  else next ; end
91
- write_attribute(attr, val)
110
+ write_attribute(field_name, val)
92
111
  end
93
112
  self
94
113
  end
@@ -103,9 +122,9 @@ module Gorillib
103
122
  # @raise [UnknownAttributeError] if the attribute is unknown
104
123
  # @return [Object] The value of the attribute, or nil if it is unset
105
124
  def read_attribute(field_name)
106
- check_field(field_name)
107
- if instance_variable_defined?("@#{field_name}")
108
- instance_variable_get("@#{field_name}")
125
+ attr_name = "@#{field_name}"
126
+ if instance_variable_defined?(attr_name)
127
+ instance_variable_get(attr_name)
109
128
  else
110
129
  read_unset_attribute(field_name)
111
130
  end
@@ -122,7 +141,6 @@ module Gorillib
122
141
  # @raise [UnknownAttributeError] if the attribute is unknown
123
142
  # @return [Object] the attribute's value
124
143
  def write_attribute(field_name, val)
125
- check_field(field_name)
126
144
  instance_variable_set("@#{field_name}", val)
127
145
  end
128
146
 
@@ -140,7 +158,6 @@ module Gorillib
140
158
  # @raise [UnknownAttributeError] if the attribute is unknown
141
159
  # @return [Object] the former value if it was set, nil if it was unset
142
160
  def unset_attribute(field_name)
143
- check_field(field_name)
144
161
  if instance_variable_defined?("@#{field_name}")
145
162
  val = instance_variable_get("@#{field_name}")
146
163
  remove_instance_variable("@#{field_name}")
@@ -159,7 +176,6 @@ module Gorillib
159
176
  # @raise [UnknownAttributeError] if the attribute is unknown
160
177
  # @return [true, false]
161
178
  def attribute_set?(field_name)
162
- check_field(field_name)
163
179
  instance_variable_defined?("@#{field_name}")
164
180
  end
165
181
 
@@ -188,9 +204,8 @@ module Gorillib
188
204
  def inspect_helper(detailed, attrs)
189
205
  str = "#<" << self.class.name.to_s
190
206
  if detailed && attrs.present?
191
- str << " "
192
- str << attrs.map do |attr, val|
193
- "#{attr}=#{val.is_a?(Gorillib::Model) || val.is_a?(Gorillib::Collection) ? val.inspect(false) : val.inspect}"
207
+ str << " " << attrs.map do |attr, val|
208
+ "#{attr}=#{val.is_a?(Gorillib::Model) || val.is_a?(Gorillib::GenericCollection) ? val.inspect(false) : val.inspect}"
194
209
  end.join(", ")
195
210
  end
196
211
  str << ">"
@@ -199,17 +214,13 @@ module Gorillib
199
214
 
200
215
  protected
201
216
 
202
- # @return [true] if the field exists
203
- # @raise [UnknownFieldError] if the field is missing
204
- def check_field(field_name)
205
- return true if self.class.has_field?(field_name)
206
- raise UnknownFieldError, "unknown field: #{field_name} for #{self}"
207
- end
208
-
209
217
  module ClassMethods
210
218
 
219
+ #
220
+ # A readable handle for this field
221
+ #
211
222
  def typename
212
- Gorillib::Inflector.underscore(self.name).gsub(%r{/}, '.')
223
+ @typename ||= Gorillib::Inflector.underscore(self.name||'anon').gsub(%r{/}, '.')
213
224
  end
214
225
 
215
226
  #
@@ -219,12 +230,12 @@ module Gorillib
219
230
  def receive(attrs={}, &block)
220
231
  return nil if attrs.nil?
221
232
  return attrs if attrs.is_a?(self)
222
- Gorillib::Model::Validate.hashlike!("attributes for #{self}", attrs)
233
+ #
234
+ Gorillib::Model::Validate.hashlike!(attrs){ "attributes for #{self.inspect}" }
223
235
  klass = attrs.has_key?(:_type) ? Gorillib::Factory(attrs[:_type]) : self
224
- warn "factory #{self} doesn't match type specified in #{attrs}" unless klass <= self
225
- obj = klass.new
226
- obj.receive!(attrs, &block)
227
- obj
236
+ warn "factory #{klass} is not a type of #{self} as specified in #{attrs}" unless klass <= self
237
+ #
238
+ klass.new(attrs, &block)
228
239
  end
229
240
 
230
241
  # Defines a new field
@@ -260,12 +271,12 @@ module Gorillib
260
271
 
261
272
  # @return [true, false] true if the field is defined on this class
262
273
  def has_field?(field_name)
263
- fields.has_key?(field_name.to_sym)
274
+ fields.has_key?(field_name)
264
275
  end
265
276
 
266
277
  # @return [Array<Symbol>] The attribute names
267
278
  def field_names
268
- fields.keys
279
+ @_field_names ||= fields.keys
269
280
  end
270
281
 
271
282
  # @return Class name and its attributes
@@ -284,7 +295,8 @@ module Gorillib
284
295
  # are added after the child class is defined.
285
296
  def _reset_descendant_fields
286
297
  ObjectSpace.each_object(::Class) do |klass|
287
- klass.__send__(:remove_instance_variable, '@_fields') if klass <= self && klass.instance_variable_defined?('@_fields')
298
+ klass.__send__(:remove_instance_variable, '@_fields') if (klass <= self) && klass.instance_variable_defined?('@_fields')
299
+ klass.__send__(:remove_instance_variable, '@_field_names') if (klass <= self) && klass.instance_variable_defined?('@_field_names')
288
300
  end
289
301
  end
290
302
 
@@ -306,8 +318,9 @@ module Gorillib
306
318
 
307
319
  # define the present method `#foo?` for a field named `:foo`
308
320
  def define_attribute_tester(field_name, field_type, visibility)
321
+ field = fields[field_name]
309
322
  define_meta_module_method("#{field_name}?", visibility) do
310
- attribute_set?(field_name)
323
+ attribute_set?(field_name) || field.has_default?
311
324
  end
312
325
  end
313
326
 
@@ -0,0 +1,15 @@
1
+ module Gorillib
2
+ module Model
3
+ module DocString
4
+ extend Gorillib::Concern
5
+ included do
6
+ field :doc, String
7
+
8
+ def doc(*args)
9
+ if s
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end