SlideShare a Scribd company logo
Presenters!
 (On Rails)
   Mike Desjardins
    @mdesjardins
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.

                    Observer
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.

                    Observer
   Factory
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.

                    Observer
   Factory                             Bridge
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.

                    Observer
   Factory                             Bridge
                    Singleton
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.

                    Observer
   Factory                             Bridge
                    Singleton

                                   Decorator
Design Pattern?
        Per our good friends at Wikipedia:
In software engineering, a design pattern is a general
reusable solution to a commonly occurring problem
within a given context in software design.

                    Observer
   Factory                             Bridge
                    Singleton

        Visitor                    Decorator
Swell!


     MVC!   Note to any Microsoft
                 knuckleheads:
         This is not a presentation on
                     “MVP.”
Model View
  Controller
        Controllers




Model                 Views
GOsh, What’s Wrong With
        MVC?
    As your project gets more complex, the
Controllers and Views become “bloated” despite
                your best efforts.
GOsh, What’s Wrong With
        MVC?
    As your project gets more complex, the
Controllers and Views become “bloated” despite
                your best efforts.
These are just the filters in CityEats’ Orders
                          Controller!
skip_before_filter :protect_private_environments, except: [:new]
before_filter :set_user,
           only: [:new, :credit_user_account, :create, :iframe, :payment_form, :offer_details]
# Are all three of these filters necessary? It doesn't seem so at a glance. -Tim
before_filter :load_restaurant,
           only: [:new, :create, :iframe, :credit_user_account, :payment_form, :offer_details],
           :if => lambda { |c| params[:restaurant_id].present? }
before_filter :load_restaurant_and_authenticate,
           only: [:new],
           :if => lambda { |c| params[:restaurant_id].present? || params[:restaurant_offer_id].present? }
before_filter :load_offer_and_set_restaurant,
           only: [:new, :credit_user_account, :create, :payment_form, :offer_details],
           :if => lambda { |c| params[:restaurant_offer_id].present? }
before_filter :require_restaurant,
           only: [:new]
before_filter :merge_request_ip_address,
           only: [:create]
around_filter :load_restaurant_time_zone,
           only: [:new, :show, :create, :destroy, :iframe, :payment_form, :credit_user_account]
before_filter :load_watched_video,
           only: [:create]
before_filter :init_reservation,
           only: [:new, :iframe, :payment_form, :credit_user_account, :create, :offer_details]
before_filter :init_order,
           only: [:new, :iframe, :payment_form, :credit_user_account, :create, :offer_details]
before_filter :set_price,
           only: [:new, :create, :credit_user_account, :payment_form]
before_filter :init_gateway_request_filter,
           only: [:new, :credit_user_account, :payment_form]
AW SHUCKS, Actions
                 too! create action:
 Here’s the OrdersController’s
def create
 if params[:iframe]
   @styling = @restaurant.try(:restaurant_widget_customization) ||
          RestaurantWidgetCustomization.new(:restaurant_id => @restaurant.try(:id))
   @styling.merge(params["restaurant_widget_customization"]) if params["restaurant_widget_customization"].present?
 end

 @order.group_emailable = params[:group_emailable]
 @reservation.landing_tag = cookies['landing_tag'] if cookies['landing_tag'].present?

 if @order.save
   UserMailer.confirm_order(@order).deliver
   @order.user.accept_current_terms_of_service!(request.remote_ip)
   flash["ignore_order_is_conversion"] = true #this is used to render the conversion tracking pixel - naudo feb.6.2012
   flash_message(:notice, 'Your order was successfully created.')

  if @order.invite_facebook_friends_to_reservation? && current_user.present? && current_user.is_a_facebook_user?
    render(:invite_on_facebook) and return
  end
  render(:iframe_confirm, layout: 'minimal') and return if params[:iframe]
  cookies['landing_tag'] = nil
  redirect_to @order
 else
  # Reverse any preauth and/or subscription
  @gateway_transaction.reverse_authorization_and_or_subscription if @gateway_transaction.present?

  render(:iframe, layout: 'minimal') and return if params[:iframe]
  render :new, layout: choose_layout
 end
end
AW SHUCKS, Actions
                 too! create action:
 Here’s the OrdersController’s
def create
 if params[:iframe]
   @styling = @restaurant.try(:restaurant_widget_customization) ||
          RestaurantWidgetCustomization.new(:restaurant_id => @restaurant.try(:id))
   @styling.merge(params["restaurant_widget_customization"]) if params["restaurant_widget_customization"].present?
 end

 @order.group_emailable = params[:group_emailable]
 @reservation.landing_tag = cookies['landing_tag'] if cookies['landing_tag'].present?

 if @order.save
   UserMailer.confirm_order(@order).deliver
   @order.user.accept_current_terms_of_service!(request.remote_ip)
   flash["ignore_order_is_conversion"] = true #this is used to render the conversion tracking pixel - naudo feb.6.2012
   flash_message(:notice, 'Your order was successfully created.')

  if @order.invite_facebook_friends_to_reservation? && current_user.present? && current_user.is_a_facebook_user?
    render(:invite_on_facebook) and return
  end
  render(:iframe_confirm, layout: 'minimal') and return if params[:iframe]
  cookies['landing_tag'] = nil
  redirect_to @order
 else
  # Reverse any preauth and/or subscription
  @gateway_transaction.reverse_authorization_and_or_subscription if @gateway_transaction.present?

  render(:iframe, layout: 'minimal') and return if params[:iframe]
  render :new, layout: choose_layout
 end
