Merging hashes using both symbols and strings as keys

S

shenry

I'm trying to merge to hashes, one using symbols as keys (the defined
default values for my class) and the other using strings as keys
(taken from the params hash).

default = { :name => "Joe", :age => 50 }

params = { "name" => "Bill" }

new_hash = default.merge(params)
What's the Ruby way to handle this so that it overwrites :name with
"name"? Do I need to implement a stringify_keys or symbolize_keys
method like in Rails? I'd like to avoid using strings as the keys in
my default hash.

Any help greatly appreciated.

Stu
 
M

Marnen Laibow-Koser

shenry said:
I'm trying to merge to hashes, one using symbols as keys (the defined
default values for my class) and the other using strings as keys
(taken from the params hash).

default = { :name => "Joe", :age => 50 }

params = { "name" => "Bill" }

new_hash = default.merge(params)

What's the Ruby way to handle this so that it overwrites :name with
"name"? Do I need to implement a stringify_keys or symbolize_keys
method like in Rails? I'd like to avoid using strings as the keys in
my default hash.

I would think it would be easiest to use something like symbolize_keys.
Or just lift the whole HashWithIndifferentAccess class from Rails.
Any help greatly appreciated.

Stu

Best,
 
7

7stud --

shenry said:
I'd like to avoid using strings as the keys in
my default hash.

h1 = { :name => "Joe", :age => 50 }
h2 = { "name" => "Bill", :phone => "123-4567"}

h2.each do |key, val|
h1[key.to_sym] = val
end

p h1

--output:--
{:age=>50, :phone=>"123-4567", :name=>"Bill"}
 
I

Intransition

I'm trying to merge to hashes, one using symbols as keys (the defined
default values for my class) and the other using strings as keys
(taken from the params hash).

default =3D { :name =3D> "Joe", :age =3D> 50 }

params =3D { "name" =3D> "Bill" }

new_hash =3D default.merge(params)


What's the Ruby way to handle this so that it overwrites :name with
"name"? Do I need to implement a stringify_keys or symbolize_keys
method like in Rails? I'd like to avoid using strings as the keys in
my default hash.

Any help greatly appreciated.

require 'facets/hash/rekey'

default.merge(params.rekey)

rekey takes a block, without a block it is the same as:

rekey(&:to_sym)
 
R

Robert Klemme

2009/10/30 7stud -- said:
shenry said:
I'd like to avoid using strings as the keys in
my default hash.

h1 =3D =A0{ :name =3D> "Joe", :age =3D> 50 }
h2 =3D { "name" =3D> "Bill", :phone =3D> "123-4567"}

h2.each do |key, val|
=A0h1[key.to_sym] =3D val
end

p h1

--output:--
{:age=3D>50, :phone=3D>"123-4567", :name=3D>"Bill"}

Typically you do not want to modify defaults so I'd probably do

irb(main):001:0> default =3D { :name =3D> "Joe", :age =3D> 50 }.freeze
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):002:0> params =3D { "name" =3D> "Bill" }
=3D> {"name"=3D>"Bill"}
irb(main):003:0> new_hash =3D default.dup
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):004:0> params.each {|k,v| new_hash[k.to_sym]=3Dv}
=3D> {"name"=3D>"Bill"}
irb(main):005:0> new_hash
=3D> {:name=3D>"Bill", :age=3D>50}

If you are allowed to change params you could do

irb(main):001:0> default =3D { :name =3D> "Joe", :age =3D> 50 }.freeze
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):002:0> params =3D { "name" =3D> "Bill" }
=3D> {"name"=3D>"Bill"}
irb(main):003:0> default.each {|k,v| params[k.to_s] ||=3D v}
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):004:0> params
=3D> {"name"=3D>"Bill", "age"=3D>50}

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I'm trying to merge to hashes, one using symbols as keys (the defined
default values for my class) and the other using strings as keys
(taken from the params hash).

default = { :name => "Joe", :age => 50 }

params = { "name" => "Bill" }

new_hash = default.merge(params)

What's the Ruby way to handle this so that it overwrites :name with
"name"? Do I need to implement a stringify_keys or symbolize_keys
method like in Rails? I'd like to avoid using strings as the keys in
my default hash.

Any help greatly appreciated.

Stu

Hi, I made a small module called SymbolizeKeys that will allow you to extend
the hash you are interested in applying this behaviour to, you can then use
it like this:

default = { :name => "Joe", :age => 50 }.extend(SymbolizeKeys)

params = { "name" => "Bill" }

default.merge(params) # => {:age=>50, :name=>"Bill"}


