augmented 0.2.0 → 0.2.5

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 (34) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +132 -28
  4. data/augmented.gemspec +14 -8
  5. data/lib/augmented.rb +4 -0
  6. data/lib/augmented/arrays/tieable.rb +1 -1
  7. data/lib/augmented/enumerators/indexing.rb +3 -1
  8. data/lib/augmented/exceptions.rb +11 -0
  9. data/lib/augmented/exceptions/chain.rb +16 -0
  10. data/lib/augmented/exceptions/detailed.rb +22 -0
  11. data/lib/augmented/exceptions/serializable.rb +25 -0
  12. data/lib/augmented/objects/pickable.rb +2 -2
  13. data/lib/augmented/procs.rb +2 -2
  14. data/lib/augmented/procs/rescuable.rb +3 -3
  15. data/lib/augmented/strings.rb +9 -0
  16. data/lib/augmented/strings/blank.rb +15 -0
  17. data/lib/augmented/strings/truncatable.rb +20 -0
  18. data/lib/augmented/version.rb +1 -1
  19. metadata +26 -47
  20. data/test/augmented/arrays/tieable_test.rb +0 -66
  21. data/test/augmented/enumerators/indexing_test.rb +0 -15
  22. data/test/augmented/hashes/mappable_test.rb +0 -37
  23. data/test/augmented/hashes/polymorphable_test.rb +0 -45
  24. data/test/augmented/hashes/transformable_test.rb +0 -87
  25. data/test/augmented/modules/refined_test.rb +0 -27
  26. data/test/augmented/objects/iffy_test.rb +0 -69
  27. data/test/augmented/objects/pickable_test.rb +0 -39
  28. data/test/augmented/objects/tackable_test.rb +0 -25
  29. data/test/augmented/objects/tappable_test.rb +0 -141
  30. data/test/augmented/objects/thru_test.rb +0 -98
  31. data/test/augmented/procs/chainable_test.rb +0 -22
  32. data/test/augmented/procs/rescuable_test.rb +0 -38
  33. data/test/augmented/symbols/arguable_test.rb +0 -51
  34. data/test/augmented/symbols/comparing_test.rb +0 -131
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e809f083463caa0de7cb0c619078a075a058aaa7
4
- data.tar.gz: e61a37488b312e6c24187a476e14d8d2bb142718
2
+ SHA256:
3
+ metadata.gz: 02a3d4fd8fbc13022569168b89c6fb60b575dea7030a6c36b1457a049c468b62
4
+ data.tar.gz: bb89fe218a192cb1eb72f9456988a65c08eec651fc6e3f89e62941e9632dac9a
5
5
  SHA512:
6
- metadata.gz: d3398225a2262317005c77498a625b769fceda255c1565a47e0b8b0d82d3f3023393dfa3d1f07401a40620d45dbee3b2fddc82d3956999d83998a006e1ae3683
7
- data.tar.gz: e7552233cc563a5eb1275b69a3a1836aa000e86af58d62a3e717aa28bd670cb357fc22a8d0cc1cdee9d38f8326cdc055b2769b39e9b04c1341f4ada380932b92
6
+ metadata.gz: c4f04bc92e1bdfbff68e62283bfb16b7f8499297916c208ae62156e84d1358efdf4a94f56cb5d0d0123ea985a7f315126026147b82e338e6fea7d4ddcbff489f
7
+ data.tar.gz: 99d873ac314d8ff5a8cc7bf6f6fc5a47cfb27080dbf69167c2a6f6f0509175e6f7cd4c6ab23c2a41a7f73cbd8a5b39cda20c473d1448228dd0316013b7659ae9
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.2.5] - 2021-05-30
4
+
5
+ - Added `Exception#details`, `Exception#details=`, `Exception#detailed`
6
+ - Added `Exception#chain`
7
+ - Added `Exception#to_h`
8
+
9
+ ## [0.2.3] - 2021-05-29
10
+
11
+ - Added `String#truncate`, `String#truncate!` and `String#blank?`
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `Augmented` is a library with some core-type utility methods that I frequently find myself copying across projects. It uses refinements instead of class modification for maximum control and an easy sleep at night.
4
4
 
5
- Many of the methods in `Augmented` facilitate a more functional style of programming and cover a few tiny gaps in Ruby's solid functional support. See more thoughts on this [blog post](http://blog.brunze.com/2015/using-ruby-refinements-fun-flow/).
5
+ Many of the methods in `Augmented` facilitate a more functional style of programming and cover a few tiny gaps in Ruby's solid functional support.
6
6
 
7
7
 
8
8
  ## Installation
