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

Off The Tracks

Off The Tracks

Challenging The Rails Mindset. Overloaded classes, POROs, domain objects, form objects, view inheritance, representers, and more.

0fe13386ba69a128e9fadc19ae9d96be?s=128

apotonick

June 18, 2013
Tweet

More Decks by apotonick

Other Decks in Programming

Transcript

  1. Off The Tracks @apotonick beta

  2. None
  3. Los Shotos!

  4. A

  5. None
  6. An Example.

  7. class User < ActiveRecord::Base # do this... # do that..

    after_save :send_sms_notification end
  8. class UsersController < Controller def create # ... @user.save end

  9. class SmartMigration < Migration def change # ... user.save end

  10. None
  11. rake db:migrate

  12. None
  13. class User < ActiveRecord::Base # do this... # do that..

    after_save :send_sms_notification end
  14. None
  15. None
  16. None
  17. None
  18. I AM A FEATURE! A

  19. None
  20. None
  21. None
  22. A

  23. Fear Of The Class

  24. User User Form Validations Notifications

  25. class User < ActiveRecord::Base # do this... # do that..

    after_save :send_sms_notification end
  26. class UsersController < Controller def create # ... @user.save notify_for(@user)

    end explicit*
  27. class UsersController < Controller def create # ... @user.save notify_for(@user)

    end class UserSaveService def call user.save notify_for(user) end def notify_for(user)
  28. class UsersController < Controller def create # ... UserSaveService.new(@user). call

    end
  29. None
  30. None
  31. Domain Persistance

  32. Want some drink, Steve? Oh, yes!

  33. Cowboy Chat Message Conversation Response

  34. Cowboy Chat Message Conversatio Response ActiveRecord:: Base

  35. The Workflow. steve = Cowboy.new(name: "Steve") jim = Cowboy.new(name: "Jim")

    question = jim.say("Want some drink, Steve?") steve.respond("Oh, yes!", question)
  36. PORO.

  37. None
  38. PORO.

  39. class Cowboy < OpenStruct end

  40. class Cowboy < OpenStruct end jim = Cowboy.new(name: "Jim") jim.name

    #=> “Jim”
  41. Want some drink, Steve?

  42. question = jim.say("Want some drink, Steve?")

  43. class Cowboy < OpenStruct end

  44. class Cowboy < OpenStruct def say(what) msg = Message.new( content:

    what) chat << msg end end
  45. Want some drink, Steve? Oh, yes!

  46. steve.respond("Oh, yes!", question)

  47. class Cowboy < OpenStruct ... def respond(what, question) msg =

    Response.new( content: what, to: question) chat << msg end end
  48. class Message < OpenStruct end class Response < Message end

  49. Running The Workflow.

  50. before do chat = [] end

  51. before do chat = [] jim = Cowboy.new(name: “Jim”, chat:

    chat) steve = ... end
  52. before do chat = [] jim = Cowboy.new(name: “Jim”, chat:

    chat) steve = ... qst = jim.say("Want some drink, Steve?") steve.respond("Oh, yes!", qst) end
  53. it “is in right order” do chat.to_readable.should == <<BONFIRE Two

    cowboys were sitting at a campfire. Jim said “Want some drink, Steve?” whereas Steve, who was already loaded as shit, responded “Oh, yes!” and both were happy to the end of their days. BONFIRE end
  54. bonfire$ rake Finished tests in 0.535564s, 5.6016 tests/s, 3.7344 assertions/s.

  55. Domain Persistance A

  56. datamappify

  57. None
  58. fredwu/datamappify

  59. Thanks! Next?

  60. class Message include Datamappify::Entity attribute :content, String end

  61. class MessageRepository include Datamappify::Repository for_entity Message default_provider :ActiveRecord map_attribute :content,

    'ActiveRecord::Message#text' end
  62. message.content #=> “Oh yes!” (from persistance layer)

  63. None
  64. None
  65. Your Message: “

  66. class Message < ActiveRecord::Base validates :content, presence: true accepts_nested_attributes ...

    end
  67. class Message < ActiveRecord::Base validates :content, presence: true accepts_nested_attributes ...

    end message.update_attributes(params)
  68. class MessageForm < Reform::Form property :content, on: :message property :author,

    on: :message validates :message, presence: true end
  69. msg = Message.find(1) @form = MessageForm.new(message: msg)

  70. = simple_form_for @form do |f| = f.input :content

  71. if @form.validate(params[:message])

  72. if @form.validate(params[:message]) @form.save do |data, nested|

  73. if @form.validate(params[:message]) @form.save do |data, nested| Message.new(content: data[:content])

  74. apotonick/reform

  75. Recent Messages “ “ Steve Jim

  76. app/views/chat/show.html.haml = render :partial => "sidebar"

  77. app/views/shared/_sidebar.html.haml .box %h3 Recent Messages = # render list with

    @chat.messages
  78. app/views/shared/_sidebar.html.haml .box %h3 Recent Messages = # render list with

    @chat.messages .box = pie_chart_for(@chat)
  79. Recent Messages “ “ Steve Jim

  80. Recent Messages “ “ 81%

  81. “ “ /home/ Steve Jim Recent Messages “ “ Steve

    Jim 81% /dashboard/ /chat/show /stats/ /chat/delete
  82. app/views/shared/_sidebar.html.haml .box %h3 Recent Messages = # render list with

    @chat.messages .box - if draw_pie_chart? = pie_chart_for(@chat) - else = happiness_chart_for(@chat)
  83. View Component.

  84. class ChatsController < ApplicationController def show @chat = Chat.find ...

    end end
  85. app/views/chat/show.html.haml = render :partial => "sidebar"

  86. app/views/chat/show.html.haml = render :partial => "sidebar" app/views/chat/show.html.haml = render_cell :sidebar,

    :show, @chat
  87. class SidebarCell < Cell::Rails def show(chat) @chat = chat render

    end end app/cells/sidebar/show.html.haml .box %h3 Recent Messages = # render list with @chat.messages .box - if draw_pie_chart? = pie_chart_for(@chat) - else = happiness_chart_for(@chat)
  88. describe SidebarCell render_cell(:sidebar, :show, chat). should ... end

  89. Recent Messages “ “ Steve Jim Recent Messages “ “

    81%
  90. Override?

  91. class Message def to_s msg = self.upper “<Message #{msg}>” end

    end
  92. class Message def format self.upper end def to_s “<Message #{format}>”

    end end
  93. class Message def format self.upper end def to_s “<Message #{format}>”

    end end class BetterMessage < Message def format self.lower end end
  94. View Inheritance!

  95. class MommyController < ApplicationContr.. def show ... render :partial =>

    'item' end end views/mommy/_item.erb ?
  96. class MommyController < ApplicationContr.. def show ... render :partial =>

    'item' end end class ChildController < MommyController def show ... render :partial => 'item' end end views/child/_item.erb ? views/mommy/_item.erb ?
  97. app/cells/sidebar/show.html.haml .box %h3 Recent Messages = # render list with

    @chat.messages .box - if draw_pie_chart? = pie_chart_for(@chat) - else = happiness_chart_for(@chat)
  98. app/cells/sidebar/show.html.haml .box %h3 Recent Messages = # render list with

    @chat.messages = render :view => lower_box
  99. app/cells/sidebar/lower_box.html.haml .box = pie_chart_for(@chat)

  100. class FunnySidebarCell < SidebarCell end app/cells/funny_sidebar/lower_box.html.haml .box = happiness_chart_for(@chat)

  101. app/views/chat/show.html.haml = render_cell :sidebar, :show, @chat Recent Messages “ “

    Steve Jim
  102. app/views/discuss/show.html.haml = render_cell :funny_sidebar, :show, @chat Recent Messages “ “

    Steve Jim 81%
  103. class FunnySidebarCell < SidebarCell end app/cells/funny_sidebar/lower_box.html.haml .box = happiness_chart_for(@chat)

  104. None
  105. apotonick/cells

  106. None
  107. None
  108. class ChatsController < ApplicationController def show chat = Chat.find(params[:id]) @messages

    = chat.messages end end
  109. # objectify 1. POST /messages

  110. # objectify 1. POST /messages class IsDrinkerPolicy def allowed?(current_user) end

  111. # objectify 1. POST /messages class IsDrinkerPolicy def allowed?(current_user) end

    class AddMessageService def call(current_user, params) end
  112. # objectify 1. POST /messages class IsDrinkerPolicy def allowed?(current_user) end

    class AddMessageService def call(current_user, params) end class MessageResponder def call end
  113. Route Render Policies Business

  114. bitlove/objectify

  115. List messages Message detail Send message API

  116. {"content": "Want some drink, Steve?"} Render Document. Parse Document.

  117. as_json(:include => ...) ActiveModel::Serializer params[..] strong_parameters

  118. Representer.

  119. GET http://messages/1 {"content": "Want some drink, Steve?", "created_at": "2012-09-28 19:40",

    }
  120. module MessageRepresenter include Roar::Representer::JSON property :content property :created_at end

  121. msg.extend(MessageRepresenter).to_json

  122. msg.extend(MessageRepresenter).to_json #=> {"content": "Want some drink, Steve?", "created_at": "2012-09-28 19:40"

    }
  123. msg = Message.new.extend(MessageRepresenter) msg.from_json('{"content": "Oh yes!"')

  124. msg = Message.new.extend(MessageRepresenter) msg.from_json('{"content": "Oh yes!"') msg.content #=> "Oh yes!"

  125. Oh no! Extend!

  126. module MessageRepresenter include Roar::Representer::JSON property :content property :created_at end msg.extend(MessageRepresenter).

    to_json class < Roar::Decorator MessageRepresenter.new(msg).
  127. Model Representer from_json to_json #content #created_at #content= #created_at=

  128. More?

  129. module MessageRepresenter include Roar::Representer::JSON property :content property :from, :extend =>

    CowboyRepresenter, :class => Cowboy end
  130. module ChatRepresenter include Roar::Representer::JSON collection :messages, :extend => MessageRepresenter end

  131. module MessageRepresenter include Roar::Representer::XML property :content end

  132. module MessageRepresenter include Roar::Representer::XML property :content end msg.extend(MessageRepresenter).to_xml #=> <message>

    <content>Yo!</content> </message
  133. David! Hypermedia!

  134. module MessageRepresenter include Roar::Representer::JSON::HAL property :content link :self do message_url(self)

    end end
  135. msg.extend(MessageRepresenter).to_json #=> {"content": "Want some drink, Steve?", "_links" : {"self":

    {"href":"http://messages/1"}} }
  136. module MessageRepresenter include Roar::Representer::JSON::Collection version “2.0” href { messages_url(self) }

    items(class: Message) do property :content end link :self do message_url(self) end end
  137. [msg].extend(MessageRepresenter).to_json #=> {"collection" => { "version": "2.0", "href": "http://messages", "items":

    [ "data": [ {"name": "content", "value": "Want some drink, Steve?"}] "links" : {"self": {"href":"http://messages/rss"}} }
  138. msg = Message.new msg.extend(MessageRepresenter). from_json(“{collection: ...”) msg.version #=> 2.0

  139. apotonick/roar apotonick/representable

  140. PORO objectify

  141. Don't Fear The Class

  142. @apotonick