bioinform 0.1.17 → 0.2.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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/LICENSE +0 -1
  4. data/README.md +1 -1
  5. data/TODO.txt +23 -30
  6. data/bin/convert_motif +4 -0
  7. data/bin/pcm2pwm +1 -1
  8. data/bin/split_motifs +1 -1
  9. data/bioinform.gemspec +0 -2
  10. data/lib/bioinform.rb +54 -16
  11. data/lib/bioinform/alphabet.rb +85 -0
  12. data/lib/bioinform/background.rb +90 -0
  13. data/lib/bioinform/cli.rb +1 -2
  14. data/lib/bioinform/cli/convert_motif.rb +52 -17
  15. data/lib/bioinform/cli/pcm2pwm.rb +32 -26
  16. data/lib/bioinform/cli/split_motifs.rb +31 -30
  17. data/lib/bioinform/conversion_algorithms.rb +6 -0
  18. data/lib/bioinform/conversion_algorithms/pcm2ppm_converter.rb +13 -11
  19. data/lib/bioinform/conversion_algorithms/pcm2pwm_converter.rb +39 -11
  20. data/lib/bioinform/conversion_algorithms/pcm2pwm_mara_converter.rb +26 -0
  21. data/lib/bioinform/conversion_algorithms/ppm2pcm_converter.rb +30 -0
  22. data/lib/bioinform/conversion_algorithms/pwm2iupac_pwm_converter.rb +23 -0
  23. data/lib/bioinform/conversion_algorithms/pwm2pcm_converter.rb +85 -0
  24. data/lib/bioinform/data_models.rb +1 -7
  25. data/lib/bioinform/data_models/named_model.rb +38 -0
  26. data/lib/bioinform/data_models/pcm.rb +18 -28
  27. data/lib/bioinform/data_models/pm.rb +73 -170
  28. data/lib/bioinform/data_models/ppm.rb +11 -24
  29. data/lib/bioinform/data_models/pwm.rb +30 -56
  30. data/lib/bioinform/errors.rb +17 -0
  31. data/lib/bioinform/formatters.rb +4 -2
  32. data/lib/bioinform/formatters/consensus_formatter.rb +35 -0
  33. data/lib/bioinform/formatters/motif_formatter.rb +69 -0
  34. data/lib/bioinform/formatters/pretty_matrix_formatter.rb +36 -0
  35. data/lib/bioinform/formatters/transfac_formatter.rb +29 -37
  36. data/lib/bioinform/parsers.rb +1 -8
  37. data/lib/bioinform/parsers/matrix_parser.rb +44 -36
  38. data/lib/bioinform/parsers/motif_splitter.rb +45 -0
  39. data/lib/bioinform/support.rb +46 -14
  40. data/lib/bioinform/support/strip_doc.rb +1 -1
  41. data/lib/bioinform/version.rb +1 -1
  42. data/spec/alphabet_spec.rb +79 -0
  43. data/spec/background_spec.rb +57 -0
  44. data/spec/cli/cli_spec.rb +6 -6
  45. data/spec/cli/convert_motif_spec.rb +88 -88
  46. data/spec/cli/data/pcm2pwm/KLF4_f2.pwm.result +9 -9
  47. data/spec/cli/data/pcm2pwm/SP1_f1.pwm.result +11 -11
  48. data/spec/cli/pcm2pwm_spec.rb +22 -23
  49. data/spec/cli/shared_examples/convert_motif/motif_list_empty.rb +1 -1
  50. data/spec/cli/shared_examples/convert_motif/several_motifs_specified.rb +1 -1
  51. data/spec/cli/shared_examples/convert_motif/single_motif_specified.rb +5 -5
  52. data/spec/cli/shared_examples/convert_motif/yield_help_string.rb +2 -2
  53. data/spec/cli/shared_examples/convert_motif/yield_motif_conversion_error.rb +3 -3
  54. data/spec/cli/split_motifs_spec.rb +6 -21
  55. data/spec/converters/pcm2ppm_converter_spec.rb +32 -0
  56. data/spec/converters/pcm2pwm_converter_spec.rb +71 -0
  57. data/spec/converters/ppm2pcm_converter_spec.rb +32 -0
  58. data/spec/converters/pwm2iupac_pwm_converter_spec.rb +65 -0
  59. data/spec/converters/pwm2pcm_converter_spec.rb +57 -0
  60. data/spec/data_models/named_model_spec.rb +41 -0
  61. data/spec/data_models/pcm_spec.rb +114 -45
  62. data/spec/data_models/pm_spec.rb +132 -333
  63. data/spec/data_models/ppm_spec.rb +47 -44
  64. data/spec/data_models/pwm_spec.rb +85 -77
  65. data/spec/fabricators/motif_formats_fabricator.rb +116 -116
  66. data/spec/formatters/consensus_formatter_spec.rb +26 -0
  67. data/spec/formatters/raw_formatter_spec.rb +169 -0
  68. data/spec/parsers/matrix_parser_spec.rb +216 -0
  69. data/spec/parsers/motif_splitter_spec.rb +87 -0
  70. data/spec/spec_helper.rb +2 -2
  71. data/spec/spec_helper_source.rb +25 -5
  72. data/spec/support_spec.rb +31 -0
  73. metadata +43 -124
  74. data/bin/merge_into_collection +0 -4
  75. data/lib/bioinform/cli/merge_into_collection.rb +0 -80
  76. data/lib/bioinform/conversion_algorithms/ppm2pwm_converter.rb +0 -0
  77. data/lib/bioinform/data_models/collection.rb +0 -75
  78. data/lib/bioinform/data_models/motif.rb +0 -56
  79. data/lib/bioinform/formatters/raw_formatter.rb +0 -41
  80. data/lib/bioinform/parsers/jaspar_parser.rb +0 -35
  81. data/lib/bioinform/parsers/parser.rb +0 -92
  82. data/lib/bioinform/parsers/splittable_parser.rb +0 -57
  83. data/lib/bioinform/parsers/string_fantom_parser.rb +0 -35
  84. data/lib/bioinform/parsers/string_parser.rb +0 -72
  85. data/lib/bioinform/parsers/trivial_parser.rb +0 -34
  86. data/lib/bioinform/parsers/yaml_parser.rb +0 -35
  87. data/lib/bioinform/support/advanced_scan.rb +0 -8
  88. data/lib/bioinform/support/array_product.rb +0 -6
  89. data/lib/bioinform/support/array_zip.rb +0 -6
  90. data/lib/bioinform/support/collect_hash.rb +0 -7
  91. data/lib/bioinform/support/deep_dup.rb +0 -5
  92. data/lib/bioinform/support/delete_many.rb +0 -14
  93. data/lib/bioinform/support/inverf.rb +0 -13
  94. data/lib/bioinform/support/multiline_squish.rb +0 -6
  95. data/lib/bioinform/support/parameters.rb +0 -28
  96. data/lib/bioinform/support/partial_sums.rb +0 -16
  97. data/lib/bioinform/support/same_by.rb +0 -12
  98. data/lib/bioinform/support/third_part/active_support/core_ext/array/extract_options.rb +0 -29
  99. data/lib/bioinform/support/third_part/active_support/core_ext/hash/indifferent_access.rb +0 -23
  100. data/lib/bioinform/support/third_part/active_support/core_ext/hash/keys.rb +0 -54
  101. data/lib/bioinform/support/third_part/active_support/core_ext/module/attribute_accessors.rb +0 -64
  102. data/lib/bioinform/support/third_part/active_support/core_ext/object/try.rb +0 -57
  103. data/lib/bioinform/support/third_part/active_support/core_ext/string/access.rb +0 -99
  104. data/lib/bioinform/support/third_part/active_support/core_ext/string/behavior.rb +0 -6
  105. data/lib/bioinform/support/third_part/active_support/core_ext/string/filters.rb +0 -49
  106. data/lib/bioinform/support/third_part/active_support/core_ext/string/multibyte.rb +0 -72
  107. data/lib/bioinform/support/third_part/active_support/hash_with_indifferent_access.rb +0 -181
  108. data/lib/bioinform/support/third_part/active_support/multibyte.rb +0 -44
  109. data/lib/bioinform/support/third_part/active_support/multibyte/chars.rb +0 -476
  110. data/lib/bioinform/support/third_part/active_support/multibyte/exceptions.rb +0 -8
  111. data/lib/bioinform/support/third_part/active_support/multibyte/unicode.rb +0 -393
  112. data/lib/bioinform/support/third_part/active_support/multibyte/utils.rb +0 -60
  113. data/spec/cli/data/merge_into_collection/GABPA_f1.pwm +0 -14
  114. data/spec/cli/data/merge_into_collection/KLF4_f2.pwm +0 -11
  115. data/spec/cli/data/merge_into_collection/SP1_f1.pwm +0 -12
  116. data/spec/cli/data/merge_into_collection/collection.txt.result +0 -40
  117. data/spec/cli/data/merge_into_collection/collection.yaml.result +0 -188
  118. data/spec/cli/data/merge_into_collection/collection_pwm.yaml.result +0 -188
  119. data/spec/cli/data/merge_into_collection/pwm_folder/GABPA_f1.pwm +0 -14
  120. data/spec/cli/data/merge_into_collection/pwm_folder/KLF4_f2.pwm +0 -11
  121. data/spec/cli/data/merge_into_collection/pwm_folder/SP1_f1.pwm +0 -12
  122. data/spec/cli/data/split_motifs/collection.yaml +0 -188
  123. data/spec/cli/merge_into_collection_spec.rb +0 -100
  124. data/spec/data_models/collection_spec.rb +0 -98
  125. data/spec/data_models/motif_spec.rb +0 -224
  126. data/spec/fabricators/collection_fabricator.rb +0 -8
  127. data/spec/fabricators/motif_fabricator.rb +0 -33
  128. data/spec/fabricators/pcm_fabricator.rb +0 -25
  129. data/spec/fabricators/pm_fabricator.rb +0 -52
  130. data/spec/fabricators/ppm_fabricator.rb +0 -14
  131. data/spec/fabricators/pwm_fabricator.rb +0 -16
  132. data/spec/parsers/parser_spec.rb +0 -152
  133. data/spec/parsers/string_fantom_parser_spec.rb +0 -70
  134. data/spec/parsers/string_parser_spec.rb +0 -77
  135. data/spec/parsers/trivial_parser_spec.rb +0 -64
  136. data/spec/parsers/yaml_parser_spec.rb +0 -50
  137. data/spec/support/advanced_scan_spec.rb +0 -32
  138. data/spec/support/array_product_spec.rb +0 -15
  139. data/spec/support/array_zip_spec.rb +0 -15
  140. data/spec/support/collect_hash_spec.rb +0 -15
  141. data/spec/support/delete_many_spec.rb +0 -44
  142. data/spec/support/inverf_spec.rb +0 -19
  143. data/spec/support/multiline_squish_spec.rb +0 -25
  144. data/spec/support/partial_sums_spec.rb +0 -30
  145. data/spec/support/same_by_spec.rb +0 -36
