-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathruby_tricks.rb
1859 lines (1539 loc) · 58.3 KB
/
ruby_tricks.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# sample from an array random
[:foo, :bar].sample # :bar
(1..10).to_a.sample 2 # [3, 10]
# debugger, don't use ruby-debug
require 'debugger'
debugger
# sort files by descending modification time
Dir['*.html'].sort_by{ |f| File.mtime(f) }.reverse -
# get the delete key working on irb: press ctrl+d in terminal preferences|keyboard|forward delete
# get duplicate elements of an array
questions.select{|e| questions.count(e) > 1 }
# allow utf8 in script
# encoding: utf-8
# better irb
pry
# get user input
gets
answer = gets
# capture control-c
begin
...
rescue Interrupt
...
end
# check if file exists
File.exist?(path)
# $: - Array of paths used for looking up external files: [/Users/nacho/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1, /Users/nacho/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/x86_64-darwin11.3.0, /Users/nacho/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby, ...]
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) # prepends "./../lib" to the $: array of paths
# now you can do
require 'ruhoh' # instead of require './../lib/ruhoh', now all the files directly below this folder are accessible, and here you set up all the requirements
# require - don't need to add the .rb extension
require 'ruhoh/client/client' # instead of 'ruhoh/client/client.rb'
Ruhoh::Client.new # because it's a subfolder
# and then in ruhoh/client/client.rb
class Ruhoh
class Client # could be a module, if you don't need to instantiate
...
end
end
# check if a string matches a regex: returns position of first match or nil
"My string" =~ /My/ # 0
"My string" =~ /string/ # 3
"My string" =~ /paco/ # nil
# check if a string matches a regex: returns a MatchData object
m = "My string".match /xxx/ # nil
m = "My string".match /My/ # m[0]=>"My" m.string=>"My string" m.regexp => /My/ m.begin 0 => 0
m = "My string".match /string/ # m[0]=>"My" m.string=>"My string" m.regexp => /My/ m.begin 0 => 3
m = "My string".match /(\w+)\s(\w+)/ # m[0]=>"My string" m[1]=>"My" m[2]=>"string" m.captures=>["My", "string"] zero-based, you can also use $1 and $2 for the captured elements, m.begin 2 => 3
m = "My string".match /\s(\w+)/ # $1 => "string" m.pre_match => "My" m.post_match => ""
# open file write
File.open(local_filename, 'w') {|f| f.write(doc) }
# read entire file into array
arr = IO.readlines(path)
# read file line by line
File.open(path) do |file|
file.each do |line|
...
end
end
# format time now
Time.now.ctime # "Sun Jul 22 12:00:43 2012"
Time.now.strftime("%H:%M:%S") # "12:01:52"
# remove array element
[1,2,3] - [2]
# remove a substring from a string, sanitize
"paco.fastq".sub(/.fastq$/,'')
str.sub(/[\/]/,"_") # remove forward slash
# invert hash
class Hash
def inverse # unix gods
i = Hash.new
self.each_pair{ |k,v|
if (v.class == Array)
v.each{ |x|
if i.has_key?(x)
i[x] = [k,i[x]].flatten
else
i[x] = k
end
}
else
if i.has_key?(v)
i[v] = [k,i[v]].flatten
else
i[v] = k
end
end
}
return i
end
end
# count frequency elements array
arr.inject(Hash.new(0)) {|h,i| h[i] += 1; h }
# sort hash by value
metrics.sort_by {|key, value| value} # ascending
metrics.sort_by {|key, value| -value} # descending
# initialize a hash with an array
h = Hash.new {|h,k| h[k] = [] }
h["a"].push(3)
h["a"].push(5)
h["b"].push(6)
# {"a"=>[3, 5], "b"=>[6]}
# puts to stderr
$stderr.puts
# exit abort error
abort "MacOS too old, see: https://gist.github.com/1144389" if macos_version < 10.5
# arguments
ARGV
ARGV[0] # first command
# current script
$0
# execute command
results = %x[ls] # same as Kernel#` (backticks)
# get all files of a given type from a directory
Dir.glob(File.join(GENE_SETS_PATH,"*.txt"))
Dir['*.html']
# get directory of current file
File.dirname(__FILE__)
File.join(File.dirname(__FILE__), '..', '/') # move up one folder
# Get filename from path
File.basename($0)
# remove file extension
File.basename(my_file,File.extname(my_file))
# prepend to an array
array.unshift 0 #=> [0, 1, 2, 3, 4]
# check if element in my array
[1,2,3].include?(2)
# ruby string interpolate
%{"paco #{pepe}"}
# map
["a","b"].map(&:to_sym)
# class level instance variables
class Polygon
@sides = 8
class << self
attr_accessor :sides
def prueba
puts @options
end
def run
@options = 3
end
end
end
puts Polygon.sides # => 8
Polygon.run #=> 3
require 'test_helper'
class RubyTest < ActiveSupport::TestCase
# Glossary
#
# first-class entities - They can be stored in variables, they are objects, they can be
# created with .new()
#
# Mix a module into a class - Make the module's instance methods available to all
# instances of the class
#
# receiver - Receiver.method(argument) => Receiver is what receives the method message.
context "Ruby" do
should "Parallel assignment" do
# Any assignment expression that has more than one l-value or r-value.
a = 3
b = [3]
c = [3,4]
assert_equal [3], [*a]
assert_equal [3], [*b]
assert_equal [3,4], [*c]
# One l-value, multiple r-values
x = 1,2,3
assert_equal x, [1,2,3]
x, = 1,2,3;
assert_equal x, 1
# assert x == 1
# Multiple l-values, one array r-value
x,y,z = [1,2,3] # same as x,y,z = 1,2,3
assert_equal 1, x
assert_equal 2, y
assert_equal 3, z
x = [1,2]; assert_equal [1,2], x
x, = [1,2]; assert_equal 1, x
# different number of l- and r-values
x,y,z = 1,2
assert_equal 1,x
assert_equal 2,y
assert_equal nil,z
x,y = 1,2,3 # 3 is not assigned
assert_equal 1,x
assert_equal 2,y
# Parentheses in parallel assignment. The lefthand side can use
# parentheses for subassignment. If two or more l-values are enclosed in
# parentheses, they are initially treated as a single l-value. When the
# corresponding r-value has been determined, the rules of parallel
# assignment are applied recursively.
x,(y,z) = 3,[4,5]
assert_equal 3, x
assert_equal 4, y
assert_equal 5, z
x,y,z = 3,[4,5]
assert_equal 3, x
assert_equal [4,5], y
assert_equal nil, z
# Parentheses with hashes
x,(y,z) = 3,{4 => 5}
assert_equal 3,x
assert_equal(({4 => 5}), y)
assert_equal nil, z
x,(y,z) = 3, *{4 => 5}
assert_equal 3, x
assert_equal 4, y
assert_equal 5, z
# Return more than one value
def return_two_values
a = 2; b = 3
[a,b]
end
x,y = return_two_values
assert_equal x,2
assert_equal y,3
# See the splat operator for more assignments
end
should "unless::" do
# Unless means: execute the LEFT unless the RIGHT evaluates to TRUE
#
# falsy values => The right is true, the left executes
a = "not changed"; b = "changed"
a = b unless false; assert_equal a,"changed"
a = b unless []; assert_equal a,"changed"
a = b unless ""; assert_equal a,"changed"
a = b unless nil; assert_equal a,"changed"
a = b unless {}; assert_equal a,"changed"
# truthy values => anything else, the left doesn't execute
a = "not changed"; b = "changed"
a = b unless true; assert_equal a,"not changed"
a = b unless [3]; assert_equal a,"not changed"
end
should "super:: Chaining" do
# It calls the method in the superclass with the same name, sending it the current
# arguments. (The superclass doesn't have to define it, it could inherit it from an
# ancestor.)
# super => All arguments get passed when calling the method in the superclass
# super() => No arguments get passed when calling the method in the superclass
# super(x,y) => x and y get passed when calling the method in the superclass
class Point
def initialize(x,y)
@x = x
@y = y
end
end
class Point3D < Point
def initialize(x,y,z)
super(x,y) # Point3D has access to instance variables @x and @y, so @x, @y = super(x,y) is unnecessary.
@z = z
end
def show
"" << "#{@x} " << "#{@y} " << "#{@z}"
end
end
p3d = Point3D.new(2,3,4)
assert_equal "2 3 4", p3d.show
############
class Parent
def age
30
end
end
class Child < Parent
def age
super - 20
end
end
c = Child.new
assert_equal 10, c.age
end
should "ensure::" do
# Ensure that housekeeping details like closing files, disconnecting DB connections
# and committing transactions get done. Use it whenever you allocate a resource
# (file, DB connection) to ensure proper cleanup occurs. Even if there is an
# exception, the ensure clause gets called.
#
# The ensure clause can replace the running exception with another one, or switch
# the current return value with another one.
def ensure_returns
begin
return 1
ensure
return 2
end
end
assert_equal 2, ensure_returns
end
should "and::" do
# Using this pattern wee can guarantee that the result we return will be valid, even
# if an unexpected error is raised
def increase_number(number)
number.is_a?(Fixnum) ? number += 1 : (raise ArgumentError)
end
def increment(number)
begin
return_value = increase_number(number) and return return_value #successful
# If the below executes, something went wrong.
rescue ArgumentError => e
return e.class
end
end
assert_equal 3, increment(2)
assert_equal(ArgumentError, increment("error"), "It wasn't a number")
end
should "for...in..." do
@mestres = ""
for mestre in ["bimba", "pastinha", "camisa"]
@mestres << mestre << " "
end
assert_equal "bimba pastinha camisa ", @mestres
end
should "alias:: " do
def existing; 3; end
alias old_existing existing
assert_equal 3, old_existing
end
should "configure ruby" do
# gem sources -a http://gems.github.com # add sources
# set HOME=.
# gem install sqlite3-ruby --version 1.2.3
end
should "case::" do
a = case ['an_array']
when is_a?(Array)
'WRONG WAY OF DOING IT'
when Array
'RIGHT'
when b = ['an option hash']
b # better than repeating ['an option hash']
else
'other'
end
assert_equal 'RIGHT', a
b = case 'a'
when 'a','b'
'yes'
end
assert_equal 'yes', b, "You can assign the result of a case structure directly"
empty = nil
res = case empty
when nil
3
when blank?
# WRONG: "empty" will never equal "blank?"
else
4
end
assert_equal 3, res
arg = "H"
case arg
when Array
when String then arg.downcase!
when :start
when :end then -1
when /[a-z]/
end
assert_equal "h", arg, "Using case with then"
case
when 1 == 1
a = 3
end
assert_equal a, 3, "case with no argument"
end
end
context "Kernel" do
should "method_missing:: obj.method_missing(symbol [, *args] ) => result " do
# symbol is the symbol for the method called, and args are any arguments that were
# passed to it
class Roman
def roman_to_int(str)
case str.to_s
when 'i' then 1
when 'ii' then 2
when 'iii' then 3
end
end
def method_missing(method)
roman_to_int(method)
end
end
r = Roman.new
assert_equal 3, r.iii
end
end
context "Operators" do
should "||=::" do
def set_results(results = nil)
results ||= []
# same thing as: results = results || [] same thing as: results = [] if
# results.nil?
end
assert_equal [], set_results
assert_equal [], set_results(false)
assert_equal [1,2,3], set_results([1,2,3])
end
should "*:: The splat operator" do
# Uses Array Expansion Syntax. It lets us group together all remaining parameters
# into a single array variable
# 1 - ASSIGNMENT: * expands r-value arrays and hashes
pet1, pet2, pet3 = 'duck', *['dog','cat']
assert_equal 'duck', pet1
assert_equal 'dog', pet2
assert_equal 'cat', pet3
pet1, pet2, pet3 = 'duck', *{'dog' => 'cat'}
assert_equal 'duck', pet1
assert_equal ['dog','cat'], pet2
assert_equal nil, pet3
# 2 - ASSIGNMENT: * on l-values contracts values into arrays
pet1, *other_pets = 'duck','dog','cat'
assert_equal 'duck', pet1
assert_equal ['dog','cat'], other_pets
# 3 - convert a non-nested array to hash
vehicles = [ :planes, 21, :cars, 36 ]
vehicles_hash = Hash[*vehicles]
assert_equal({ :planes => 21, :cars => 36 }, vehicles_hash)
# convert a nested array into a hash
vehicles_nested = [[:planes, 21], [:cars, 36]] # same as vehicles_hash.to_a
vehicles_hash_unnested = Hash[*vehicles_nested.flatten]
assert_equal({ :planes => 21, :cars => 36 }, vehicles_hash_unnested)
assert_equal [:planes, 21, :cars, 36], [*vehicles_nested.flatten]
assert_equal [:planes, 21, :cars, 36], vehicles_nested.flatten #WRONG - produces an error when converting to hash
# 4 - Convert Hash to array by exploding hash. Not a good idea. Better use to_a
vehicles_array = *{ :planes => 21, :cars => 36 }
assert_same_elements [ [:planes, 21], [:cars, 36] ], vehicles_array
# 4.1 - special case, with only one element. Convert hash to array.
h = {'paco' => 343}
a = *h
b = h.to_a
assert_equal ['paco',343], a
assert_equal [['paco',343]], b
h = {'paco' => 343, 'luis' => 454}
a = *h
b = h.to_a
assert_equal [['paco',343],['luis',454]], a
assert_equal a, b
# 5 - Use in a case statement - explode array
WILD = %w( lion ñu )
TAME = %w( cat cow )
def determine_continent(animal)
case animal
when *WILD then "Africa"
when *TAME then "Europe"
end
end
assert_equal "Africa", determine_continent("ñu")
# 6 - Array#* operates as Array#join if supplied a string. If give a number, it does
# repetition
array = %w( join this sentence )
assert_equal "join—this—sentence", ( array * '—')
assert_equal [0,0,0,0,0], [0] * 5
# 7 - Define methods that take an unlimited number of arguments
def method_with_unlimited_arguments(*args)
true
end
def receives_arguments_and_hash(*args)
options = args.extract_options!
end
def receives_only_hash(*opts)
opts = opts.first.merge(opts.last) # e.g.
end
some_ints = [1,2,3]
assert method_with_unlimited_arguments(2)
assert method_with_unlimited_arguments(some_ints)
assert method_with_unlimited_arguments([1,2,3])
# * repeats a string if followed by an integer.
repeated = 'hola' * 2
assert_equal "holahola", repeated
end
should "!!::Double negation" do
# Used to force an object into an explicit true/false. Usually not necessary
def is_this_true?
@fishy_variable
end
@fishy_variable = nil
assert_false @fishy_variable, "The variable is evaluated as false"
assert_not_equal false, !@fishy_variable, "Even thought it isn't actually <false>"
assert_equal false, !!@fishy_variable, "The double negation coerces it into <false>"
end
should "String#<<:: Append altering the left operand (also Array#<< and Set#<<)" do
greeting = "A bananeira"
greeting << " caiu"
assert_equal "A bananeira caiu", greeting
end
should "%::formatting interpolation" do
assert_equal "9.50", ("%.2f" % 9.5)
assert_equal "<p>hello</p>", ( "<%s>%s</%s>" % %w{p hello p} )
end
should "<=>::" do
# Fixnum
assert_equal -1, 2 <=> 3
assert_equal 0, 3 <=> 3
assert_equal 1, 3 <=> 2
# String
assert_equal -1, "abc" <=> "abcd"
assert_equal 0, "abc" <=> "abc"
assert_equal 1, "abcd" <=> "abc"
# Array
assert_equal -1, [1] <=> [1,2]
assert_equal 0, [1] <=> [1]
assert_equal 1, [1,2] <=> [1]
end
end
context "Methods & Classes" do
setup do
class Point
end
end
should "Singleton Method::" do
# Singleton Method - Method that is defined for only a single object rather than a
# class of objects.
def Point.sum
# sum is a singleton method on an object Point
end
# The singleton methods of an object are instance methods of its singleton class.
class Iuna
def self.don ; "don"; end
def self.yin ; "yin"; end
def self.singleton_class
class << self
self
end
end
end
iuna = Iuna.new
assert_contains iuna.class.singleton_class.instance_methods(false), "don"
assert_contains iuna.class.singleton_class.instance_methods(false), "yin"
end
should "Class methods::" do
# Class methods are singleton methods of an object of Class type
class Iuna
def self.don ; "don"; end
def self.yin ; "yin"; end
end
assert_same_elements ["don","yin"], Iuna.singleton_methods(false)
end
should "define_method(meth_name, meth_body):: Defines an instance method in the receiver" do
# It expects a Symbol as meth_name and it creates a method with that name, using the
# associated "block" as the method body. Instead of a block, it can also be a Proc
# or a Method object. The block is evaluated using instance_eval.
class A
def create_method(name, &block)
self.class.send(:define_method, name, &block)
end
define_method(:paco) { 3 }
end
a = A.new
assert_equal 3, a.paco
a.create_method(:camaron) { 4 }
assert_equal 4, a.camaron
# you can do the same thing with class_eval
A.class_eval { define_method(:camaron2){ 3 }}
assert_equal 3, a.camaron2
end
should "Eigenclass:: or singleton class" do
class << Point # opening the singleton class
def class_method1
# instance method of the eigenclass, and also class method of Point.
end
def class_method2
end
end
# Alternative Syntax: If you open the eigenclass of a class object within the
# definition of a class itself, you can use self instead of the name of the class
class Point
# instance methods go here
class << self # opening the singleton class
# class methods go here as instance methods of the eigenclass
def class_method3
end
end
end
end
should "method visibility public:: private:: protected::" do
# methods are normally public (except the initialize method, which is private).
# private methods are implicitely invoked on self, and may not be explicitly invoked
# on an object.
# public, private and protected only apply to methods. Instance & class variables
# are encapsulated and thus, behave as private (the only way to make an instance
# variable accessible from outside a class is defining an accessor method).
# Constants behave as public (there is no way of making them unaccessible to outside
# use).
# A protected method is like a private method (it can only be invoked from within
# the implementation of a class or its subclasses) but it may be explicitely invoked
# on any instance of the class.
#
class Esquiva
def patada(esq) esq.esquivar end
protected
def esquivar() "esquivando" end
end
esq1 = Esquiva.new
esq2 = Esquiva.new
assert_equal "esquivando", esq1.patada(esq2), "Calling protected method"
assert_raise NoMethodError do
esq1.esquivar # esquivar is protected
end
# def safe_send(receiver, method, message) # regular send bypasses visibility rules
# eval "receiver.#{method}" # receiver.method
# rescue => e
# puts "#{message}: #{e}" # This is my message: NoMethodError
# end
#
# visibility = ARGV.shift || "public"
#
#
end
should "Subclassing <:: or inheritance" do
# Used to extend a class with the methods in another class. When we define a class,
# we may specify that it extends—or inherits from—another class, known as the
# superclass.
#
# class Gem < Ruby #=> Gem is a subclass of Ruby. Ruby is the superclass of Gem
# If you do not specify a superclass, the class implicitly extends Object.
# < is a boolean operator
assert String < Comparable, "String inherits from Comparable"
assert_false String < Integer, "String doesn't inherit from Integer"
end
end
context "Module" do
# Modules are classes that can't be instantiated or subclassed. They can be used as
# namespaces (to group related methods or constants) and as mixins (to include
# instance methods in classes).
setup do
module InstanceMethods
def an_instance_method; self.class ;end
end
module ClassMethods
def a_class_method; self ;end
end
end
should "include:: and extend:: can only be called on A MODULE " do
# include: Adds module methods as INSTANCE METHODS.
#
# extend: Adds all module methods ONLY TO THE INSTANCE it's called on.
#
# If you want to access a method defined inside a class
class MyClass
include InstanceMethods
extend ClassMethods # ClassMethods == SingletonMethods
# If extend is called inside the class, it adds module methods as CLASS METHODS
# (because adding instance methods to its eigenclass makes them class methods).
end
my_class_instance = MyClass.new
assert_equal MyClass, my_class_instance.an_instance_method, "Instance methods are now available"
assert_equal MyClass, MyClass.a_class_method, "Class methods are now available"
# Using extend outside the class definition, adds the methods as singleton methods
# (meaning, only to that instance of the class).
obj_instance = Object.new
obj_instance.extend InstanceMethods # Other object instances won't have access to "an_instance_method"
assert_equal Object, obj_instance.an_instance_method, "Instance methods are now available"
end
should "include:: and extend:: simplified" do
module InstanceMethodsWithExtend
# Called everytime a module is included. Same thing with extended. base is also
# known as klass
def self.included(base)
base.extend(ClassMethods)
end
end
class MyClass
include InstanceMethodsWithExtend
end
my_class_instance = MyClass.new
assert_equal MyClass, my_class_instance.an_instance_method, "Instance methods are now available"
assert_equal MyClass, MyClass.a_class_method, "Class methods are now available"
end
should "require::" do
# Loads an external file. Ruby finds these files by searching in the LOAD_PATH. You
# may need to include after requiring (if they contain modules).
# If a file is in the lib directory, you require it directly by name. require
# 'easter' # lib/easter.rb
#
# If it's in a subdirectory of lib, you need to specify it.
# require 'shipping/airmail' # lib/shipping/airmail.rb
end
should "const_get:: mod.const_get(sym) => obj" do
# Returns the value of the named constant in mod.
assert_equal 3, Math.const_get(:PI).round
# Leave the lefthandside empty to look for a global constant
assert ::ARGV, "global constant"
end
should "class << self::" do
class Capoeira
def an_instance_method;end
class << self
def a_class_method;end
end
def self.another_class_method;end
end
assert_same_elements %w( a_class_method another_class_method), Capoeira.public_methods - Capoeira.superclass.public_methods
end
end
context "Literals" do
should "String literals" do
# There are four matching delimiters: [], {}, (), <>. Any other delimiter used has
# to be repeated: !!,||, "", '', //,++,**,:: etc.
# No interpolation: '' or %q
assert_equal "paco", 'paco'
assert_equal "paco", %q[paco]
assert_equal "paco", %q{paco}
assert_equal "paco", %q(paco)
assert_equal "pa<c>o", %q<pa<c>o>, "Because they are paired, matching delimiters can be nested"
assert_equal "pa{c}o", %q/pa{c}o/
assert_equal "pa_co", %q_pa\_co_
# Interpolation "", %Q or just %
assert_equal "4", "#{2*2}"
assert_equal "4", %Q+#{2*2}+
assert_equal "4", %|#{2*2}|
# Here documents
multiline_no_dash = <<termina_con_esto
Hola
Paco
termina_con_esto
assert_equal " Hola\n Paco\n", multiline_no_dash
multiline_with_dash = <<-termina_con_esto
Hola
Paco
termina_con_esto
assert_equal " Hola\n Paco\n", multiline_with_dash
end
should "Array literals (arrays of strings)" do
# No interpolation %w
assert_equal ['a', 'b', 'c'], %w[a b c]
assert_equal ['(', '[', '{', '<'], %w| ( [ { < |
assert_equal ['\s'], %w(\s)
# Interpolation %W
assert_equal [' '], %W(\s)
assert_equal ["\s"], %W(\s)
a = 3
assert_not_equal 3, %W( a ), %q(it doesn't work, you have to use #{a})
end
should "Symbol literals" do
assert_equal :"hola hermano", %s(hola hermano)
end
end
context "Blocks, Procs, Lambdas and Methods" do
# Blocks are syntactic structures; they are NOT objects, and cannot be manipulated
# as objects. It's possible to create an object that represents a block: a proc or a
# lambda.
should "not assign a block to a variable" do
# my_block = { |x| x*x } #=> SyntaxError use this instead: my_block = lambda{ |x|
# x*x } my_block.call
end
should "Blocks: yield::" do
# IF YOU SEE A "yield", it means that the function that has it will recieve a
# block at runtime.
def pass_number_as_block_argument(number)
yield number # "number" becomes the argument of the block. number is yielded to the block
end
assert pass_number_as_block_argument(4) { |x| x > 3 }
assert_false pass_number_as_block_argument(2) { |x| x > 3 }
class IsTwo
def is_two?(num)
num == 2
end
end
def foo
yield IsTwo.new
end
assert_false foo { |bar| bar.is_two? 3 }
assert foo { |bar| bar.is_two? 2 }
#[1,2,3].select
# def select
# ary = []
# each do |item|
# if yield(item)
# ary << item
# end
# end
# ary
# end
Array.class_eval do
def select_words1
if block_given?
self.select { |word, frequency| yield(word, frequency) }.collect { |word, frequency| word }
end
end
def select_words2(&block)
if block
self.select(&block).collect { |word, frequency| word }
end
end
end
assert_equal ["a"], [["a",2],["b",1]].select_words1 { |word, freq| freq == 2 }
assert_equal ["a"], [["a",2],["b",1]].select_words2 { |word, freq| freq == 2 }
end
should "a block refers to outer variables, not inner" do
def execute_the_block_three_times_with_local_variable
my_var = 100 #var in the context from which it is called
yield
yield
yield
end
my_var = 5 #var in the same context as the block
execute_the_block_three_times_with_local_variable { my_var += 1 }
assert_equal 8, my_var
end
should "&:: creates procs implicitely. It converts Blocks into Procs and viceversa" do
def convert_block_to_proc(&block)# The unary ampersand operator
block #now it's a proc
end
external_proc = convert_block_to_proc { |x| x > 3 }
assert external_proc.call(4), "the block returns true"
assert_false external_proc.call(1), "the block returns false"
# any? expects a block and we're sending it a proc
assert_raise ArgumentError do
[1,4,7].any? external_proc
end
assert [1,4,7].any?(&external_proc), "We turn the proc back into a block"
end
should "Proc:: use call(). proc { |...| block } => a_proc. Also, call proc with Proc#[]::" do
# Procs have block-like behavior, they are objects. Greatest feature => Procs can
# be saved in variables.
def convert_block_to_proc_using_ampersand(&block)
block
end
def convert_block_to_proc_using_proc_new(&block)
Proc.new(&block)
end
proc_with_ampersand = convert_block_to_proc_using_ampersand { "&" }
proc_with_proc_new = convert_block_to_proc_using_proc_new { "Proc.new()" }
assert_equal "&", proc_with_ampersand.call
assert_equal "Proc.new()", proc_with_proc_new.call