Hm. Only 3 days? That's not a week; things were just getting interesting.
module ActsAsCsv
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def acts_as_csv
include InstanceMethods
end
end
module InstanceMethods
attr_accessor :headers, :csv_contents
def initialize
read
end
def read
@csv_contents = []
file = File.new(self.class.to_s.downcase + '.txt')
@headers = file.gets.chomp.split(', ')
file.each do |row|
@csv_contents << row.chomp.split(', ')
end
end
def each
@csv_contents.each do |row|
yield(CsvRow.new(@headers, row))
end
end
end
end
class CsvRow
def initialize(headers, row)
@data = Hash[headers.zip(row)]
end
def method_missing name, *args
puts @data[name.to_s]
end
end
class RubyCsv
include ActsAsCsv
acts_as_csv
end
m = RubyCsv.new
puts m.headers.inspect
puts m.csv_contents.inspect
puts "============================"
class Day3Csv
include ActsAsCsv
acts_as_csv
end
m = Day3Csv.new
puts m.headers.inspect
puts m.csv_contents.inspect
puts "----------------------------"
m.each {|row| puts row.one}
It doesn't look like much (and my additions to the book's template are probably quite poorly written or idiosyncratic) but there's some interesting metaprogramming going on. Including ActsAsCsv causes the class method self.included to execute with the including class as the parameter. In this case the including class is extended with the ClassMethods module which defines a single method, acts_as_csv, on the class.
Later in the CsvRow class we include ActsAsCsv and then call the acts_as_csv method. This, I guess, occurs at instantiation which means the specific instance of the CsvRow class gains the InstanceMethods module included by acts_as_csv rather than at the class level though in this case it applies to all instances. The InstanceMethods module defines methods which create the behavior of a CSV file. At multiple points the cascade of includes and method definitions is open to tinkering, conditional method inclusion or definition &etc. It's just gotten interesting, and now this 'week' is done. Maybe I'll study Ruby more later.
Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts
Wednesday, December 10, 2014
7L7W Week 1: Ruby Day 2>Take 2
Reading through the 3rd day's treatment and I decided to go back over this exercise from day 2.
This is better. I'm still not entirely convinced.
class Hash
def each_hash
h = self
h.each do |element|
yield({element[0] => element[1]})
end
end
end
class Tree
attr_accessor :children, :node_name
def initialize(name, children=[])
if name.is_a?(String)
@node_name = name
elsif name.is_a?(Hash)
@node_name = name.keys[0]
name[@node_name].each_hash {|trunk|
children << Tree.new(trunk)
}
end
@children = children
end
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
end
familyTree = Tree.new({'grandpa' => {'dad' => {'child 1' => {}, 'child 2' => {}}, 'uncle' => {'child 3' => {}, 'child 4' => {}}}})
puts "Visiting a node"
familyTree.visit {|node| puts node.node_name}
puts
puts "Visiting entire tree"
familyTree.visit_all {|node| puts node.node_name}
The notion of editing the base class at will and indeed the basic predefined classes/types, while apparently being an integral part of Ruby's design, doesn't come easily. And still this solution requires testing the type of the incoming parameter, making Ruby's blasé treatment of types rather dubious. Or, I'm doing it wrong™ which is also quite possible seeing as this is exactly the 3rd day I've done anything in Ruby.
What I've done here is basically the same as a C# extension method. What gives me the 'icks' is the notion of directly altering Hash.each to do what I wanted it to do. It seems that you can't scope the changes so it's a global edit of the existing class. Great power, &etc, plus TDD, but if anyone comes along later to update the script and tries to do anything with a hash while those changes are in effect it's going to bite them in the tail.
I think I'm feeling a tension between reducing failures and productivity at the cost of stability. It's hard to decide where the sweet spot is there. That's interesting since I spent a good bit of time developing in PHP which is pretty loosely typed.
This is better. I'm still not entirely convinced.
class Hash
def each_hash
h = self
h.each do |element|
yield({element[0] => element[1]})
end
end
end
class Tree
attr_accessor :children, :node_name
def initialize(name, children=[])
if name.is_a?(String)
@node_name = name
elsif name.is_a?(Hash)
@node_name = name.keys[0]
name[@node_name].each_hash {|trunk|
children << Tree.new(trunk)
}
end
@children = children
end
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
end
familyTree = Tree.new({'grandpa' => {'dad' => {'child 1' => {}, 'child 2' => {}}, 'uncle' => {'child 3' => {}, 'child 4' => {}}}})
puts "Visiting a node"
familyTree.visit {|node| puts node.node_name}
puts
puts "Visiting entire tree"
familyTree.visit_all {|node| puts node.node_name}
The notion of editing the base class at will and indeed the basic predefined classes/types, while apparently being an integral part of Ruby's design, doesn't come easily. And still this solution requires testing the type of the incoming parameter, making Ruby's blasé treatment of types rather dubious. Or, I'm doing it wrong™ which is also quite possible seeing as this is exactly the 3rd day I've done anything in Ruby.
What I've done here is basically the same as a C# extension method. What gives me the 'icks' is the notion of directly altering Hash.each to do what I wanted it to do. It seems that you can't scope the changes so it's a global edit of the existing class. Great power, &etc, plus TDD, but if anyone comes along later to update the script and tries to do anything with a hash while those changes are in effect it's going to bite them in the tail.
I think I'm feeling a tension between reducing failures and productivity at the cost of stability. It's hard to decide where the sweet spot is there. That's interesting since I spent a good bit of time developing in PHP which is pretty loosely typed.
Tuesday, December 9, 2014
7L7W Week 1: Ruby Day 2>A spoonful of sugar
I feel like I must be missing something. Because this:
class Tree
attr_accessor :children, :node_name
def initialize(name, children=[])
if children != []
@node_name = name
elsif name.is_a?(Hash)
@node_name = name.keys[0]
name[@node_name].each {|trunk|
children << Tree.new(trunk)
}
elsif name.is_a?(Array)
@node_name = name[0]
name[1].each {|trunk|
children << Tree.new(trunk)
}
end
@children = children
end
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
end
looks ridiculous. The main problem seems to be that name[@node_name].each is giving me arrays instead of hashes. Which means I have to account for another type that I can't readily predict in advance... which seems to end up being the bane of dynamically typed languages. Yesyesyes ducktyping, &etc. I can't access elements in a hash the same way I access elements in an array so that's not really applicable here.
On the other hand,
i=0
File.open("data.txt", 'r') do |file|
file.each do |line|
i += 1
puts "#{i} #{line}" if /dialog/ =~ line
end
end
puts "----------------------------"
puts "Searched #{i} lines of file."
class Tree
attr_accessor :children, :node_name
def initialize(name, children=[])
if children != []
@node_name = name
elsif name.is_a?(Hash)
@node_name = name.keys[0]
name[@node_name].each {|trunk|
children << Tree.new(trunk)
}
elsif name.is_a?(Array)
@node_name = name[0]
name[1].each {|trunk|
children << Tree.new(trunk)
}
end
@children = children
end
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
end
On the other hand,
i=0
File.open("data.txt", 'r') do |file|
file.each do |line|
i += 1
puts "#{i} #{line}" if /dialog/ =~ line
end
end
puts "----------------------------"
puts "Searched #{i} lines of file."
was relatively easy. Except for the false start with File.foreach which didn't seem to actually be giving me full lines from the file. Looked like it was giving me strings the size of some internal read buffer. Frankly, I expect enumerating a file to give me each line. Maybe it was user error.
Still. I'm not particularly impressed with Ruby. It feels a lot like working in PHP 8 years ago. Only with anonymous functions. Day3 is subtitled 'Serious Change' so maybe that'll be interesting.
Monday, December 8, 2014
7L7W Week 1: Ruby Day 1> Hellooooo Ruby
Somehow I was expecting more... wow factor? For as popular as Ruby is and for the kudos it gets it really doesn't, yet, feel more natural or seamless to me. Maybe I've spent too much time with 'harder' or 'crustier' or 'more obtuse' languages to get it. /shrug
Maybe day 2 will have more excitement; I will endeavor to be open minded about it until at least day 5.
Maybe day 2 will have more excitement; I will endeavor to be open minded about it until at least day 5.
Subscribe to:
Posts (Atom)