I'm going through "Ruby Best Practices" right now, which touches on testing
in the first chapter. This seemed like a good opportunity to practice that
(while I eagerly await PragProg's RSpec book), so I'll include the tests I
wrote.

If anyone has relevant thoughts / criticisms, I welcome them (I don't
promise to agree, though). Is extending the object a wise approach? Are my
tests appropriate / follow good testing methodologies? Is there a better way
to implement anything I've done?




# file: symbolize_keys.rb

module SymbolizeKeys

# converts any current string keys to symbol keys
def self.extended(hash)
hash.each do |key,value|
if key.is_a?(String)
hash.delete key
hash[key] = value #through overridden []=
end
end
end

# assigns a new key/value pair
# converts they key to a symbol if it is a string
def []=(*args)
args[0] = args[0].to_sym if args[0].is_a?(String)
super
end

# returns new hash which is the merge of self and other hashes
# the returned hash will also be extended by SymbolizeKeys
def merge(*other_hashes , &resolution_proc )
merged = Hash.new.extend SymbolizeKeys
merged.merge! self , *other_hashes , &resolution_proc
end

# merges the other hashes into self
# if a proc is submitted , it's return will be the value for the key
def merge!( *other_hashes , &resolution_proc )

# default resolution: value of the other hash
resolution_proc ||= proc{ |key,oldval,newval| newval }

# merge each hash into self
other_hashes.each do |hash|
hash.each{ |k,v|
# assign new k/v into self, resolving conflicts with resolution_proc
self[k] = self.has_key?(k) ? resolution_proc[k,self[k],v] : v
}
end

self
end

end


--------------------------------------------------


# file: symbolize_keys_test.rb

require 'test/unit'
require 'symbolize_keys'

# this method was written by Gregory Brown
# and comes from
http://github.com/sandal/rbp/blob/7...e2432f0db7cb8/testing/test_unit_extensions.rb
module Test::Unit
# Used to fix a minor minitest/unit incompatibility in flexmock
AssertionFailedError = Class.new(StandardError)

class TestCase

def self.must(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end

end
end


class ExtendingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
}
@default.extend SymbolizeKeys
end

must "convert string keys to symbols when extended" do
assert_nil @default[ 'initially a string']
assert_equal @default[:'initially a string'] , 51
end

must "leave symbol keys as symbols" do
assert_equal @default[:age] , 50
end

must 'leave non symbols / strings as they are' do
assert_equal @default[/neither string nor symbol/] , 52
end

end


class SettingKeysWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = Hash.new.extend SymbolizeKeys

end

must "convert string keys to symbols" do
@default['foo'] = :bar
assert_equal @default[:foo] , :bar
end

must "leave symbol keys as symbols" do
@default[:foo] = :bar
assert_equal @default[:foo] , :bar
end

must 'leave non symbols / strings as they are' do
@default[/foo/] = :bar
assert_equal @default[/foo/] , :bar
end

end


class MergingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:name => 'Joe' ,
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
:'from default' => :default ,
}
@params1 = {
:name => 'Bill' ,
'alias' => 'Billy' ,
:'from params1' => :params1 ,
}
@params2 = {
'name' => 'Sam' ,
'alias' => 'Sammy' ,
12 => 'favourite number' ,
:'from params2' => :params2 ,
}
@default.extend SymbolizeKeys
end

must "retain new keys for merge" do
merged = @default.merge(@params2)
assert_equal merged[12] , 'favourite number'
end

must "retain new keys for merge!" do
@default.merge!(@params2)
assert_equal @default[12] , 'favourite number'
end

must "replace current values with new values for merge" do
merged = @default.merge(@params1)
assert_equal merged[:name] , 'Bill'
end

must "replace current values with new values for merge!" do
@default.merge!(@params1)
assert_equal @default[:name] , 'Bill'
end

must "not change original hash for merge" do
@default.merge(@params1)
assert_equal @default[:name] , 'Joe'
end

must "receive [key,oldval,newval] as params to block" do
h1 = {:no_conflict_1 => 1 , :conflict => 2 }.extend(SymbolizeKeys)
h2 = {:conflict => 3 , :no_conflict_2 => 4}
resolution_proc_params = nil
h1.merge(h2){ |*params| resolution_proc_params = params }
assert_equal resolution_proc_params , [:conflict,2,3]
end

must "replace resolve conflicts with block for merge" do
merged = @default.merge(@params1){ |key,oldval,newval| oldval }
assert_equal merged[:name] , 'Joe'

merged = @default.merge(@params1){ |key,oldval,newval| newval }
assert_equal merged[:name] , 'Bill'
end

must "replace resolve conflicts with block for merge!" do
@default.merge!(@params1){ |key,oldval,newval| oldval }
assert_equal @default[:name] , 'Joe'

