DSL or NoDSL?


José Valim       blog.plataformatec.com   @josevalim
DSL or NoDSL?


   ID                    blog              twitter

José Valim       blog.plataformatec.com   @josevalim
A brief introduction...




José Valim          blog.plataformatec.com   @josevalim
A short story...




José Valim      blog.plataformatec.com   @josevalim
José Valim   blog.plataformatec.com   @josevalim
mail_form




José Valim    blog.plataformatec.com   @josevalim
Contact form recipe




José Valim        blog.plataformatec.com   @josevalim
Contact form recipe

               ✦   Controller (20 LOC)




José Valim            blog.plataformatec.com   @josevalim
Contact form recipe

               ✦ Controller (20 LOC)
               ✦ Model (30 LOC)




José Valim          blog.plataformatec.com   @josevalim
Contact form recipe

               ✦ Controller (20 LOC)
               ✦ Model (30 LOC)

               ✦ View (30 LOC)




José Valim          blog.plataformatec.com   @josevalim
Contact form recipe

               ✦ Controller (20 LOC)
               ✦ Model (30 LOC)

               ✦ View (30 LOC)



               ✦   Mailer (15 LOC)



José Valim            blog.plataformatec.com   @josevalim
Contact form recipe

               ✦ Controller (20 LOC)
               ✦ Model (30 LOC)

               ✦ View (30 LOC)



               ✦ Mailer (15 LOC)
               ✦ View (20 LOC)




José Valim          blog.plataformatec.com   @josevalim
Contact form recipe

               ✦ Controller (20 LOC)
               ✦ Model (30 LOC)

               ✦ View (30 LOC)



               ✦ Mailer (15 LOC)
               ✦ View (20 LOC)




José Valim          blog.plataformatec.com   @josevalim
Let’s create a DSL!




José Valim        blog.plataformatec.com   @josevalim
Domain Speci c Language




José Valim       blog.plataformatec.com   @josevalim
class ContactForm < MailForm::Base
         to "jose.valim@plataformatec.com.br"
         from "contact_form@app_name.com"
         subject "Contact form"

         attributes :name, :email, :message
       end

       ContactForm.new(params[:contact_form]).deliver




José Valim              blog.plataformatec.com          @josevalim
from { |c| "#{c.name} <#{c.email}>" }




José Valim               blog.plataformatec.com      @josevalim
to :author_email

             def author_email
               Author.find(self.author_id).email
             end




José Valim              blog.plataformatec.com     @josevalim
headers { |c|
               { "X-Spam" => "True" } if c.honey_pot
             }




José Valim                blog.plataformatec.com       @josevalim
class ContactForm < MailForm::Base
               to :author_email
               from { |c| "#{c.name} <#{c.email}>" }
               subject "Contact form"

              headers { |c|
                { "X-Spam" => "True" } if c.honey_pot
              }

              attributes :name, :email, :message

               def author_email
                 Author.find(self.author_id).email
               end
             end



José Valim                 blog.plataformatec.com       @josevalim
Issues




José Valim   blog.plataformatec.com   @josevalim
Issues


             ✦   Lacks simplicity




José Valim                 blog.plataformatec.com   @josevalim
Issues


             ✦ Lacks simplicity
             ✦ Reduces programmer happiness




José Valim             blog.plataformatec.com   @josevalim
NoDSL!


José Valim   blog.plataformatec.com   @josevalim
From: GitHub <noreply@github.com>
         To: jose.valim@plataformatec.com.br
         Message-Id: <4bfe3e812c22fc17d@github.com>
         Subject: [GitHub] someone commented on a commit
         Mime-Version: 1.0
         Content-Type: text/plain; charset=utf-8

         MESSAGE BODY




José Valim               blog.plataformatec.com       @josevalim
class ContactForm < MailForm::Base
               attributes :name, :email, :message

               def headers
                 {
                   :to => author_email,
                   :from => "#{name} <#{email}>",
                   :subject => "Contact form"
                 }
               end

               def author_email
                 Author.find(self.author_id).email
               end
             end



José Valim               blog.plataformatec.com      @josevalim
It provides a nice DSL




José Valim          blog.plataformatec.com   @josevalim
It relies on a simple contract




