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

Rails Engines as an SOA Middle Ground

Rails Engines as an SOA Middle Ground

C756ca1b50655ad7eb1fe4ce06b789b3?s=128

Jeff Felchner

January 28, 2014
Tweet

Transcript

  1. Rails Engines as an SOA Middle Ground

  2. Before We Begin

  3. None
  4. None
  5. I DON’T ALWAYS SAY ALWAYS BUT WHEN I DO I

    ALMOST NEVER MEAN IT
  6. None
  7. None
  8. None
  9. None
  10. Have You Been Here?

  11. 5 minutes?

  12. 15 minutes?

  13. 30 minutes?

  14. Longer!?

  15. None
  16. WE FEAR CHANGE

  17. None
  18. None
  19. None
  20. None
  21. Object-Orientation

  22. None
  23. None
  24. None
  25. None
  26. =

  27. $ bundle update my_engine

  28. gem  'my_engine',        :path  =>  '/path/to/my/engine'

  29. Gems

  30. "This might make a nice open source project."

  31. "If I don't release it to rubygems.org, how do I

    use it in my projects?"
  32. Add it to your GEM_PATH manually

  33. $ export GEM_PATH=/path/to/gem;$GEM_PATH

  34. Use a private git repo in your Gemfile

  35. gem  'my_engine',        :git  =>  '/path/to/my/git/repo'

  36. Use a local gem server

  37. $ gem server

  38. None
  39. $ gem sources --add http://localhost:8808

  40. source ‘http://localhost:8808’ # Gemfile

  41. Use a 3rd party service

  42. None
  43. Railties

  44. A Type of Gem

  45. class  MyRailtie  <  Rails::Railtie    #  Does  Stuff end

  46. require  ‘my_railtie’  if  const_defined?(Rails)

  47. Add Middleware

  48. Create/Update Configuration Settings

  49. Add Generators

  50. Run Code at Rails Console Load Time

  51. Add Rake Tasks

  52. Create Rails Initializers

  53. after_initialize app_middleware before_configuration before_eager_load before_initialize generators to_prepare

  54. $ rake spec

  55. module  RSpec module  Rails class    Railtie  <  ::Rails::Railtie  

     config.generators.integration_tool  :rspec    config.generators.test_framework      :rspec    rake_tasks  do        load  "rspec/rails/tasks/rspec.rake"    end end end end
  56. Engines

  57. irb> Rails::Engine.ancestors # => [        [0]  Rails::Engine

     <  Rails::Railtie,        [1]  Rails::Railtie  <  Object,        [2]  Rails::Initializable,        [3]  Object  <  BasicObject,        [4]  Kernel,        [5]  BasicObject ]
  58. irb> MyRailsApp::Application.ancestors # => [        [0]  MyApp::Application

     <  Rails::Application,        [1]  Rails::Application  <  Rails::Engine,        [2]  Rails::Engine  <  Rails::Railtie,        [3]  Rails::Railtie  <  Object,        [4]  Rails::Initializable,        [5]  Object  <  BasicObject,        [6]  Kernel,        [7]  BasicObject ]
  59. None
  60. None
  61. None
  62. Images CSS Javascript Models Controllers Observers Mailers Migrations

  63. Rails.application.routes.draw  do    mount  MyEngine::Engine  =>  "/my_engine" end

  64. http://yourapp/admin

  65. Now What?

  66. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................ ................................................ ................................................ ................................................ ................................................ ................................................
  67. Gem Extraction

  68. None
  69. You're formatting them You're converting them to latitude/longitude You're validating

    them You're verifying them against an external API
  70. class  Event    include  ActiveModel::Model    define_attribute_methods    :venue_name,  

             :venue_street,                                                        :venue_city,            :venue_state,                                                        :venue_zip_code,    :venue_country    validates                                  :venue_name,                                                        :presence  =>  true    validates                                  :venue_city,                                                        :presence  =>  {                                                            :if  =>  :venue_partially_set?  }    #  Lots  more  lines  of  validations...    def  address_in_one_line_format;                #  Code  here;  end    def  address_in_multiline_text_format;    #  Code  here;  end    def  address_in_html_format;                        #  Code  here;  end end
  71. class  User    include  ActiveModel::Model    define_attribute_methods    :business_address_name,  

             :business_address_street,                                                        :business_address_city,            :business_address_state,                                                        :business_address_zip_code,    :business_address_country    validates                                  :business_address_name,                                                        :presence  =>  true    validates                                  :business_address_city,                                                        :presence  =>  {                                                            :if  =>  :business_address_partially_set?  }    #  Lots  more  lines  of  validations...    def  address_in_one_line_format;                #  Code  here;  end    def  address_in_multiline_text_format;    #  Code  here;  end    def  address_in_html_format;                        #  Code  here;  end end
  72. None
  73. class  Event    include  ActiveModel::Model    define_attribute_methods    :venue_name,  

             :venue_street,                                                        :venue_city,            :venue_state,                                                        :venue_zip_code,    :venue_country    validates                                  :venue_name,                                                        :presence  =>  true    validates                                  :venue_city,                                                        :presence  =>  {                                                            :if  =>  :venue_partially_set?  }    #  Lots  more  lines  of  validations...    def  address_in_one_line_format;                #  Code  here;  end    def  address_in_multiline_text_format;    #  Code  here;  end    def  address_in_html_format;                        #  Code  here;  end end
  74. class  Event    include  Pinpoint::Composable    pinpoint  :venue end

  75. event  =  Event.new(some_sweet_attributes_including_an_address) event.venue.to_s(:one_line) #  =>  'Kwik-­‐E-­‐Mart,  123  Apu  Lane,

     Springfield,  NW  12345,  United  States' event.venue.to_s(:multi_line) #  =>  'Kwik-­‐E-­‐Mart #          123  Apu  Lane #          Springfield,  NW  12345 #          United  States' event.venue.to_s(:html) #  =>  '<address> #              <span  class="section"> #                  <span  class="name">Kwik-­‐E-­‐Mart</span> #              </span> #              <span  class="section"> #                  <span  class="street">123  Apu  Lane</span> #              </span> #              <span  class="section"> #                  <span  class="city  locality">Springfield</span> #                  <span  class="state  state_or_province">NW</span> #                  <span  class="zip_code  postal_code">12345</span> #              </span> #              <span  class="section"> #                  <span  class="country">United  States</span> #              </span> #          </address>'
  76. None
  77. event.venue.to_s(:one_line,  :country  =>  :uk)

  78. None
  79. event.venue.to_s(:json)

  80. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................ ................................................ ................................................ ................................................ ................................................ ...................................
  81. Engine Extraction

  82. What services does my application provide to its users?

  83. What would I pay someone else to do?

  84. What services are others creating businesses around?

  85. None
  86. None
  87. None
  88. When placing an order for tickets, we provide them with

    a shopping cart that they can use to store their purchase choices and then give them a way to pay for them. Assuming that everything was successful, we send out an email for the receipt and the tickets to the user.
  89. When placing an order for tickets, we provide them with

    a shopping cart that they can use to store their purchase choices and then give them a way to pay for them. Assuming that everything was successful, we send out an email for the receipt and the tickets to the user.
  90. When placing an order for tickets, we provide them with

    a shopping cart that they can use to store their purchase choices and then give them a way to pay for them. Assuming that everything was successful, we send out an email for the receipt and the tickets to the user.
  91. When placing an order for tickets, we provide them with

    a shopping cart that they can use to store their purchase choices and then give them a way to pay for them. Assuming that everything was successful, we send out an email for the receipt and the tickets to the user.
  92. When placing an order for tickets, we provide them with

    a shopping cart that they can use to store their purchase choices and then give them a way to pay for them. Assuming that everything was successful, we send out an email for the receipt and the tickets to the user.
  93. When placing an order for tickets, we provide them with

    a shopping cart that they can use to store their purchase choices and then give them a way to pay for them. Assuming that everything was successful, we send out an email for the receipt and the tickets to the user.
  94. $ rails plugin new ticketing --mountable

  95. create README.rdoc create Rakefile create ticketing.gemspec create MIT-LICENSE create .gitignore

    create Gemfile create app create app/controllers/ticketing/application_controller.rb create app/helpers/ticketing/application_helper.rb create app/views/layouts/ticketing/application.html.erb create app/assets/images/ticketing create app/assets/images/ticketing/.gitkeep create config/routes.rb create lib/ticketing.rb create lib/tasks/ticketing_tasks.rake create lib/ticketing/version.rb create lib/ticketing/engine.rb create app/assets/stylesheets/ticketing/application.css create app/assets/javascripts/ticketing/application.js create script create script/rails create test/test_helper.rb create test/ticketing_test.rb append Rakefile create test/integration/navigation_test.rb vendor_app test/dummy
  96. create README.rdoc create Rakefile create ticketing.gemspec create MIT-LICENSE create .gitignore

    create Gemfile create app create app/controllers/ticketing/application_controller.rb create app/helpers/ticketing/application_helper.rb create app/views/layouts/ticketing/application.html.erb create app/assets/images/ticketing create app/assets/images/ticketing/.gitkeep create config/routes.rb create lib/ticketing.rb create lib/tasks/ticketing_tasks.rake create lib/ticketing/version.rb create lib/ticketing/engine.rb create app/assets/stylesheets/ticketing/application.css create app/assets/javascripts/ticketing/application.js create script create script/rails create test/test_helper.rb create test/ticketing_test.rb append Rakefile create test/integration/navigation_test.rb vendor_app test/dummy
  97. create README.rdoc create Rakefile create ticketing.gemspec create MIT-LICENSE create .gitignore

    create Gemfile create app create app/controllers/ticketing/application_controller.rb create app/helpers/ticketing/application_helper.rb create app/views/layouts/ticketing/application.html.erb create app/assets/images/ticketing create app/assets/images/ticketing/.gitkeep create config/routes.rb create lib/ticketing.rb create lib/tasks/ticketing_tasks.rake create lib/ticketing/version.rb create lib/ticketing/engine.rb create app/assets/stylesheets/ticketing/application.css create app/assets/javascripts/ticketing/application.js create script create script/rails create test/test_helper.rb create test/ticketing_test.rb append Rakefile create test/integration/navigation_test.rb vendor_app test/dummy
  98. create README.rdoc create Rakefile create ticketing.gemspec create MIT-LICENSE create .gitignore

    create Gemfile create app create app/controllers/ticketing/application_controller.rb create app/helpers/ticketing/application_helper.rb create app/views/layouts/ticketing/application.html.erb create app/assets/images/ticketing create app/assets/images/ticketing/.gitkeep create config/routes.rb create lib/ticketing.rb create lib/tasks/ticketing_tasks.rake create lib/ticketing/version.rb create lib/ticketing/engine.rb create app/assets/stylesheets/ticketing/application.css create app/assets/javascripts/ticketing/application.js create script create script/rails create test/test_helper.rb create test/ticketing_test.rb append Rakefile create test/integration/navigation_test.rb vendor_app test/dummy
  99. $ git push origin master

  100. gem  'ticketing',        :git  =>  'git://path/to/my/git/repo.git'

  101. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................ ................................................ ................................................ ................................................ ................................................ ...................................
  102. Lorem Ipsum Dolor Event Amet Consectetur Adipiscing Elit Vestibulum Lobortis

    DiscountCode Quis Est Gravida Posuere Aliquam Sit Amet Justo Leo In Facilisis Mi TicketType Diam Rutrum Vel Malesuada Ticket Dignissim
  103. Lorem Ipsum Dolor Event Amet Consectetur Adipiscing Elit Vestibulum Lobortis

    DiscountCode Quis Est Gravida Posuere Aliquam Sit Amet Justo Leo In Facilisis Mi TicketType Diam Rutrum Vel Malesuada Ticket Dignissim
  104. Lorem Ipsum Dolor Event Amet Consectetur Adipiscing Elit Vestibulum Lobortis

    DiscountCode Quis Est Gravida Posuere Aliquam Sit Amet Justo Leo In Facilisis Mi TicketType Diam Rutrum Vel Malesuada Ticket Dignissim
  105. Lorem Ipsum Dolor Event Amet Consectetur Adipiscing Elit Vestibulum Lobortis

    DiscountCode Quis Est Gravida Posuere Aliquam Sit Amet Justo Leo In Facilisis Mi TicketType Diam Rutrum Vel Malesuada Ticket Dignissim
  106. Lorem Ipsum Dolor Event Amet Consectetur Adipiscing Elit Vestibulum Lobortis

    DiscountCode Quis Est Gravida Posuere Aliquam Sit Amet Justo Leo In Facilisis Mi TicketType Diam Rutrum Vel Malesuada Ticket Dignissim
  107. main/app/models/event.rb main/app/models/discount_code.rb main/app/models/ticket_type.rb main/app/models/ticket.rb ticketing/app/models/event.rb ticketing/app/models/discount_code.rb ticketing/app/models/ticket_type.rb ticketing/app/models/ticket.rb

  108. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................ ................................................ ................................................ ................................................ ................................................ ...................................
  109. main/spec/models/event_spec.rb main/spec/models/discount_code_spec.rb main/spec/models/ticket_type_spec.rb main/spec/models/ticket_spec.rb ticketing/spec/models/event_spec.rb ticketing/spec/models/discount_code_spec.rb ticketing/spec/models/ticket_type_spec.rb ticketing/spec/models/ticket_spec.rb

  110. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................
  111. Muddied Boundaries

  112. User

  113. Ticketing Main Invoicing User

  114. it  ‘can  determine  its  account  balance’  do    #  Spec

     here end
  115. Ticketing Main Invoicing User

  116. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................
  117. ................................................ ................................................ ................................................ ................................................

  118. Ticketing Main Invoicing User Invoice

  119. email encrypted_password password_salt reset_password_token remember_token remember_created_at sign_in_count current_sign_in_at last_sign_in_at current_sign_in_ip

    last_sign_in_ip confirmation_token confirmed_at confirmation_sent_at created_at updated_at billing_first_name billing_last_name authentication_token public_email address_street_and_premises address_city address_state_or_province address_postal_code phone_number billing_address_street_and_premises billing_address_city billing_address_state_or_province billing_address_postal_code billing_phone_number name logo description address_name billing_address_name
  120. Ticketing Main Invoicing User Invoice

  121. Ticketing Main Invoicing Invoice Account EventOrganizer User

  122. Ticketing Main Invoicing Invoice Event Organizer Account User

  123. ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................

    ................................................
  124. Invoicing Ticketing Main Invoice Event Organizer Account User

  125. email encrypted_password password_salt reset_password_token remember_token remember_created_at sign_in_count current_sign_in_at last_sign_in_at current_sign_in_ip

    last_sign_in_ip confirmation_token confirmed_at confirmation_sent_at created_at updated_at billing_first_name billing_last_name authentication_token public_email address_street_and_premises address_city address_state_or_province address_postal_code phone_number billing_address_street_and_premises billing_address_city billing_address_state_or_province billing_address_postal_code billing_phone_number name logo description address_name billing_address_name
  126. Creating the Interface

  127. None
  128. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        event              =  Event.find(params[:event_id])        ticket_type  =  TicketType.find(params[:ticket_type_id])        params[:quantity].to_i.times  do            #  Create  the  Ticket            #  Apply  Discount  Codes            #  Send  Emails        end        redirect_to  some_other_path    end    #  Removed  code end
  129. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        event              =  Event.find(params[:event_id])        ticket_type  =  TicketType.find(params[:ticket_type_id])        params[:quantity].to_i.times  do            #  Create  the  Ticket            #  Apply  Discount  Codes            #  Send  Emails        end        redirect_to  some_other_path    end    #  Removed  code end
  130. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        event              =  Event.find(params[:event_id])        ticket_type  =  TicketType.find(params[:ticket_type_id])        params[:quantity].to_i.times  do            #  Create  the  Ticket            #  Apply  Discount  Codes            #  Send  Emails        end        redirect_to  some_other_path    end    #  Removed  code end
  131. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        event              =  Event.find(params[:event_id])        ticket_type  =  TicketType.find(params[:ticket_type_id])        params[:quantity].to_i.times  do            #  Create  the  Ticket            #  Apply  Discount  Codes            #  Send  Emails        end        redirect_to  some_other_path    end    #  Removed  code end
  132. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        event              =  Event.find(params[:event_id])        ticket_type  =  TicketType.find(params[:ticket_type_id])        params[:quantity].to_i.times  do            #  Create  the  Ticket            #  Apply  Discount  Codes            #  Send  Emails        end        redirect_to  some_other_path    end    #  Removed  code end
  133. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        event              =  Event.find(params[:event_id])        ticket_type  =  TicketType.find(params[:ticket_type_id])        params[:quantity].to_i.times  do            #  Create  the  Ticket            #  Apply  Discount  Codes            #  Send  Emails        end        redirect_to  some_other_path    end    #  Removed  code end
  134. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        BoxOffice.register_tickets(  type:                              params[:ticket_type_id]                                                                quantity:                      params[:quantity],                                                                send_notifications:  true)        redirect_to  some_other_path    end    #  Removed  code end
  135. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        BoxOffice.register_tickets(  type:                              params[:ticket_type_id]                                                                quantity:                      params[:quantity],                                                                send_notifications:  true)        redirect_to  some_other_path    end    #  Removed  code end
  136. class  TicketsController  <  ApplicationController    #  Removed  code    def

     create        BoxOffice.register_tickets(  type:                              params[:ticket_type_id]                                                                quantity:                      params[:quantity],                                                                send_notifications:  true)        redirect_to  some_other_path    end    #  Removed  code end
  137. Progress Check

  138. Engines Gems Total Test Time: ~ 2.5 minutes Average Per-Gem

    Test Time: ~ 4 seconds
  139. None
  140. None
  141. semver.org

  142. Step 3: Profit

  143. None
  144. None
  145. None
  146. Main Ticketing Invoicing User Ticketing Ticketing Load Balancer

  147. Conclusion

  148. None
  149. Image Credits https://secure.flickr.com/photos/lwr/168150955/in/photostream/ http://alleverestnepal.files.wordpress.com/2011/07/everest-summit-view.jpg http://www.engineeringwellness.com/wp-content/uploads/2013/02/bigstock-Child-Listening-19669727.jpg http://www.parentspartner.com/wp-content/uploads/2011/05/angry-girl.jpg http://www.buriedwithchildren.com/wp-content/uploads/2010/02/DSCN3428.jpg http://fc08.deviantart.net/fs70/f/2012/038/2/d/1980__s___cartoon_a_gasim___by_timswit-d4p0eta.jpg http://www.pachd.com/free-images/household-images/20-dollar-bill-01.jpg http://cdn.madamenoire.com/wp-content/uploads/2011/10/Creepy-Guy.jpg

    http://images.wikia.com/lionking/images/c/c2/Simba_amazed.jpeg http://www.o5demo.com/userfiles/Aerial%20view4.jpg http://www.propstore.com/img/products/182/Waynes%20World_Garth%20Glasses.jpg https://kodequirks.files.wordpress.com/2010/05/fail.png?w=640 http://www.hotforsecurity.com/wp-content/uploads/2013/01/ruby-on-rails-steams-critical-security-patch.jpg http://iphonewallpapers-hd.com/walls/travel_for_kids_mac_train_wallpapers_rail_trip-other.jpg https://images-na.ssl-images-amazon.com/images/G/01/dvd/lionsgate/Thomas2_lg.jpg http://www.vacationrentalmarketingblog.com/wp-content/uploads/2012/06/12969446611855980761do-not-symbol.jpg http://www.mostphotos.com/preview/389643/3d-ruby-gem-isolated.jpg http://thumbs.imagekind.com/member/3730ab28-65ec-4744-a89a-a36f0f6282bb/uploadedartwork/650X650/9c4774b8-b41a-4896-bef7-0c65ec7c9825.jpg http://www2.2space.net/images/upl_news/111017/386382/full/%26%23039%3Bsesame-street%26%23039%3B-back-online-after-porn-hacking.jpg https://www.library.qut.edu.au/blog/wp-content/uploads/2011/10/UN_flags_access_copy1.jpg http://www.hobbeslives.com/wp-content/uploads/2013/02/jigsaw-puzzle-2.jpg https://emilyrudisill.files.wordpress.com/2011/06/dsc04539.jpg https://lh3.ggpht.com/_WJjc0N1hIH4/S9ohkRZnDBI/AAAAAAAAK6M/0L-nWy8qsTs/s1600/old+ticket+stub.JPG http://theinsuranceblogger.co.uk/wp-content/uploads/2011/03/black-box.jpg http://raedevelopment.com/wp-content/uploads/2012/09/team-hands1.jpg https://krivstudiosblog.files.wordpress.com/2008/08/stephanie_michelle05.jpg http://zachandjody.com/blog/wp-content/uploads/2010/08/Kid-Stuffing-Face.jpg http://img1.etsystatic.com/005/1/6446101/il_fullxfull.383264621_3lm4.jpg http://www.whatsupyasieve.com/wp-content/uploads/2012/09/sad-panda.jpg http://remixacupuncture.com/wp-content/uploads/2013/05/ren-and-stimpy-1-745973.jpg https://lh3.ggpht.com/-uor9A9sz6xU/UZD4hwwnoEI/AAAAAAAAAnI/sXgYd4u946o/s1600/evildead2bdcap2_original.jpg https://lh3.ggpht.com/-L6z1kIIhPkc/T8wFQ_BdIDI/AAAAAAAABxU/42mDrQkAeqo/s1600/1%2Ba%2Ba%2Ba%2Btl%2Batirando.jpg http://michaelpiggott.files.wordpress.com/2012/12/thunder-buddies.jpg
  150. Thanks! Jeff Felchner @jfelchner