gorillib 0.4.2 → 0.5.0

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/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