Mercurial > repos > other > SevenLanguagesInSevenWeeks
changeset 27:2ea3b5e9b41b
Implement day 3 self-study extensions:
* `each` method to iterate over rows
* `method_missing` to handle ".header_name" as value retrieval
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Tue, 05 Sep 2017 20:24:03 +0100 |
parents | d50ff917c163 |
children | 25e66d77e1fe |
files | 1-Ruby/acts_as_csv_module-irb.output 1-Ruby/acts_as_csv_module-ruby.output 1-Ruby/acts_as_csv_module.rb |
diffstat | 3 files changed, 129 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1-Ruby/acts_as_csv_module-irb.output Tue Sep 05 20:24:03 2017 +0100 @@ -0,0 +1,93 @@ +acts_as_csv_module.rb(main):001:0> #--- +acts_as_csv_module.rb(main):002:0* # Excerpted from "Seven Languages in Seven Weeks", +acts_as_csv_module.rb(main):003:0* # published by The Pragmatic Bookshelf. +acts_as_csv_module.rb(main):004:0* # Copyrights apply to this code. It may not be used to create training material, +acts_as_csv_module.rb(main):005:0* # courses, books, articles, and the like. Contact us if you are in doubt. +acts_as_csv_module.rb(main):006:0* # We make no guarantees that this code is fit for any purpose. +acts_as_csv_module.rb(main):007:0* # Visit http://www.pragmaticprogrammer.com/titles/btlang for more book information. +acts_as_csv_module.rb(main):008:0* #--- +acts_as_csv_module.rb(main):009:0* module ActsAsCsv +acts_as_csv_module.rb(main):010:1> def self.included(base) +acts_as_csv_module.rb(main):011:2> base.extend ClassMethods +acts_as_csv_module.rb(main):012:2> # The book doesn't do this, but it is possible to add the methods +acts_as_csv_module.rb(main):013:2* # without having the inheriting class call a second function to do it +acts_as_csv_module.rb(main):014:2* base.acts_as_csv +acts_as_csv_module.rb(main):015:2> end +acts_as_csv_module.rb(main):016:1> +acts_as_csv_module.rb(main):017:1* module ClassMethods +acts_as_csv_module.rb(main):018:2> def acts_as_csv +acts_as_csv_module.rb(main):019:3> include InstanceMethods +acts_as_csv_module.rb(main):020:3> end +acts_as_csv_module.rb(main):021:2> end +acts_as_csv_module.rb(main):022:1> +acts_as_csv_module.rb(main):023:1* module InstanceMethods +acts_as_csv_module.rb(main):024:2> +acts_as_csv_module.rb(main):025:2* def read +acts_as_csv_module.rb(main):026:3> self.csv_contents = [] +acts_as_csv_module.rb(main):027:3> filename = self.class.to_s.downcase + '.txt' +acts_as_csv_module.rb(main):028:3> file = File.new(filename) +acts_as_csv_module.rb(main):029:3> @headers = file.gets.chomp.split(', ') +acts_as_csv_module.rb(main):030:3> +acts_as_csv_module.rb(main):031:3* file.each do |row| +acts_as_csv_module.rb(main):032:4* csv_row = CsvRow.new(headers, row.chomp.split(', ')) +acts_as_csv_module.rb(main):033:4> @csv_contents << csv_row +acts_as_csv_module.rb(main):034:4> end +acts_as_csv_module.rb(main):035:3> end +acts_as_csv_module.rb(main):036:2> +acts_as_csv_module.rb(main):037:2* attr_accessor :headers, :csv_contents +acts_as_csv_module.rb(main):038:2> def initialize +acts_as_csv_module.rb(main):039:3> read +acts_as_csv_module.rb(main):040:3> end +acts_as_csv_module.rb(main):041:2> +acts_as_csv_module.rb(main):042:2* def each() +acts_as_csv_module.rb(main):043:3> self.csv_contents.each { |row| yield row } +acts_as_csv_module.rb(main):044:3> end +acts_as_csv_module.rb(main):045:2> end +acts_as_csv_module.rb(main):046:1> end +=> :each +acts_as_csv_module.rb(main):047:0> +acts_as_csv_module.rb(main):048:0* class RubyCsv # no inheritance! You can mix it in +acts_as_csv_module.rb(main):049:1> include ActsAsCsv +acts_as_csv_module.rb(main):050:1> # Also, nothing to do beyond the "include" - it automatically defines the methods! +acts_as_csv_module.rb(main):051:1* end +=> RubyCsv +acts_as_csv_module.rb(main):052:0> +acts_as_csv_module.rb(main):053:0* class CsvRow +acts_as_csv_module.rb(main):054:1> attr_reader :values +acts_as_csv_module.rb(main):055:1> +acts_as_csv_module.rb(main):056:1* def initialize(headers, values) +acts_as_csv_module.rb(main):057:2> @values = Hash[headers.zip(values)] +acts_as_csv_module.rb(main):058:2> end +acts_as_csv_module.rb(main):059:1> +acts_as_csv_module.rb(main):060:1* def method_missing(methId, *args) +acts_as_csv_module.rb(main):061:2> str = methId.id2name +acts_as_csv_module.rb(main):062:2> if @values.key?(str) +acts_as_csv_module.rb(main):063:3> return @values[str] +acts_as_csv_module.rb(main):064:3> else +acts_as_csv_module.rb(main):065:3* super.method_missing(methId) +acts_as_csv_module.rb(main):066:3> end +acts_as_csv_module.rb(main):067:2> end +acts_as_csv_module.rb(main):068:1> end +=> :method_missing +acts_as_csv_module.rb(main):069:0> +acts_as_csv_module.rb(main):070:0* m = RubyCsv.new +=> #<RubyCsv:0x0000000212caf8 @csv_contents=[#<CsvRow:0x0000000212c558 @values={"first"=>"Yukihiro", "last"=>"Matsumoto", "country"=>"Japan"}>, #<CsvRow:0x0000000212c170 @values={"first"=>"Bruce", "last"=>"Tate", "country"=>"USA"}>], @headers=["first", "last", "country"]> +acts_as_csv_module.rb(main):071:0> puts m.headers.inspect +["first", "last", "country"] +=> nil +acts_as_csv_module.rb(main):072:0> puts m.csv_contents.inspect +[#<CsvRow:0x0000000212c558 @values={"first"=>"Yukihiro", "last"=>"Matsumoto", "country"=>"Japan"}>, #<CsvRow:0x0000000212c170 @values={"first"=>"Bruce", "last"=>"Tate", "country"=>"USA"}>] +=> nil +acts_as_csv_module.rb(main):073:0> # Use method_missing to implement heading-as-a-method +acts_as_csv_module.rb(main):074:0* puts m.csv_contents[0].country +Japan +=> nil +acts_as_csv_module.rb(main):075:0> # And show that we still get normal failure behaviour +acts_as_csv_module.rb(main):076:0* puts m.csv_contents[0].Country +NoMethodError: undefined method `Country' for #<CsvRow:0x0000000212c558> + from acts_as_csv_module.rb:65:in `method_missing' + from acts_as_csv_module.rb:76 + from /usr/bin/irb:11:in `<main>' +acts_as_csv_module.rb(main):077:0> +acts_as_csv_module.rb(main):078:0* +acts_as_csv_module.rb(main):078:0>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1-Ruby/acts_as_csv_module-ruby.output Tue Sep 05 20:24:03 2017 +0100 @@ -0,0 +1,5 @@ +acts_as_csv_module.rb:65:in `method_missing': undefined method `Country' for #<CsvRow:0x00000002342d10> (NoMethodError) + from acts_as_csv_module.rb:76:in `<main>' +["first", "last", "country"] +[#<CsvRow:0x00000002342d10 @values={"first"=>"Yukihiro", "last"=>"Matsumoto", "country"=>"Japan"}>, #<CsvRow:0x00000002342a68 @values={"first"=>"Bruce", "last"=>"Tate", "country"=>"USA"}>] +Japan
--- a/1-Ruby/acts_as_csv_module.rb Fri Jan 20 21:03:13 2017 +0000 +++ b/1-Ruby/acts_as_csv_module.rb Tue Sep 05 20:24:03 2017 +0100 @@ -20,7 +20,7 @@ end end - module InstanceMethods + module InstanceMethods def read @csv_contents = [] filename = self.class.to_s.downcase + '.txt' @@ -28,13 +28,18 @@ @headers = file.gets.chomp.split(', ') file.each do |row| - @csv_contents << row.chomp.split(', ') + csv_row = CsvRow.new(headers, row.chomp.split(', ')) + @csv_contents << csv_row end end attr_accessor :headers, :csv_contents def initialize - read + read + end + + def each(&block) + @csv_contents.each { |row| block.call row } end end end @@ -44,7 +49,30 @@ # Also, nothing to do beyond the "include" - it automatically defines the methods! end +class CsvRow + attr_reader :values + + def initialize(headers, values) + @values = Hash[headers.zip(values)] + end + + def method_missing(methId, *args) + str = methId.id2name + if @values.key?(str) + return @values[str] + else + super.method_missing(methId) + end + end +end + m = RubyCsv.new puts m.headers.inspect puts m.csv_contents.inspect +# Show the "each" method +m.each { |row| puts row.inspect } +# Use method_missing to implement heading-as-a-method +puts m.csv_contents[0].country +# And show that we still get normal failure behaviour +puts m.csv_contents[0].Country