end
Gee-
Willikers!
It’s not just the controllers
  that get bloated, Views
   get messed up, too...
Thicker than a five dollar
                    malt
   = order_form.fields_for :reservation do |reservation_form|
    = render "orders/reservation_hidden_fields", :reservation_form => reservation_form

     - unless mobile_prefered?
      = render "orders/restaurant_offer_details", :reservation_form => reservation_form

     .psuedo-section
       - if @order.restaurant.custom_logo_url.present?
        %p.logo=image_tag(@order.restaurant.custom_logo_url)
       %section#reservation_show
        - if @ios_app
          = render "orders/reservation_details"
        = render "orders/reservation_datetime_form_new", :order_form => order_form, :reservation_form => reservation_form
       - if mobile_prefered?
        = render "orders/restaurant_offer_details", :reservation_form => reservation_form
       = render "orders/reservation_info_form_new", :reservation_form => reservation_form
       = hidden_field_tag :orderPage_receiptResponseURL, credit_user_account_orders_url
       = hidden_field_tag :orderPage_declineResponseURL, credit_user_account_orders_url

      - if @ios_app
       #payment-info
         - if @payment_required
           - no_show_fee_amount = @restaurant.no_show_fee(@order.reservation)
           - if no_show_fee_amount && no_show_fee_amount > 0.0
             = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type =>
'noshow', :no_show_fee_amount => no_show_fee_amount }
           - else
             = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type =>
'purchase', :no_show_fee_amount => 0.0 }

      = render "orders/reservation_submit", :reservation_form => reservation_form, :order_form => order_form

 - if @layout != 'nometro' && @restaurant.metro.published?
  .sidebar
    = render "orders/reservation_faq"
    = render :partial => "orders/loyalty_box", :locals => {:restaurant => @restaurant}
    - if @order.has_offer?
Thicker than a five dollar
                    malt
   = order_form.fields_for :reservation do |reservation_form|
    = render "orders/reservation_hidden_fields", :reservation_form => reservation_form

     - unless mobile_prefered?
      = render "orders/restaurant_offer_details", :reservation_form => reservation_form

     .psuedo-section
       - if @order.restaurant.custom_logo_url.present?
        %p.logo=image_tag(@order.restaurant.custom_logo_url)
       %section#reservation_show
        - if @ios_app
          = render "orders/reservation_details"
        = render "orders/reservation_datetime_form_new", :order_form => order_form, :reservation_form => reservation_form
       - if mobile_prefered?
        = render "orders/restaurant_offer_details", :reservation_form => reservation_form
       = render "orders/reservation_info_form_new", :reservation_form => reservation_form
       = hidden_field_tag :orderPage_receiptResponseURL, credit_user_account_orders_url
       = hidden_field_tag :orderPage_declineResponseURL, credit_user_account_orders_url

      - if @ios_app
       #payment-info
         - if @payment_required
           - no_show_fee_amount = @restaurant.no_show_fee(@order.reservation)
           - if no_show_fee_amount && no_show_fee_amount > 0.0
             = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type =>
'noshow', :no_show_fee_amount => no_show_fee_amount }
           - else
             = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type =>
'purchase', :no_show_fee_amount => 0.0 }

      = render "orders/reservation_submit", :reservation_form => reservation_form, :order_form => order_form

 - if @layout != 'nometro' && @restaurant.metro.published?
  .sidebar
    = render "orders/reservation_faq"
    = render :partial => "orders/loyalty_box", :locals => {:restaurant => @restaurant}
    - if @order.has_offer?
Who will
maintain
and test
 all this
logic?!?
Presente
rs to the
Rescue!
Let’s Review...
        Controllers




Model                 Views
GOLLY, that’s bad
     news!
         Controllers




 Model                 Views
Presenter
           s
           Presenter




Controller




 Model             View
Represent “Current State of the
            View”
               Presenter




  Controller




   Model                   View
Invoicing!
  Scripps needed a
   way to preview
Invoices that were to
      be sent to
 Restaurants, as well
   as view existing
       invoices