@@ -31,10 +31,12 @@ You can load all refinements for just one type:
31
31
  ```ruby
32
32
  using Augmented::Arrays
33
33
  using Augmented::Enumerators
34
+ using Augmented::Exceptions
34
35
  using Augmented::Hashes
35
36
  using Augmented::Modules
36
37
  using Augmented::Objects
37
38
  using Augmented::Procs
39
+ using Augmented::Strings
38
40
  using Augmented::Symbols
39
41
  # etc.
40
42
  ```
@@ -60,7 +62,7 @@ Weaves an object between the elements of an array. Like `join` but without flatt
60
62
  ```ruby
61
63
  using Augmented::Arrays::Tieable
62
64
 
63
- [1, 2, 3].tie :hello
65
+ [1, 2, 3].tie(:hello)
64
66
  # [1, :hello, 2, :hello, 3]
65
67
 
66
68
  [1, 5, 12].tie{ |a, b| a + b }
@@ -72,13 +74,88 @@ using Augmented::Arrays::Tieable
72
74
 
73
75
  ##### `Enumerator#index_by`
74
76
 
75
- Builds an index of all elements of an enumerator according to the given criterion.
77
+ Builds an index of all elements of an enumerator according to the given criterion. Last element wins.
76
78
 
77
79
  ```ruby
78
80
  using Augmented::Enumerators::Indexing
79
81
 
80
- ['a', 'bb', 'ccccc'].to_enum.index_by(&:length)
81
- # {1=>"a", 2=>"bb", 5=>"ccccc"}
82
+ ['a', 'bb', 'c', 'ddddd'].to_enum.index_by(&:length)
83
+ # {1=>"c", 2=>"bb", 5=>"ddddd"}
84
+ ```
85
+
86
+
87
+ #### `Augmented::Exceptions`
88
+
89
+ ##### `Exception#chain`
90
+
91
+ Returns an enumerator over the exception's causal chain, starting with the exception itself.
92
+
93
+ ```ruby
94
+ using Augmented::Exceptions::Chain
95
+
96
+ begin
97
+ begin
98
+ begin
99
+ raise 'first'
100
+ rescue
101
+ raise 'second'
102
+ end
103
+ rescue
104
+ raise 'third'
105
+ end
106
+ rescue => error
107
+ error.chain.map(&:message)
108
+ end
109
+ # ["third", "second", "first"]
110
+ ```
111
+
112
+
113
+ ##### `Exception#details`, `Exception#details=`, `Exception#detailed`
114
+
115
+ Attach a hash of details to any exception.
116
+
117
+ ```ruby
118
+ using Augmented::Exceptions::Detailed
119
+
120
+ exception = RuntimeError.new('oops!').detailed(foo: 10, bar: { baz: 30 })
121
+ exception.details
122
+ # {:foo=>10, :bar=>{:baz=>30}}
123
+ exception.details = { bam: 40 }
124
+ exception.details
125
+ # {:bam=>40}
126
+ ```
127
+
128
+
129
+ ##### `Exception#to_h`
130
+
131
+ Serializes an exception into a Hash including its backtrace, details and causal chain.
132
+
133
+ ```ruby
134
+ using Augmented::Exceptions::Serializable
135
+ using Augmented::Exceptions::Detailed
136
+
137
+ begin
138
+ begin
139
+ raise RuntimeError.new('first').detailed(foo: 10)
140
+ rescue
141
+ raise RuntimeError.new('second').detailed(bar: 20)
142
+ end
143
+ rescue => error
144
+ error.to_h
145
+ end
146
+ # {
147
+ # :class => "RuntimeError",
148
+ # :message => "second",
149
+ # :details => { :bar => 20 },
150
+ # :backtrace => [ ... ],
151
+ # :cause => {
152
+ # :class => "RuntimeError",
153
+ # :message => "first",
154
+ # :details => { :foo => 10 },
155
+ # :backtrace => [ ... ],
156
+ # :cause => nil
157
+ # }
158
+ # }
82
159
  ```
83
160
 
84
161
 
@@ -149,21 +226,16 @@ tree.transform({ lorem: :upcase, dolor: { sit: triple } })
149
226
  Makes it less verbose to create small refinements.
150
227
 
151
228
  ```ruby
152
- using Augmented::Hashes::Transformable
229
+ using Augmented::Modules::Refined
153
230
 
154
231
  class TextPage
155
232
  using refined String,
156
- as_phrase: -> { self.strip.capitalize.gsub /\.?\z/, '.' },
157
- fill: -> filler { (filler * self.length)[0..length] }
233
+ to_phrase: -> { self.strip.capitalize.gsub(/\.?\z/, '.') }
158
234
 
159
235
  # ...
160
236
 
161
237
  def text
162
- @strings.map(&:as_phrase).join ' '
163
- end
164
-
165
- def obscured_text
166
- text.fill '?'
238
+ @lines.map(&:to_phrase).join(' ')
167
239
  end
168
240
  end
169
241
  ```