@default.merge!(@params1){ |key,oldval,newval| newval }
assert_equal @default[:name] , 'Bill'
end

must "convert string keys to symbols for merge" do
merged = @default.merge(@params1)
assert_nil merged['alias']
assert_equal merged[ :alias] , 'Billy'
end

must "convert string keys to symbols for merge!" do
@default.merge!(@params1)
assert_nil @default['alias']
assert_equal @default[ :alias] , 'Billy'
end

must "merge with multiple hashes" do
merged = @default.merge(@params1,@params2)
assert_equal merged[:'from default'] , :default
assert_equal merged[:'from params1'] , :params1
assert_equal merged[:'from params2'] , :params2
end

must "merge! with multiple hashes" do
@default.merge!(@params1,@params2)
assert_equal @default[:'from default'] , :default
assert_equal @default[:'from params1'] , :params1
assert_equal @default[:'from params2'] , :params2
end

must "return object that is extended with SymbolizeKeys, for merge" do
merged = @default.merge(@params1)
assert_kind_of SymbolizeKeys , merged
end

must "not modify original hash, for merge" do
original = @default.dup
@default.merge(@params1,@params2)
assert_equal original , @default
end

end
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I'm trying to merge to hashes, one using symbols as keys (the defined
default values for my class) and the other using strings as keys
(taken from the params hash).

default = { :name => "Joe", :age => 50 }

params = { "name" => "Bill" }

new_hash = default.merge(params)

What's the Ruby way to handle this so that it overwrites :name with
"name"? Do I need to implement a stringify_keys or symbolize_keys
method like in Rails? I'd like to avoid using strings as the keys in
my default hash.

Any help greatly appreciated.

Stu
I decided that I wasn't happy with the tests, it should be able to access
the same object through either a string or a symbol (previously it just
turned everything into a symbol, then if you tried to access that object w/
the string, it would not find it).

So I overrode [] and has_key? also, and changed some of the tests.

Here is the updated version



# file: symbolize_keys.rb

module SymbolizeKeys

# converts any current string keys to symbol keys
def self.extended(hash)
hash.each do |key,value|
if key.is_a?(String)
hash.delete key
hash[key] = value #through overridden []=
end
end
end

#considers string keys and symbol keys to be the same
def [](key)
key = convert_key(key)
super(key)
end

#considers string keys and symbol keys to be the same
def has_key?(key)
key = convert_key(key)
super(key)
end

# assigns a new key/value pair
# converts they key to a symbol if it is a string
def []=(*args)
args[0] = convert_key(args[0])
super
end

# returns new hash which is the merge of self and other hashes
# the returned hash will also be extended by SymbolizeKeys
def merge(*other_hashes , &resolution_proc )
merged = Hash.new.extend SymbolizeKeys
merged.merge! self , *other_hashes , &resolution_proc
end

# merges the other hashes into self
# if a proc is submitted , it's return will be the value for the key
def merge!( *other_hashes , &resolution_proc )

# default resolution: value of the other hash
resolution_proc ||= proc{ |key,oldval,newval| newval }

# merge each hash into self
other_hashes.each do |hash|
hash.each{ |k,v|
# assign new k/v into self, resolving conflicts with resolution_proc
self[k] = self.has_key?(k) ? resolution_proc[k.to_sym,self[k],v] : v
}
end

self
end

private

def convert_key(key)
key.is_a?(String) ? key.to_sym : key
end

end


--------------------------------------------------


# file: symbolize_keys_test.rb

require 'test/unit'
require 'symbolize_keys'

# this method was written by Gregory Brown
# and comes from
http://github.com/sandal/rbp/blob/7...e2432f0db7cb8/testing/test_unit_extensions.rb
module Test::Unit
# Used to fix a minor minitest/unit incompatibility in flexmock
AssertionFailedError = Class.new(StandardError)

class TestCase

def self.must(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end

end
end


class ExtendingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
}
@default.extend SymbolizeKeys
end

must "convert string keys to symbols when extended" do
assert_equal @default[:'initially a string'] , 51
end