@@ -1,34 +0,0 @@
1
- require_relative '../support'
2
- require_relative '../parsers/parser'
3
- require_relative '../data_models/collection'
4
- require 'yaml'
5
-
6
- module Bioinform
7
- # TrivialParser can be used to parse hashes returned by #parse method of other parsers:
8
- # PM.new({matrix:[[1,2,3,4],[5,6,7,8]], name: 'Name'}, TrivialParser)
9
- # PM.new(StringParser.new("1 2 3 4\n5 6 7 8").parse)
10
- # StringParser.new("First\n1 2 3 4\n5 6 7 8\nSecond\n0 0 0 0").map{|inp| PM.new(inp, TrivialParser)}
11
- class TrivialParser < Parser
12
- def initialize(input)
13
- @input = input
14
- end
15
- def parse!
16
- case input
17
- when PM then input
18
- when Motif then input.pm
19
- when OpenStruct then input
20
- when Hash then OpenStruct.new(input)
21
- end
22
- end
23
- end
24
-
25
- class TrivialCollectionParser < Parser
26
- include MultipleMotifsParser
27
- def initialize(input)
28
- @input = input
29
- end
30
- def parse!
31
- input.container.shift.pm
32
- end
33
- end
34
- end
@@ -1,35 +0,0 @@
1
- require_relative '../support'
2
- require_relative 'parser'
3
- require_relative '../data_models/collection'
4
- require 'yaml'
5
-
6
- module Bioinform
7
- # YAMLParser can be used to parse hashes returned by #parse method of other parsers:
8
- # yaml_dump_of_pm = PM.new(...).to_yaml
9
- # PM.new(yaml_dump_of_pm, YAMLParser)
10
- class YAMLParser < Parser
11
- def initialize(input)
12
- @input = input
13
- end
14
- def parse!
15
- YAML.load(input)
16
- rescue Psych::SyntaxError
17
- raise 'parsing error'
18
- end
19
- end
20
-
21
- class YAMLCollectionParser < Parser
22
- include MultipleMotifsParser
23
- def initialize(input)
24
- @input = input
25
- end
26
- def collection
27
- @collection ||= YAML.load(input)
28
- end
29
- def parse!
30
- collection.container.shift.pm
31
- rescue Psych::SyntaxError
32
- raise 'parsing error'
33
- end
34
- end
35
- end
@@ -1,8 +0,0 @@
1
- require 'strscan'
2
-
3
- class StringScanner
4
- def advanced_scan(pat)
5
- result = scan(pat)
6
- result && result.match(pat)
7
- end
8
- end
@@ -1,6 +0,0 @@
1
- class Array
2
- def self.product(*arrays)
3
- return [] if arrays.empty?
4
- arrays.first.product(*arrays[1..-1])
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- class Array
2
- def self.zip(*arrays)
3
- return [] if arrays.empty?
4
- arrays.first.zip(*arrays[1..-1])
5
- end
6
- end
@@ -1,7 +0,0 @@
1
- module Enumerable
2
- # %w{A C G T}.collect_hash{|k| [k*2, k*3] }
3
- # # ==> {"AA" => "AAA", "CC" => "CCC", "GG" => "GGG", "TT" => "TTT"}
4
- def collect_hash(&block)
5
- block_given? ? Hash[ collect(&block) ] : Hash[ collect{|k,v| [k,v]} ]
6
- end
7
- end
@@ -1,5 +0,0 @@
1
- class Object
2
- def deep_dup
3
- Marshal.load(Marshal.dump(self))
4
- end
5
- end
@@ -1,14 +0,0 @@
1
- class Array
2
- def delete_at_many(*indices)
3
- indices.uniq.sort.reverse.each{|ind| delete_at ind}
4
- end
5
- def delete_many(*elements)
6
- elements.each{|el| delete el}
7
- end
8
- end
9
-
10
- class Hash
11
- def delete_many(*keys)
12
- keys.each{|el| delete el}
13
- end
14
- end
@@ -1,13 +0,0 @@
1
- module Math
2
- def self.inverf(x)
3
- sign = x < 0 ? -1 : 1
4
- x = x.abs
5
- a = 8 / (3*Math::PI) * (Math::PI-3) / (4-Math::PI)
6
- part0 = ( 2/(Math::PI*a) + (Math.log(1-x*x)) / 2 )**2
7
- part = -2 / (Math::PI * a) - Math.log(1-x*x)/2 + Math.sqrt(-1/a * Math.log(1-x*x) + part0)
8
- sign * Math.sqrt(part)
9
- end
10
- def inverf(x)
11
- Math.inverf(x)
12
- end
13
- end
@@ -1,6 +0,0 @@
1
- require_relative 'third_part/active_support/core_ext/string/filters'
2
- class String
3
- def multiline_squish
4
- split("\n").map(&:squish).join("\n").gsub(/\A\n+/,'').gsub(/\n+\z/,'')
5
- end
6
- end
@@ -1,28 +0,0 @@
1
- require 'ostruct'
2
- module Bioinform
3
- module Parameters
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
- module ClassMethods
8
- def make_parameters(*params)
9
- params.each do |param|
10
- define_method(param){ parameters.send(param) }
11
- define_method("#{param}="){|new_value| parameters.send("#{param}=", new_value) }
12
- end
13
- end
14
- end
15
- def parameters; @parameters ||= OpenStruct.new; end
16
- def set_parameters(hsh)
17
- hsh.each{|k,v| send("#{k}=", v) }
18
- self
19
- end
20
- # return hash of parameters
21
- def get_parameters
22
- @parameters.marshal_dump
23
- end
24
- def parameter_defined?(param_name)
25
- get_parameters.has_key?(param_name)
26
- end
27
- end
28
- end
@@ -1,16 +0,0 @@
1
- require_relative 'collect_hash'
2
-
3
- class Array
4
- def partial_sums(initial = 0.0)
5
- sums = initial
6
- map{|el| sums += el}
7
- end
8
- end
9
-
10
- class Hash
11
- # {1 => 5, 4 => 3, 3 => 2}.partial_sums == {1=>5, 3=>7, 4=>10}
12
- def partial_sums(initial = 0.0)
13
- sums = initial
14
- sort.collect_hash{|k,v| [k, sums += v]}
15
- end
16
- end
@@ -1,12 +0,0 @@
1
- module Enumerable
2
- def same_by?(&block)
3
- return true if empty?
4
- if block_given?
5
- first_result = yield(first)
6
- all?{|el| first_result == yield(el)}
7
- else
8
- first_result = first
9
- all?{|el| first_result == el}
10
- end
11
- end
12
- end
@@ -1,29 +0,0 @@
1
- class Hash
2
- # By default, only instances of Hash itself are extractable.
3
- # Subclasses of Hash may implement this method and return
4
- # true to declare themselves as extractable. If a Hash
5
- # is extractable, Array#extract_options! pops it from
6
- # the Array when it is the last element of the Array.
7
- def extractable_options?
8
- instance_of?(Hash)
9
- end
10
- end
11
-
12
- class Array
13
- # Extracts options from a set of arguments. Removes and returns the last
14
- # element in the array if it's a hash, otherwise returns a blank hash.
15
- #
16
- # def options(*args)
17
- # args.extract_options!
18
- # end
19
- #
20
- # options(1, 2) # => {}
21
- # options(1, 2, :a => :b) # => {:a=>:b}
22
- def extract_options!
23
- if last.is_a?(Hash) && last.extractable_options?
24
- pop
25
- else
26
- {}
27
- end
28
- end
29
- end
@@ -1,23 +0,0 @@
1
- require_relative '../../hash_with_indifferent_access'
2
-
3
- class Hash
4
- # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
5
- #
6
- # {:a => 1}.with_indifferent_access["a"] # => 1
7
- #
8
- def with_indifferent_access
9
- ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
10
- end
11
-
12
- # Called when object is nested under an object that receives
13
- # #with_indifferent_access. This method will be called on the current object
14
- # by the enclosing object and is aliased to #with_indifferent_access by
15
- # default. Subclasses of Hash may overwrite this method to return +self+ if
16
- # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
17
- # desirable.
18
- #
19
- # b = {:b => 1}
20
- # {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
21
- #
22
- alias nested_under_indifferent_access with_indifferent_access
23
- end
@@ -1,54 +0,0 @@
1
- class Hash
2
- # Return a new hash with all keys converted to strings.
3
- #
4
- # { :name => 'Rob', :years => '28' }.stringify_keys
5
- # #=> { "name" => "Rob", "years" => "28" }
6
- def stringify_keys
7
- dup.stringify_keys!
8
- end
9
-
10
- # Destructively convert all keys to strings. Same as
11
- # +stringify_keys+, but modifies +self+.
12
- def stringify_keys!
13
- keys.each do |key|
14
- self[key.to_s] = delete(key)
15
- end
16
- self
17
- end
18
-
19
- # Return a new hash with all keys converted to symbols, as long as
20
- # they respond to +to_sym+.
21
- #
22
- # { 'name' => 'Rob', 'years' => '28' }.symbolize_keys
23
- # #=> { :name => "Rob", :years => "28" }
24
- def symbolize_keys
25
- dup.symbolize_keys!
26
- end
27
-
28
- # Destructively convert all keys to symbols, as long as they respond
29
- # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
30
- def symbolize_keys!
31
- keys.each do |key|
32
- self[(key.to_sym rescue key) || key] = delete(key)
33
- end
34
- self
35
- end
36
-
37
- alias_method :to_options, :symbolize_keys
38
- alias_method :to_options!, :symbolize_keys!
39
-
40
- # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
41
- # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
42
- # as keys, this will fail.
43
- #
44
- # ==== Examples
45
- # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
46
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name"
47
- # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
48
- def assert_valid_keys(*valid_keys)
49
- valid_keys.flatten!
50
- each_key do |k|
51
- raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k)
52
- end
53
- end
54
- end
@@ -1,64 +0,0 @@
1
- require_relative '../array/extract_options'
2
-
3
- class Module
4
- def mattr_reader(*syms)
5
- options = syms.extract_options!
6
- syms.each do |sym|
7
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
8
- @@#{sym} = nil unless defined? @@#{sym}
9
-
10
- def self.#{sym}
11
- @@#{sym}
12
- end
13
- EOS
14
-
15
- unless options[:instance_reader] == false || options[:instance_accessor] == false
16
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
17
- def #{sym}
18
- @@#{sym}
19
- end
20
- EOS
21
- end
22
- end
23
- end
24
-
25
- def mattr_writer(*syms)
26
- options = syms.extract_options!
27
- syms.each do |sym|
28
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
29
- def self.#{sym}=(obj)
30
- @@#{sym} = obj
31
- end
32
- EOS
33
-
34
- unless options[:instance_writer] == false || options[:instance_accessor] == false
35
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
36
- def #{sym}=(obj)
37
- @@#{sym} = obj
38
- end
39
- EOS
40
- end
41
- end
42
- end
43
-
44
- # Extends the module object with module and instance accessors for class attributes,
45
- # just like the native attr* accessors for instance attributes.
46
- #
47
- # module AppConfiguration
48
- # mattr_accessor :google_api_key
49
- # self.google_api_key = "123456789"
50
- #
51
- # mattr_accessor :paypal_url
52
- # self.paypal_url = "www.sandbox.paypal.com"
53
- # end
54
- #
55
- # AppConfiguration.google_api_key = "overriding the api key!"
56
- #
57
- # To opt out of the instance writer method, pass :instance_writer => false.
58
- # To opt out of the instance reader method, pass :instance_reader => false.
59
- # To opt out of both instance methods, pass :instance_accessor => false.
60
- def mattr_accessor(*syms)
61
- mattr_reader(*syms)
62
- mattr_writer(*syms)
63
- end
64
- end
@@ -1,57 +0,0 @@
1
- class Object
2
- # Invokes the method identified by the symbol +method+, passing it any arguments
3
- # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
4
- #
5
- # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
6
- # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
7
- #
8
- # If try is called without a method to call, it will yield any given block with the object.
9
- #
10
- # Please also note that +try+ is defined on +Object+, therefore it won't work with
11
- # subclasses of +BasicObject+. For example, using try with +SimpleDelegator+ will
12
- # delegate +try+ to target instead of calling it on delegator itself.
13
- #
14
- # ==== Examples
15
- #
16
- # Without +try+
17
- # @person && @person.name
18
- # or
19
- # @person ? @person.name : nil
20
- #
21
- # With +try+
22
- # @person.try(:name)
23
- #
24
- # +try+ also accepts arguments and/or a block, for the method it is trying
25
- # Person.try(:find, 1)
26
- # @people.try(:collect) {|p| p.name}
27
- #
28
- # Without a method argument try will yield to the block unless the receiver is nil.
29
- # @person.try { |p| "#{p.first_name} #{p.last_name}" }
30
- #--
31
- # +try+ behaves like +Object#send+, unless called on +NilClass+.
32
- def try(*a, &b)
33
- if a.empty? && block_given?
34
- yield self
35
- else
36
- __send__(*a, &b)
37
- end
38
- end
39
- end
40
-
41
- class NilClass
42
- # Calling +try+ on +nil+ always returns +nil+.
43
- # It becomes specially helpful when navigating through associations that may return +nil+.
44
- #
45
- # === Examples
46
- #
47
- # nil.try(:name) # => nil
48
- #
49
- # Without +try+
50
- # @person && !@person.children.blank? && @person.children.first.name
51
- #
52
- # With +try+
53
- # @person.try(:children).try(:first).try(:name)
54
- def try(*args)
55
- nil
56
- end
57
- end