Invoice
class InvoicePresenter                  Presenter
 attr_accessor :reservation_transactions, :non_reservation_transactions, :transactions, :id, :date, :due_date, :account,
           :reservation_transactions_total, :restaurant

    def initialize(thing)
     self.restaurant = thing.account.accountable
     self.transactions = thing.transactions
     self.date = thing.date
     self.account = thing.account
     self.reservation_transactions = thing.reservation_transactions
     self.non_reservation_transactions = thing.non_reservation_transactions
     if thing.is_a? Invoice
       init_from_invoice(thing)
     elsif thing.is_a? InvoicePreview
       init_from_invoice_preview(thing)
     else
       raise ArgumentError.new("I don't know what to do with this thing.")
     end
    end
.
.
.
    private

     def init_from_invoice(invoice)
      self.id = invoice.id
      self.due_date = invoice.due_date || self.date.end_of_month + Invoice::INVOICE_DAYS_AFTER
     end

  def init_from_invoice_preview(preview)
    self.id = "PREVIEW"
    invoiced_on_date = self.date < Date.today ? Date.today : self.date
    due_date = invoiced_on_date + Invoice::INVOICE_DAYS_AFTER
    self.due_date = due_date
  end
end
Invoice
                     Presenter
def render_performance_summary(context)
 by_source = {}
 total = 0
 reservation_transactions.each do |txn|
  unless txn.source.nil? # how does this happen?
    by_source[txn.source.reservation_source.name] =
      by_source.fetch(txn.source.reservation_source.name,0) + 1
    total = total + 1
  end
 end
 context.render partial: 'invoice_performance_summary',
            locals: {total: total, by_source: by_source}
