gorillib 0.4.1pre → 0.4.2pre
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.
- data/.gitignore +13 -10
- data/.rspec +1 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +47 -0
- data/Gemfile +22 -19
- data/Guardfile +23 -9
- data/README.md +12 -12
- data/Rakefile +29 -40
- data/VERSION +1 -1
- data/examples/benchmark/factories_benchmark.rb +87 -0
- data/examples/builder/ironfan.rb +1 -19
- data/examples/hash/slicing_methods.rb +101 -0
- data/gorillib.gemspec +36 -35
- data/lib/gorillib/array/deep_compact.rb +4 -3
- data/lib/gorillib/array/simple_statistics.rb +76 -0
- data/lib/gorillib/base.rb +0 -1
- data/lib/gorillib/builder.rb +15 -30
- data/lib/gorillib/collection.rb +159 -57
- data/lib/gorillib/collection/model_collection.rb +136 -43
- data/lib/gorillib/datetime/parse.rb +4 -2
- data/lib/gorillib/{array → deprecated/array}/average.rb +0 -0
- data/lib/gorillib/{array → deprecated/array}/random.rb +2 -1
- data/lib/gorillib/{array → deprecated/array}/sorted_median.rb +0 -0
- data/lib/gorillib/{array → deprecated/array}/sorted_percentile.rb +0 -0
- data/lib/gorillib/deprecated/array/sorted_sample.rb +13 -0
- data/lib/gorillib/{metaprogramming → deprecated/metaprogramming}/aliasing.rb +0 -0
- data/lib/gorillib/enumerable/sum.rb +3 -3
- data/lib/gorillib/exception/raisers.rb +92 -22
- data/lib/gorillib/factories.rb +550 -0
- data/lib/gorillib/hash/mash.rb +15 -58
- data/lib/gorillib/hashlike/deep_compact.rb +2 -2
- data/lib/gorillib/hashlike/slice.rb +55 -40
- data/lib/gorillib/model.rb +5 -3
- data/lib/gorillib/model/base.rb +33 -119
- data/lib/gorillib/model/defaults.rb +58 -14
- data/lib/gorillib/model/errors.rb +10 -0
- data/lib/gorillib/model/factories.rb +1 -367
- data/lib/gorillib/model/field.rb +40 -18
- data/lib/gorillib/model/fixup.rb +16 -0
- data/lib/gorillib/model/positional_fields.rb +35 -0
- data/lib/gorillib/model/schema_magic.rb +162 -0
- data/lib/gorillib/model/serialization.rb +1 -2
- data/lib/gorillib/model/serialization/csv.rb +59 -0
- data/lib/gorillib/pathname.rb +19 -8
- data/lib/gorillib/some.rb +2 -0
- data/lib/gorillib/string/constantize.rb +17 -10
- data/lib/gorillib/string/inflector.rb +11 -7
- data/lib/gorillib/type/boolean.rb +40 -0
- data/lib/gorillib/type/extended.rb +76 -40
- data/lib/gorillib/type/url.rb +6 -4
- data/lib/gorillib/utils/console.rb +1 -18
- data/lib/gorillib/utils/edge_cases.rb +18 -0
- data/spec/examples/builder/ironfan_spec.rb +5 -10
- data/spec/gorillib/array/compact_blank_spec.rb +36 -21
- data/spec/gorillib/array/simple_statistics_spec.rb +143 -0
- data/spec/gorillib/builder_spec.rb +16 -20
- data/spec/gorillib/collection_spec.rb +131 -35
- data/spec/gorillib/exception/raisers_spec.rb +39 -0
- data/spec/gorillib/hash/deep_compact_spec.rb +3 -3
- data/spec/gorillib/model/{record/defaults_spec.rb → defaults_spec.rb} +5 -1
- data/spec/gorillib/model/factories_spec.rb +335 -0
- data/spec/gorillib/model/{record/overlay_spec.rb → overlay_spec.rb} +0 -0
- data/spec/gorillib/model/serialization_spec.rb +2 -2
- data/spec/gorillib/model_spec.rb +19 -18
- data/spec/gorillib/pathname_spec.rb +7 -7
- data/spec/gorillib/string/truncate_spec.rb +3 -13
- data/spec/gorillib/type/extended_spec.rb +50 -2
- data/spec/gorillib/utils/capture_output_spec.rb +1 -1
- data/spec/spec_helper.rb +10 -7
- data/spec/support/factory_test_helpers.rb +76 -0
- data/spec/support/gorillib_test_helpers.rb +36 -24
- data/spec/support/model_test_helpers.rb +39 -2
- metadata +86 -51
- data/lib/alt/kernel/call_stack.rb +0 -56
- data/lib/gorillib/array/sorted_sample.rb +0 -12
- data/lib/gorillib/builder/field.rb +0 -5
- data/lib/gorillib/collection/has_collection.rb +0 -31
- data/lib/gorillib/collection/list_collection.rb +0 -58
- data/lib/gorillib/exception/confidence.rb +0 -17
- data/lib/gorillib/io/system_helpers.rb +0 -30
- data/lib/gorillib/model/record_schema.rb +0 -9
- data/lib/gorillib/utils/stub_module.rb +0 -33
- data/spec/array/average_spec.rb +0 -24
- data/spec/array/sorted_median_spec.rb +0 -18
- data/spec/array/sorted_percentile_spec.rb +0 -24
- data/spec/array/sorted_sample_spec.rb +0 -28
- data/spec/gorillib/metaprogramming/aliasing_spec.rb +0 -180
- data/spec/gorillib/model/record/factories_spec.rb +0 -335
- data/spec/support/kcode_test_helper.rb +0 -16
data/lib/gorillib/model/field.rb
CHANGED
@@ -15,7 +15,7 @@ module Gorillib
|
|
15
15
|
attr_reader :model
|
16
16
|
|
17
17
|
# [Hash] all options passed to the field not recognized by one of its own current fields
|
18
|
-
attr_reader :
|
18
|
+
attr_reader :_extra_attributes
|
19
19
|
|
20
20
|
# Note: `Gorillib::Model::Field` is assembled in two pieces, so that it
|
21
21
|
# can behave as a model itself. Defining `name` here, along with some
|
@@ -28,7 +28,6 @@ module Gorillib
|
|
28
28
|
class_attribute :visibilities, :instance_writer => false
|
29
29
|
self.visibilities = { :reader => :public, :writer => :public, :receiver => :public, :tester => false }
|
30
30
|
|
31
|
-
|
32
31
|
# @param [#to_sym] name Field name
|
33
32
|
# @param [#receive] type Factory for field values. To accept any object as-is, specify `Object` as the type.
|
34
33
|
# @param [Gorillib::Model] model Field's owner
|
@@ -38,11 +37,14 @@ module Gorillib
|
|
38
37
|
# @option options [true, false, :public, :protected, :private] :writer Visibility for the writer (`#foo=`) method; `false` means don't create one.
|
39
38
|
# @option options [true, false, :public, :protected, :private] :receiver Visibility for the receiver (`#receive_foo`) method; `false` means don't create one.
|
40
39
|
#
|
41
|
-
def initialize(name, type,
|
40
|
+
def initialize(model, name, type, options={})
|
42
41
|
Validate.identifier!(name)
|
42
|
+
type_opts = options.extract!(:blankish, :empty_product, :items, :keys, :of)
|
43
|
+
type_opts[:items] = type_opts.delete(:of) if type_opts.has_key?(:of)
|
44
|
+
#
|
43
45
|
@model = model
|
44
46
|
@name = name.to_sym
|
45
|
-
@type =
|
47
|
+
@type = Gorillib::Factory.factory_for(type, type_opts)
|
46
48
|
default_visabilities = visibilities
|
47
49
|
@visibilities = default_visabilities.merge( options.extract!(*default_visabilities.keys) )
|
48
50
|
@doc = options.delete(:name){ "#{name} field" }
|
@@ -56,29 +58,28 @@ module Gorillib
|
|
56
58
|
name.to_s
|
57
59
|
end
|
58
60
|
|
59
|
-
def factory_for(type)
|
60
|
-
Gorillib::Factory(type)
|
61
|
-
end
|
62
|
-
|
63
61
|
# @return [String] Human-readable presentation of the field definition
|
64
62
|
def inspect
|
65
|
-
args = [name.inspect, type.to_s]
|
66
|
-
"field(#{args.join(",
|
63
|
+
args = [name.inspect, type.to_s, attributes.reject{|k,v| k =~ /^(name|type)$/}.inspect]
|
64
|
+
"field(#{args.join(",")})"
|
65
|
+
end
|
66
|
+
def inspect_compact
|
67
|
+
"field(#{name})"
|
67
68
|
end
|
68
69
|
|
69
70
|
def to_hash
|
70
|
-
attributes.merge!(@visibility).merge!(@
|
71
|
+
attributes.merge!(@visibility).merge!(@_extra_attributes)
|
71
72
|
end
|
72
73
|
|
73
74
|
def ==(val)
|
74
|
-
super && (val.
|
75
|
+
super && (val._extra_attributes == self._extra_attributes) && (val.model == self.model)
|
75
76
|
end
|
76
77
|
|
77
78
|
def self.receive(hsh)
|
78
79
|
name = hsh.fetch(:name)
|
79
80
|
type = hsh.fetch(:type)
|
80
81
|
model = hsh.fetch(:model)
|
81
|
-
new(name, type,
|
82
|
+
new(model, name, type, hsh)
|
82
83
|
end
|
83
84
|
|
84
85
|
#
|
@@ -110,22 +111,43 @@ module Gorillib
|
|
110
111
|
# Now we can construct the actual fields.
|
111
112
|
#
|
112
113
|
|
114
|
+
field :position, Integer, :tester => true, :doc => "Indicates this is a positional initialization arg -- you can pass it as a plain value in the given slot to #initialize"
|
115
|
+
|
113
116
|
# Name of this field. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]` (required)
|
114
117
|
# @macro [attach] field
|
115
118
|
# @attribute $1
|
116
119
|
# @return [$2] the $1 field $*
|
117
|
-
field :name, String, :writer
|
120
|
+
field :name, String, position: 0, writer: false, doc: "The field name. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]` (required)"
|
118
121
|
|
119
|
-
|
120
|
-
field :type, Class
|
122
|
+
field :type, Class, position: 1, doc: "Factory to generate field's values"
|
121
123
|
|
122
|
-
|
123
|
-
field :doc, String
|
124
|
+
field :doc, String, doc: "Field's description"
|
124
125
|
|
125
126
|
# remove the attr_reader method (needed for scaffolding), leaving the meta_module method to remain
|
126
127
|
remove_possible_method(:name)
|
127
128
|
|
128
129
|
end
|
130
|
+
|
131
|
+
|
132
|
+
class SimpleCollectionField < Gorillib::Model::Field
|
133
|
+
field :item_type, Class, default: Whatever, doc: "Factory for collection items"
|
134
|
+
# field :collection_attrs, Hash, default: Hash.new, doc: "Extra attributes to pass to the collection on creation -- eg. key_method"
|
135
|
+
|
136
|
+
def initialize(model, name, type, options={})
|
137
|
+
super
|
138
|
+
collection_type = self.type
|
139
|
+
item_type = self.item_type
|
140
|
+
key_method = options[:key_method] if options[:key_method]
|
141
|
+
raise "Please supply an item type for #{self.inspect} -- eg 'collection #{name.inspect}, of: FooClass'" unless item_type
|
142
|
+
self.default ||= ->{ collection_type.new(item_type: item_type, belongs_to: self, key_method: key_method) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def inscribe_methods(model)
|
146
|
+
super
|
147
|
+
model.__send__(:define_collection_receiver, self)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
129
151
|
end
|
130
152
|
end
|
131
153
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
::Gorillib::Model::Field.class_eval do
|
2
|
+
field :fixup, Symbol, doc: 'key to remap before receive', tester: true
|
3
|
+
end
|
4
|
+
|
5
|
+
module ::Gorillib::StringFixup
|
6
|
+
extend Gorillib::Concern
|
7
|
+
|
8
|
+
# intercept to replace fixup-able hash keys with the proper field name in the receive hash
|
9
|
+
def receive!(hsh={})
|
10
|
+
self.class.fields.each do |field_name, field|
|
11
|
+
next unless field.fixup?
|
12
|
+
hsh[field_name] = hsh.delete(field.fixup) if hsh.has_key?(field.fixup)
|
13
|
+
end
|
14
|
+
super(hsh)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Model
|
3
|
+
|
4
|
+
#
|
5
|
+
# give each field the next position in order
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class Foo
|
9
|
+
# include Gorillib::Model
|
10
|
+
# field :bob, String # not positional
|
11
|
+
# field :zoe, String, position: 0 # positional 0 (explicit)
|
12
|
+
# end
|
13
|
+
# class Subby < Foo
|
14
|
+
# include Gorillib::Model::PositionalFields
|
15
|
+
# field :wun, String # positional 1
|
16
|
+
# end
|
17
|
+
# Foo.field :nope, String # not positional
|
18
|
+
# Subby.field :toofer, String # positional 2
|
19
|
+
#
|
20
|
+
# @note: make sure you're keeping positionals straight in super classes, or
|
21
|
+
# in anything added after this.
|
22
|
+
#
|
23
|
+
module PositionalFields
|
24
|
+
extend Gorillib::Concern
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def field(*args)
|
28
|
+
options = args.extract_options!
|
29
|
+
super(*args, {position: positionals.count}.merge(options))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Model
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
# Defines a new field
|
7
|
+
#
|
8
|
+
# For each field that is defined, a getter and setter will be added as
|
9
|
+
# an instance method to the model. An Field instance will be added to
|
10
|
+
# result of the fields class method.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# field :height, Integer
|
14
|
+
#
|
15
|
+
# @param [Symbol] field_name The field name. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]`
|
16
|
+
# @param [Class] type The field's type (required)
|
17
|
+
# @option options [String] doc Documentation string for the field (optional)
|
18
|
+
# @option options [Proc, Object] default Default value, or proc that instance can evaluate to find default value
|
19
|
+
#
|
20
|
+
# @return Gorillib::Model::Field
|
21
|
+
def field(field_name, type, options={})
|
22
|
+
options = options.symbolize_keys
|
23
|
+
field_type = options.delete(:field_type){ ::Gorillib::Model::Field }
|
24
|
+
fld = field_type.new(self, field_name, type, options)
|
25
|
+
@_own_fields[fld.name] = fld
|
26
|
+
_reset_descendant_fields
|
27
|
+
fld.send(:inscribe_methods, self)
|
28
|
+
fld
|
29
|
+
end
|
30
|
+
|
31
|
+
def collection(field_name, collection_type, options={})
|
32
|
+
options[:item_type] = options[:of] if options.has_key?(:of)
|
33
|
+
field(field_name, collection_type, {
|
34
|
+
field_type: ::Gorillib::Model::SimpleCollectionField}.merge(options))
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [{Symbol => Gorillib::Model::Field}]
|
38
|
+
def fields
|
39
|
+
return @_fields if defined?(@_fields)
|
40
|
+
@_fields = ancestors.reverse.inject({}){|acc, ancestor| acc.merge!(ancestor.try(:_own_fields) || {}) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [true, false] true if the field is defined on this class
|
44
|
+
def has_field?(field_name)
|
45
|
+
fields.has_key?(field_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Array<Symbol>] The attribute names
|
49
|
+
def field_names
|
50
|
+
@_field_names ||= fields.keys
|
51
|
+
end
|
52
|
+
|
53
|
+
def positionals
|
54
|
+
@_positionals ||= assemble_positionals
|
55
|
+
end
|
56
|
+
|
57
|
+
def assemble_positionals
|
58
|
+
positionals = fields.values.keep_if{|fld| fld.position? }.sort_by!{|fld| fld.position }
|
59
|
+
return [] if positionals.empty?
|
60
|
+
if (positionals.map(&:position) != (0..positionals.length-1).to_a) then raise ConflictingPositionError, "field positions #{positionals.map(&:position).join(",")} for #{positionals.map(&:name).join(",")} aren't in strict minimal order" ; end
|
61
|
+
positionals.map!(&:name)
|
62
|
+
end
|
63
|
+
|
64
|
+
# turn model constructor args (`*positional_args, {attrs}`) into a combined
|
65
|
+
# attrs hash. positional_args are mapped to the set of attribute names in
|
66
|
+
# order -- by default, the class' field names.
|
67
|
+
#
|
68
|
+
# Notes:
|
69
|
+
#
|
70
|
+
# * Positional args always clobber elements of the attribute hash.
|
71
|
+
# * Nil positional args are treated as present-and-nil (this might change).
|
72
|
+
# * Raises an error if positional args
|
73
|
+
#
|
74
|
+
# @param [Array[Symbol]] args list of attributes, in order, to map.
|
75
|
+
#
|
76
|
+
# @return [Hash] a combined, reconciled hash of attributes to set
|
77
|
+
def attrs_hash_from_args(args)
|
78
|
+
attrs = args.extract_options!
|
79
|
+
if args.present?
|
80
|
+
ArgumentError.check_arity!(args, 0..positionals.length){ "extracting args #{args} for #{self}" }
|
81
|
+
positionals_to_map = positionals[0..(args.length-1)]
|
82
|
+
attrs = attrs.merge(Hash[positionals_to_map.zip(args)])
|
83
|
+
end
|
84
|
+
attrs
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
attr_reader :_own_fields
|
90
|
+
|
91
|
+
# Ensure that classes inherit all their parents' fields, even if fields
|
92
|
+
# are added after the child class is defined.
|
93
|
+
def _reset_descendant_fields
|
94
|
+
ObjectSpace.each_object(::Class) do |klass|
|
95
|
+
klass.__send__(:remove_instance_variable, '@_fields') if (klass <= self) && klass.instance_variable_defined?('@_fields')
|
96
|
+
klass.__send__(:remove_instance_variable, '@_field_names') if (klass <= self) && klass.instance_variable_defined?('@_field_names')
|
97
|
+
klass.__send__(:remove_instance_variable, '@_positionals') if (klass <= self) && klass.instance_variable_defined?('@_positionals')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# define the reader method `#foo` for a field named `:foo`
|
102
|
+
def define_attribute_reader(field_name, field_type, visibility)
|
103
|
+
define_meta_module_method(field_name, visibility) do
|
104
|
+
begin
|
105
|
+
read_attribute(field_name)
|
106
|
+
rescue StandardError => err ; err.polish("#{self.class}.#{field_name}") rescue nil ; raise ; end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# define the writer method `#foo=` for a field named `:foo`
|
111
|
+
def define_attribute_writer(field_name, field_type, visibility)
|
112
|
+
define_meta_module_method("#{field_name}=", visibility) do |val|
|
113
|
+
write_attribute(field_name, val)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# define the present method `#foo?` for a field named `:foo`
|
118
|
+
def define_attribute_tester(field_name, field_type, visibility)
|
119
|
+
field = fields[field_name]
|
120
|
+
define_meta_module_method("#{field_name}?", visibility) do
|
121
|
+
attribute_set?(field_name) || field.has_default?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def define_attribute_receiver(field_name, field_type, visibility)
|
126
|
+
# @param [Object] val the raw value to type-convert and adopt
|
127
|
+
# @return [Object] the attribute's new value
|
128
|
+
define_meta_module_method("receive_#{field_name}", visibility) do |val|
|
129
|
+
begin
|
130
|
+
val = field_type.receive(val)
|
131
|
+
write_attribute(field_name, val)
|
132
|
+
rescue StandardError => err ; err.polish("#{self.class}.#{field_name} type #{type} on #{val}") rescue nil ; raise ; end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# Collection receiver --
|
138
|
+
#
|
139
|
+
def define_collection_receiver(field)
|
140
|
+
collection_field_name = field.name; collection_type = field.type
|
141
|
+
# @param [Array[Object],Hash[Object]] the collection to merge
|
142
|
+
# @return [Gorillib::Collection] the updated collection
|
143
|
+
define_meta_module_method("receive_#{collection_field_name}", true) do |coll, &block|
|
144
|
+
begin
|
145
|
+
if collection_type.native?(coll)
|
146
|
+
write_attribute(collection_field_name, coll)
|
147
|
+
else
|
148
|
+
read_attribute(collection_field_name).receive!(coll, &block)
|
149
|
+
end
|
150
|
+
rescue StandardError => err ; err.polish("#{self.class} #{collection_field_name} collection on #{args}'") rescue nil ; raise ; end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def inherited(base)
|
155
|
+
base.instance_eval do
|
156
|
+
@_own_fields ||= {}
|
157
|
+
end
|
158
|
+
super
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
class Array
|
3
2
|
def to_tsv
|
4
3
|
join("\t")
|
@@ -25,7 +24,7 @@ module Gorillib
|
|
25
24
|
|
26
25
|
module ClassMethods
|
27
26
|
def from_tuple(*vals)
|
28
|
-
receive Hash[field_names[0..vals.length].zip(vals)]
|
27
|
+
receive Hash[field_names[0..vals.length-1].zip(vals)]
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'gorillib/pathname'
|
3
|
+
|
4
|
+
module Gorillib
|
5
|
+
module Model
|
6
|
+
|
7
|
+
module LoadFromCsv
|
8
|
+
extend Gorillib::Concern
|
9
|
+
included do |base|
|
10
|
+
# Options that will be passed to CSV. Be careful to modify with assignment (`+=`) and not in-place (`<<`)
|
11
|
+
base.class_attribute :csv_options
|
12
|
+
base.csv_options = Hash.new
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
# Iterate a block over each line of a CSV file
|
18
|
+
#
|
19
|
+
# @raise [Gorillib::Model::RawDataMismatchError] if a line has too many or too few fields
|
20
|
+
# @yield an object instantiated from each line in the file.
|
21
|
+
def each_in_csv(filename, options={})
|
22
|
+
filename = Pathname.path_to(filename)
|
23
|
+
options = csv_options.merge(options)
|
24
|
+
pop_headers = options.delete(:pop_headers)
|
25
|
+
num_fields = options.delete(:num_fields){ (fields.length .. fields.length) }
|
26
|
+
raise ArgumentError, "The :headers option to CSV changes its internal behavior; use 'pop_headers: true' to ignore the first line" if options[:headers]
|
27
|
+
CSV.open(filename, options) do |csv_file|
|
28
|
+
csv_file.shift if pop_headers
|
29
|
+
csv_file.each do |tuple|
|
30
|
+
next if tuple.empty?
|
31
|
+
unless num_fields.include?(tuple.length) then raise Gorillib::Model::RawDataMismatchError, "yark, spurious fields: #{tuple.inspect}" ; end
|
32
|
+
yield from_tuple(*tuple)
|
33
|
+
end
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# With a block, calls block on each object in turn (and returns nil)
|
39
|
+
#
|
40
|
+
# With no block, accumulates all the instances into the array it
|
41
|
+
# returns. As opposed to the with-a-block case, the memory footprint of
|
42
|
+
# this increases as the filesize does, so use caution with large files.
|
43
|
+
#
|
44
|
+
# @return with a block, returns nil; with no block, an array of this class' instances
|
45
|
+
def load_csv(*args)
|
46
|
+
if block_given?
|
47
|
+
each_in_csv(*args, &Proc.new)
|
48
|
+
else
|
49
|
+
objs = []
|
50
|
+
each_in_csv(*args){|obj| objs << obj }
|
51
|
+
objs
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/lib/gorillib/pathname.rb
CHANGED
@@ -52,9 +52,11 @@ module Gorillib
|
|
52
52
|
# any mixture of strings (literal sub-paths) and symbols (interpreted as references)
|
53
53
|
# @return [Pathname] A single expanded Pathname
|
54
54
|
#
|
55
|
-
def
|
56
|
-
|
55
|
+
def of(*pathsegs)
|
56
|
+
relpath_to(*pathsegs).expand_path
|
57
57
|
end
|
58
|
+
alias_method :path_to, :of
|
59
|
+
|
58
60
|
|
59
61
|
# Expand a path with late-evaluated segments
|
60
62
|
# @see `.path_to`
|
@@ -62,16 +64,12 @@ module Gorillib
|
|
62
64
|
# Calls cleanpath (removing `//` double slashes and useless `..`s), but does
|
63
65
|
# not reference the filesystem or make paths absolute
|
64
66
|
#
|
65
|
-
def
|
67
|
+
def relpath_to(*pathsegs)
|
66
68
|
ArgumentError.arity_at_least!(pathsegs, 1)
|
67
69
|
pathsegs = pathsegs.map{|ps| expand_pathseg(ps) }.flatten
|
68
70
|
self.new(File.join(*pathsegs)).cleanpath(true)
|
69
71
|
end
|
70
|
-
|
71
|
-
|
72
|
-
# def make_pathname(*args)
|
73
|
-
# Pathname.new(*args)
|
74
|
-
# end
|
72
|
+
alias_method :relative_path_to, :relpath_to
|
75
73
|
|
76
74
|
protected
|
77
75
|
# Recursively expand a path handle
|
@@ -88,6 +86,19 @@ class Pathname
|
|
88
86
|
extend Gorillib::Pathref
|
89
87
|
class << self ; alias_method :new_pathname, :new ; end
|
90
88
|
|
89
|
+
def self.receive(obj)
|
90
|
+
return obj if obj.nil?
|
91
|
+
obj.is_a?(self) ? obj : new(obj)
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return the basename without extension (using self.extname as the extension)
|
95
|
+
def corename
|
96
|
+
basename(self.extname)
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [String] compact string rendering
|
100
|
+
def inspect_compact() to_path.dump ; end
|
101
|
+
|
91
102
|
# FIXME: find out if this is dangerous
|
92
103
|
alias_method :to_str, :to_path
|
93
104
|
end
|