@@ -178,11 +250,11 @@ Allows you to conditionally return an object, allowing you to be more concise in
178
250
  ```ruby
179
251
  using Augmented::Objects::Iffy
180
252
 
181
- Person.new.eat toast.if(toast.buttered?).else(muffin)
182
- Person.new.eat toast.if(&:buttered?).else(muffin)
253
+ Person.new.eat(toast.if(toast.buttered?).else(muffin))
254
+ Person.new.eat(toast.if(&:buttered?).else(muffin))
183
255
 
184
- Person.new.eat toast.unless(toast.soggy?).else(muffin)
185
- Person.new.eat toast.unless(&:soggy?).else(muffin)
256
+ Person.new.eat(toast.unless(toast.soggy?).else(muffin))
257
+ Person.new.eat(toast.unless(&:soggy?).else(muffin))
186
258
  ```
187
259
 
188
260
  ##### `Object#pick`
@@ -193,13 +265,13 @@ Calls a bunch of methods on an object and collects the results.
193
265
  using Augmented::Objects::Pickable
194
266
 
195
267
  class MyThing
196
- def lorem; 'hello'; end
197
- def ipsum; 'cruel'; end
198
- def dolor; 'world'; end
268
+ def foo; 'lorem'; end
269
+ def bar; 'ipsum'; end
270
+ def baz; 'dolor'; end
199
271
  end
200
272
 
201
- MyThing.new.pick :lorem, :dolor
202
- # {:lorem=>"hello", :dolor=>"world"}
273
+ MyThing.new.pick(:foo, :baz)
274
+ # {:foo=>"lorem", :baz=>"dolor"}
203
275
  ```
204
276
 
205
277
  ##### `Object#tack`
@@ -209,8 +281,8 @@ Appends a bunch of singleton methods to an object.
209
281
  ```ruby
210
282
  using Augmented::Objects::Tackable
211
283
 
212
- Object.new.tack(id: 11, greet: -> { puts "hello I'm #{id}" }).greet
213
- # hello I'm 11
284
+ Object.new.tack(name: 'Alice', greet: -> { puts "hello I'm #{name}" }).greet
285
+ # hello I'm Alice
214
286
  ```
215
287
 
216
288
  ##### `Object#tap_if`, `Object#tap_unless`
@@ -269,13 +341,45 @@ Wraps a `Proc` to rescue it from certain exceptions while returning a given valu
269
341
  ```ruby
270
342
  using Augmented::Procs::Rescuable
271
343
 
272
- integerify = proc{ |x| Integer(x) }.rescues ArgumentError, 42
344
+ integerify = proc{ |x| Integer(x) }.rescues(ArgumentError, 42)
273
345
 
274
- ['1', '2', 'abc', '4'].map &integerify
346
+ ['1', '2', 'oops!', '4'].map(&integerify)
275
347
  # [1, 2, 42, 4]
276
348
  ```
277
349
 
278
350
 
351
+ #### `Augmented::Strings`
352
+
353
+ ##### `String#blank?`
354
+
355
+ Tests if a string is empty or made of whitespace.
356
+
357
+ ```ruby
358
+ using Augmented::Strings::Blank
359
+
360
+ ''.blank?
361
+ # true
362
+ ' '.blank?
363
+ # true
364
+ ' hello '.blank?
365
+ # false
366
+ ```
367
+
368
+
369
+ ##### `String#truncate`, `String#truncate!`
370
+
371
+ Returns a prefix of a string up to a given number of characters.
372
+
373
+ ```ruby
374
+ using Augmented::Strings::Truncatable
375
+
376
+ 'hello world'.truncate(5)
377
+ # "hello"
378
+ [(string = 'hello world'), string.truncate!(5)]
379
+ # ["hello", "hello"]
380
+ ```
381
+
382
+
279
383
  #### `Augmented::Symbols`
280
384
 
281
385
  ##### `Symbol#with`
@@ -311,8 +415,8 @@ end
311
415
 
312
416
  users = [ User.new('Marianne'), User.new('Jeremy') ]
313
417
 