must "sym/str keys can access through either, but only one key" do
assert_equal @default[ 'initially a string'] , 51
assert_equal @default[:'initially a string'] , 51
assert_equal @default[:'initially a string'] , @default['initially a
string']
assert_equal @default.size , 3
end

must "leave symbol keys as symbols" do
assert_equal @default[:age] , 50
end

must 'leave non symbols / strings as they are' do
assert_equal @default[/neither string nor symbol/] , 52
end

end


class SettingKeysWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = Hash.new.extend SymbolizeKeys
end

must "enable access to strings through symbols" do
@default['foo'] = 'bar'
assert_equal @default[:foo] , 'bar'
assert_same @default[:foo] , @default['foo']
end

must "enable access to symbols through strings" do
@default[:foo] = 'bar'
assert_equal @default['foo'] , 'bar'
assert_same @default[:foo] , @default['foo']
end

must 'leave non symbols / strings as they are' do
@default[/foo/] = :bar
assert_equal @default[/foo/] , :bar
end

end


class MergingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:name => 'Joe' ,
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
:'from default' => :default ,
}
@params1 = {
:name => 'Bill' ,
'alias' => 'Billy' ,
:'from params1' => :params1 ,
}
@params2 = {
'name' => 'Sam' ,
'alias' => 'Sammy' ,
12 => 'favourite number' ,
:'from params2' => :params2 ,
}
@default.extend SymbolizeKeys
end

must "retain new keys for merge" do
merged = @default.merge(@params2)
assert_equal merged[12] , 'favourite number'
end

must "retain new keys for merge!" do
@default.merge!(@params2)
assert_equal @default[12] , 'favourite number'
end

must "replace current values with new values for merge" do
merged = @default.merge(@params1)
assert_equal merged[:name] , 'Bill'
end

must "replace current values with new values for merge!" do
@default.merge!(@params1)
assert_equal @default[:name] , 'Bill'
end

must "not change original hash for merge" do
@default.merge(@params1)
assert_equal @default[:name] , 'Joe'
end

must "receive [key,oldval,newval] as params to block" do
h1 = {:no_conflict_1 => 1 , :conflict => 2 }.extend(SymbolizeKeys)
h2 = {:conflict => 3 , :no_conflict_2 => 4}
resolution_proc_params = nil
h1.merge(h2){ |*params| resolution_proc_params = params }
assert_equal resolution_proc_params , [:conflict,2,3]
end

must "only invoke the resolution proc on conflicts" do
conflict_count = 0
conflicts = { :name => false , :alias => false }
@params1.extend(SymbolizeKeys).merge(@params2) do |k,ov,nv|
conflict_count += 1
conflicts[k] = true
end
assert_equal conflict_count , 2
assert conflicts[:name]
assert conflicts[:alias]
end

must "replace resolve conflicts with block for merge" do
merged = @default.merge(@params1){ |key,oldval,newval| oldval }
assert_equal merged[:name] , 'Joe'

merged = @default.merge(@params1){ |key,oldval,newval| newval }
assert_equal merged[:name] , 'Bill'
end

must "replace resolve conflicts with block for merge!" do
@default.merge!(@params1){ |key,oldval,newval| oldval }
assert_equal @default[:name] , 'Joe'

@default.merge!(@params1){ |key,oldval,newval| newval }
assert_equal @default[:name] , 'Bill'
end

must "convert string keys to symbols for merge" do
unique_keys = @default.keys.map{|k|k.to_s.to_sym} |
@params1.keys.map{|k|k.to_s.to_sym}
merged = @default.merge(@params1)
assert_equal merged['alias'] , 'Billy'
assert_equal merged[ :alias] , 'Billy'
assert_equal merged.size , unique_keys.size
end

must "convert string keys to symbols for merge!" do
unique_keys = @default.keys.map{|k|k.to_s.to_sym} |
@params1.keys.map{|k|k.to_s.to_sym}
@default.merge!(@params1)
assert_equal @default['alias'] , 'Billy'
assert_equal @default[ :alias] , 'Billy'
assert_equal @default.size , unique_keys.size
end

must "merge with multiple hashes" do
merged = @default.merge(@params1,@params2)
assert_equal merged[:'from default'] , :default
assert_equal merged[:'from params1'] , :params1
assert_equal merged[:'from params2'] , :params2
end

must "merge! with multiple hashes" do
@default.merge!(@params1,@params2)
assert_equal @default[:'from default'] , :default
assert_equal @default[:'from params1'] , :params1
assert_equal @default[:'from params2'] , :params2
end

must "return object that is extended with SymbolizeKeys, for merge" do
merged = @default.merge(@params1)
assert_kind_of SymbolizeKeys , merged
end

must "not modify original hash, for merge" do
original = @default.dup
@default.merge(@params1,@params2)
assert_equal original , @default
end

end
 
R

Robert Klemme

2009/11/3 Josh Cheek said:
Here is the updated version

Josh, I believe it's better to create a gist for code of that length
and paste the link only. That makes it easier to follow - especially
since you'll get code highlighting and versioning for free.

Kind regards

robert
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Josh, I believe it's better to create a gist for code of that length
and paste the link only. That makes it easier to follow - especially
since you'll get code highlighting and versioning for free.

