gorillib 0.4.1pre → 0.4.2pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|