314
- users.find &(:name.eq 'Marianne')
315
- # <User:0x... name='Marianne'>
418
+ users.find(&:name.eq('Marianne'))
419
+ # <User:0x... @name='Marianne'>
316
420
  ```
317
421
 
318
422
 
data/augmented.gemspec CHANGED
@@ -6,18 +6,24 @@ require 'augmented/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "augmented"
8
8
  spec.version = Augmented::VERSION
9
- spec.authors = ["bruno"]
10
- spec.email = ["bruno@brunze.com"]
9
+ spec.authors = ["brunze"]
11
10
  spec.summary = %q{Useful extra methods for some Ruby core types.}
12
11
  spec.description = %q{Adds a few useful extra methods to some of Ruby's core types, available as refinements.}
13
12
  spec.homepage = "https://github.com/brunze/augmented"
14
13
  spec.license = "MIT"
15
14
 
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = spec.homepage
17
+ spec.metadata['changelog_uri'] = spec.homepage + '/CHANGELOG.md'
20
18
 
21
- spec.add_development_dependency "bundler", "~> 1.7"
22
- spec.add_development_dependency "rake", "~> 10.0"
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'bin'
25
+ spec.require_paths = ['lib']
26
+
27
+ spec.add_development_dependency "bundler", "~> 2"
28
+ spec.add_development_dependency "rake", ">= 13.0.3"
23
29
  end
data/lib/augmented.rb CHANGED
@@ -2,18 +2,22 @@ require 'augmented/version'
2
2
 
3
3
  require 'augmented/arrays'
4
4
  require 'augmented/enumerators'
5
+ require 'augmented/exceptions'
5
6
  require 'augmented/hashes'
6
7
  require 'augmented/modules'
7
8
  require 'augmented/objects'
8
9
  require 'augmented/procs'
10
+ require 'augmented/strings'
9
11
  require 'augmented/symbols'
10
12
 
11
13
  module Augmented
12
14
  include Arrays
13
15
  include Enumerators
16
+ include Exceptions
14
17
  include Hashes
15
18
  include Modules
16
19
  include Objects
17
20
  include Procs
21
+ include Strings
18
22
  include Symbols
19
23
  end
@@ -7,7 +7,7 @@ module Augmented
7
7
  raise ArgumentError, 'you must provide a non-nil tie object or block' if object.nil? && !block_given?
8
8
 
9
9
  tie_function = block_given? ? block : proc{ object }
10
- ties = self.each_cons(2).map &tie_function
10
+ ties = self.each_cons(2).map(&tie_function)
11
11
 
12
12
  self.zip(ties).flatten(1)[0...-1]
13
13
  end
@@ -4,7 +4,9 @@ module Augmented
4
4
  refine Enumerator do
5
5
 
6
6
  def index_by &criterion
7
- Hash[ self.map(&criterion).zip(self) ]
7
+ self.each_with_object({}) do |element, index|
8
+ index[criterion.(element)] = element
9
+ end
8
10
  end
9
11
 
10
12
  end
@@ -0,0 +1,11 @@
1
+ require 'augmented/exceptions/chain'
2
+ require 'augmented/exceptions/detailed'
3
+ require 'augmented/exceptions/serializable'
4
+
5
+ module Augmented
6
+ module Exceptions
7
+ include Chain
8
+ include Detailed
9
+ include Serializable
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module Augmented
2
+ module Exceptions
3
+ module Chain
4
+ refine Exception do
5
+
6
+ def chain
7
+ Enumerator.new do |yielder|
8
+ yielder << exception = self
9
+ yielder << exception while exception = exception.cause
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module Augmented
2
+ module Exceptions
3
+ module Detailed
4
+ refine Exception do
5
+
6
+ def details
7
+ @_details ||= {}
8
+ end
9
+
10
+ def details= **details
11
+ @_details = details
12
+ end
13
+
14
+ def detailed **details
15
+ self.details = details
16
+ self
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module Augmented
2
+ module Exceptions
3
+ module Serializable
4
+ refine Exception do
5
+ using Chain
6
+ using Detailed
7
+
8
+ def to_h
9
+ self.chain.map do |exception|
10
+ {
11
+ class: exception.class.name,
12
+ message: exception.message,
13
+ details: exception.details,
14
+ backtrace: exception.backtrace || [],
15
+ cause: nil,
16
+ }
17
+ end.reverse.reduce do |cause, exception|
18
+ exception.merge!(cause: cause)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -7,14 +7,14 @@ module Augmented
7
7
  ensure_array = -> thing { thing.kind_of?(Array) ? thing : Array[thing] }
8
8
 
9
9
  if self.respond_to? :each
10
- self.map{ |thing| thing.pick *picks }
10
+ self.map{ |thing| thing.pick(*picks) }
11
11
  else
12
12
  picks.each_with_object({}) do |pick, result|
13
13
 
14
14
  if pick.kind_of? Hash
15
15
 
16
16
  pick.each do |attribute, nested_picks|
17
- result[attribute] = self.__send__(attribute.to_sym).pick *ensure_array[nested_picks]
17
+ result[attribute] = self.__send__(attribute.to_sym).pick(*ensure_array[nested_picks])
18
18
  end
19
19
 
20
20
  else