José Valim       blog.plataformatec.com   @josevalim
José Valim   blog.plataformatec.com   @josevalim
Rack




José Valim   blog.plataformatec.com   @josevalim
class RackApp
   def self.call(env)
     [200, { "Content-Type" => "text/html"}, ["Hello"]]
   end
 end




José Valim           blog.plataformatec.com       @josevalim
RackApp = Rack::AppBuilder.new do |env|
               status 200
               headers "Content-Type" => "text/html"
               body ["Hello"]

               after_return {
                 # do something
               }
             end




José Valim                blog.plataformatec.com       @josevalim
Rake and Thor




José Valim      blog.plataformatec.com   @josevalim
task :process do
               # do some processing
             end

             namespace :app do
               task :setup do
                 # do some setup
                 #
               end
             end

             rake app:setup




José Valim            blog.plataformatec.com   @josevalim
task :process do
               # do some processing
             end

             namespace :app do
               task :setup do
                 # do some setup
                 Rake::Task[:process].invoke
               end
             end

             rake app:setup




José Valim            blog.plataformatec.com   @josevalim
class Default < Thor
               def process
                 # do some processing
               end
             end

             class App < Thor
               def setup
                 # do some setup
                 #
               end
             end

             thor app:setup




José Valim        blog.plataformatec.com   @josevalim
class Default < Thor
               def process
                 # do some processing
               end
             end

             class App < Thor
               def setup
                 # do some setup
                 Default.new.process
               end
             end

             thor app:setup




José Valim        blog.plataformatec.com   @josevalim
Rspec and Test::Unit




José Valim         blog.plataformatec.com   @josevalim
# Rspec
             describe User do
               it "should be valid" do
                 User.new(@attributes).should be_valid
               end
             end

             # Test::Unit
             class UserTest < Test::Unit::Case
               def test_user_validity
                 assert User.new(@attributes).valid?
               end
             end




José Valim                 blog.plataformatec.com        @josevalim
A nal story...




José Valim      blog.plataformatec.com   @josevalim
class UsersController < ApplicationController
                  def index
                    @users = User.all


                      respond_to do |format|
                        format.html # index.html.erb
                        format.xml { render :xml => @users }
                    end
                  end


                  def show
                      @user = User.find(params[:id])


                      respond_to do |format|
                        format.html # show.html.erb
                        format.xml   { render :xml => @user }
                      end
                  end


                  def new
                    @user = User.new


                      respond_to do |format|
                        format.html # new.html.erb
                        format.xml { render :xml => @user }
                    end
                  end


                  def edit
                    @user = User.find(params[:id])
                  end




             Scaffold Controller
                  def create
                      @user = User.new(params[:user])


                      respond_to do |format|
                        if @user.save
                          format.html { redirect_to(@user, :notice => 'User was successfully created.') }
                          format.xml { render :xml => @user, :status => :created, :location => @user }
                        else
                          format.html { render :action => "new" }
                          format.xml   { render :xml => @user.errors, :status => :unprocessable_entity }
                        end
                    end
                  end


                  def update
                      @user = User.find(params[:id])


                      respond_to do |format|
                        if @user.update_attributes(params[:user])
                          format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
                          format.xml { head :ok }
                        else
                          format.html { render :action => "edit" }
                          format.xml   { render :xml => @user.errors, :status => :unprocessable_entity }
                        end
                    end
                  end


                  def destroy
                      @user = User.find(params[:id])
                      @user.destroy


                      respond_to do |format|
                        format.html { redirect_to(users_url) }
                        format.xml { head :ok }
                    end
                  end
                end




José Valim                      blog.plataformatec.com                                                      @josevalim
class UsersController < ApplicationController
               restful!
             end




José Valim                    blog.plataformatec.com         @josevalim
class UsersController < ApplicationController
               restful!

              create do
                before {
                  # do something before
                }

                 success.flash "This is the message"
               end

               create.after do
                 # do something after
               end
             end




José Valim                    blog.plataformatec.com         @josevalim
InheritedResources




José Valim        blog.plataformatec.com   @josevalim
class UsersController < InheritedResources::Base
         def create
           # do something before
           flash[:success] = "This is the message"
           super
           # do something after
         end
       end




José Valim              blog.plataformatec.com       @josevalim
Bene ts




