ruby language - a quick tour
TRANSCRIPT
![Page 2: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/2.jpg)
TOC
1.Overview
2.Installation & IDE
3.Structure & Execution
4.Data types & Objects
5.Expression & Operators
6.Statement & Control Structure
7.Method,Block,Proc,Lambda
8.Class & Module
9.Reflection & Metaprogramming
10.Gems & Rake
11.Books & Links & Persons
![Page 3: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/3.jpg)
Overview - Birthstone
January - Garnet February - Amethyst
March - Aquamarine April- Diamond
May - Emerald June - Pearl
July - Ruby August - Peridot
September - Sapphire October- Tourmaline
November - Yellow Topaz December - Blue Topaz
![Page 4: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/4.jpg)
Overview
•Pure OO,Everything is Object,Yes!Everything
5.times{|i|puts i}
•Highly Dynamic
class HelloWorld;end
HelloWorld.send(:define_method,:say){puts "HelloWorld!"}
•Excute while parsing
class Klass
puts "Hello"
end
•Cross Platform
Windows,Linux,Unix,Mac,...
•Open Source
http://www.ruby-lang.org/en/downloads/
![Page 5: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/5.jpg)
To C++ Programmer
•No variable declaration
•ONLY false & nil are falsy
•'constructor' is called 'initialize'
•All methods are always 'virtual'
•First letter is important: $,@,@@,Klass,i
•Member variables are private
•Use 'self' instead of 'this'
•?,! ARGV.size.zero? obj.nil? str.strip!
•Single Inheritance, Mixin to help
•Parentheses & semicolons are usually optional
•Classes are not sealed!can be reopen
•No Generic/Template
•Iteration are easy
•There's only two container: Array,Hash
•Almost everything is an expression
![Page 6: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/6.jpg)
Installation & Implementations
•Windows
One-Click Installer, Zip
•Ubuntu
sudo apt-get install ruby1.8/ruby1.9
sudo apt-get install irb1.8/irb1.9
•Mac
port install ruby
•Implementations
c ruby, JRuby, IronRuby
•Version
ruby1.8 ruby1.9
![Page 7: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/7.jpg)
"IDE"
•Windows/Linux
Eclipse+RDT, *RubyInSteel*,
NetBeans,IDEA,RadRails,3rdRails
Notepad++/Ultraedit/Editplus+irb
•Mac
Textmate
![Page 8: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/8.jpg)
Structure & Execution
#!/usr/bin/ruby shebang comment
# -*- coding: utf-8 -*- coding comment
require 'erb' loading library
class Klass define class
include Enumerable include module
def k
require 'socket' loading library
...
end
end
if $0 == __FILE__
o=Klass.new.k create object
DATA.read
end
__END__
embeded string embeded string resource
![Page 9: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/9.jpg)
Data types - Number
dec = 123 dec.class==Fixnum
million = 1_000_000
hex = 0xFF
binary = 0b0010
octal = 0377
float = 1_000.0 float.class==Float
2**1024 = 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477
322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302
219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239
947245938479716304835356329624224137216
(2**1024).class==Bignum
Conversion:
other_type.to_i
other_type.to_f
![Page 10: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/10.jpg)
Data types - Text
name = 'Jack'
name = %q[Jack]
name = %q{Jack}
name = %q!Jack!
name = %q|Jack|
name = %q+Jack+
name = %q-Jack-
name = %q=Jack=
name = %q,Jack,
name = %q.Jack.
name = %q/Jack/
name = %q\Jack\
...
all symbol on
keyboard!
variable_name = %PREFIX SYM content SYM
SYM = [~`!@#$%^&*_-+=|\:;"',.?/] + [] {} ()
No Escape!
PREFIX = [%q,%Q,%x,%r,%s]
Backtick command Execute
%x[notepad] `notepad`
Regular expression
%r[hello] /hello/
Array literal
%w[hello world] => ['hello','world']
%W[hello world] => ["hello","world"]
Symbol literal
%s[symbol]
![Page 11: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/11.jpg)
Data types - Text
a = "Hello" + "World" => "HelloWorld"
a.sub("World","There") => "HelloThere" sub!
a.gsub(/o/,"*") => "Hell*W*rld" gsub!
a.chomp("d") => "HelloWorl"
a.center(15,"=") => "===HelloWorld==="
a.ljust(16) => "HelloWorld "
a.rjust(16) => " HelloWorld"
a.capitalize => "Helloworld"
a.upcase => "HELLOWORLD"
a.downcase => "helloworld"
a.reverse => "dlroWolleH"
a.swapcase => "hELLOwORLD"
a.length => 10
a.size => 10
a.count('o') => 2
a.include?('o') => true
"BBBBBaaaaaadddd".squeeze => "Bad"
![Page 12: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/12.jpg)
Data types - Text
a = "Hello World!"
a[index] a[0] => "H"
a[from,count] a[0,5] => "Hello"
a[range] a[0...-1] => "Hello World"
a[-6..-1] => "World!"
a[regex] a[/W.*?!/] => "World!"
a.split(/[ !]/) => ["Hello", "World"]
a.split.join(",").concat("~") => "Hello,World!~"
a.sub(" ",",") << "~" => "Hello,World!~"
Conversion:
everything.to_s
![Page 13: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/13.jpg)
Data types - Here Document
str = <<-STR.gsub("world","there")
hello world
hello world hello world hello world
hello world
hello world
hello world
STR
cpp_tepmlate = <<-CPPTPL
class <%=name%> : public <%=basename>
{
public:
<% methods.join("\n") %>
};
CPPTPL
result = ERB.new(cpp_template).result(binding)
![Page 14: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/14.jpg)
Data types - Array
As Vector:a = [1,"h",3.14]
n = %w[1 2 3] => ['1','2','3']
e = %W[\s \t \r \n] => ["\s","\t","\r","\n"]
empty = Array.new=> []
nils = Array.new(3) => [nil,nil,nil]
[0]*3 => [0,0,0]
copy = Array.new(nils) => [nil,nil,nil]
Array.new(3){|i|i} => [0,1,2]
As Set:[1,2]+[3,4] => [1,2,3,4]
[1,2,3,4] - [1,2] => [3,4]
[1,2] | [2,3] => [1,2,3]
[1,2] & [2,3] => [2]
As Stack:[1,3].push(3,4) => [1,2,3,4]
[1,2,3].pop => [1,2]
As Queue:[1,2,3,4].shift => [2,3,4]
[3,4,5].unshift(2) => [2,3,4,5]
![Page 15: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/15.jpg)
Data types - Array
As List:a = [1,2]
a.concat(3) => [1,2,3]
a << 1 => [1,2,3,1]
a.insert(a.index(2),'a') => [1,'a',2,3,1]
a.delete("a") => [1,2,3,1]
a.delete(1) => [2,3]
[1,2,3,4].reject{|e|e%2==0} => [1,3]
As Sequence:a = ('a'..'c').to_a => ['a','b','c']
a[0,1] => ['a']
a[0..2] => ['a',b','c']
a[0..-2] => ['a','b']
Iteration:a = [1,2,3]
a.each{|e| print(e)} => 123
a.each_with_index{|e,i| print("#{i}:#{e},")} => 0:1,1:2,2:3,
a.inject(0){|s,e|s+=e} => 6
for i in a
print(i)
end => 123
![Page 16: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/16.jpg)
Data types - Array
Conversion:
other_type.to_a
(1..10).to_a => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
('a'..'c').to_a => ['a','b','c']
('ab'..'bf').to_a => ["ab", "ac", "ad", "ae", "af", "ag",
"ah", "ai", "aj", "ak", "al", "am", "an", "ao", "ap", "aq",
"ar", "as", "at", "au", "av", "aw", "ax", "ay", "az", "ba",
"bb", "bc", "bd", "be", "bf"]
{:a=>1,:b=>2}.to_a => [[:b, 2], [:a, 1]]
![Page 17: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/17.jpg)
Data types - Hash
As HashTable:
j = {"name"=>"jack",:age=>25}
j["gender"] = "male"
Iteration:
j.each{|k,v| ... }
for k,v in j
...
end
Conversion:
j.to_a => [["name", "jack"], [:age, 25]]
j.to_s => "namejackage25"
![Page 18: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/18.jpg)
Data types - Range
1..10 => 1..10 (iterable, to_a)
1.0..1.5 => 1.0..1.5 (un-iterable,no to_a)
cold_war => 1945..1989
letter = 'a'..'z' => 'a'..'z' (to_a)
Iteration:
n = 3
(1..n).each{|i|print(i)} => 123
for i in (1..n)...
Membership test:
(1..100).member?(50) => true
(1..100).include?(50) => true
(1..100).cover?(2..50) => true (1.9)
![Page 19: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/19.jpg)
Data types - Symbols
:symbol => :symbol
:name => :name
:'with quote' => :'with quote'
:"hello #{:name}" => :"hello name"
Conversion:
:name.to_s => "name"
"name".to_sym => :name
"name".intern => :name
"name".id2name => :name
Symbol Table:
symbols are entris in symbol table of interpreter
symbols have an index in symbol table while string have not
![Page 20: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/20.jpg)
Data types - True,False,Nil
Ruby has NO Boolean type!!!
true.class => TrueClass
false.class => FalseClass
nil.class => NilClass
true.nil? => false
false.nil? => false
nil.nil? => true
Only false&nil are falsy,everything else are truthy:
puts "hi" if nil => nil
puts "hi" if false => nil
puts "hi" if true => "hi"
puts "hi" if 0 => "hi"
puts "hi" if 0.0 => "hi"
puts "hi" if "" => "hi"
puts "hi" if [] => "hi"
puts "hi" if {} => "hi"
![Page 21: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/21.jpg)
Expression & Operators
Class Variable @@[_a-z][_a-zA-Z0-9]*
Instance Variable @[_a-z][_a-zA-Z0-9]*
Global Variable $[_a-z][_a-zA-Z0-9]*
Local Variable [_a-z][_a-zA-Z0-9]*
Constant [A-Z][_a-zA-Z0-9]*
class Klass;end Klass is constant!
![Page 22: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/22.jpg)
Expression & Operators
Assignment:
a = [1,2,3]
Parallel assignment:
x,y,z,u = a -> x=1,y=2,z=3,u=nil
u,x,y,z = 4,*a -> u=4,x=1,y=2,z=3
Default operator:
port = port || 80 -> if port==nil then port=90
port ||= 80
Guard operator:
port = port && 8080 -> if port!=nil then port=8080
port &&= 8080
Equality:
==,!= -> equality operator
=~,!~ -> pattern-matching operator
"hello"=~/[a-z]+/
![Page 23: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/23.jpg)
Expression & Operators
Boolean Operator:
&& and
|| or
! not
Conditional:
msg = "you have #{n} #{n==1? "message" : "messages"}"
defined? operator:
y = f(x) if defined? f
![Page 24: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/24.jpg)
Statement & Control Structure
Conditional:
[result = ]if expression [then]
end
[result = ]if expression
elsif
else
end
if/unless as statment modifer:
... if expression
... unless expression
![Page 25: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/25.jpg)
Statement & Control Structure
case:
result = case expression
when ...,... then ...
next
when ...,... then ...
break
else ...
end
Loops:(can be statement modifer)
while expression do
...
end
until expression do
...
end
for var in collection do
...
end
![Page 26: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/26.jpg)
Method,Block,Proc,Lambda
Method Invocation:
puts "hello" -> self,puts,"hello"
Math.sqrt(2) -> Math(Module),sqrt,2
"a".size -> "a",size
[1,2,3].each{|e|...}-> [1,2,3],each, {|e| ...}
object.method(arg1,arg2,...){|a1,a2,..|...}
def object.method(*args,&block)
arg1,arg2,arg3 = *args
block.call(1,2,3...)
end
receiver.send(:private_message,arg1,arg2,...)
![Page 27: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/27.jpg)
Method,Block,Proc,Lambda
Block:
One of the most powerfull feature of Ruby!
[1,2,3].each {|i| ... }
[1,2,3].each do |i|
...
end
def say_hello
print("Jack said:")
yield 1,2 if block_given?
end
say_hello{|a,b| print "hello",a,b} => Jack said:hello12
b = Proc.new{|a,b|print "hello",a,b}
l = lambda{|a,b|print "hello",a,b}
say_hello(&b)
say_hello(&l)
![Page 28: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/28.jpg)
Class
The class keyword in Ruby is more like a scope operator than a
class declaration. the core job is to move you in the context of the
class, where you can define methods.
Class Constructor:
Class Person
def initialize(name)
@name = name
end
end
Person.new("Jack").name =>
undefined method `name' for #<Person:0x28406f0 @name="Jack"> (NoMethodError)
class Person
def name;return @name;end
def name=(value);@name=value;end
end
class Person
attr_reader :name
end
attr_writer , attr_reader , attr_accessor
![Page 29: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/29.jpg)
Class
Class method(Static function):Class Person
def Person.class_func
end
def self.class_func
end
end
Method of object(singleton_method):j = Person.new("jack")
def j.say_hi
puts "hi, i'am jack"
end
Define operators:class Person
def +();end +-*/
def [];end
def <=>;end
end
j
Person
---------
methods
j
Person
---------
methods
???
---------
say_hi
![Page 30: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/30.jpg)
Class
Method Visibility:class Person
def initialize(name,age)
@name,@age = name,age
end
def introduce
"my name is #{@name},i'm #{@age} years old"
end
protected #every method below will be protected!
def privacy;"my nick name is jacky";end
def secret;end
end
j = Person.new("jack",24)
puts j.introduce
puts j.privacy
class Person
private :privacy #Moduel.private/protected/public
private :secret
end
class Person
private *[:privacy,:secret] #OK!
end
Person.send(:private,:privacy,:secret) #OK!
every time you call a private method,
it must be on the implicit receiver—self.
![Page 31: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/31.jpg)
Class
Make class on-the-fly:Person = Class.new
[:name,:age].each do |attr|
Person.send(:attr_accessor,attr)
end
Person.send(:define_method,:hi) do
print "I'm #{@name}"
end
class Person
def bye
"bye bye!"
end
end
j = Person.new
j.name = "jack"
j.hi => "I'm jack"
j.bye => "bye bye!"
![Page 32: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/32.jpg)
Class
Inheritance:class Person;end
class Student < Person
end
s = Student.new
s.class => Student
s.class.class => Class
s.class.superclass => Person
Person.superclass => Object
Student.ancestors => [Student, Person, Object, Kernel]
![Page 33: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/33.jpg)
Module
As namespace:
module Util
PI = 3.14
def area
self.radius**2 * PI
end
def Util.pi2
PI**2
end
end
PI2 = Util.pi2 => 9.8596
As mixin:
class Shape;end
class Pie < Shape
include Util
def initialize(r);@radius=r;end
def radius;@radius;end
end
Pie.new(2).area => 12.56
template<class T>
class Util
{
public:
double area() {
float r = ((T*)this)->radius;
return r*r*3.14;
}
};
class Shape{};
class Pie : public Util<Pie>, public Shape
{
public:
float radius;
Pie():radius(2){}
};
#include <iostream>
void main()
{
using namespace std;
Pie p;
cout<<p.area()<<endl;
}
![Page 34: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/34.jpg)
Module
Loading and Requring:
require 'win32/clipboard'
load("c:/a.rb")
load("c:/extension.dll")
load("/home/jack/rb_extension/ext.so")
$LOAD_PATH ($:)
$LOAD_PATH.class => Array
$LOAD_PATH << "d:/my_library"
![Page 35: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/35.jpg)
Reflection & Metaprogramming
Methods:
object.methods
object.private_methods(false)
object.protected_methods
object.public_methods
object.singleton_methods
Variables:
object.instance_variable_defined?
object.instance_variable_get
object.instance_variable_set
object.instance_variables
object.class.class_variables
Parents:
object.superclass
object.ancestors
![Page 36: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/36.jpg)
Reflection & Metaprogramming
*eval (evaluate):
eval(string[,bindings])
Klass.class_eval(&block)
Mod.mod_eval(&block)
object.instance_eval(&block)
def object.hi
puts "hi"
end
object.instance_eval do
def hi
puts "hi"
end
end
alias:
class Klass
def hi;puts("hi");end
alias :greeting,:hi
end
alias :old_method :method
def method(*args,&block)
...
old_method(*args,&block)
...
end
![Page 37: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/37.jpg)
Reflection & Metaprogramming
method_missing:
class Klass
def method_missing(m)
m.to_s
end
end
k = Klass.new
k.any_thing! => "any_thing!"
const_missing:class Unicode
def self.const_missing(name)
if name.to_s=~/^U[0-9a-fA-F]{4,5}|10[0-9a-fA-F]{4}$/
codepoint = $1.to_i(16)
utf8 = [codepoint].unpack("U").freeze
const_set(name,utf8)
else
raise NameError,"Uninitialized Constant : Unicode::#{name}"
end
end
end
copyright = Unicode::U00A9 (©)
![Page 38: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/38.jpg)
Gems & Rake
Rake:
Make for ruby - Rake
c - make
java - ant
...
Gems: (Jar,Egg)
gem install gem_name
gem uninstall gem_name
gem list --local
parseOpts-0.0.2 net-ssh-2.0.14 mysql-2.8.1-x86 log4r-1.0.5
nokogiri-1.4.1-x86 win32-api-1.3.0 oniguruma-1.1.0 RubyInline-3.8.4
rubyzip-0.9.1 sqlite-2.0.1 Redmine RMagic
wxruby-2.0.1-x86 coderay-0.9.5 ffi-0.5.4-x86 flvtool2-1.0.6
pdf-toolkit-0.5.0 opencv-0.0.6 rubyrss-1.1 *Rails*
FOX,Qt,Tcl/Tk Hpricot .........
![Page 39: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/39.jpg)
Books & Links & Persons
http://www.ruby-lang.org
http://rubyforge.net
https://github.com/languages/Ruby
http://www.sf.net
http://confreaks.net/
http://confreaks.net/presenters/137-dave-thomas
http://www.rubyconf.org/
![Page 40: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/40.jpg)
Books & Links & Persons
Programming Ruby by Dave Thomas
The Ruby Programming Language
by David Flanagan and Yukihiro Matsumoto
The Ruby Way 3nd
by Hal Edwin Fulton
Design Patterns in Ruby by Russ Olsen
Ruby Cookbook
by Lucas Carlson and Leonard Richardson
Metaprogramming Ruby by Paolo Perrotta
![Page 41: Ruby Language - A quick tour](https://reader034.vdocuments.site/reader034/viewer/2022042715/55817f6fd8b42a132d8b4851/html5/thumbnails/41.jpg)
Books & Links & Persons
YUKIHIRO
MATSUMOTO
DAVE THOMAS DAVID
HEINEMEIER
HANSSON