augmented 0.2.1 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a9a3e2008882b819fbc6dc68c9362aeaf6248d19
4
- data.tar.gz: bb67deffad6d08202a575e58c7df7f21319439ae
2
+ SHA256:
3
+ metadata.gz: cd88ac261436008274986613fc7ce91c1874b454b380e295f7acc5a43831e223
4
+ data.tar.gz: 77826a1b98d4b243c87b646941c164601f628a03ceee802ab2e44acb982c7894
5
5
  SHA512:
6
- metadata.gz: fb0a1f8b46a34bb071478f515a981634f57e4b73791fcf139d51475cebd6cd79c7236f23b1d9b094dd09042cb703c9e78fc58b7dfd2386ab402b88514cb3608c
7
- data.tar.gz: 24f16b6a859d76cab7d6f3befd0f2c665bb568d7a7ed2d50a946d3fc13e22ac2aaa6278ac0e27bf25989c1401af3477e000b0f734069cdd7e3c732640009d1a6
6
+ metadata.gz: 87e68662e972f392fca81ad431f2a7b7c712ae25eb28ba251a2663e2aa89d1f2223bac2d5962426c0d6dde6aadf14347b5c7664ea0150691c8e2634075c701e0
7
+ data.tar.gz: 73e6fb575d524ccde9a3a545334a2159b961d07430d042aefcb114dcfbb7e06c3b7ba5776a94d3c1a7f1669971f4f335a7c6a365e91c3bd5f459000d2c504ad2
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.2.6] - 2021-06-23
4
+
5
+ - Fixed `String#blank?` not working on Ruby 2.3.
6
+
7
+ ## [0.2.5] - 2021-05-30
8
+
9
+ - Added `Exception#details`, `Exception#details=`, `Exception#detailed`
10
+ - Added `Exception#chain`
11
+ - Added `Exception#to_h`
12
+
13
+ ## [0.2.3] - 2021-05-29
14
+
15
+ - 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