José Valim   blog.plataformatec.com   @josevalim
Bene ts

             ✦   Simple




José Valim                blog.plataformatec.com   @josevalim
Bene ts

             ✦ Simple
             ✦ Less code to maintain




José Valim              blog.plataformatec.com   @josevalim
Bene ts

             ✦ Simple
             ✦ Less code to maintain

             ✦ More time to focus on important

               features




José Valim             blog.plataformatec.com    @josevalim
Bene ts

             ✦ Simple
             ✦ Less code to maintain

             ✦ More time to focus on important

               features
             ✦ Just Ruby™




José Valim             blog.plataformatec.com    @josevalim
José Valim   blog.plataformatec.com   @josevalim
ORM Callbacks




José Valim      blog.plataformatec.com   @josevalim
# DSL
 class User < ActiveRecord::Base
   after_save :deliver_notification, :unless => :admin?
 end

 # NoDSL
 class User < ActiveRecord::Base
   def save(*)
     if super
       deliver_notification unless admin?
     end
   end
 end




José Valim            blog.plataformatec.com      @josevalim
DSL or NoDSL?


José Valim       blog.plataformatec.com   @josevalim
Thank you!




José Valim    blog.plataformatec.com   @josevalim
Questions? Feedback?
                  Opinions?



José Valim         blog.plataformatec.com   @josevalim
Questions? Feedback?
                  Opinions?


   ID                      blog              twitter

José Valim         blog.plataformatec.com   @josevalim

More Related Content

PDF
[Practical] Functional Programming in Rails
PPTX
Testing ASP.net Web Applications using Ruby
ODP
Scti 2011 minicurso jquery
PPT
CefaléIa HipertensãO
KEY
Ruby For Web Development
PDF
Making Rails Really restful
PDF
Rails vs Web2py
ZIP
Rails 3 (beta) Roundup
[Practical] Functional Programming in Rails
Testing ASP.net Web Applications using Ruby
Scti 2011 minicurso jquery
CefaléIa HipertensãO
Ruby For Web Development
Making Rails Really restful
Rails vs Web2py
Rails 3 (beta) Roundup

Similar to DSL or NoDSL - Euruko - 29may2010 (20)

PDF
Rails 3 Beautiful Code
PDF
Get shit done
ZIP
REST: Theory vs Practice
PDF
Rails2 Pr
KEY
Something something rack
PDF
Connecting the Worlds of Java and Ruby with JRuby
PDF
Doing REST Right
PDF
RubyEnRails2007 - Dr Nic Williams - Keynote
PDF
Rest And Rails
KEY
Ruby For Startups
KEY
More to RoC weibo
KEY
Routes Controllers
PDF
Rails 4.0
PDF
api-driven-development.pdf
KEY
Rails Antipatterns | Open Session with Chad Pytel
KEY
You're Doing It Wrong
KEY
You're Doing It Wrong
PDF
Ruby talk romania
PDF
Finding Restfulness - Madrid.rb April 2014
PDF
Ruby on Rails at PROMPT ISEL '11
Rails 3 Beautiful Code
Get shit done
REST: Theory vs Practice
Rails2 Pr
Something something rack
Connecting the Worlds of Java and Ruby with JRuby
Doing REST Right
RubyEnRails2007 - Dr Nic Williams - Keynote
Rest And Rails
Ruby For Startups
More to RoC weibo
Routes Controllers
Rails 4.0
api-driven-development.pdf
Rails Antipatterns | Open Session with Chad Pytel
You're Doing It Wrong
You're Doing It Wrong
Ruby talk romania
Finding Restfulness - Madrid.rb April 2014
Ruby on Rails at PROMPT ISEL '11
Ad

More from Plataformatec (16)

