Your own (little) gem: building an online business with Ruby

2,233 views
2,160 views

Published on

0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,233
On SlideShare
0
From Embeds
0
Number of Embeds
19
Actions
Shares
0
Downloads
64
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

Your own (little) gem: building an online business with Ruby

  1. 1. Your own gem (little) building an online business with Ruby Lindsay Holmwood <lindsay@holmwood.id.au>
  2. 2. (.com)
  3. 3. business development operations
  4. 4. have an idea
  5. 5. check that it’s profitable
  6. 6. apples oranges are both fruit
  7. 7. web store book shop are both businesses
  8. 8. financials
  9. 9. are important.
  10. 10. do them!
  11. 11. (conservative estimates are best)
  12. 12. check your tech
  13. 13. time is an expense
  14. 14. halve your deliverables
  15. 15. then halve them again
  16. 16. 1-2 core features
  17. 17. write ideas down
  18. 18. development
  19. 19. Merb (merbivore.com) development
  20. 20. Merb (merbivore.com) DataMapper (datamapper.org) development
  21. 21. Merb (merbivore.com) DataMapper (datamapper.org) MooTools (mootools.net) development
  22. 22. development
  23. 23. bootstrap
  24. 24. $ sudo gem install merb-core merb-more bootstrap
  25. 25. $ sudo gem install merb-core merb-more $ sudo gem install dm-core dm-more do_sqlite3 bootstrap
  26. 26. $ sudo gem install merb-core merb-more $ sudo gem install dm-core dm-more do_sqlite3 $ merb-gen app schoonerwatch.com bootstrap
  27. 27. $ sudo gem install merb-core merb-more $ sudo gem install dm-core dm-more do_sqlite3 $ merb-gen app schoonerwatch.com $ cd schoonerwatch.com bootstrap
  28. 28. $ sudo gem install merb-core merb-more $ sudo gem install dm-core dm-more do_sqlite3 $ merb-gen app schoonerwatch.com $ cd schoonerwatch.com $ bzr init ; bzr add $ bzr commit -m quot;initial commitquot; bootstrap
  29. 29. $ sudo gem install merb-core merb-more $ sudo gem install dm-core dm-more do_sqlite3 $ merb-gen app schoonerwatch.com $ cd schoonerwatch.com $ bzr init ; bzr add $ bzr commit -m quot;initial commitquot; s/bzr/git/g bootstrap
  30. 30. merb-gen
  31. 31. $ merb & merb-gen
  32. 32. $ merb & $ merb-gen resource pub merb-gen
  33. 33. $ merb & $ merb-gen resource pub $ bzr commit -m quot;added merb-gen'd pub resourcequot; merb-gen
  34. 34. $ merb & $ merb-gen resource pub $ bzr commit -m quot;added merb-gen'd pub resourcequot; $ vim spec/spec.opts spec/spec_helper.rb merb-gen
  35. 35. --colour --format profile spec/spec.opts
  36. 36. ... Spec::Runner.configure do |config| config.include(Merb::Test::ViewHelper) config.include(Merb::Test::RouteHelper) config.include(Merb::Test::ControllerHelper) end DataMapper.auto_migrate! spec/spec_helper.rb
  37. 37. run specs
  38. 38. $ rake spec:request run specs
  39. 39. $ rake spec:request .**...... Pending: resource(:pubs) GET contains a list of pubs (TODO) Called from ./spec/requests/pubs_spec.rb:21 resource(:pubs) GET has a list of pubs (TODO) Called from ./spec/requests/pubs_spec.rb:33 Finished in 0.322787 seconds 9 examples, 0 failures, 2 pending run specs
  40. 40. request specs
  41. 41. $ vim spec/requests/pubs_spec.rb request specs
  42. 42. $ vim spec/requests/pubs_spec.rb ... it quot;contains a sorted list of pubsquot; do @response.should have_xpath(quot;//h3[contains(.,'Cheapest')]quot;) @response.should have_xpath(quot;//h3[contains(.,'Nearest')]quot;) end request specs
  43. 43. run specs
  44. 44. $ rake spec:request run specs
  45. 45. $ rake spec:request Pending: resource(:pubs) GET has a list of pubs (TODO) Called from ./spec/requests/pubs_spec.rb:33 1) 'resource(:pubs) GET contains a sorted list of pubs' FAILED expected following text to match xpath //h3[contains(.,'Cheapest')]’ ./spec/requests/pubs_spec.rb:21: Finished in 0.411139 seconds 9 examples, 1 failure, 1 pending run specs
  46. 46. haml
  47. 47. $ vim config/dependencies.rb haml
  48. 48. $ vim config/dependencies.rb ... dependency quot;merb-hamlquot;, merb_gems_version haml
  49. 49. $ vim config/dependencies.rb ... dependency quot;merb-hamlquot;, merb_gems_version $ vim config/init.rb haml
  50. 50. $ vim config/dependencies.rb ... dependency quot;merb-hamlquot;, merb_gems_version $ vim config/init.rb ... use_template_engine :haml haml
  51. 51. hamlise view
  52. 52. $ bzr mv app/views/pubs/index.html.erb app/views/pubs/index.html.haml hamlise view
  53. 53. $ bzr mv app/views/pubs/index.html.erb app/views/pubs/index.html.haml $ vim app/views/pubs/index.html.haml hamlise view
  54. 54. $ bzr mv app/views/pubs/index.html.erb app/views/pubs/index.html.haml $ vim app/views/pubs/index.html.haml %h3 Nearest %h3 Cheapest hamlise view
  55. 55. $ bzr mv app/views/pubs/index.html.erb app/views/pubs/index.html.haml $ vim app/views/pubs/index.html.haml %h3 Nearest %h3 Cheapest $ rake spec:request Pending: resource(:pubs) GET has a list of pubs (TODO) Called from ./spec/requests/pubs_spec.rb:33 Finished in 0.389194 seconds 9 examples, 0 failures, 1 pending hamlise view tests pass!
  56. 56. model specs
  57. 57. $ vim spec/models/pubs_spec.rb model specs
  58. 58. $ vim spec/models/pubs_spec.rb 1. model specs
  59. 59. $ vim spec/models/pubs_spec.rb 1. it quot;should have name, address, pricequot; do @pubs.each do |pub| pub.name.should_not be_nil pub.address.should_not be_nil pub.price.should_not be_nil end end model specs
  60. 60. $ vim spec/models/pubs_spec.rb 2. it quot;should handle different url formatsquot; do @pubs.each do |pub| pub.url = 'mygreatpub.com.au' pub.save.should be_true pub.url = 'http://mygreatpub.com.au/' pub.save.should be_true end end model specs
  61. 61. $ vim spec/models/pubs_spec.rb 3. it quot;should consistently format + store urlsquot; do @pubs.each do |pub| adjective = %w[great swanky awesome stylish][rand(4)] pub.url = quot;#{adjective}pub.com.auquot; pub.save.should be_true pub.url.should =~ /^http://.+/$/ end end model specs
  62. 62. model specs
  63. 63. $ vim spec/models/pubs_spec.rb before(:each) do @pubs = 10.of { Pub.generate } end model specs
  64. 64. $ vim spec/models/pubs_spec.rb before(:each) do @pubs = 10.of { Pub.generate } end $ vim spec/spec_fixtures.rb require 'dm-sweatshop' Pub.fixture do { :name => ( name = %w[John Jane Jerry Justin][rand(4)] + quot;'s Pubquot;), :price => (1..30).to_a[rand(30)], :address => quot;#{(40..166).to_a[rand(126)]} Spring quot; + %w[Street Road Avenue][rand(3)], :url => quot;http://#{name.gsub(/W/, '').downcase}.com/quot; } end model specs
  65. 65. run specs
  66. 66. $ rake spec:models run specs
  67. 67. $ rake spec:models 1) NameError in 'Pub should have name, address, price' address= is not a public property 2) NameError in 'Pub should handle different url formats' address= is not a public property 3) NameError in 'Pub should consistently format + store urls' address= is not a public property Finished in 0.252169 seconds 3 examples, 3 failures run specs
  68. 68. define properties
  69. 69. $ vim app/models/pub.rb define properties
  70. 70. $ vim app/models/pub.rb class Pub include DataMapper::Resource property :id, Serial property :name, String, :nullable => false property :address, String, :nullable => false property :price, BigDecimal, :nullable => false property :url, String define properties
  71. 71. $ vim app/models/pub.rb ... def url=(string) string = quot;http://#{string}quot; unless string =~ /^http://.+// string += '/' unless string =~ //$/ @url = string end end custom setter
  72. 72. run specs
  73. 73. $ rake spec:models run specs
  74. 74. $ rake spec:models Finished in 0.488351 seconds 3 examples, 0 failures run specs win!
  75. 75. request specs
  76. 76. $ vim spec/requests/pubs_spec.rb request specs
  77. 77. $ vim spec/requests/pubs_spec.rb ... it quot;contains a list of cheapest pubsquot; do @response.should have_xpath(quot;//div[@id=’cheapest’]quot;) @response.should have_xpath(quot;//div[@id=’cheapest’]//table//tr//td[@class='name']quot;) @response.should have_xpath(quot;//div[@id=’cheapest’]//table//tr//td[@class='address']quot;) @response.should have_xpath(quot;//div[@id=’cheapest’]//table//tr//td[contains(@class,'price')]quot;) end request specs
  78. 78. run specs
  79. 79. $ rake spec:request run specs
  80. 80. $ rake spec:request Pending: resource(:pubs) GET has a list of pubs (TODO) Called from ./spec/requests/pubs_spec.rb:37 1) 'resource(:pubs) GET contains a list cheapest pubs' FAILED expected following text to match xpath //div[@id='cheapest'] ./spec/requests/pubs_spec.rb:26: Finished in 0.411139 seconds 10 examples, 1 failure, 1 pending run specs
  81. 81. build partial
  82. 82. $ vim app/views/pubs/index.html.haml build partial
  83. 83. $ vim app/views/pubs/index.html.haml %h3 Nearest %h3 Cheapest %div#cheapest = partial quot;cheapestquot; build partial
  84. 84. $ vim app/views/pubs/index.html.haml %h3 Nearest %h3 Cheapest %div#cheapest = partial quot;cheapestquot; $ vim app/views/pubs/_cheapest.html.haml build partial
  85. 85. $ vim app/views/pubs/index.html.haml %h3 Nearest %h3 Cheapest %div#cheapest = partial quot;cheapestquot; $ vim app/views/pubs/_cheapest.html.haml %table - @pubs.each_with_index do |pub, index| %tr %td.name= link_to pub.name, pub.url %td.address= pub.address %td{:class => quot;price cheapest-#{index}quot;} = '$' + sprintf(quot;%.2fquot;, pub.price) build partial
  86. 86. run specs
  87. 87. $ rake spec:request run specs
  88. 88. $ rake spec:request Pending: resource(:pubs) GET has a list of pubs (TODO) Called from ./spec/requests/pubs_spec.rb:37 Finished in 0.854704 seconds 10 examples, 0 failures, 1 pending run specs
  89. 89. nearest
  90. 90. $ vim app/views/pubs/index.html.haml nearest
  91. 91. $ vim app/views/pubs/index.html.haml %h3 Nearest %div#nearest = partial quot;nearestquot; %h3 Cheapest %div#cheapest = partial quot;cheapestquot; nearest
  92. 92. $ vim app/views/pubs/index.html.haml %h3 Nearest %div#nearest = partial quot;nearestquot; %h3 Cheapest %div#cheapest = partial quot;cheapestquot; $ vim app/views/pubs/_nearest.html.haml nearest
  93. 93. $ vim app/views/pubs/index.html.haml %h3 Nearest %div#nearest = partial quot;nearestquot; %h3 Cheapest %div#cheapest = partial quot;cheapestquot; $ vim app/views/pubs/_nearest.html.haml %div#map{:style => quot;width: 800px; height: 600px; margin: auto;quot;} nearest
  94. 94. global layout
  95. 95. $ vim app/views/layout/application.html.erb global layout
  96. 96. $ vim app/views/layout/application.html.erb <%= js_include_tag 'mootools' %> <%= js_include_tag 'SimpleTabs' %> <%= js_include_tag 'application' %> <script src=quot;http://maps.google.com/maps?file=api&amp;v=2&amp;key= <%= Merb.config.gmaps_api_key %>quot; type=quot;text/javascriptquot;></script> global layout
  97. 97. $ vim app/views/layout/application.html.erb <%= js_include_tag 'mootools' %> <%= js_include_tag 'SimpleTabs' %> <%= js_include_tag 'application' %> <script src=quot;http://maps.google.com/maps?file=api&amp;v=2&amp;key= <%= Merb.config.gmaps_api_key %>quot; type=quot;text/javascriptquot;></script> $ vim config/environments/development.rb c[:gmaps_api_key] = quot;fooquot; api key
  98. 98. $ vim app/views/layout/application.html.erb <%= js_include_tag 'mootools' %> <%= js_include_tag 'SimpleTabs' %> <%= js_include_tag 'application' %> <script src=quot;http://maps.google.com/maps?file=api&amp;v=2&amp;key= <%= Merb.config.gmaps_api_key %>quot; type=quot;text/javascriptquot;></script> $ vim config/environments/production.rb c[:gmaps_api_key] = quot;barquot; production api key
  99. 99. $ vim public/javascripts/application.js window.addEvent('domready', function() { }); nearest
  100. 100. $ vim public/javascripts/application.js window.addEvent('domready', function() { if (GBrowserIsCompatible()) { var map = new GMap2($(quot;mapquot;)); map.enableScrollWheelZoom(); map.setCenter(new GLatLng(-33.86336, 151.207151), 12); var geocoder = new GClientGeocoder(); /* geocode top-ten addresses */ $$('div#cheapest tr').each( function(element) { showAddress(map, geocoder, element); }); } }); dash o’ javascript
  101. 101. $ vim public/javascripts/application.js function showAddress(map, geocoder, element) { var name = element.getElements('td.name a').get('html'); var address = element.getChildren('td.address').get('html'); var price = element.getElements('td.price').get('html'); var link = element.getElements('td.name a').get('href'); geocoder.getLatLng(address, function(point) { if (point) { var marker = new GMarker(point); marker.bindInfoWindowHtml(quot;<h4>quot; + name + quot;</h4>quot; + ... ); map.addOverlay(marker); } }); } custom geocoder
  102. 102. some stylesheet foo
  103. 103. slices freezing param-protection action-args
  104. 104. operations
  105. 105. monitoring
  106. 106. collectd
  107. 107. lightweight statistics collection daemon
  108. 108. lightweight statistics collection daemon
  109. 109. LoadPlugin cpu LoadPlugin df LoadPlugin disk LoadPlugin interface LoadPlugin load LoadPlugin memory LoadPlugin network LoadPlugin processes LoadPlugin rrdtool LoadPlugin socket LoadPlugin swap LoadPlugin users ...
  110. 110. <Plugin df> MountPoint quot;/quot; </Plugin> <Plugin interface> Interface quot;eth0quot; Interface quot;eth1quot; </Plugin> ...
  111. 111. <Plugin rrdtool> DataDir quot;/var/lib/collectd/rrdquot; CacheTimeout 180 CacheFlush 7200 </Plugin> <Plugin network> Server quot;my.monitoring.server.netquot; # Port 25826 </Plugin> ...
  112. 112. <Plugin processes> Process quot;rubyquot; # Process quot;mysqldquot; </Plugin> ...
  113. 113. eek!
  114. 114. collectd-nagios
  115. 115. $ /usr/bin/collectd-nagios -s /var/run/collectd/socket -n cpu-0/cpu-idle -H my.server.com -w 40: -c 10: 0 critical, 1 warning, 0 okay | value=34.000000;;;; $ /usr/bin/collectd-nagios -s /var/run/collectd/socket -n df/df-root -H my.server.com -d free -c 314572800: -w 524288000: 0 critical, 0 warning, 1 okay |free=924288000.0;;;;
  116. 116. 0 good 1 bad 2 ugly
  117. 117. $ ls /var/lib/collectd/rrd/my.server.com cpu-0 cpu-1 df disk-sda disk-sda1 interface load memory swap users
  118. 118. apache apcups apple sensors ascent battery cpu cpufreq csv df disk dns email entropy exec filecount hddtemp interface iptables ipmi ipvs irqs libvirt load logfile mbmon memcached memory multimeter mysql netlink network nfs nginx notify_desktop notify_email ntpd nut onewire perl ping postgresql powerdns processes rrdtool sensors serial snmp swap syslog tail tape tcpconns teamspeak2 thermal unixsock users uuid vmem vserver wireless
  119. 119. apache apcups apple sensors ascent battery cpu cpufreq csv df disk dns email entropy exec filecount hddtemp interface iptables ipmi ipvs irqs libvirt load logfile mbmon memcached memory multimeter mysql netlink network nfs nginx notify_desktop notify_email ntpd nut onewire perl ping postgresql powerdns processes rrdtool sensors serial snmp swap syslog tail tape tcpconns teamspeak2 thermal unixsock users uuid vmem vserver wireless
  120. 120. configuration management
  121. 121. Puppet language => client/server => library
  122. 122. manifests types classes nodes
  123. 123. manifests => describe types classes nodes
  124. 124. manifests types => manage classes nodes
  125. 125. package { apache2: ensure => present }
  126. 126. package { apache2: ensure => present } file { quot;/etc/apache2/apache2.confquot;, content => template(quot;/etc/puppet/ config/classes/httpd_server/ httpd.conf.erbquot;), mode => 644 }
  127. 127. package { apache2: ensure => present } file { quot;/etc/apache2/apache2.confquot;, content => template(quot;/etc/puppet/ config/classes/httpd_server/ httpd.conf.erbquot;), mode => 644 } service { apache2: ensure => running, subscribe => File[quot;/etc/apache2/ apache2.confquot;] }
  128. 128. manifests types => manage classes nodes
  129. 129. manifests types classes => group nodes
  130. 130. package { apache2: ensure => present } # ...
  131. 131. class apache2 { package { apache2: ensure => present } # ... }
  132. 132. class passenger inherits apache2 { package { libapache2-mod-passenger: ensure => present } # ... }
  133. 133. define apache2 ($port) { package { apache2: ensure => present } # ... }
  134. 134. manifests types classes => group nodes
  135. 135. manifests types classes nodes => apply
  136. 136. node quot;srv01.skippy.comquot; { include passenger }
  137. 137. node quot;srv01.skippy.comquot; { include passenger }
  138. 138. node quot;srv01.skippy.comquot; { include passenger } node quot;server02.skippy.comquot; { include passenger } node quot;server03.skippy.comquot; { include passenger }
  139. 139. node quot;merbnodequot; { include passenger } node quot;server01.skippy.comquot; inherits passenger {} node quot;server02.skippy.comquot; inherits passenger {} node quot;server03.skippy.comquot; { include customisation }
  140. 140. versionable infrastructure
  141. 141. versionable infrastructure (for free)
  142. 142. $ cd skippy.puppet.lindsay $ git init # puppet magic happens $ git push
  143. 143. # puppetd --test --debug
  144. 144. resources
  145. 145. getting real seth godin wiki.merbivore.com
  146. 146. Thank you! (and questions)

×