finishing_moves 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +58 -0
- data/LICENSE +22 -0
- data/README.md +602 -0
- data/Vagrantfile +44 -0
- data/finishing_moves.gemspec +31 -0
- data/lib/finishing_moves.rb +7 -0
- data/lib/finishing_moves/fixnum.rb +23 -0
- data/lib/finishing_moves/hash.rb +27 -0
- data/lib/finishing_moves/object.rb +42 -0
- data/lib/finishing_moves/to_bool.rb +33 -0
- data/lib/finishing_moves/version.rb +3 -0
- data/provision.sh +71 -0
- data/spec/fixnum_spec.rb +35 -0
- data/spec/hash_spec.rb +30 -0
- data/spec/object_spec.rb +105 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/to_bool_spec.rb +69 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 70d5165e29dbddcb1383c9216698d22a734e8394
|
4
|
+
data.tar.gz: 0a7bea820fa4b2a2423f20f43ab2ca9243393dd8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 823eb4ebc4605b5cad2e3dcfb1ed2609ffb789bd5169c8c9d59bce26733924f125844148a7478a555e8afe5da9556c106e8aa50c867c0e44862f720747a75f20
|
7
|
+
data.tar.gz: 308f6cc084cb11a58e110021e2dc73967cd4db7e51eeb3bb57ff41f7b95d8e0f86819a544f1e1fc94b6a7e7caff600aa65177bfe0344c49d0aa70f0ebf30c922
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
finishing_moves (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (3.5.1)
|
10
|
+
columnize (~> 0.8)
|
11
|
+
debugger-linecache (~> 1.2)
|
12
|
+
slop (~> 3.6)
|
13
|
+
coderay (1.1.0)
|
14
|
+
colorize (0.7.3)
|
15
|
+
columnize (0.8.9)
|
16
|
+
debugger-linecache (1.2.0)
|
17
|
+
diff-lcs (1.2.5)
|
18
|
+
fuubar (2.0.0)
|
19
|
+
rspec (~> 3.0)
|
20
|
+
ruby-progressbar (~> 1.4)
|
21
|
+
method_source (0.8.2)
|
22
|
+
priscilla (1.0.3)
|
23
|
+
colorize (~> 0.7)
|
24
|
+
rumoji (~> 0.3)
|
25
|
+
pry (0.10.1)
|
26
|
+
coderay (~> 1.1.0)
|
27
|
+
method_source (~> 0.8.1)
|
28
|
+
slop (~> 3.4)
|
29
|
+
pry-byebug (2.0.0)
|
30
|
+
byebug (~> 3.4)
|
31
|
+
pry (~> 0.10)
|
32
|
+
rb-readline (0.5.1)
|
33
|
+
rspec (3.1.0)
|
34
|
+
rspec-core (~> 3.1.0)
|
35
|
+
rspec-expectations (~> 3.1.0)
|
36
|
+
rspec-mocks (~> 3.1.0)
|
37
|
+
rspec-core (3.1.7)
|
38
|
+
rspec-support (~> 3.1.0)
|
39
|
+
rspec-expectations (3.1.2)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.1.0)
|
42
|
+
rspec-mocks (3.1.3)
|
43
|
+
rspec-support (~> 3.1.0)
|
44
|
+
rspec-support (3.1.2)
|
45
|
+
ruby-progressbar (1.7.0)
|
46
|
+
rumoji (0.4.0)
|
47
|
+
slop (3.6.0)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
ruby
|
51
|
+
|
52
|
+
DEPENDENCIES
|
53
|
+
finishing_moves!
|
54
|
+
fuubar
|
55
|
+
priscilla
|
56
|
+
pry-byebug
|
57
|
+
rb-readline
|
58
|
+
rspec (~> 3.1.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Forge Software
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,602 @@
|
|
1
|
+
# Finishing Moves
|
2
|
+
|
3
|
+
##### By the guys at [Forge Software](http://www.forgecrafted.com/)
|
4
|
+
|
5
|
+
Ruby includes a huge amount of default awesomeness that tackles most common development challenges. But every now and then, you find yourself in a situation where an *elaborate-yet-precise* coding maneuver wins the day. Finishing Moves is a collection of methods designed to assist in those just-typical-enough-to-be-annoying scenarios.
|
6
|
+
|
7
|
+
In gamer terms, if standard Ruby methods are your default moves, `finishing_moves` would be mana-consuming techniques. Your cooldown spells. Your grenades (there's never enough grenades). In the right situation, they kick serious cyclomatic butt.
|
8
|
+
|
9
|
+
## Development approach
|
10
|
+
|
11
|
+
- **Never** override default Ruby behavior, only add functionality. No hacks.
|
12
|
+
- Follow the Unix philosophy of *"Do one job really well."*
|
13
|
+
- Minimize assumptions within the method, e.g. avoid formatting output, mutating values, and long conditional logic flows.
|
14
|
+
- Test all the things.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
```
|
19
|
+
(Gemification coming soon!)
|
20
|
+
```
|
21
|
+
|
22
|
+
## Current Finishers
|
23
|
+
|
24
|
+
### Extensions to `Object`
|
25
|
+
|
26
|
+
#### `Object#nil_chain`
|
27
|
+
Arguably the sharpest knife in the block, `#nil_chain` allows you to write elaborate method chains without fear of tripping over `NoMethodError` and `NameError` exceptions when something in the chain throws out a nil value.
|
28
|
+
|
29
|
+
##### Examples
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# foobar may have a transmogrify method...or it may not! Doooooom!
|
33
|
+
|
34
|
+
# without nil_chain, we check to make sure the method exists
|
35
|
+
|
36
|
+
foobar.transmogrify if foobar.respond_to? :transmogrify
|
37
|
+
|
38
|
+
# with nil_chain, we just do it, and kick those nil ghosts in the teeth
|
39
|
+
|
40
|
+
nil_chain{ foobar.transmogrify }
|
41
|
+
# => result of foobar.transmogrify, or nil
|
42
|
+
```
|
43
|
+
|
44
|
+
Not really saving much typing there, but how about an object assigned to a hash?
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# without nil_chain, we check to make sure the key exists
|
48
|
+
|
49
|
+
if my_hash.has_key? :foo
|
50
|
+
my_hash[:foo].do_stuff
|
51
|
+
end
|
52
|
+
|
53
|
+
# with nil_chain, things look a lot cleaner
|
54
|
+
|
55
|
+
nil_chain{ my_hash[:foo].do_stuff }
|
56
|
+
# => result of my_hash[:foo].do_stuff, or nil
|
57
|
+
```
|
58
|
+
|
59
|
+
Still pretty simple. Let's try it on a series of connected objects.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
class A
|
63
|
+
attr_accessor :b
|
64
|
+
def initialize(b)
|
65
|
+
@b = b
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class B
|
70
|
+
attr_accessor :c
|
71
|
+
def initialize(c)
|
72
|
+
@c = c
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class C
|
77
|
+
def hello
|
78
|
+
"Hello, world!"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
c = C.new
|
83
|
+
b = B.new c
|
84
|
+
a = A.new b
|
85
|
+
|
86
|
+
a.b.c.hello
|
87
|
+
# => "Hello, world!"
|
88
|
+
```
|
89
|
+
|
90
|
+
Let's suppose the presence of attribute `c` is conditional. We must then check for a proper association between objects `b` and `c` before calling `hello`.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
b.c = nil
|
94
|
+
a.b.c.hello
|
95
|
+
# => NoMethodError: undefined method `hello' for nil:NilClass
|
96
|
+
|
97
|
+
a.b.c.hello unless b.c.nil? || b.c.empty?
|
98
|
+
# => nil
|
99
|
+
|
100
|
+
a.b = nil
|
101
|
+
|
102
|
+
# Now it's really getting ugly.
|
103
|
+
if !a.b.nil? && !a.b.empty?
|
104
|
+
a.b.c.hello unless b.c.nil? || b.c.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Imagine if we had a fourth association, or a fifth! The patterns, man!
|
108
|
+
```
|
109
|
+
|
110
|
+
Or we can just skip all that conditional nonsense.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
nil_chain{ a.b.c.hello }
|
114
|
+
# => output "Hello, world!" or nil
|
115
|
+
|
116
|
+
a = nil
|
117
|
+
nil_chain{ a.b.c.hello }
|
118
|
+
# => still just nil
|
119
|
+
```
|
120
|
+
|
121
|
+
##### Examples in Rails
|
122
|
+
|
123
|
+
We use `nil_chain` all the time in Rails projects. The A-B-C class example above was derived from a frequent use case in our models...
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# Model User has ZERO or more addresses, one of which is the primary.
|
127
|
+
# Model Address has a zip_code attribute.
|
128
|
+
|
129
|
+
user = User.find(9876)
|
130
|
+
nil_chain{ user.addresses.primary.zip_code }
|
131
|
+
# => returns nil if no addresses, or primary not set, otherwise returns zip_code
|
132
|
+
```
|
133
|
+
|
134
|
+
It also helps when dealing with optional parameters coming in from forms...
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
# Somewhere in a random rails controller...
|
138
|
+
|
139
|
+
def search
|
140
|
+
case nil_chain { params[:case_state].downcase }
|
141
|
+
when 'open' then filter_only_open
|
142
|
+
when 'closed' then filter_only_closed
|
143
|
+
when 'invalid' then filter_only_invalid
|
144
|
+
when 'withdrawn' then filter_only_withdrawn
|
145
|
+
when 'canceled' then filter_only_canceled
|
146
|
+
end
|
147
|
+
# => apply a case state filter, or do nothing
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
Setting default values on form inputs in views...
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
select_tag :date_field,
|
155
|
+
options_for_select(@dropdown_date_field, nil_chain{params[:date_field]} )
|
156
|
+
# => Sets the selected option in the dropdown if the :date_field parameter exists
|
157
|
+
```
|
158
|
+
|
159
|
+
##### Custom return value
|
160
|
+
|
161
|
+
You can change the value that `nil_chain` returns when it catches a `NoMethodError` or `NameError` exception. `nil_chain` accepts a single optional argument before the block to represent the return value. The default is `nil`, but you can set it to whatever you want.
|
162
|
+
|
163
|
+
We recently used this functionality in generating a CSV report. The client's use case required us to spit out an `'N/A'` string anytime a proper field value was missing. `nil_chain` made the adjustment easy.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
CSV.generate do |csv|
|
167
|
+
@records.each do |record| # each record represents a single line in the CSV
|
168
|
+
values = []
|
169
|
+
csv_fields_in_order.each do |field|
|
170
|
+
values << nil_chain('N/A') { record.send(field) }
|
171
|
+
# respond with a pretty value when the field is empty or invalid
|
172
|
+
end
|
173
|
+
csv << values
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
We also find this handy when doing conditional stuff based on presence/absence of a key in a hash.
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
# without nil_chain
|
182
|
+
if my_hash[:foo]
|
183
|
+
# (by default, ruby returns nil when you request an unset key)
|
184
|
+
var = my_hash[:foo]
|
185
|
+
else
|
186
|
+
var = :default_value
|
187
|
+
end
|
188
|
+
|
189
|
+
# with nil_chain, we get a nice one liner
|
190
|
+
|
191
|
+
var = nil_chain(:default_value) { my_hash[:foo] }
|
192
|
+
|
193
|
+
# What if the default value is coming from somewhere else?
|
194
|
+
# What if we want to call a method directly on the hash?
|
195
|
+
# What if the ley lines are out of alignment!?
|
196
|
+
# No problem.
|
197
|
+
|
198
|
+
var = nil_chain(Geomancer.reset_ley_lines) { summon_fel_beast[:step_3].scry }
|
199
|
+
# => value of summon_fel_beast[:step_3].scry if it's set, or
|
200
|
+
# Geomancer.reset_ley_lines if it's not
|
201
|
+
```
|
202
|
+
|
203
|
+
##### Aliases
|
204
|
+
|
205
|
+
`nil_chain` is aliased to `chain` for more brevity, and `method_chain` for alternative clarity.
|
206
|
+
|
207
|
+
|
208
|
+
---
|
209
|
+
|
210
|
+
#### `Object#bool_chain`
|
211
|
+
|
212
|
+
This is the same logic under the hood as `nil_chain`, however we forcibly return a boolean `false` instead of `nil` if the chain breaks.
|
213
|
+
|
214
|
+
Following our A-B-C example above...
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
bool_chain{ a.b.c.hello }
|
218
|
+
# => false
|
219
|
+
```
|
220
|
+
|
221
|
+
If you read about `nil_chain`'s custom return value, you know that you can do this explicitly too. This shortcut just saves some typing.
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
nil_chain(false) { a.b.c.hello }
|
225
|
+
# => false
|
226
|
+
```
|
227
|
+
|
228
|
+
---
|
229
|
+
|
230
|
+
#### `Object#same_as`
|
231
|
+
|
232
|
+
Comparison operator that normalizes both sides into strings, then runs them over `==`.
|
233
|
+
|
234
|
+
The comparison will work on any class that has a `to_s` method defined on it.
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
# All these comparisons will return true
|
238
|
+
|
239
|
+
:foobar.same_as 'foobar'
|
240
|
+
'foobar'.same_as :foobar
|
241
|
+
'1'.same_as 1
|
242
|
+
2.same_as '2'
|
243
|
+
3.same_as 3
|
244
|
+
```
|
245
|
+
|
246
|
+
Normal case-sensitivity rules apply.
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
:symbol.same_as :SYMBOL
|
250
|
+
# => false
|
251
|
+
|
252
|
+
:symbol.same_as 'SYMBOL'
|
253
|
+
# => still false
|
254
|
+
```
|
255
|
+
|
256
|
+
Since this method is defined in Object, your own custom classes inherit it automatically, allowing you to compare literally anything at any time, without worrying about typecasting!
|
257
|
+
|
258
|
+
**Make sure you define sane output for `to_s`** and you're all set.
|
259
|
+
|
260
|
+
We love working with symbols in our code, but symbol values become strings when they hit the database. This meant typecasting wherever new and existing data might collide. No more!
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
class User
|
264
|
+
attr_writer :handle
|
265
|
+
|
266
|
+
def handle
|
267
|
+
@handle || "faceless_one"
|
268
|
+
end
|
269
|
+
|
270
|
+
def to_s
|
271
|
+
handle.to_s
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
user = User.new
|
276
|
+
:faceless_one.same_as user
|
277
|
+
# => true
|
278
|
+
user.same_as :faceless_one
|
279
|
+
# => true
|
280
|
+
user.same_as 'faceless_one'
|
281
|
+
# => true
|
282
|
+
user.same_as 'FACELESS_ONE'
|
283
|
+
# => false
|
284
|
+
```
|
285
|
+
|
286
|
+
---
|
287
|
+
|
288
|
+
#### `Object#class_exists?`
|
289
|
+
|
290
|
+
> *I just want to know if [insert class name] has been defined!*
|
291
|
+
>
|
292
|
+
> -- Every dev at some point
|
293
|
+
|
294
|
+
Sure, Ruby has the `defined?` method, but the output is less than helpful when you're doing conditional flows.
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
defined?(SuperSaiyan)
|
298
|
+
# => nil
|
299
|
+
|
300
|
+
require 'super_saiyan'
|
301
|
+
|
302
|
+
defined?(SuperSaiyan)
|
303
|
+
# => 'constant'
|
304
|
+
|
305
|
+
if defined?(SuperSaiyan) == 'constant'
|
306
|
+
# Power up to level 4
|
307
|
+
# But after that obtuse if-statement, I'm just too tired
|
308
|
+
end
|
309
|
+
```
|
310
|
+
|
311
|
+
`class_exists?` does exactly what you want, and provides an obvious, natural boolean response.
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
class_exists? :Symbol
|
315
|
+
# => true
|
316
|
+
class_exists? :Symbology
|
317
|
+
# => false, unless you're Dan Brown
|
318
|
+
class_exists? :Rails
|
319
|
+
# => true in a Rails app
|
320
|
+
```
|
321
|
+
|
322
|
+
Because the class **might** exist, we cannot pass in the constant version of the name. You **must** use a symbol or string value.
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
class_exists? DefinitelyFakeClass
|
326
|
+
# => NameError: uninitialized constant DefinitelyFakeClass
|
327
|
+
|
328
|
+
class_exists? :DefinitelyFakeClass
|
329
|
+
# => false (at least it better be; if you actually use this name, I will find you...)
|
330
|
+
```
|
331
|
+
|
332
|
+
---
|
333
|
+
|
334
|
+
#### `Object#not_nil?`
|
335
|
+
|
336
|
+
Because that dangling `!` on the front of a call to `nil?` is just oh so not-ruby-chic.
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
nil.not_nil?
|
340
|
+
# => false
|
341
|
+
'foobar'.not_nil?
|
342
|
+
# => true
|
343
|
+
```
|
344
|
+
|
345
|
+
There, much more legible. Now pass me my fedora and another PBR.
|
346
|
+
|
347
|
+
---
|
348
|
+
|
349
|
+
### Extensions to `Hash`
|
350
|
+
|
351
|
+
#### `Hash#delete!`
|
352
|
+
|
353
|
+
The normal [`Hash#delete`](http://www.ruby-doc.org/core-2.1.5/Hash.html#method-i-delete) method returns the value that's been removed from the hash, but it can be equally useful if we return the newly modified hash instead.
|
354
|
+
|
355
|
+
This approach effectively throws away the value being deleted, so don't use this when the deleted hash entry is valuable.
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
power_rangers = {
|
359
|
+
:red => 'Jason Scott',
|
360
|
+
:blue => 'Billy Cranston',
|
361
|
+
:green => 'Tommy Oliver'
|
362
|
+
}
|
363
|
+
|
364
|
+
power_rangers.delete! :green
|
365
|
+
# => { :red => 'Jason Lee Scott', :blue => 'Billy Cranston' }
|
366
|
+
```
|
367
|
+
|
368
|
+
If the key is not found, the hash is returned unaltered.
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
power_rangers.delete! :radiant_orchid
|
372
|
+
# => { :red => 'Jason Lee Scott', :blue => 'Billy Cranston' }
|
373
|
+
# It probably would've triggered if I included Kimberly
|
374
|
+
```
|
375
|
+
|
376
|
+
---
|
377
|
+
|
378
|
+
#### `Hash#delete_each`
|
379
|
+
Deletes all records in a hash matching the keys passed in as an array. Returns a hash of deleted entries. Silently ignores any keys which are not found.
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
mega_man_bosses = { :metal_man => 1, :bubble_man => 2, :heat_man => 3, :wood_man => 4 }
|
383
|
+
|
384
|
+
mega_man_bosses.delete_each :chill_penguin, :spark_mandrill
|
385
|
+
# => nil, and get your series straight
|
386
|
+
mega_man_bosses
|
387
|
+
# => { :metal_man => 1, :bubble_man => 2, :heat_man => 3, :wood_man => 4 }
|
388
|
+
|
389
|
+
mega_man_bosses.delete_each :metal_man
|
390
|
+
# => { :metal_man => 1 }
|
391
|
+
mega_man_bosses
|
392
|
+
# => { :bubble_man => 2, :heat_man => 3, :wood_man => 4 }
|
393
|
+
|
394
|
+
mega_man_bosses.delete_each :bubble_man, :heat_man, :wheel_gator
|
395
|
+
# => { :bubble_man => 2, :heat_man => 3 }
|
396
|
+
mega_man_bosses
|
397
|
+
# => { :wood_man => 4 }
|
398
|
+
```
|
399
|
+
|
400
|
+
---
|
401
|
+
|
402
|
+
#### `Hash#delete_each!`
|
403
|
+
|
404
|
+
Same logic as `delete_each`, but return the modified hash, and discard the deleted values.
|
405
|
+
|
406
|
+
Maintains parity with the contrast of `delete` vs `delete!` described above.
|
407
|
+
|
408
|
+
```ruby
|
409
|
+
mega_man_bosses = { :air_man => 5, :crash_man => 6, :flash_man => 7, :quick_man => 8 }
|
410
|
+
|
411
|
+
mega_man_bosses.delete_each! :yellow_devil, :air_man
|
412
|
+
# => { :crash_man => 6, :flash_man => 7, :quick_man => 8 }
|
413
|
+
|
414
|
+
mega_man_bosses.delete_each! :flash_man
|
415
|
+
# => { :crash_man => 6, :quick_man => 8 }
|
416
|
+
# Take out flash anytime after metal, I like to wait until I need a breather.
|
417
|
+
|
418
|
+
mega_man_bosses.delete_each! :crash_man, :quick_man
|
419
|
+
# => { }
|
420
|
+
```
|
421
|
+
|
422
|
+
---
|
423
|
+
|
424
|
+
### `Fixnum#length` and `Bignum#length`
|
425
|
+
|
426
|
+
Ruby doesn't provide a native way to see how many digits are in an integer, but that's exactly what we worry about anytime out database `INT` lengths collide with Ruby `Fixnum` or `Bignum` values.
|
427
|
+
|
428
|
+
```ruby
|
429
|
+
1.length
|
430
|
+
# => 1
|
431
|
+
9.length
|
432
|
+
# => 1
|
433
|
+
90.length
|
434
|
+
# => 2
|
435
|
+
900.length
|
436
|
+
# => 3
|
437
|
+
9000.length
|
438
|
+
# => 4
|
439
|
+
9001.length
|
440
|
+
# => OVER NINE THOUSAAAAAAND (also 4)
|
441
|
+
|
442
|
+
12356469787881584554556.class.name
|
443
|
+
# => "Bignum"
|
444
|
+
12356469787881584554556.length
|
445
|
+
# => 23
|
446
|
+
```
|
447
|
+
|
448
|
+
For consistency, we added matching methods to `Float` and `BigDecimal` that simply raise an `ArgumentError`.
|
449
|
+
|
450
|
+
```ruby
|
451
|
+
12356469.987.class.name
|
452
|
+
# => "Float"
|
453
|
+
12356469.987.length
|
454
|
+
# => ArgumentError: Cannot get length: "12356469.987" is not an integer
|
455
|
+
|
456
|
+
1265437718438866624512.123.class.name
|
457
|
+
# => "Float" (it's really BigDecimal, trust us)
|
458
|
+
1265437718438866624512.123.length
|
459
|
+
# => ArgumentError: Cannot get length: "1.2654377184388666e+21" is not an integer
|
460
|
+
```
|
461
|
+
|
462
|
+
---
|
463
|
+
|
464
|
+
### Typecasting *to* `Boolean`
|
465
|
+
|
466
|
+
Boolean values are frequently represented as strings and integers in databases and file storage. So we always thought it was a little odd that Ruby lacked a boolean typecasting method, given the proliferation of `to_*` methods for `String`, `Symbol`, `Integer`, `Float`, `Hash`, etc.
|
467
|
+
|
468
|
+
So we made one for strings and integers.
|
469
|
+
|
470
|
+
#### `String#to_bool`
|
471
|
+
|
472
|
+
Strings get analyzed and return true/false for a small set of potential values. These comparisons are case-insensitive.
|
473
|
+
|
474
|
+
```ruby
|
475
|
+
['1', 't', 'true', 'on', 'y', 'yes'].each do |true_string|
|
476
|
+
true_string.to_bool
|
477
|
+
# => true
|
478
|
+
|
479
|
+
true_string.upcase.to_bool
|
480
|
+
# => true
|
481
|
+
end
|
482
|
+
|
483
|
+
['0', 'f', 'false', 'off', 'n', 'no'].each do |false_string|
|
484
|
+
false_string.to_bool
|
485
|
+
# => false
|
486
|
+
|
487
|
+
false_string.upcase.to_bool
|
488
|
+
# => false
|
489
|
+
end
|
490
|
+
|
491
|
+
# empty strings and strings with only spaces evaluate to false
|
492
|
+
["", " ", " ", " "].each do |empty_string|
|
493
|
+
empty_string.to_bool
|
494
|
+
# => false
|
495
|
+
end
|
496
|
+
```
|
497
|
+
|
498
|
+
A string with anything other than these matching values will throw an error.
|
499
|
+
|
500
|
+
```ruby
|
501
|
+
["foo", "tru", "trueish", "druish", "000"].each do |bad_string|
|
502
|
+
bad_string.to_bool
|
503
|
+
# => ArgumentError: invalid value for Boolean
|
504
|
+
end
|
505
|
+
```
|
506
|
+
|
507
|
+
#### `Fixnum#to_bool`
|
508
|
+
|
509
|
+
A zero is false, a one is true. That's it. Everything else throws `ArgumentError`
|
510
|
+
|
511
|
+
```ruby
|
512
|
+
0.to_bool
|
513
|
+
# => false
|
514
|
+
|
515
|
+
1.to_bool
|
516
|
+
# => true
|
517
|
+
|
518
|
+
2.to_bool
|
519
|
+
# => ArgumentError: invalid value for Boolean: "2"
|
520
|
+
|
521
|
+
-1.to_bool
|
522
|
+
# => ArgumentError: invalid value for Boolean: "-1"
|
523
|
+
|
524
|
+
8675309.to_bool
|
525
|
+
# => ArgumentError: invalid value for Boolean: "8675309"
|
526
|
+
```
|
527
|
+
|
528
|
+
#### `NilClass#to_bool`
|
529
|
+
|
530
|
+
A nil value typecasted to a boolean is false.
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
nil == false
|
534
|
+
# => false
|
535
|
+
|
536
|
+
nil.to_bool
|
537
|
+
# => false
|
538
|
+
|
539
|
+
nil.to_bool == false
|
540
|
+
# => true
|
541
|
+
```
|
542
|
+
|
543
|
+
#### `TrueClass#to_bool` and `FalseClass#to_bool`
|
544
|
+
|
545
|
+
They return what you expect, we added them simply for sake of consistency, in case your code calls `to_bool` on a variable of indeterminate type.
|
546
|
+
|
547
|
+
```ruby
|
548
|
+
true.to_bool
|
549
|
+
# => true
|
550
|
+
|
551
|
+
false.to_bool
|
552
|
+
# => false
|
553
|
+
```
|
554
|
+
|
555
|
+
---
|
556
|
+
|
557
|
+
### Typecasting *from* `Boolean` and `Nil`
|
558
|
+
|
559
|
+
Complementing the methods to typecast boolean values coming out of data storage, we have methods to convert booleans and `nil` into string and symbol representations.
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
true.to_i
|
563
|
+
# => 1
|
564
|
+
true.to_sym
|
565
|
+
# => :true
|
566
|
+
|
567
|
+
false.to_i
|
568
|
+
# => 0
|
569
|
+
false.to_sym
|
570
|
+
# => :false
|
571
|
+
|
572
|
+
nil.to_i
|
573
|
+
# => 0 (follows same logic as `NilClass#to_bool`)
|
574
|
+
nil.to_sym
|
575
|
+
# => :nil
|
576
|
+
```
|
577
|
+
|
578
|
+
## Share your finishing moves!
|
579
|
+
|
580
|
+
### Got an idea for another finisher?
|
581
|
+
|
582
|
+
1. Fork this repo
|
583
|
+
2. Write your tests
|
584
|
+
3. Add your finisher
|
585
|
+
4. Repeat steps 2 and 3 until badass
|
586
|
+
5. Submit a pull request
|
587
|
+
6. Everyone kicks even more ass!
|
588
|
+
|
589
|
+
###### Got a good nerdy reference for our code samples?
|
590
|
+
We'll take pull requests on those too. Bonus karma points if you apply the reference to the specs too.
|
591
|
+
|
592
|
+
## License
|
593
|
+
|
594
|
+
Finishing Moves is distributed under MIT license.
|
595
|
+
|
596
|
+
Copyright (c) 2015 Forge Software, LLC
|
597
|
+
|
598
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
599
|
+
|
600
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
601
|
+
|
602
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|