PDF
Do your own hacking evening - RubyConf UR
PDF
Product Owner - Simples como dizem? - Agile Tour 2011
PDF
As reais razões do porque eu devo ser Ágil - Agile Tour São Paulo
KEY
Código Saudável => Programador Feliz - Rs on Rails 2010
KEY
Writing your own programming language to understand Ruby better - Euruko 2011
KEY
Railties - Ruby Masters Conf - 26Feb2011
PDF
Rails 2.3, 3.0 and 3.1 - RubyConfBR - 26oct2010
KEY
Rails 3 - The Developers Conference - 21aug2010
KEY
CRUDing Open Source - WhyDay - 19aug2010
KEY
Rails 3 - RS on Rails - 21aug2010
PDF
O que há de novo no Rails 3 - Ruby on Rails no Mundo Real - 23may2010
PDF
Project Rescue - Oxente Rails - 05aug2010
PDF
The Plafatorma Way - Oxente Rails - 05aug2010
KEY
Classificação de textos - Dev in Sampa - 28nov2009
PDF
Devise - RSLA - 13oct2009
PDF
Thor - RSLA - 13oct2009
Do your own hacking evening - RubyConf UR
Product Owner - Simples como dizem? - Agile Tour 2011
As reais razões do porque eu devo ser Ágil - Agile Tour São Paulo
Código Saudável => Programador Feliz - Rs on Rails 2010
Writing your own programming language to understand Ruby better - Euruko 2011
Railties - Ruby Masters Conf - 26Feb2011
Rails 2.3, 3.0 and 3.1 - RubyConfBR - 26oct2010
Rails 3 - The Developers Conference - 21aug2010
CRUDing Open Source - WhyDay - 19aug2010
Rails 3 - RS on Rails - 21aug2010
O que há de novo no Rails 3 - Ruby on Rails no Mundo Real - 23may2010
Project Rescue - Oxente Rails - 05aug2010
The Plafatorma Way - Oxente Rails - 05aug2010
Classificação de textos - Dev in Sampa - 28nov2009
Devise - RSLA - 13oct2009
Thor - RSLA - 13oct2009
Ad

Recently uploaded (20)

PDF
STKI Israel Market Study 2025 version august
PDF
NewMind AI Weekly Chronicles – August ’25 Week III
PPTX
Chapter 5: Probability Theory and Statistics
PDF
A proposed approach for plagiarism detection in Myanmar Unicode text
PPT
What is a Computer? Input Devices /output devices
PDF
Zenith AI: Advanced Artificial Intelligence
PPTX
TEXTILE technology diploma scope and career opportunities
PPTX
Configure Apache Mutual Authentication
PPT
Galois Field Theory of Risk: A Perspective, Protocol, and Mathematical Backgr...
PPTX
MicrosoftCybserSecurityReferenceArchitecture-April-2025.pptx
PDF
UiPath Agentic Automation session 1: RPA to Agents
PPT
Geologic Time for studying geology for geologist
PDF
Produktkatalog für HOBO Datenlogger, Wetterstationen, Sensoren, Software und ...
PDF
Flame analysis and combustion estimation using large language and vision assi...
DOCX
search engine optimization ppt fir known well about this
PDF
Hybrid horned lizard optimization algorithm-aquila optimizer for DC motor
PDF
How ambidextrous entrepreneurial leaders react to the artificial intelligence...
PDF
A review of recent deep learning applications in wood surface defect identifi...
PDF
sustainability-14-14877-v2.pddhzftheheeeee
PPTX
Final SEM Unit 1 for mit wpu at pune .pptx
STKI Israel Market Study 2025 version august
NewMind AI Weekly Chronicles – August ’25 Week III
Chapter 5: Probability Theory and Statistics
A proposed approach for plagiarism detection in Myanmar Unicode text
What is a Computer? Input Devices /output devices
Zenith AI: Advanced Artificial Intelligence
TEXTILE technology diploma scope and career opportunities
Configure Apache Mutual Authentication
Galois Field Theory of Risk: A Perspective, Protocol, and Mathematical Backgr...
MicrosoftCybserSecurityReferenceArchitecture-April-2025.pptx
UiPath Agentic Automation session 1: RPA to Agents
Geologic Time for studying geology for geologist
Produktkatalog für HOBO Datenlogger, Wetterstationen, Sensoren, Software und ...
Flame analysis and combustion estimation using large language and vision assi...
search engine optimization ppt fir known well about this
Hybrid horned lizard optimization algorithm-aquila optimizer for DC motor
How ambidextrous entrepreneurial leaders react to the artificial intelligence...
A review of recent deep learning applications in wood surface defect identifi...
sustainability-14-14877-v2.pddhzftheheeeee
Final SEM Unit 1 for mit wpu at pune .pptx