end
Invoice
def render_line_items(context)
                                                  Presenter
                                                                  Yeah, it can still be kinda gross...
  output = []

  # First, the one time fees.
  one_time_fee_total = 0.0
  one_time_fees.each do |otf|
   amount = otf[:unit_price] * otf[:quantity]
   item = {unit_price: otf[:unit_price], quantity: otf[:quantity], description: otf[:description], discount: 0.00, amount: amount}
   one_time_fee_total = one_time_fee_total + amount
   output << context.render(partial: 'invoice_item', locals: {item: item})
  end
  unless one_time_fees.empty?
   output << context.render(partial: 'invoice_total', locals: {total: one_time_fee_total, description: 'One Time Fees Subtotal', cssclass: 'sub-total'})
  end

  # Next, the reservation transactions
  reservation_fee_total = 0.0
  grouped_reservation_transactions.each do |txn|
   amount = txn[:unit_price] * txn[:quantity]
   item = {unit_price: txn[:unit_price], quantity: txn[:quantity], description: txn[:description], discount: 0.00, amount: amount}
   reservation_fee_total = reservation_fee_total + amount
   output << context.render(partial: 'invoice_item', locals: {item: item})
  end
  unless grouped_reservation_transactions.empty?
   output << context.render(partial: 'invoice_total', locals: {total: reservation_fee_total, description: 'Reservation Fees Fees Subtotal', cssclass: 'sub-total'})
  end

  unless monthly_fee_cap_amount.blank? || monthly_fee_cap_amount.zero?
   output << context.render(partial: 'invoice_total', locals: {total: "After Monthly Fee Cap - #{number_to_currency monthly_fee_cap_amount}", description: 'Balance at the
end of last period', cssclass: 'monthly-cap'})
  end

  # Other Totals
  output << context.render(partial:   'invoice_total', locals: {total: balance_at_end_of_last_period, description: 'Balance at the end of last period', cssclass: 'sub-total'})
  output << context.render(partial:   'invoice_total', locals: {total: last_payment_received_amount, description: "Payment Received - #{last_payment_received_on} - Thank
You", cssclass: 'sub-total'})
  output << context.render(partial:   'invoice_total', locals: {total: sales_tax, description: 'Tax', cssclass: 'tax'})
  output << context.render(partial:   'invoice_total', locals: {total: reservation_fee_total + one_time_fee_total + sales_tax, description: 'Total', cssclass: 'total'})
  output.join
 end
Invoice
%section
 %h4#invoice-header
  Invoice
 %h4#ce-logo
                                                     Presenter
  %img{:alt => 'CityEats', :src => '/assets/logo-cityeats-black.png'}
 #invoice-summary
  %h4 Invoice Summary:
  %table
    %tr


                                                                 But the view is
     %th
       Invoice Id:
     %td= @presenter.id




                                                                   outta site!
   %tr#invoice-date
    %th Invoice Date:
    %td= @presenter.date

   %tr#due-date
    %th Due Date:
    %td= @presenter.due_date
   %tr#amount-due
    %th
      Amount Due:
    %td= number_to_currency(@presenter.amount_due)

 #bill-to
  %h4 Bill To:
  = render partial: 'invoice_bill_to_address', locals: {name: @presenter.restaurant.name, address: @presenter.restaurant.address}

  %h4 Remittance
  %p The amount owing will automatically be charged to your credit card or debited from your bank account, according to the terms of your contract. <br /><em>If paying by check, please
include a copy of this statement.</em>

 %h4 Fee Summary
 %table#fee-summary
  %tr


                                                                                          Gosh, no references to
   %th Description
   %th.quantity Quantity
   %th.unit-price Unit Price
   %th.discount Discount


                                                                                           models anywhere!
   %th.amount Amount
  = raw @presenter.render_line_items(self)

 %h4 Performance Summary
 = raw @presenter.render_performance_summary(self)

= javascript_include_tag "invoicing"
...and the controller is tiny, too!

def show
 invoice = Invoice.find(params[:id])
 @presenter = InvoicePresenter.new(invoice)
end

def new
 account = @restaurant.account
 invoice_date = @restaurant.next_invoice_date
 @presenter = InvoicePresenter.new(InvoicePreview.new(account, invoice_date))
end
Made in the
  Shade
Whoop-de-
freakin-Do!
Have you ever written
 a good view test?
Have you ever written
 a good view test?
  No, seriously. Be
      Honest.
Live
Demo
Can’t I just do all this with
          Helpers?
Helpers don’t have
      State
Not Jesus.
Avdi Grimm
http://www.objectsonrails.com
Exhibitor Pattern
 Uses “Decorator Pattern” to extend an
 existing model
 Implements Decorator Pattern using
 Ruby’s SimpleDelegator class
Decorator Pattern?
Decorator Pattern?
  Delegate                Decorator




 Hey look, it’s UML! I read
 about this in a Computer
 Science Archaeology Book
           once!
Decorator Pattern?
     Delegate           Decorator
+jumpFromSpaceBalloon
Decorator Pattern?
     Delegate               Decorator
+jumpFromSpaceBalloon   +jumpFromSpaceBalloon
                        +drinkRedBull
Decorator Pattern?
     Delegate           SimpleDelegator
+jumpFromSpaceBalloon   +initializer(thing: Delegate)
                        +drinkRedBull




 Gosh, Ruby
sure is spiffy!
Decorator Pattern?
          SimpleDelegator




  Model         Exhibitor
          +initializer(a_model: Model)
          +render_body(context:View)
Exhibitor Pattern
          Uses “Decorator Pattern” to extend an
          existing model
          Implements Decorator Pattern using
          Ruby’s SimpleDelegator class
# exhibits/text_post_exhibit.rb
require 'delegate'
class TextPostExhibit < SimpleDelegator
 def initialize(model, context)
   @context = context
   super(model)
 end

 def render_body
  @context.render(partial: "/posts/text_body", locals: {post: self})
 end
end
some People in the rails
Community conflate these
two notions (exhibitor vs.
       presenter)




 But now you’re smarter
    than all of them!
FURTHER Reading
http://blog.jayfields.com/2007/03/rails-presenter-pattern.html




http://broadcastingadam.com/2011/06/present_yourself/




http://railsvideos.net/railsconf-2012-presenters-and-decorators-a-co
FURTHER Reading
http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

Simple one, does some similar stuff w/ delegation like Avdi without using
SimpleDelagator



http://broadcastingadam.com/2011/06/present_yourself/

Does some neat stuff with memoization




http://railsvideos.net/railsconf-2012-presenters-and-decorators-a-co
Very Good RailsConf 2012 Presentation by Mike Moore. Uses
ActiveDecorator to implement a form of Exhibitor
Quest ions?


    Retro Clip Art Provided By Tack-o-Rama
             http://tackorama.net

More Related Content

Similar to Presenters in Rails

Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy Koshovyi
Pivorak MeetUp
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-public
Chul Ju Hong
 
Rails antipatterns
Rails antipatternsRails antipatterns
Rails antipatterns
Chul Ju Hong
 
Going web native
Going web nativeGoing web native
Going web native
Marcus Hellberg
 
Angular.js Fundamentals
Angular.js FundamentalsAngular.js Fundamentals
Angular.js Fundamentals
Mark
 
Introduction to Magento 2 module development - PHP Antwerp Meetup 2017
Introduction to Magento 2 module development - PHP Antwerp Meetup 2017Introduction to Magento 2 module development - PHP Antwerp Meetup 2017
Introduction to Magento 2 module development - PHP Antwerp Meetup 2017
Joke Puts
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
Brenda Cook
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Again
jonknapp
 
Building Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsBuilding Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in Rails
Jim Jeffers
 
5 Reasons To Love CodeIgniter
5 Reasons To Love CodeIgniter5 Reasons To Love CodeIgniter
5 Reasons To Love CodeIgniternicdev
 
Big Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuff
Big Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuffBig Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuff
Big Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuff
Moshe Kaplan
 
Manipulating Magento - Mage Titans Italy 2018
Manipulating Magento - Mage Titans Italy 2018Manipulating Magento - Mage Titans Italy 2018
Manipulating Magento - Mage Titans Italy 2018
Joke Puts
 
Zend - Installation And Sample Project Creation
Zend - Installation And Sample Project Creation Zend - Installation And Sample Project Creation
Zend - Installation And Sample Project Creation Compare Infobase Limited
 
Building Large Web Applications That Are Easy to Maintain
Building Large Web Applications That Are Easy to MaintainBuilding Large Web Applications That Are Easy to Maintain
Building Large Web Applications That Are Easy to Maintain
MarsBased
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
Eliran Eliassy
 
Continuous Quality
Continuous QualityContinuous Quality
Continuous Quality
Stefano Galati
 
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Coupa Software
 
WebcampZG - Rails 4
WebcampZG - Rails 4WebcampZG - Rails 4
WebcampZG - Rails 4shnikola
 
A Brief Introduction to JQuery Mobile
A Brief Introduction to JQuery MobileA Brief Introduction to JQuery Mobile
A Brief Introduction to JQuery Mobile
Dan Pickett
 
Introduction to Ruby on Rails
Introduction to Ruby on RailsIntroduction to Ruby on Rails
Introduction to Ruby on Rails
Diki Andeas
 

Similar to Presenters in Rails (20)

Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy Koshovyi
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-public
 
Rails antipatterns
Rails antipatternsRails antipatterns
Rails antipatterns
 
Going web native
Going web nativeGoing web native
Going web native
 
Angular.js Fundamentals
Angular.js FundamentalsAngular.js Fundamentals
Angular.js Fundamentals
 
Introduction to Magento 2 module development - PHP Antwerp Meetup 2017
Introduction to Magento 2 module development - PHP Antwerp Meetup 2017Introduction to Magento 2 module development - PHP Antwerp Meetup 2017
Introduction to Magento 2 module development - PHP Antwerp Meetup 2017
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Again
 
Building Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsBuilding Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in Rails
 
5 Reasons To Love CodeIgniter
5 Reasons To Love CodeIgniter5 Reasons To Love CodeIgniter
5 Reasons To Love CodeIgniter
 
Big Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuff
Big Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuffBig Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuff
Big Data Seminar: Analytics, Hadoop, Map Reduce, Mongo and other great stuff
 
Manipulating Magento - Mage Titans Italy 2018
Manipulating Magento - Mage Titans Italy 2018Manipulating Magento - Mage Titans Italy 2018
Manipulating Magento - Mage Titans Italy 2018
 
Zend - Installation And Sample Project Creation
Zend - Installation And Sample Project Creation Zend - Installation And Sample Project Creation
Zend - Installation And Sample Project Creation
 
Building Large Web Applications That Are Easy to Maintain
Building Large Web Applications That Are Easy to MaintainBuilding Large Web Applications That Are Easy to Maintain
Building Large Web Applications That Are Easy to Maintain
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
Continuous Quality
Continuous QualityContinuous Quality
Continuous Quality
 
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
 
WebcampZG - Rails 4
WebcampZG - Rails 4WebcampZG - Rails 4
WebcampZG - Rails 4
 
A Brief Introduction to JQuery Mobile
A Brief Introduction to JQuery MobileA Brief Introduction to JQuery Mobile
A Brief Introduction to JQuery Mobile
 
Introduction to Ruby on Rails
Introduction to Ruby on RailsIntroduction to Ruby on Rails
Introduction to Ruby on Rails
 

Recently uploaded

Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Paige Cruz
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
Thijs Feryn
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
Peter Spielvogel
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Aggregage
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
Quantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIsQuantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIs
Vlad Stirbu
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex ProofszkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
Alex Pruden
 

Recently uploaded (20)

Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
Quantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIsQuantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIs
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex ProofszkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
zkStudyClub - Reef: Fast Succinct Non-Interactive Zero-Knowledge Regex Proofs
 

Presenters in Rails

  • 1. Presenters! (On Rails) Mike Desjardins @mdesjardins
  • 2. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design.
  • 3. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Observer
  • 4. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Observer Factory
  • 5. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Observer Factory Bridge
  • 6. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Observer Factory Bridge Singleton
  • 7. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Observer Factory Bridge Singleton Decorator
  • 8. Design Pattern? Per our good friends at Wikipedia: In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Observer Factory Bridge Singleton Visitor Decorator
  • 9. Swell! MVC! Note to any Microsoft knuckleheads: This is not a presentation on “MVP.”
  • 10. Model View Controller Controllers Model Views
  • 11. GOsh, What’s Wrong With MVC? As your project gets more complex, the Controllers and Views become “bloated” despite your best efforts.
  • 12. GOsh, What’s Wrong With MVC? As your project gets more complex, the Controllers and Views become “bloated” despite your best efforts.
  • 13. These are just the filters in CityEats’ Orders Controller! skip_before_filter :protect_private_environments, except: [:new] before_filter :set_user, only: [:new, :credit_user_account, :create, :iframe, :payment_form, :offer_details] # Are all three of these filters necessary? It doesn't seem so at a glance. -Tim before_filter :load_restaurant, only: [:new, :create, :iframe, :credit_user_account, :payment_form, :offer_details], :if => lambda { |c| params[:restaurant_id].present? } before_filter :load_restaurant_and_authenticate, only: [:new], :if => lambda { |c| params[:restaurant_id].present? || params[:restaurant_offer_id].present? } before_filter :load_offer_and_set_restaurant, only: [:new, :credit_user_account, :create, :payment_form, :offer_details], :if => lambda { |c| params[:restaurant_offer_id].present? } before_filter :require_restaurant, only: [:new] before_filter :merge_request_ip_address, only: [:create] around_filter :load_restaurant_time_zone, only: [:new, :show, :create, :destroy, :iframe, :payment_form, :credit_user_account] before_filter :load_watched_video, only: [:create] before_filter :init_reservation, only: [:new, :iframe, :payment_form, :credit_user_account, :create, :offer_details] before_filter :init_order, only: [:new, :iframe, :payment_form, :credit_user_account, :create, :offer_details] before_filter :set_price, only: [:new, :create, :credit_user_account, :payment_form] before_filter :init_gateway_request_filter, only: [:new, :credit_user_account, :payment_form]
  • 14. AW SHUCKS, Actions too! create action: Here’s the OrdersController’s def create if params[:iframe] @styling = @restaurant.try(:restaurant_widget_customization) || RestaurantWidgetCustomization.new(:restaurant_id => @restaurant.try(:id)) @styling.merge(params["restaurant_widget_customization"]) if params["restaurant_widget_customization"].present? end @order.group_emailable = params[:group_emailable] @reservation.landing_tag = cookies['landing_tag'] if cookies['landing_tag'].present? if @order.save UserMailer.confirm_order(@order).deliver @order.user.accept_current_terms_of_service!(request.remote_ip) flash["ignore_order_is_conversion"] = true #this is used to render the conversion tracking pixel - naudo feb.6.2012 flash_message(:notice, 'Your order was successfully created.') if @order.invite_facebook_friends_to_reservation? && current_user.present? && current_user.is_a_facebook_user? render(:invite_on_facebook) and return end render(:iframe_confirm, layout: 'minimal') and return if params[:iframe] cookies['landing_tag'] = nil redirect_to @order else # Reverse any preauth and/or subscription @gateway_transaction.reverse_authorization_and_or_subscription if @gateway_transaction.present? render(:iframe, layout: 'minimal') and return if params[:iframe] render :new, layout: choose_layout end end
  • 15. AW SHUCKS, Actions too! create action: Here’s the OrdersController’s def create if params[:iframe] @styling = @restaurant.try(:restaurant_widget_customization) || RestaurantWidgetCustomization.new(:restaurant_id => @restaurant.try(:id)) @styling.merge(params["restaurant_widget_customization"]) if params["restaurant_widget_customization"].present? end @order.group_emailable = params[:group_emailable] @reservation.landing_tag = cookies['landing_tag'] if cookies['landing_tag'].present? if @order.save UserMailer.confirm_order(@order).deliver @order.user.accept_current_terms_of_service!(request.remote_ip) flash["ignore_order_is_conversion"] = true #this is used to render the conversion tracking pixel - naudo feb.6.2012 flash_message(:notice, 'Your order was successfully created.') if @order.invite_facebook_friends_to_reservation? && current_user.present? && current_user.is_a_facebook_user? render(:invite_on_facebook) and return end render(:iframe_confirm, layout: 'minimal') and return if params[:iframe] cookies['landing_tag'] = nil redirect_to @order else # Reverse any preauth and/or subscription @gateway_transaction.reverse_authorization_and_or_subscription if @gateway_transaction.present? render(:iframe, layout: 'minimal') and return if params[:iframe] render :new, layout: choose_layout end end
  • 17. It’s not just the controllers that get bloated, Views get messed up, too...
  • 18. Thicker than a five dollar malt = order_form.fields_for :reservation do |reservation_form| = render "orders/reservation_hidden_fields", :reservation_form => reservation_form - unless mobile_prefered? = render "orders/restaurant_offer_details", :reservation_form => reservation_form .psuedo-section - if @order.restaurant.custom_logo_url.present? %p.logo=image_tag(@order.restaurant.custom_logo_url) %section#reservation_show - if @ios_app = render "orders/reservation_details" = render "orders/reservation_datetime_form_new", :order_form => order_form, :reservation_form => reservation_form - if mobile_prefered? = render "orders/restaurant_offer_details", :reservation_form => reservation_form = render "orders/reservation_info_form_new", :reservation_form => reservation_form = hidden_field_tag :orderPage_receiptResponseURL, credit_user_account_orders_url = hidden_field_tag :orderPage_declineResponseURL, credit_user_account_orders_url - if @ios_app #payment-info - if @payment_required - no_show_fee_amount = @restaurant.no_show_fee(@order.reservation) - if no_show_fee_amount && no_show_fee_amount > 0.0 = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type => 'noshow', :no_show_fee_amount => no_show_fee_amount } - else = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type => 'purchase', :no_show_fee_amount => 0.0 } = render "orders/reservation_submit", :reservation_form => reservation_form, :order_form => order_form - if @layout != 'nometro' && @restaurant.metro.published? .sidebar = render "orders/reservation_faq" = render :partial => "orders/loyalty_box", :locals => {:restaurant => @restaurant} - if @order.has_offer?
  • 19. Thicker than a five dollar malt = order_form.fields_for :reservation do |reservation_form| = render "orders/reservation_hidden_fields", :reservation_form => reservation_form - unless mobile_prefered? = render "orders/restaurant_offer_details", :reservation_form => reservation_form .psuedo-section - if @order.restaurant.custom_logo_url.present? %p.logo=image_tag(@order.restaurant.custom_logo_url) %section#reservation_show - if @ios_app = render "orders/reservation_details" = render "orders/reservation_datetime_form_new", :order_form => order_form, :reservation_form => reservation_form - if mobile_prefered? = render "orders/restaurant_offer_details", :reservation_form => reservation_form = render "orders/reservation_info_form_new", :reservation_form => reservation_form = hidden_field_tag :orderPage_receiptResponseURL, credit_user_account_orders_url = hidden_field_tag :orderPage_declineResponseURL, credit_user_account_orders_url - if @ios_app #payment-info - if @payment_required - no_show_fee_amount = @restaurant.no_show_fee(@order.reservation) - if no_show_fee_amount && no_show_fee_amount > 0.0 = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type => 'noshow', :no_show_fee_amount => no_show_fee_amount } - else = render :partial => "shared/payment_details", :locals => { :countdown_minutes => nil, :payment_type => 'purchase', :no_show_fee_amount => 0.0 } = render "orders/reservation_submit", :reservation_form => reservation_form, :order_form => order_form - if @layout != 'nometro' && @restaurant.metro.published? .sidebar = render "orders/reservation_faq" = render :partial => "orders/loyalty_box", :locals => {:restaurant => @restaurant} - if @order.has_offer?
  • 20. Who will maintain and test all this logic?!?
  • 22. Let’s Review... Controllers Model Views
  • 23. GOLLY, that’s bad news! Controllers Model Views
  • 24. Presenter s Presenter Controller Model View
  • 25. Represent “Current State of the View” Presenter Controller Model View
  • 26. Invoicing! Scripps needed a way to preview Invoices that were to be sent to Restaurants, as well as view existing invoices
  • 27. Invoice class InvoicePresenter Presenter attr_accessor :reservation_transactions, :non_reservation_transactions, :transactions, :id, :date, :due_date, :account, :reservation_transactions_total, :restaurant def initialize(thing) self.restaurant = thing.account.accountable self.transactions = thing.transactions self.date = thing.date self.account = thing.account self.reservation_transactions = thing.reservation_transactions self.non_reservation_transactions = thing.non_reservation_transactions if thing.is_a? Invoice init_from_invoice(thing) elsif thing.is_a? InvoicePreview init_from_invoice_preview(thing) else raise ArgumentError.new("I don't know what to do with this thing.") end end . . . private def init_from_invoice(invoice) self.id = invoice.id self.due_date = invoice.due_date || self.date.end_of_month + Invoice::INVOICE_DAYS_AFTER end def init_from_invoice_preview(preview) self.id = "PREVIEW" invoiced_on_date = self.date < Date.today ? Date.today : self.date due_date = invoiced_on_date + Invoice::INVOICE_DAYS_AFTER self.due_date = due_date end end
  • 28. Invoice Presenter def render_performance_summary(context) by_source = {} total = 0 reservation_transactions.each do |txn| unless txn.source.nil? # how does this happen? by_source[txn.source.reservation_source.name] = by_source.fetch(txn.source.reservation_source.name,0) + 1 total = total + 1 end end context.render partial: 'invoice_performance_summary', locals: {total: total, by_source: by_source} end
  • 29. Invoice def render_line_items(context) Presenter Yeah, it can still be kinda gross... output = [] # First, the one time fees. one_time_fee_total = 0.0 one_time_fees.each do |otf| amount = otf[:unit_price] * otf[:quantity] item = {unit_price: otf[:unit_price], quantity: otf[:quantity], description: otf[:description], discount: 0.00, amount: amount} one_time_fee_total = one_time_fee_total + amount output << context.render(partial: 'invoice_item', locals: {item: item}) end unless one_time_fees.empty? output << context.render(partial: 'invoice_total', locals: {total: one_time_fee_total, description: 'One Time Fees Subtotal', cssclass: 'sub-total'}) end # Next, the reservation transactions reservation_fee_total = 0.0 grouped_reservation_transactions.each do |txn| amount = txn[:unit_price] * txn[:quantity] item = {unit_price: txn[:unit_price], quantity: txn[:quantity], description: txn[:description], discount: 0.00, amount: amount} reservation_fee_total = reservation_fee_total + amount output << context.render(partial: 'invoice_item', locals: {item: item}) end unless grouped_reservation_transactions.empty? output << context.render(partial: 'invoice_total', locals: {total: reservation_fee_total, description: 'Reservation Fees Fees Subtotal', cssclass: 'sub-total'}) end unless monthly_fee_cap_amount.blank? || monthly_fee_cap_amount.zero? output << context.render(partial: 'invoice_total', locals: {total: "After Monthly Fee Cap - #{number_to_currency monthly_fee_cap_amount}", description: 'Balance at the end of last period', cssclass: 'monthly-cap'}) end # Other Totals output << context.render(partial: 'invoice_total', locals: {total: balance_at_end_of_last_period, description: 'Balance at the end of last period', cssclass: 'sub-total'}) output << context.render(partial: 'invoice_total', locals: {total: last_payment_received_amount, description: "Payment Received - #{last_payment_received_on} - Thank You", cssclass: 'sub-total'}) output << context.render(partial: 'invoice_total', locals: {total: sales_tax, description: 'Tax', cssclass: 'tax'}) output << context.render(partial: 'invoice_total', locals: {total: reservation_fee_total + one_time_fee_total + sales_tax, description: 'Total', cssclass: 'total'}) output.join end
  • 30. Invoice %section %h4#invoice-header Invoice %h4#ce-logo Presenter %img{:alt => 'CityEats', :src => '/assets/logo-cityeats-black.png'} #invoice-summary %h4 Invoice Summary: %table %tr But the view is %th Invoice Id: %td= @presenter.id outta site! %tr#invoice-date %th Invoice Date: %td= @presenter.date %tr#due-date %th Due Date: %td= @presenter.due_date %tr#amount-due %th Amount Due: %td= number_to_currency(@presenter.amount_due) #bill-to %h4 Bill To: = render partial: 'invoice_bill_to_address', locals: {name: @presenter.restaurant.name, address: @presenter.restaurant.address} %h4 Remittance %p The amount owing will automatically be charged to your credit card or debited from your bank account, according to the terms of your contract. <br /><em>If paying by check, please include a copy of this statement.</em> %h4 Fee Summary %table#fee-summary %tr Gosh, no references to %th Description %th.quantity Quantity %th.unit-price Unit Price %th.discount Discount models anywhere! %th.amount Amount = raw @presenter.render_line_items(self) %h4 Performance Summary = raw @presenter.render_performance_summary(self) = javascript_include_tag "invoicing"
  • 31. ...and the controller is tiny, too! def show invoice = Invoice.find(params[:id]) @presenter = InvoicePresenter.new(invoice) end def new account = @restaurant.account invoice_date = @restaurant.next_invoice_date @presenter = InvoicePresenter.new(InvoicePreview.new(account, invoice_date)) end
  • 32. Made in the Shade
  • 34. Have you ever written a good view test?
  • 35. Have you ever written a good view test? No, seriously. Be Honest.
  • 37. Can’t I just do all this with Helpers?
  • 39.
  • 43. Exhibitor Pattern Uses “Decorator Pattern” to extend an existing model Implements Decorator Pattern using Ruby’s SimpleDelegator class
  • 45. Decorator Pattern? Delegate Decorator Hey look, it’s UML! I read about this in a Computer Science Archaeology Book once!
  • 46. Decorator Pattern? Delegate Decorator +jumpFromSpaceBalloon
  • 47. Decorator Pattern? Delegate Decorator +jumpFromSpaceBalloon +jumpFromSpaceBalloon +drinkRedBull
  • 48. Decorator Pattern? Delegate SimpleDelegator +jumpFromSpaceBalloon +initializer(thing: Delegate) +drinkRedBull Gosh, Ruby sure is spiffy!
  • 49. Decorator Pattern? SimpleDelegator Model Exhibitor +initializer(a_model: Model) +render_body(context:View)
  • 50. Exhibitor Pattern Uses “Decorator Pattern” to extend an existing model Implements Decorator Pattern using Ruby’s SimpleDelegator class # exhibits/text_post_exhibit.rb require 'delegate' class TextPostExhibit < SimpleDelegator def initialize(model, context) @context = context super(model) end def render_body @context.render(partial: "/posts/text_body", locals: {post: self}) end end
  • 51. some People in the rails Community conflate these two notions (exhibitor vs. presenter) But now you’re smarter than all of them!
  • 53. FURTHER Reading http://blog.jayfields.com/2007/03/rails-presenter-pattern.html Simple one, does some similar stuff w/ delegation like Avdi without using SimpleDelagator http://broadcastingadam.com/2011/06/present_yourself/ Does some neat stuff with memoization http://railsvideos.net/railsconf-2012-presenters-and-decorators-a-co Very Good RailsConf 2012 Presentation by Mike Moore. Uses ActiveDecorator to implement a form of Exhibitor
  • 54. Quest ions? Retro Clip Art Provided By Tack-o-Rama http://tackorama.net

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n