gorillib 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -42,6 +42,7 @@ Jeweler::Tasks.new do |gem|
42
42
  reject{|f| File.directory?(f) }.
43
43
  reject{|f| ignores.any?{|i| File.fnmatch(i, f) || File.fnmatch(i+'/**/*', f) || File.fnmatch(i+'/*', f) } }
44
44
  gem.test_files = gem.files.grep(/^spec\//)
45
+ gem.extra_rdoc_files = [gem.files.grep(/^notes\//), gem.files.grep(/\.md$/)].flatten.uniq
45
46
  gem.require_paths = ['lib']
46
47
  end
47
48
  Jeweler::RubygemsDotOrgTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.2
1
+ 0.5.0
data/gorillib.gemspec CHANGED
@@ -5,16 +5,26 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "gorillib"
8
- s.version = "0.4.2"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Infochimps"]
12
- s.date = "2012-09-06"
12
+ s.date = "2012-10-22"
13
13
  s.description = "Gorillib: infochimps lightweight subset of ruby convenience methods"
14
14
  s.email = "coders@infochimps.org"
15
15
  s.extra_rdoc_files = [
16
+ "CHANGELOG.md",
16
17
  "LICENSE.md",
17
- "README.md"
18
+ "README.md",
19
+ "TODO.md",
20
+ "notes/HOWTO.md",
21
+ "notes/bucket.md",
22
+ "notes/builder.md",
23
+ "notes/collection.md",
24
+ "notes/factories.md",
25
+ "notes/model-overlay.md",
26
+ "notes/model.md",
27
+ "notes/structured-data-classes.md"
18
28
  ]
19
29
  s.files = [
20
30
  ".gitignore",
@@ -37,6 +47,7 @@ Gem::Specification.new do |s|
37
47
  "lib/gorillib/array/compact_blank.rb",
38
48
  "lib/gorillib/array/deep_compact.rb",
39
49
  "lib/gorillib/array/extract_options.rb",
50
+ "lib/gorillib/array/hashify.rb",
40
51
  "lib/gorillib/array/simple_statistics.rb",
41
52
  "lib/gorillib/array/wrap.rb",
42
53
  "lib/gorillib/base.rb",
@@ -99,12 +110,16 @@ Gem::Specification.new do |s|
99
110
  "lib/gorillib/model/schema_magic.rb",
100
111
  "lib/gorillib/model/serialization.rb",
101
112
  "lib/gorillib/model/serialization/csv.rb",
113
+ "lib/gorillib/model/serialization/json.rb",
114
+ "lib/gorillib/model/serialization/lines.rb",
115
+ "lib/gorillib/model/serialization/tsv.rb",
102
116
  "lib/gorillib/model/validate.rb",
103
117
  "lib/gorillib/numeric/clamp.rb",
104
118
  "lib/gorillib/object/blank.rb",
105
119
  "lib/gorillib/object/try.rb",
106
120
  "lib/gorillib/object/try_dup.rb",
107
121
  "lib/gorillib/pathname.rb",
122
+ "lib/gorillib/pathname/utils.rb",
108
123
  "lib/gorillib/serialization/to_wire.rb",
109
124
  "lib/gorillib/some.rb",
110
125
  "lib/gorillib/string/constantize.rb",
@@ -115,16 +130,26 @@ Gem::Specification.new do |s|
115
130
  "lib/gorillib/string/truncate.rb",
116
131
  "lib/gorillib/type/boolean.rb",
117
132
  "lib/gorillib/type/extended.rb",
133
+ "lib/gorillib/type/ip_address.rb",
118
134
  "lib/gorillib/type/url.rb",
119
135
  "lib/gorillib/utils/capture_output.rb",
120
136
  "lib/gorillib/utils/console.rb",
121
137
  "lib/gorillib/utils/edge_cases.rb",
122
138
  "lib/gorillib/utils/nuke_constants.rb",
139
+ "notes/HOWTO.md",
140
+ "notes/bucket.md",
141
+ "notes/builder.md",
142
+ "notes/collection.md",
143
+ "notes/factories.md",
144
+ "notes/model-overlay.md",
145
+ "notes/model.md",
146
+ "notes/structured-data-classes.md",
123
147
  "spec/examples/builder/ironfan_spec.rb",
124
148
  "spec/extlib/hash_spec.rb",
125
149
  "spec/extlib/mash_spec.rb",
126
150
  "spec/gorillib/array/compact_blank_spec.rb",
127
151
  "spec/gorillib/array/extract_options_spec.rb",
152
+ "spec/gorillib/array/hashify_spec.rb",
128
153
  "spec/gorillib/array/simple_statistics_spec.rb",
129
154
  "spec/gorillib/builder_spec.rb",
130
155
  "spec/gorillib/collection_spec.rb",
@@ -133,6 +158,7 @@ Gem::Specification.new do |s|
133
158
  "spec/gorillib/datetime/to_flat_spec.rb",
134
159
  "spec/gorillib/enumerable/sum_spec.rb",
135
160
  "spec/gorillib/exception/raisers_spec.rb",
161
+ "spec/gorillib/factories_spec.rb",
136
162
  "spec/gorillib/hash/compact_spec.rb",
137
163
  "spec/gorillib/hash/deep_compact_spec.rb",
138
164
  "spec/gorillib/hash/deep_merge_spec.rb",
@@ -150,9 +176,9 @@ Gem::Specification.new do |s|
150
176
  "spec/gorillib/metaprogramming/delegation_spec.rb",
151
177
  "spec/gorillib/metaprogramming/singleton_class_spec.rb",
152
178
  "spec/gorillib/model/defaults_spec.rb",
153
- "spec/gorillib/model/factories_spec.rb",
154
179
  "spec/gorillib/model/lint_spec.rb",
155
180
  "spec/gorillib/model/overlay_spec.rb",
181
+ "spec/gorillib/model/serialization/tsv_spec.rb",
156
182
  "spec/gorillib/model/serialization_spec.rb",
157
183
  "spec/gorillib/model_spec.rb",
158
184
  "spec/gorillib/numeric/clamp_spec.rb",
@@ -166,6 +192,7 @@ Gem::Specification.new do |s|
166
192
  "spec/gorillib/string/inflector_test_cases.rb",
167
193
  "spec/gorillib/string/truncate_spec.rb",
168
194
  "spec/gorillib/type/extended_spec.rb",
195
+ "spec/gorillib/type/ip_address_spec.rb",
169
196
  "spec/gorillib/utils/capture_output_spec.rb",
170
197
  "spec/spec_helper.rb",
171
198
  "spec/support/factory_test_helpers.rb",
@@ -186,7 +213,7 @@ Gem::Specification.new do |s|
186
213
  s.require_paths = ["lib"]
187
214
  s.rubygems_version = "1.8.24"
188
215
  s.summary = "include only what you need. No dependencies, no creep"
189
- s.test_files = ["spec/extlib/mash_spec.rb", "spec/extlib/hash_spec.rb", "spec/support/gorillib_test_helpers.rb", "spec/support/hashlike_helper.rb", "spec/support/shared_examples/included_module.rb", "spec/support/hashlike_fuzzing_helper.rb", "spec/support/matchers/be_hash_eql.rb", "spec/support/matchers/be_array_eql.rb", "spec/support/matchers/enumerate_method.rb", "spec/support/matchers/evaluate_to_true.rb", "spec/support/model_test_helpers.rb", "spec/support/factory_test_helpers.rb", "spec/support/hashlike_via_delegation.rb", "spec/support/hashlike_struct_helper.rb", "spec/gorillib/model_spec.rb", "spec/gorillib/builder_spec.rb", "spec/gorillib/numeric/clamp_spec.rb", "spec/gorillib/string/human_spec.rb", "spec/gorillib/string/inflections_spec.rb", "spec/gorillib/string/inflector_test_cases.rb", "spec/gorillib/string/constantize_spec.rb", "spec/gorillib/string/truncate_spec.rb", "spec/gorillib/metaprogramming/class_attribute_spec.rb", "spec/gorillib/metaprogramming/singleton_class_spec.rb", "spec/gorillib/metaprogramming/delegation_spec.rb", "spec/gorillib/hashlike_spec.rb", "spec/gorillib/exception/raisers_spec.rb", "spec/gorillib/utils/capture_output_spec.rb", "spec/gorillib/collection_spec.rb", "spec/gorillib/type/extended_spec.rb", "spec/gorillib/hash/zip_spec.rb", "spec/gorillib/hash/reverse_merge_spec.rb", "spec/gorillib/hash/slice_spec.rb", "spec/gorillib/hash/keys_spec.rb", "spec/gorillib/hash/compact_spec.rb", "spec/gorillib/hash/deep_compact_spec.rb", "spec/gorillib/hash/deep_merge_spec.rb", "spec/gorillib/datetime/parse_spec.rb", "spec/gorillib/datetime/to_flat_spec.rb", "spec/gorillib/hashlike/deep_hash_spec.rb", "spec/gorillib/hashlike/hashlike_behavior_spec.rb", "spec/gorillib/hashlike/hashlike_via_accessors_spec.rb", "spec/gorillib/hashlike/behave_same_as_hash_spec.rb", "spec/gorillib/configurable_spec.rb", "spec/gorillib/model/lint_spec.rb", "spec/gorillib/model/defaults_spec.rb", "spec/gorillib/model/serialization_spec.rb", "spec/gorillib/model/overlay_spec.rb", "spec/gorillib/model/factories_spec.rb", "spec/gorillib/pathname_spec.rb", "spec/gorillib/enumerable/sum_spec.rb", "spec/gorillib/object/try_spec.rb", "spec/gorillib/object/blank_spec.rb", "spec/gorillib/object/try_dup_spec.rb", "spec/gorillib/array/compact_blank_spec.rb", "spec/gorillib/array/simple_statistics_spec.rb", "spec/gorillib/array/extract_options_spec.rb", "spec/gorillib/logger/log_spec.rb", "spec/spec_helper.rb", "spec/examples/builder/ironfan_spec.rb"]
216
+ s.test_files = ["spec/extlib/mash_spec.rb", "spec/extlib/hash_spec.rb", "spec/support/gorillib_test_helpers.rb", "spec/support/hashlike_helper.rb", "spec/support/shared_examples/included_module.rb", "spec/support/hashlike_fuzzing_helper.rb", "spec/support/matchers/be_hash_eql.rb", "spec/support/matchers/be_array_eql.rb", "spec/support/matchers/enumerate_method.rb", "spec/support/matchers/evaluate_to_true.rb", "spec/support/model_test_helpers.rb", "spec/support/factory_test_helpers.rb", "spec/support/hashlike_via_delegation.rb", "spec/support/hashlike_struct_helper.rb", "spec/gorillib/model_spec.rb", "spec/gorillib/builder_spec.rb", "spec/gorillib/numeric/clamp_spec.rb", "spec/gorillib/string/human_spec.rb", "spec/gorillib/string/inflections_spec.rb", "spec/gorillib/string/inflector_test_cases.rb", "spec/gorillib/string/constantize_spec.rb", "spec/gorillib/string/truncate_spec.rb", "spec/gorillib/metaprogramming/class_attribute_spec.rb", "spec/gorillib/metaprogramming/singleton_class_spec.rb", "spec/gorillib/metaprogramming/delegation_spec.rb", "spec/gorillib/hashlike_spec.rb", "spec/gorillib/exception/raisers_spec.rb", "spec/gorillib/utils/capture_output_spec.rb", "spec/gorillib/collection_spec.rb", "spec/gorillib/type/extended_spec.rb", "spec/gorillib/type/ip_address_spec.rb", "spec/gorillib/hash/zip_spec.rb", "spec/gorillib/hash/reverse_merge_spec.rb", "spec/gorillib/hash/slice_spec.rb", "spec/gorillib/hash/keys_spec.rb", "spec/gorillib/hash/compact_spec.rb", "spec/gorillib/hash/deep_compact_spec.rb", "spec/gorillib/hash/deep_merge_spec.rb", "spec/gorillib/datetime/parse_spec.rb", "spec/gorillib/datetime/to_flat_spec.rb", "spec/gorillib/hashlike/deep_hash_spec.rb", "spec/gorillib/hashlike/hashlike_behavior_spec.rb", "spec/gorillib/hashlike/hashlike_via_accessors_spec.rb", "spec/gorillib/hashlike/behave_same_as_hash_spec.rb", "spec/gorillib/configurable_spec.rb", "spec/gorillib/factories_spec.rb", "spec/gorillib/model/serialization/tsv_spec.rb", "spec/gorillib/model/lint_spec.rb", "spec/gorillib/model/defaults_spec.rb", "spec/gorillib/model/serialization_spec.rb", "spec/gorillib/model/overlay_spec.rb", "spec/gorillib/pathname_spec.rb", "spec/gorillib/enumerable/sum_spec.rb", "spec/gorillib/object/try_spec.rb", "spec/gorillib/object/blank_spec.rb", "spec/gorillib/object/try_dup_spec.rb", "spec/gorillib/array/compact_blank_spec.rb", "spec/gorillib/array/simple_statistics_spec.rb", "spec/gorillib/array/extract_options_spec.rb", "spec/gorillib/array/hashify_spec.rb", "spec/gorillib/logger/log_spec.rb", "spec/spec_helper.rb", "spec/examples/builder/ironfan_spec.rb"]
190
217
 
191
218
  if s.respond_to? :specification_version then
192
219
  s.specification_version = 3
@@ -0,0 +1,11 @@
1
+ class Array
2
+ #
3
+ # Gets value of block on each element;
4
+ # constructs a hash of element-value pairs
5
+ #
6
+ # @return [Hash] hash of key-value pairs
7
+ def hashify
8
+ raise ArgumentError, 'hashify requires a block' unless block_given?
9
+ Hash[ self.map{|el| [el, yield(el)] } ]
10
+ end
11
+ end
data/lib/gorillib/base.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'gorillib/object/blank'
2
2
  require 'gorillib/array/extract_options'
3
3
  require 'gorillib/array/compact_blank'
4
+ require 'gorillib/array/hashify'
4
5
  require 'gorillib/hash/reverse_merge'
5
6
  require 'gorillib/hash/compact'
6
7
  require 'gorillib/hash/keys'
@@ -1,4 +1,6 @@
1
- require 'gorillib'
1
+ require 'multi_json'
2
+
3
+ require 'gorillib/base'
2
4
  require 'gorillib/array/wrap'
3
5
  require 'gorillib/type/extended'
4
6
  require 'gorillib/hash/slice'
@@ -6,3 +8,8 @@ require 'gorillib/pathname'
6
8
  require 'gorillib/logger/log'
7
9
  require 'gorillib/model'
8
10
  require 'gorillib/factories'
11
+ require 'gorillib/model/serialization'
12
+ require 'gorillib/model/serialization/csv'
13
+ require 'gorillib/model/serialization/tsv'
14
+ require 'gorillib/model/serialization/json'
15
+ require 'gorillib/model/indexable'
@@ -25,7 +25,7 @@ Exception.class_eval do
25
25
  # end
26
26
  #
27
27
  def polish(extra_info)
28
- filename, _, method_name = self.class.caller_parts
28
+ filename, _, method_name = self.class.caller_parts(2)
29
29
  method_name.gsub!(/rescue in /, '')
30
30
  most_recent_line = backtrace.detect{|line|
31
31
  line.include?(filename) && line.include?(method_name) && line.end_with?("'") }
@@ -102,6 +102,11 @@ class ArgumentError
102
102
  raise self, message, *args
103
103
  end
104
104
 
105
+
106
+ def self.block_required!(block)
107
+ raise self.new("Block is required") unless block
108
+ end
109
+
105
110
  #
106
111
  # @param obj [Object] Object to check
107
112
  # @param types [Array[Symbol,Class,Module]] Types or methods to compare
@@ -32,19 +32,6 @@ module Gorillib
32
32
  klass.new(options)
33
33
  end
34
34
 
35
- # Manufactures objects from their raw attributes hash
36
- #
37
- # A hash with a value for `:_type` is dispatched to the corresponding factory
38
- # Everything else is returned directly
39
- def self.make(obj)
40
- if obj.respond_to?(:has_key?) && (obj.has_key?(:_type) || obj.has_key?('_type'))
41
- factory = Gorillib::Factory(attrs[:_type])
42
- factory.receive(obj)
43
- else
44
- obj
45
- end
46
- end
47
-
48
35
  def self.register_factory(factory, typenames)
49
36
  typenames.each{|typename| factories[typename] = factory }
50
37
  end
@@ -185,6 +172,14 @@ module Gorillib
185
172
  end
186
173
  end
187
174
 
175
+ # __________________________________________________________________________
176
+ #
177
+ # Generic Factories
178
+ # __________________________________________________________________________
179
+
180
+ #
181
+ # Factory that accepts whatever given and uses it directly -- no nothin'
182
+ #
188
183
  class ::Whatever < BaseFactory
189
184
  def initialize(options={})
190
185
  options.slice!(:convert, :blankish)
@@ -200,6 +195,24 @@ module Gorillib
200
195
  end
201
196
  IdenticalFactory = ::Whatever unless defined?(IdenticalFactory)
202
197
 
198
+
199
+ # Manufactures objects from their raw attributes hash
200
+ #
201
+ # The hash must have a value for `:_type`, used to retrieve the actual factory
202
+ #
203
+ class ::GenericModel < BaseFactory
204
+ def blankish?(obj) obj.nil? ; end
205
+ def native?(obj) false ; end
206
+ def receive(attrs, &block)
207
+ Gorillib::Model::Validate.hashlike!(attrs){ "attributes for typed object" }
208
+ klass = Gorillib::Factory(attrs.fetch(:_type){ attrs.fetch("_type") })
209
+ #
210
+ klass.new(attrs, &block)
211
+ end
212
+ def self.receive(obj) allocate.receive(obj) end
213
+ register_factory!(GenericModel, :generic)
214
+ end
215
+
203
216
  # __________________________________________________________________________
204
217
  #
205
218
  # Concrete Factories
@@ -31,6 +31,7 @@ module Gorillib
31
31
  # @return [{Symbol => Object}] The Hash of all attributes
32
32
  def attributes
33
33
  self.class.field_names.inject(Hash.new) do |hsh, fn|
34
+ # hsh[fn] = attribute_set?(fn) ? read_attribute(fn) : nil
34
35
  hsh[fn] = read_attribute(fn)
35
36
  hsh
36
37
  end
@@ -199,6 +200,10 @@ module Gorillib
199
200
  str << '>'
200
201
  end
201
202
 
203
+ def to_s
204
+ inspect
205
+ end
206
+
202
207
  def inspect_compact
203
208
  str = "#<#{self.class.name.to_s}>"
204
209
  end
@@ -208,7 +213,6 @@ module Gorillib
208
213
  def to_inspectable
209
214
  compact_attributes
210
215
  end
211
- private :to_inspectable
212
216
 
213
217
  protected
214
218
 
@@ -260,6 +264,7 @@ module Gorillib
260
264
  base.instance_eval do
261
265
  extend Gorillib::Model::NamedSchema
262
266
  extend Gorillib::Model::ClassMethods
267
+ self.meta_module
263
268
  @_own_fields ||= {}
264
269
  end
265
270
  end
@@ -153,6 +153,7 @@ module Gorillib
153
153
 
154
154
  def inherited(base)
155
155
  base.instance_eval do
156
+ self.meta_module
156
157
  @_own_fields ||= {}
157
158
  end
158
159
  super
@@ -21,9 +21,11 @@ module Gorillib
21
21
  def each_in_csv(filename, options={})
22
22
  filename = Pathname.path_to(filename)
23
23
  options = csv_options.merge(options)
24
+ #
24
25
  pop_headers = options.delete(:pop_headers)
25
26
  num_fields = options.delete(:num_fields){ (fields.length .. fields.length) }
26
27
  raise ArgumentError, "The :headers option to CSV changes its internal behavior; use 'pop_headers: true' to ignore the first line" if options[:headers]
28
+ #
27
29
  CSV.open(filename, options) do |csv_file|
28
30
  csv_file.shift if pop_headers
29
31
  csv_file.each do |tuple|
@@ -0,0 +1,44 @@
1
+ require_relative './lines'
2
+
3
+ module Gorillib
4
+ module Model
5
+
6
+ module LoadFromJson
7
+ extend Gorillib::Concern
8
+ include LoadLines
9
+
10
+ module ClassMethods
11
+
12
+ # Iterate a block over each line of a file having JSON records, one per
13
+ # line, in a big stack
14
+ #
15
+ # @yield an object instantiated from each line in the file.
16
+ def _each_from_json(filename, options={})
17
+ _each_raw_line(filename, options) do |line|
18
+ hsh = MultiJson.load(line)
19
+ yield receive(hsh)
20
+ end
21
+ end
22
+
23
+ # With a block, calls block on each object in turn (and returns nil)
24
+ #
25
+ # With no block, accumulates all the instances into the array it
26
+ # returns. As opposed to the with-a-block case, the memory footprint of
27
+ # this increases as the filesize does, so use caution with large files.
28
+ #
29
+ # @return with a block, returns nil; with no block, an array of this class' instances
30
+ def load_json(*args)
31
+ if block_given?
32
+ _each_from_json(*args, &Proc.new)
33
+ else
34
+ objs = []
35
+ _each_from_json(*args){|obj| objs << obj }
36
+ objs
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ module Gorillib
2
+ module Model
3
+
4
+ module LoadLines
5
+ extend Gorillib::Concern
6
+
7
+ module ClassMethods
8
+
9
+ # Iterate a block over each line of a file
10
+ # @yield each line in the file.
11
+ def _each_raw_line(filename, options={})
12
+ filename = Pathname.path_to(filename)
13
+ #
14
+ pop_headers = options.delete(:pop_headers)
15
+ #
16
+ File.open(filename) do |file|
17
+ file.readline if pop_headers
18
+ file.each do |line|
19
+ line.chomp! ; next if line.empty?
20
+ yield line
21
+ end
22
+ nil
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,55 @@
1
+ require_relative './lines'
2
+
3
+ module Gorillib
4
+ module Model
5
+
6
+ module LoadFromTsv
7
+ extend Gorillib::Concern
8
+ include LoadLines
9
+
10
+ included do |base|
11
+ # Options that will be passed to CSV. Be careful to modify with assignment (`+=`) and not in-place (`<<`)
12
+ base.class_attribute :tsv_options
13
+ base.tsv_options = Hash.new
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ # Iterate a block over each line of a TSV file
19
+ #
20
+ # @raise [Gorillib::Model::RawDataMismatchError] if a line has too many or too few fields
21
+ # @yield an object instantiated from each line in the file.
22
+ def _each_from_tsv(filename, options={})
23
+ options = tsv_options.merge(options)
24
+ num_fields = options.delete(:num_fields){ (fields.length .. fields.length) }
25
+ max_fields = num_fields.max # need to make sure "1\t2\t\t\t" becomes ["1","2","","",""]
26
+ #
27
+ _each_raw_line(filename, options) do |line|
28
+ tuple = line.split("\t", max_fields)
29
+ unless num_fields.include?(tuple.length) then raise Gorillib::Model::RawDataMismatchError, "yark, spurious fields: #{tuple.inspect}" ; end
30
+ yield from_tuple(*tuple)
31
+ end
32
+ end
33
+
34
+ # With a block, calls block on each object in turn (and returns nil)
35
+ #
36
+ # With no block, accumulates all the instances into the array it
37
+ # returns. As opposed to the with-a-block case, the memory footprint of
38
+ # this increases as the filesize does, so use caution with large files.
39
+ #
40
+ # @return with a block, returns nil; with no block, an array of this class' instances
41
+ def load_tsv(*args)
42
+ if block_given?
43
+ _each_from_tsv(*args, &Proc.new)
44
+ else
45
+ objs = []
46
+ _each_from_tsv(*args){|obj| objs << obj }
47
+ objs
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,34 @@
1
+ class Pathname
2
+ # same as `#exist?`
3
+ def exists?(*args) exist?(*args) ; end
4
+
5
+ # @example It chains nicely:
6
+ # # put each file in eg. dest/f/foo.json
7
+ # Pathname.of(:dest, slug[0..0], "#{slug}.json").mkparent.open('w') do |file|
8
+ # # ...
9
+ # end
10
+ #
11
+ # @returns the path itself (not its parent)
12
+ def mkparent
13
+ dirname.mkpath
14
+ return self
15
+ end
16
+
17
+ #
18
+ # Executes the block (passing the opened file) if the file does not
19
+ # exist. Ignores the block otherwise. The block is required.
20
+ #
21
+ # @param options
22
+ # @option options[:force] Force creation of the file
23
+ #
24
+ # @returns the path itself (not the file)
25
+ def if_missing(options={}, &block)
26
+ ArgumentError.block_required!(block)
27
+ return self if exist? && (not options[:force])
28
+ #
29
+ mkparent
30
+ open((options[:mode] || 'w'), &block)
31
+ return self
32
+ end
33
+
34
+ end