Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Escrevendo Aplicações Melhores com Active Model

Escrevendo Aplicações Melhores com Active Model

Por Carlos Antonio, na RubyConf Brasil 2012.

Plataformatec

August 31, 2012
Tweet

More Decks by Plataformatec

Other Decks in Programming

Transcript

  1. obj = MyClass.new obj.attr = 'test' puts obj.valid? # =>

    true obj.attr = 'tst' puts obj.valid? # => false
  2. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end
  3. “I had to go through some hoops to make this

    code work.” Paolo Perrotta Metaprogramming Ruby
  4. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end What? ActiveRecord::Base # autoload all modules
  5. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end def save; end def save!; end LOL
  6. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end def new_record?; true; end trolling
  7. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 there we go!
  8. “However, that wasn’t too much work in exchange for a

    flexible set of validation Class Macros.” Paolo Perrotta Metaprogramming Ruby
  9. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end ActiveRecord::Base # autoload all modules def save; end def save!; end def new_record?; true; end
  10. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4
  11. require 'activerecord' ActiveRecord::Base # autoload all modules class MyClass def

    save; end def save!; end def new_record?; true; end include ActiveRecord::Validations attr_accessor :attr validates_length_of :attr, :minimum => 4 end
  12. module ActiveRecord class Base extend ActiveModel::Naming # Uses ActiveModel::Translation extend

    Translation # Uses ActiveModel::MassAssignmentSecurity include AttributeAssignment include ActiveModel::Conversion # Uses ActiveModel::Validations include Validations # Uses ActiveModel::AttributeMethods include AttributeMethods # Uses ActiveModel::Callbacks include Callbacks include ActiveModel::Observing include ActiveModel::SecurePassword # Uses ActiveModel::Serialization include Serialization # ... end end
  13. module ActiveResource class Base extend ActiveModel::Naming # Uses ActiveModel::Observing include

    Observing # Uses ActiveModel::Validations include Validations include ActiveModel::Conversion include ActiveModel::Serializers::JSON include ActiveModel::Serializers::Xml # ... end end
  14. class ReportValidator < ActiveModel::Validator def validate(report) if report.name.blank? report.errors.add :name,

    :blank end # More validations... end end Active Record ReportValidator < ActiveModel::Validator def validate(report)
  15. class Report include ActiveModel::Validations validates_with ReportValidator attr_accessor :name end Active

    Model include ActiveModel::Validations validates_with ReportValidator
  16. class Report include ActiveModel::Validations attr_accessor :name # validates_presence_of :name validates

    :name, presence: true end Active Model include ActiveModel::Validations validates :name, presence: true
  17. class Report include ActiveModel::Validations attr_accessor :no_name validates :no_name, absence: true

    end include ActiveModel::Validations validates :no_name, absence: true Active Model
  18. class AbsenceValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value.present?

    record.errors.add attribute, :absence end end end AbsenceValidator Active Model
  19. class AbsenceValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value.present?

    record.errors.add attribute, :absence end end end AbsenceValidator Active Model AbsenceValidator < ActiveModel::EachValidator def validate_each(record, attribute, value)
  20. class Report < ActiveRecord::Base before_save { puts "it works before"

    } after_save { puts "it works after as well" } end Active Record
  21. class Report extend ActiveModel::Callbacks define_model_callbacks :save before_save { puts "it

    works before" } after_save { puts "it works after as well" } def save run_callbacks :save do puts "saving..." end end end Active Model
  22. class Report extend ActiveModel::Callbacks define_model_callbacks :save before_save { puts "it

    works before" } after_save { puts "it works after as well" } def save run_callbacks :save do puts "saving..." end end end extend ActiveModel::Callbacks define_model_callbacks :save before_save { puts "it works before" } after_save { puts "it works after as well" } run_callbacks :save do puts "saving..." end Active Model
  23. class Report extend ActiveModel::Naming include ActiveModel::Serializers::JSON attr_accessor :name def attributes

    { name: name } end end Active Model include ActiveModel::Serializers::JSON def attributes { name: name } end
  24. report = Report.new report.name = "Omglol" puts report.to_xml #=> <?xml

    version="1.0" encoding="UTF-8"?> #=> <report> #=> <name>Omglol</name> #=> </report> Active Record
  25. class Report extend ActiveModel::Naming include ActiveModel::Serializers::Xml attr_accessor :name def attributes

    { name: name } end end include ActiveModel::Serializers::Xml def attributes { name: name } end Active Model
  26. report = Report.new report.name = "Omglol" puts report.to_xml #=> <?xml

    version="1.0" encoding="UTF-8"?> #=> <report> #=> <name>Omglol</name> #=> </report> Active Model
  27. report = Report.new puts report.name? #=> false report.name = "Omglol"

    puts report.name? #=> true report.name? #=> false report.name = "Omglol" report.name? #=> true Active Record
  28. class Report include ActiveModel::AttributeMethods attribute_method_suffix '?' define_attribute_methods [:name] attr_accessor :name

    private def attribute?(attr) send(attr).present? end end include ActiveModel::AttributeMethods attribute_method_suffix '?' define_attribute_methods [:name] def attribute?(attr) send(attr).present? end Active Model
  29. report = Report.new puts report.name? #=> false report.name = "Omglol"

    puts report.name? #=> true report.name? #=> false report.name = "Omglol" report.name? #=> true Active Model
  30. class Report include ActiveModel::AttributeMethods attribute_method_prefix 'clear_' define_attribute_methods [:name] attr_accessor :name

    private def clear_attribute(attr) send "#{attr}=", nil end end Active Model include ActiveModel::AttributeMethods attribute_method_prefix 'clear_' define_attribute_methods [:name] def clear_attribute(attr) send "#{attr}=", nil end
  31. report.name = "Omglol" puts report.name? #=> true report.clear_name puts report.name?

    #=> false report.name? #=> true report.clear_name report.name? #=> false Active Model
  32. class Invitation attr_accessor :contacts, :message def new_record? true end def

    id nil end end def new_record? true end def id nil end
  33. class Invitation attr_accessor :contacts, :message def new_record? true end def

    id nil end end def new_record? true end def id nil end
  34. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form>
  35. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <form action="/reports" class="new_report" id="new_report" method="post"> como?
  36. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <form action="/reports" class="new_report" id="new_report" method="post"> como?
  37. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <form action="/reports" class="new_report" id="new_report" method="post"> como?
  38. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <label for="report_name">Nome</label> como?
  39. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <label for="report_name">Nome</label> como?
  40. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <label for="report_name">Nome</label> como?
  41. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <input id="report_name" size="30" name="report[name]" type="text" /> como?
  42. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <input id="report_name" size="30" name="report[name]" type="text" /> como?
  43. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <input id="report_name" size="30" name="report[name]" type="text" /> como?
  44. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <input name="commit" type="submit" value="Criar Relatório" /> como?
  45. <form action="/reports" class="new_report" id="new_report" method="post"> <label for="report_name">Nome</label> <input id="report_name" size="30"

    name="report[name]" type="text" /> <input name="commit" type="submit" value="Criar Relatório" /> </form> <input name="commit" type="submit" value="Criar Relatório" /> como?
  46. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end
  47. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages
  48. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages Naming
  49. puts Report.model_name #=> "Report" puts Report.model_name.singular #=> report puts Report.model_name.plural

    #=> reports puts Report.model_name.param_key #=> report model_name "Report" model_name.singular report model_name.plural reports model_name.param_key report
  50. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages Conversion
  51. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages Implementar
  52. report = Report.new report.id = 1 puts report.persisted? #=> true

    puts report.to_key #=> [1] puts report.to_param #=> '1'
  53. report = Report.new report.id = 1 puts report.persisted? #=> true

    puts report.to_key #=> [1] puts report.to_param #=> '1' report.persisted? #=> true report.to_key #=> [1] report.to_param #=> '1'
  54. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages Validations
  55. require 'minitest/autorun' class OmglolTest < MiniTest::Unit::TestCase include ActiveModel::Lint::Tests def setup

    @model = Omglol.new end end include ActiveModel::Lint::Tests def setup @model = Omglol.new end
  56. require 'active_model' class Omglol extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations

    def persisted?; false; end end extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations def persisted?; false; end
  57. $ ruby activemodel_test.rb Run options: --seed 9998 # Running tests:

    ........ Finished tests in 0.029155s, 274.3955 tests/s, 1303.3785 assertions/s. 8 tests, 38 assertions, 0 failures, 0 errors, 0 skips
  58. $ ruby activemodel_test.rb Run options: --seed 9998 # Running tests:

    ........ Finished tests in 0.029155s, 274.3955 tests/s, 1303.3785 assertions/s. 8 tests, 38 assertions, 0 failures, 0 errors, 0 skips
  59. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages
  60. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages Validations?
  61. module ActiveModel::Lint::Tests def test_to_key; end def test_to_param; end def test_to_partial_path;

    end def test_valid?; end def test_persisted?; end def test_model_naming; end def test_errors_aref; end def test_errors_full_messages; end end RAILS 4 to_key to_param to_partial_path valid? persisted? model_naming errors_aref errors_full_messages
  62. require 'minitest/autorun' class OmglolTest < MiniTest::Unit::TestCase include ActiveModel::Lint::Tests def setup

    @model = Omglol.new end end RAILS 4 include ActiveModel::Lint::Tests def setup @model = Omglol.new end
  63. require 'active_model' class Omglol extend ActiveModel::Naming include ActiveModel::Conversion def persisted?;

    false; end def errors; Hash.new([]); end end RAILS 4 def errors; Hash.new([]); end
  64. require 'active_model' class Omglol extend ActiveModel::Naming include ActiveModel::Conversion def persisted?;

    false; end def errors; Hash.new([]); end end RAILS 4 extend ActiveModel::Naming include ActiveModel::Conversion def persisted?; false; end def errors; Hash.new([]); end def errors; Hash.new([]); end
  65. $ ruby activemodel_master_test.rb Run options: --seed 10850 # Running tests:

    ...... Finished tests in 0.002590s, 2316.6023 tests/s, 11583.0116 assertions/s. 6 tests, 30 assertions, 0 failures, 0 errors, 0 skip
  66. $ ruby activemodel_master_test.rb Run options: --seed 10850 # Running tests:

    ...... Finished tests in 0.002590s, 2316.6023 tests/s, 11583.0116 assertions/s. 6 tests, 30 assertions, 0 failures, 0 errors, 0 skip
  67. require 'active_model' class Omglol extend ActiveModel::Naming include ActiveModel::Conversion def persisted?;

    false; end def errors; Hash.new([]); end end RAILS 4 extend ActiveModel::Naming include ActiveModel::Conversion def persisted?; false; end def errors; Hash.new([]); end
  68. module Model def self.included(base) #:nodoc: base.class_eval do extend ActiveModel::Naming extend

    ActiveModel::Translation include ActiveModel::Validations include ActiveModel::Conversion end end def initialize(params={}) params.each do |attr, value| self.public_send("#{attr}=", value) end if params end def persisted? false end end RAILS 4
  69. module Model def self.included(base) #:nodoc: base.class_eval do extend ActiveModel::Naming extend

    ActiveModel::Translation include ActiveModel::Validations include ActiveModel::Conversion end end def initialize(params={}) params.each do |attr, value| self.public_send("#{attr}=", value) end if params end def persisted? false end end RAILS 4 extend ActiveModel::Naming extend ActiveModel::Translation include ActiveModel::Validations include ActiveModel::Conversion def persisted? false end
  70. Active Model Attribute Methods Callbacks Dirty Mass Assignment Secure Password

    Observing Serialization Validations Validator Model Conversion Naming Translation Lint