DSL or NoDSL - Euruko - 29may2010

  • 1. DSL or NoDSL? José Valim blog.plataformatec.com @josevalim
  • 2. DSL or NoDSL? ID blog twitter José Valim blog.plataformatec.com @josevalim
  • 3. A brief introduction... José Valim blog.plataformatec.com @josevalim
  • 4. A short story... José Valim blog.plataformatec.com @josevalim
  • 5. José Valim blog.plataformatec.com @josevalim
  • 6. mail_form José Valim blog.plataformatec.com @josevalim
  • 7. Contact form recipe José Valim blog.plataformatec.com @josevalim
  • 8. Contact form recipe ✦ Controller (20 LOC) José Valim blog.plataformatec.com @josevalim
  • 9. Contact form recipe ✦ Controller (20 LOC) ✦ Model (30 LOC) José Valim blog.plataformatec.com @josevalim
  • 10. Contact form recipe ✦ Controller (20 LOC) ✦ Model (30 LOC) ✦ View (30 LOC) José Valim blog.plataformatec.com @josevalim
  • 11. Contact form recipe ✦ Controller (20 LOC) ✦ Model (30 LOC) ✦ View (30 LOC) ✦ Mailer (15 LOC) José Valim blog.plataformatec.com @josevalim
  • 12. Contact form recipe ✦ Controller (20 LOC) ✦ Model (30 LOC) ✦ View (30 LOC) ✦ Mailer (15 LOC) ✦ View (20 LOC) José Valim blog.plataformatec.com @josevalim
  • 13. Contact form recipe ✦ Controller (20 LOC) ✦ Model (30 LOC) ✦ View (30 LOC) ✦ Mailer (15 LOC) ✦ View (20 LOC) José Valim blog.plataformatec.com @josevalim
  • 14. Let’s create a DSL! José Valim blog.plataformatec.com @josevalim
  • 15. Domain Speci c Language José Valim blog.plataformatec.com @josevalim
  • 16. class ContactForm < MailForm::Base to "jose.valim@plataformatec.com.br" from "contact_form@app_name.com" subject "Contact form" attributes :name, :email, :message end ContactForm.new(params[:contact_form]).deliver José Valim blog.plataformatec.com @josevalim
  • 17. from { |c| "#{c.name} <#{c.email}>" } José Valim blog.plataformatec.com @josevalim
  • 18. to :author_email def author_email Author.find(self.author_id).email end José Valim blog.plataformatec.com @josevalim
  • 19. headers { |c| { "X-Spam" => "True" } if c.honey_pot } José Valim blog.plataformatec.com @josevalim
  • 20. class ContactForm < MailForm::Base to :author_email from { |c| "#{c.name} <#{c.email}>" } subject "Contact form" headers { |c| { "X-Spam" => "True" } if c.honey_pot } attributes :name, :email, :message def author_email Author.find(self.author_id).email end end José Valim blog.plataformatec.com @josevalim
  • 21. Issues José Valim blog.plataformatec.com @josevalim
  • 22. Issues ✦ Lacks simplicity José Valim blog.plataformatec.com @josevalim
  • 23. Issues ✦ Lacks simplicity ✦ Reduces programmer happiness José Valim blog.plataformatec.com @josevalim
  • 24. NoDSL! José Valim blog.plataformatec.com @josevalim
  • 25. From: GitHub <noreply@github.com> To: jose.valim@plataformatec.com.br Message-Id: <4bfe3e812c22fc17d@github.com> Subject: [GitHub] someone commented on a commit Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 MESSAGE BODY José Valim blog.plataformatec.com @josevalim
  • 26. class ContactForm < MailForm::Base attributes :name, :email, :message def headers { :to => author_email, :from => "#{name} <#{email}>", :subject => "Contact form" } end def author_email Author.find(self.author_id).email end end José Valim blog.plataformatec.com @josevalim
  • 27. It provides a nice DSL José Valim blog.plataformatec.com @josevalim
  • 28. It relies on a simple contract José Valim blog.plataformatec.com @josevalim
  • 29. José Valim blog.plataformatec.com @josevalim
  • 30. Rack José Valim blog.plataformatec.com @josevalim
  • 31. class RackApp def self.call(env) [200, { "Content-Type" => "text/html"}, ["Hello"]] end end José Valim blog.plataformatec.com @josevalim
  • 32. RackApp = Rack::AppBuilder.new do |env| status 200 headers "Content-Type" => "text/html" body ["Hello"] after_return { # do something } end José Valim blog.plataformatec.com @josevalim
  • 33. Rake and Thor José Valim blog.plataformatec.com @josevalim
  • 34. task :process do # do some processing end namespace :app do task :setup do # do some setup # end end rake app:setup José Valim blog.plataformatec.com @josevalim
  • 35. task :process do # do some processing end namespace :app do task :setup do # do some setup Rake::Task[:process].invoke end end rake app:setup José Valim blog.plataformatec.com @josevalim
  • 36. class Default < Thor def process # do some processing end end class App < Thor def setup # do some setup # end end thor app:setup José Valim blog.plataformatec.com @josevalim
  • 37. class Default < Thor def process # do some processing end end class App < Thor def setup # do some setup Default.new.process end end thor app:setup José Valim blog.plataformatec.com @josevalim
  • 38. Rspec and Test::Unit José Valim blog.plataformatec.com @josevalim
  • 39. # Rspec describe User do it "should be valid" do User.new(@attributes).should be_valid end end # Test::Unit class UserTest < Test::Unit::Case def test_user_validity assert User.new(@attributes).valid? end end José Valim blog.plataformatec.com @josevalim
  • 40. A nal story... José Valim blog.plataformatec.com @josevalim
  • 41. class UsersController < ApplicationController def index @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @users } end end def show @user = User.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @user } end end def new @user = User.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @user } end end def edit @user = User.find(params[:id]) end Scaffold Controller def create @user = User.new(params[:user]) respond_to do |format| if @user.save format.html { redirect_to(@user, :notice => 'User was successfully created.') } format.xml { render :xml => @user, :status => :created, :location => @user } else format.html { render :action => "new" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end def update @user = User.find(params[:id]) respond_to do |format| if @user.update_attributes(params[:user]) format.html { redirect_to(@user, :notice => 'User was successfully updated.') } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end def destroy @user = User.find(params[:id]) @user.destroy respond_to do |format| format.html { redirect_to(users_url) } format.xml { head :ok } end end end José Valim blog.plataformatec.com @josevalim
  • 42. class UsersController < ApplicationController restful! end José Valim blog.plataformatec.com @josevalim
  • 43. class UsersController < ApplicationController restful! create do before { # do something before } success.flash "This is the message" end create.after do # do something after end end José Valim blog.plataformatec.com @josevalim
  • 44. InheritedResources José Valim blog.plataformatec.com @josevalim
  • 45. class UsersController < InheritedResources::Base def create # do something before flash[:success] = "This is the message" super # do something after end end José Valim blog.plataformatec.com @josevalim
  • 46. Bene ts José Valim blog.plataformatec.com @josevalim
  • 47. Bene ts ✦ Simple José Valim blog.plataformatec.com @josevalim
  • 48. Bene ts ✦ Simple ✦ Less code to maintain José Valim blog.plataformatec.com @josevalim
  • 49. Bene ts ✦ Simple ✦ Less code to maintain ✦ More time to focus on important features José Valim blog.plataformatec.com @josevalim
  • 50. Bene ts ✦ Simple ✦ Less code to maintain ✦ More time to focus on important features ✦ Just Ruby™ José Valim blog.plataformatec.com @josevalim
  • 51. José Valim blog.plataformatec.com @josevalim
  • 52. ORM Callbacks José Valim blog.plataformatec.com @josevalim
  • 53. # DSL class User < ActiveRecord::Base after_save :deliver_notification, :unless => :admin? end # NoDSL class User < ActiveRecord::Base def save(*) if super deliver_notification unless admin? end end end José Valim blog.plataformatec.com @josevalim
  • 54. DSL or NoDSL? José Valim blog.plataformatec.com @josevalim
  • 55. Thank you! José Valim blog.plataformatec.com @josevalim
  • 56. Questions? Feedback? Opinions? José Valim blog.plataformatec.com @josevalim
  • 57. Questions? Feedback? Opinions? ID blog twitter José Valim blog.plataformatec.com @josevalim