Escrevendo Aplicações Melhores com Active Model

Escrevendo Aplicações Melhores com Active Model

Por Carlos Antonio, na RubyConf Brasil 2012.

7c12adb8b5521c060ab4630360a4fa27?s=128

Plataformatec

August 31, 2012
Tweet

Transcript

  1. 6.

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

    true obj.attr = 'tst' puts obj.valid? # => false
  2. 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
  3. 8.

    “I had to go through some hoops to make this

    code work.” Paolo Perrotta Metaprogramming Ruby
  4. 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 What? ActiveRecord::Base # autoload all modules
  5. 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 def save; end def save!; end LOL
  6. 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 def new_record?; true; end trolling
  7. 12.

    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. 13.

    “However, that wasn’t too much work in exchange for a

    flexible set of validation Class Macros.” Paolo Perrotta Metaprogramming Ruby
  9. 15.

    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. 16.

    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. 22.
  12. 25.
  13. 27.
  14. 32.

    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
  15. 33.
  16. 34.
  17. 35.
  18. 40.

    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
  19. 42.

    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
  20. 48.
  21. 50.
  22. 53.

    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)
  23. 56.

    class Report include ActiveModel::Validations validates_with ReportValidator attr_accessor :name end Active

    Model include ActiveModel::Validations validates_with ReportValidator
  24. 60.

    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
  25. 63.

    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
  26. 65.

    class AbsenceValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value.present?

    record.errors.add attribute, :absence end end end AbsenceValidator Active Model
  27. 66.

    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)
  28. 75.
  29. 76.

    class Report < ActiveRecord::Base before_save { puts "it works before"

    } after_save { puts "it works after as well" } end Active Record
  30. 78.

    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
  31. 79.

    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
  32. 84.

    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
  33. 86.

    report = Report.new report.name = "Omglol" puts report.to_xml #=> <?xml

    version="1.0" encoding="UTF-8"?> #=> <report> #=> <name>Omglol</name> #=> </report> Active Record
  34. 88.

    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
  35. 89.

    report = Report.new report.name = "Omglol" puts report.to_xml #=> <?xml

    version="1.0" encoding="UTF-8"?> #=> <report> #=> <name>Omglol</name> #=> </report> Active Model
  36. 91.
  37. 92.

    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
  38. 94.

    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
  39. 96.

    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
  40. 98.

    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
  41. 100.

    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
  42. 101.
  43. 108.
  44. 109.
  45. 115.

    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
  46. 119.
  47. 127.

    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
  48. 128.
  49. 129.
  50. 130.
  51. 140.

    <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>
  52. 141.

    <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?
  53. 142.

    <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?
  54. 143.

    <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?
  55. 144.

    <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?
  56. 145.

    <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?
  57. 146.

    <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?
  58. 147.

    <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?
  59. 148.

    <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?
  60. 149.

    <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?
  61. 150.

    <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?
  62. 151.

    <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?
  63. 154.

    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
  64. 155.

    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
  65. 156.

    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
  66. 159.

    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
  67. 160.

    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
  68. 161.

    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
  69. 163.

    report = Report.new report.id = 1 puts report.persisted? #=> true

    puts report.to_key #=> [1] puts report.to_param #=> '1'
  70. 164.

    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'
  71. 165.

    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
  72. 167.

    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
  73. 169.

    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
  74. 170.

    $ 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
  75. 171.

    $ 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
  76. 203.
  77. 206.
  78. 232.

    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
  79. 234.

    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?
  80. 236.

    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
  81. 238.

    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
  82. 240.

    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
  83. 241.

    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
  84. 242.

    $ 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
  85. 243.

    $ 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
  86. 244.
  87. 246.
  88. 252.

    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
  89. 254.

    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
  90. 255.

    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
  91. 262.
  92. 263.

    Active Model Attribute Methods Callbacks Dirty Mass Assignment Secure Password

    Observing Serialization Validations Validator Model Conversion Naming Translation Lint
  93. 268.