Kind regards

robert
Hi, Robert, I always thought that too, but I was told I should put them in
the email in case the host site went down (
http://www.ruby-forum.com/topic/192987#841723 )

Is there some standard that I can follow, because there seems to be
conflicts of opinion regarding the best approach.
 
P

Paul Smith

Hi, Robert, I always thought that too, but I was told I should put them i= n
the email in case the host site went down (
http://www.ruby-forum.com/topic/192987#841723 )

Is there some standard that I can follow, because there seems to be
conflicts of opinion regarding the best approach.

A big difference here is that a pastie is only available through
pastie.org, a gist is a git repository that can be cloned.

--=20
Paul Smith
http://www.nomadicfun.co.uk

(e-mail address removed)
 
R

Robert Klemme

Hi, Robert, I always thought that too, but I was told I should put them i= n
the email in case the host site went down (
http://www.ruby-forum.com/topic/192987#841723 )

Well, it seems both sides have valid and good arguments. :) The only
footnote I'd put on that statement is: the real disadvantage of
pastebin and similar services is that they are typically intended for
short lived data only and may or actually do delete older content. A
gist on the other hand belongs to an account and lives as long as that
account lives or the gist is deleted. Of course, since this is
separate from the posting the issue remains that the posting can be
available and the code not...
Is there some standard that I can follow, because there seems to be
conflicts of opinion regarding the best approach.

No written standard as far as I know. So you're back to square and
have to decide yourself. :)

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
R

Rick DeNatale


It might be of interest to note that this code basically take the
opposite tack from the HashWithIndifferentAccess which is part of
ActiveSupport and therefore Rails.

HashWithIndifferentAccess (which I'll abbreviate to HWIA) converts
symbol keys to strings in the access methods ([], []= etc.) Contrary
to some people's thinking (including mine when I first encountered it)
the actual keys in the hash are strings, not symbols.

The trade-offs between the two approaches include:

1) Symbols once created can't be garbage collected. Since Rails uses
HWIA for things like the params hash, and the keys come from parts of
arbitrary client provided URIs this could be an opening for a DOS
attack which creates tons of useless extra parameters which result in
non-GCable symbols. Since HWIA uses strings, for the keys, keys
coming from query parameters and the like never get turned into
symbols. The only symbols will be those coming from the application
itself.

2) Using strings for the keys is slightly slower on access because
computing the hash value of a string is O(length of the string) while
the hash value of a symbol is computed once and is O(1) subsequently.
On the other hand this only becomes significant if the same key is
used to access the hash multiple times, since interning a string as a
symbol requires computing the hash of the string anyway.

In practice, and particularly for the typical usage in Rails apps, I
doubt that there's any real effect on performance from storing strings
rather than symbols for the keys. The main advantage of HWIA is that
it allows you to save a keystroke

params[:id]
vs
params['id']

and some think the former looks a little bit nicer. Although this is
no doubt a potential source of controversy. I for one have a slight
preference for the :id form, but that might be the result of Stockholm
syndrome having worked on so many Rails apps.
--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
D

David A. Black

Hi --

params[:id]
vs
params['id']

and some think the former looks a little bit nicer. Although this is
no doubt a potential source of controversy. I for one have a slight
preference for the :id form, but that might be the result of Stockholm
syndrome having worked on so many Rails apps.

When it involves Rails, we call it "Copenhagen Syndrome" :)


David

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)
 
R

Rick DeNatale

Hi --

=A0params[:id]
vs
=A0params['id']

and some think the former looks a little bit nicer. =A0Although this is
no doubt a potential source of controversy. =A0I for one have a slight
preference for the :id form, but that might be the result of Stockholm
syndrome having worked on so many Rails apps.

When it involves Rails, we call it "Copenhagen Syndrome" :)

I dunno, there IS a bit of Chicago mixed in.

I have to repeat the story I told at the end of my RubyConf
presentation last year, about the classification of OO languages by
geographic origin:

Smalltalk (which came from Xerox Parc in California) is the surfer's
language, Smalltalk programmers wear baggy swimsuits, and hang loose.

ObjectiveC (which came from an ITT lab in Connecticut) was a "New
England Yankee" language, it did just what it had to do and nothing
more. (Not sure this is still true)

Eiffel (the brainchild of Bertrand Meyer) was/is "quintessentially French=
")

and

C++ (from Bjarne Stroustrup, a compatriot of DHH, and who at the
time was at Bell/ATT labs) is like Haagen Dazs Ice Cream, you think
it's from Scandanavia, but it's really an industrial product from New
Jersey!


--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top