sum 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. data/Capfile +3 -0
  2. data/MIT-LICENSE +18 -0
  3. data/README.markdown +69 -0
  4. data/Rakefile +56 -0
  5. data/config.ru +3 -0
  6. data/config/database.example.yml +10 -0
  7. data/config/deploy.example.rb +36 -0
  8. data/config/externals.yml +18 -0
  9. data/config/mail.example.yml +32 -0
  10. data/config/schedule.rb +20 -0
  11. data/db/migrate/001_users.rb +22 -0
  12. data/db/migrate/002_user_emails.rb +15 -0
  13. data/features/front_page.feature +15 -0
  14. data/features/read_email.feature +445 -0
  15. data/features/receive_email.feature +59 -0
  16. data/features/step_definitions/email.rb +52 -0
  17. data/features/step_definitions/given.rb +60 -0
  18. data/features/step_definitions/then.rb +36 -0
  19. data/features/step_definitions/when.rb +46 -0
  20. data/features/support/env.rb +38 -0
  21. data/features/support/helpers.rb +50 -0
  22. data/features/support/rspec.rb +11 -0
  23. data/features/support/webrat.rb +3 -0
  24. data/features/update_user.feature +83 -0
  25. data/gemspec.rb +45 -0
  26. data/lib/sum.rb +30 -0
  27. data/lib/sum/boot.rb +29 -0
  28. data/lib/sum/controller/cron.rb +12 -0
  29. data/lib/sum/controller/front.rb +6 -0
  30. data/lib/sum/controller/new.rb +15 -0
  31. data/lib/sum/helper/application.rb +8 -0
  32. data/lib/sum/helper/cron.rb +12 -0
  33. data/lib/sum/helper/new.rb +27 -0
  34. data/lib/sum/model/incoming_mail.rb +89 -0
  35. data/lib/sum/model/user.rb +298 -0
  36. data/lib/sum/model/user_email.rb +31 -0
  37. data/lib/sum/view/email.haml +70 -0
  38. data/lib/sum/view/field.haml +23 -0
  39. data/lib/sum/view/form.haml +13 -0
  40. data/lib/sum/view/front.haml +29 -0
  41. data/lib/sum/view/layout.haml +26 -0
  42. data/lib/sum/view/new.haml +5 -0
  43. data/lib/sum/view/success.haml +14 -0
  44. data/lib/sum/view/tos.haml +3 -0
  45. data/public/image/1.png +0 -0
  46. data/public/image/2.png +0 -0
  47. data/public/image/3.png +0 -0
  48. data/public/image/bg.png +0 -0
  49. data/public/image/bot.png +0 -0
  50. data/public/image/facebox/b.png +0 -0
  51. data/public/image/facebox/bl.png +0 -0
  52. data/public/image/facebox/br.png +0 -0
  53. data/public/image/facebox/closelabel.gif +0 -0
  54. data/public/image/facebox/loading.gif +0 -0
  55. data/public/image/facebox/tl.png +0 -0
  56. data/public/image/facebox/tr.png +0 -0
  57. data/public/image/favicon.png +0 -0
  58. data/public/image/field_off.png +0 -0
  59. data/public/image/field_on.png +0 -0
  60. data/public/image/ribbon.png +0 -0
  61. data/public/image/screenshot_receive.png +0 -0
  62. data/public/image/screenshot_send.png +0 -0
  63. data/public/image/submit.png +0 -0
  64. data/public/image/top.png +0 -0
  65. data/public/javascript/facebox.js +319 -0
  66. data/public/javascript/jquery-1.3.2.js +19 -0
  67. data/public/javascript/sum.js +13 -0
  68. data/public/style/facebox.css +95 -0
  69. data/public/style/ie.css +35 -0
  70. data/public/style/print.css +30 -0
  71. data/public/style/screen.css +256 -0
  72. data/public/style/sum.css +119 -0
  73. data/public/style/sum.less +159 -0
  74. data/spec/spec.opts +1 -0
  75. data/spec/spec_helper.rb +59 -0
  76. data/spec/sum/model/incoming_mail_spec.rb +106 -0
  77. data/spec/sum/model/user_spec.rb +172 -0
  78. data/sum.gemspec +69 -0
  79. data/vendor/webrat/History.txt +358 -0
  80. data/vendor/webrat/MIT-LICENSE.txt +19 -0
  81. data/vendor/webrat/README.rdoc +85 -0
  82. data/vendor/webrat/Rakefile +200 -0
  83. data/vendor/webrat/install.rb +1 -0
  84. data/vendor/webrat/lib/webrat.rb +31 -0
  85. data/vendor/webrat/lib/webrat/core.rb +14 -0
  86. data/vendor/webrat/lib/webrat/core/configuration.rb +102 -0
  87. data/vendor/webrat/lib/webrat/core/elements/area.rb +31 -0
  88. data/vendor/webrat/lib/webrat/core/elements/element.rb +33 -0
  89. data/vendor/webrat/lib/webrat/core/elements/field.rb +409 -0
  90. data/vendor/webrat/lib/webrat/core/elements/form.rb +103 -0
  91. data/vendor/webrat/lib/webrat/core/elements/label.rb +31 -0
  92. data/vendor/webrat/lib/webrat/core/elements/link.rb +93 -0
  93. data/vendor/webrat/lib/webrat/core/elements/select_option.rb +35 -0
  94. data/vendor/webrat/lib/webrat/core/locators.rb +20 -0
  95. data/vendor/webrat/lib/webrat/core/locators/area_locator.rb +38 -0
  96. data/vendor/webrat/lib/webrat/core/locators/button_locator.rb +54 -0
  97. data/vendor/webrat/lib/webrat/core/locators/field_by_id_locator.rb +37 -0
  98. data/vendor/webrat/lib/webrat/core/locators/field_labeled_locator.rb +56 -0
  99. data/vendor/webrat/lib/webrat/core/locators/field_locator.rb +25 -0
  100. data/vendor/webrat/lib/webrat/core/locators/field_named_locator.rb +41 -0
  101. data/vendor/webrat/lib/webrat/core/locators/form_locator.rb +19 -0
  102. data/vendor/webrat/lib/webrat/core/locators/label_locator.rb +34 -0
  103. data/vendor/webrat/lib/webrat/core/locators/link_locator.rb +74 -0
  104. data/vendor/webrat/lib/webrat/core/locators/locator.rb +20 -0
  105. data/vendor/webrat/lib/webrat/core/locators/select_option_locator.rb +59 -0
  106. data/vendor/webrat/lib/webrat/core/logging.rb +24 -0
  107. data/vendor/webrat/lib/webrat/core/matchers.rb +4 -0
  108. data/vendor/webrat/lib/webrat/core/matchers/have_content.rb +73 -0
  109. data/vendor/webrat/lib/webrat/core/matchers/have_selector.rb +74 -0
  110. data/vendor/webrat/lib/webrat/core/matchers/have_tag.rb +21 -0
  111. data/vendor/webrat/lib/webrat/core/matchers/have_xpath.rb +147 -0
  112. data/vendor/webrat/lib/webrat/core/methods.rb +63 -0
  113. data/vendor/webrat/lib/webrat/core/mime.rb +29 -0
  114. data/vendor/webrat/lib/webrat/core/save_and_open_page.rb +48 -0
  115. data/vendor/webrat/lib/webrat/core/scope.rb +350 -0
  116. data/vendor/webrat/lib/webrat/core/session.rb +299 -0
  117. data/vendor/webrat/lib/webrat/core/xml.rb +115 -0
  118. data/vendor/webrat/lib/webrat/core/xml/hpricot.rb +19 -0
  119. data/vendor/webrat/lib/webrat/core/xml/nokogiri.rb +76 -0
  120. data/vendor/webrat/lib/webrat/core/xml/rexml.rb +24 -0
  121. data/vendor/webrat/lib/webrat/core_extensions/blank.rb +58 -0
  122. data/vendor/webrat/lib/webrat/core_extensions/deprecate.rb +8 -0
  123. data/vendor/webrat/lib/webrat/core_extensions/detect_mapped.rb +12 -0
  124. data/vendor/webrat/lib/webrat/core_extensions/meta_class.rb +6 -0
  125. data/vendor/webrat/lib/webrat/core_extensions/nil_to_param.rb +5 -0
  126. data/vendor/webrat/lib/webrat/core_extensions/tcp_socket.rb +27 -0
  127. data/vendor/webrat/lib/webrat/mechanize.rb +74 -0
  128. data/vendor/webrat/lib/webrat/merb.rb +9 -0
  129. data/vendor/webrat/lib/webrat/merb_multipart_support.rb +27 -0
  130. data/vendor/webrat/lib/webrat/merb_session.rb +82 -0
  131. data/vendor/webrat/lib/webrat/rack.rb +25 -0
  132. data/vendor/webrat/lib/webrat/rails.rb +106 -0
  133. data/vendor/webrat/lib/webrat/rspec-rails.rb +10 -0
  134. data/vendor/webrat/lib/webrat/selenium.rb +81 -0
  135. data/vendor/webrat/lib/webrat/selenium/application_server_factory.rb +40 -0
  136. data/vendor/webrat/lib/webrat/selenium/application_servers.rb +5 -0
  137. data/vendor/webrat/lib/webrat/selenium/application_servers/base.rb +46 -0
  138. data/vendor/webrat/lib/webrat/selenium/application_servers/external.rb +26 -0
  139. data/vendor/webrat/lib/webrat/selenium/application_servers/merb.rb +50 -0
  140. data/vendor/webrat/lib/webrat/selenium/application_servers/rails.rb +44 -0
  141. data/vendor/webrat/lib/webrat/selenium/application_servers/sinatra.rb +37 -0
  142. data/vendor/webrat/lib/webrat/selenium/location_strategy_javascript/button.js +19 -0
  143. data/vendor/webrat/lib/webrat/selenium/location_strategy_javascript/label.js +16 -0
  144. data/vendor/webrat/lib/webrat/selenium/location_strategy_javascript/webrat.js +5 -0
  145. data/vendor/webrat/lib/webrat/selenium/location_strategy_javascript/webratlink.js +12 -0
  146. data/vendor/webrat/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
  147. data/vendor/webrat/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
  148. data/vendor/webrat/lib/webrat/selenium/matchers.rb +4 -0
  149. data/vendor/webrat/lib/webrat/selenium/matchers/have_content.rb +66 -0
  150. data/vendor/webrat/lib/webrat/selenium/matchers/have_selector.rb +49 -0
  151. data/vendor/webrat/lib/webrat/selenium/matchers/have_tag.rb +72 -0
  152. data/vendor/webrat/lib/webrat/selenium/matchers/have_xpath.rb +45 -0
  153. data/vendor/webrat/lib/webrat/selenium/selenium_extensions.js +6 -0
  154. data/vendor/webrat/lib/webrat/selenium/selenium_rc_server.rb +84 -0
  155. data/vendor/webrat/lib/webrat/selenium/selenium_session.rb +248 -0
  156. data/vendor/webrat/lib/webrat/selenium/silence_stream.rb +18 -0
  157. data/vendor/webrat/lib/webrat/sinatra.rb +11 -0
  158. data/vendor/webrat/spec/fakes/test_session.rb +34 -0
  159. data/vendor/webrat/spec/integration/merb/Rakefile +35 -0
  160. data/vendor/webrat/spec/integration/merb/app/controllers/application.rb +2 -0
  161. data/vendor/webrat/spec/integration/merb/app/controllers/exceptions.rb +13 -0
  162. data/vendor/webrat/spec/integration/merb/app/controllers/testing.rb +27 -0
  163. data/vendor/webrat/spec/integration/merb/app/views/exceptions/not_acceptable.html.erb +63 -0
  164. data/vendor/webrat/spec/integration/merb/app/views/exceptions/not_found.html.erb +47 -0
  165. data/vendor/webrat/spec/integration/merb/app/views/layout/application.html.erb +12 -0
  166. data/vendor/webrat/spec/integration/merb/app/views/testing/show_form.html.erb +27 -0
  167. data/vendor/webrat/spec/integration/merb/app/views/testing/upload.html.erb +9 -0
  168. data/vendor/webrat/spec/integration/merb/config/environments/development.rb +15 -0
  169. data/vendor/webrat/spec/integration/merb/config/environments/rake.rb +11 -0
  170. data/vendor/webrat/spec/integration/merb/config/environments/test.rb +14 -0
  171. data/vendor/webrat/spec/integration/merb/config/init.rb +25 -0
  172. data/vendor/webrat/spec/integration/merb/config/rack.rb +11 -0
  173. data/vendor/webrat/spec/integration/merb/config/router.rb +34 -0
  174. data/vendor/webrat/spec/integration/merb/spec/spec.opts +1 -0
  175. data/vendor/webrat/spec/integration/merb/spec/spec_helper.rb +24 -0
  176. data/vendor/webrat/spec/integration/merb/spec/webrat_spec.rb +39 -0
  177. data/vendor/webrat/spec/integration/merb/tasks/merb.thor/app_script.rb +31 -0
  178. data/vendor/webrat/spec/integration/merb/tasks/merb.thor/common.rb +64 -0
  179. data/vendor/webrat/spec/integration/merb/tasks/merb.thor/gem_ext.rb +124 -0
  180. data/vendor/webrat/spec/integration/merb/tasks/merb.thor/main.thor +150 -0
  181. data/vendor/webrat/spec/integration/merb/tasks/merb.thor/ops.rb +93 -0
  182. data/vendor/webrat/spec/integration/merb/tasks/merb.thor/utils.rb +40 -0
  183. data/vendor/webrat/spec/integration/rack/Rakefile +5 -0
  184. data/vendor/webrat/spec/integration/rack/app.rb +73 -0
  185. data/vendor/webrat/spec/integration/rack/test/helper.rb +20 -0
  186. data/vendor/webrat/spec/integration/rack/test/webrat_rack_test.rb +62 -0
  187. data/vendor/webrat/spec/integration/rails/Rakefile +30 -0
  188. data/vendor/webrat/spec/integration/rails/app/controllers/application.rb +15 -0
  189. data/vendor/webrat/spec/integration/rails/app/controllers/buttons_controller.rb +7 -0
  190. data/vendor/webrat/spec/integration/rails/app/controllers/fields_controller.rb +4 -0
  191. data/vendor/webrat/spec/integration/rails/app/controllers/links_controller.rb +7 -0
  192. data/vendor/webrat/spec/integration/rails/app/controllers/webrat_controller.rb +43 -0
  193. data/vendor/webrat/spec/integration/rails/app/helpers/buttons_helper.rb +2 -0
  194. data/vendor/webrat/spec/integration/rails/app/helpers/fields_helper.rb +2 -0
  195. data/vendor/webrat/spec/integration/rails/app/helpers/links_helper.rb +2 -0
  196. data/vendor/webrat/spec/integration/rails/app/views/buttons/show.html.erb +11 -0
  197. data/vendor/webrat/spec/integration/rails/app/views/fields/show.html.erb +9 -0
  198. data/vendor/webrat/spec/integration/rails/app/views/links/show.html.erb +5 -0
  199. data/vendor/webrat/spec/integration/rails/app/views/webrat/before_redirect_form.html.erb +4 -0
  200. data/vendor/webrat/spec/integration/rails/app/views/webrat/buttons.html.erb +11 -0
  201. data/vendor/webrat/spec/integration/rails/app/views/webrat/form.html.erb +28 -0
  202. data/vendor/webrat/spec/integration/rails/config/boot.rb +109 -0
  203. data/vendor/webrat/spec/integration/rails/config/environment.rb +12 -0
  204. data/vendor/webrat/spec/integration/rails/config/environments/development.rb +17 -0
  205. data/vendor/webrat/spec/integration/rails/config/environments/selenium.rb +22 -0
  206. data/vendor/webrat/spec/integration/rails/config/environments/test.rb +22 -0
  207. data/vendor/webrat/spec/integration/rails/config/initializers/inflections.rb +10 -0
  208. data/vendor/webrat/spec/integration/rails/config/initializers/mime_types.rb +5 -0
  209. data/vendor/webrat/spec/integration/rails/config/initializers/new_rails_defaults.rb +17 -0
  210. data/vendor/webrat/spec/integration/rails/config/locales/en.yml +5 -0
  211. data/vendor/webrat/spec/integration/rails/config/routes.rb +18 -0
  212. data/vendor/webrat/spec/integration/rails/public/404.html +30 -0
  213. data/vendor/webrat/spec/integration/rails/public/422.html +30 -0
  214. data/vendor/webrat/spec/integration/rails/public/500.html +33 -0
  215. data/vendor/webrat/spec/integration/rails/script/about +4 -0
  216. data/vendor/webrat/spec/integration/rails/script/console +3 -0
  217. data/vendor/webrat/spec/integration/rails/script/dbconsole +3 -0
  218. data/vendor/webrat/spec/integration/rails/script/destroy +3 -0
  219. data/vendor/webrat/spec/integration/rails/script/generate +3 -0
  220. data/vendor/webrat/spec/integration/rails/script/performance/benchmarker +3 -0
  221. data/vendor/webrat/spec/integration/rails/script/performance/profiler +3 -0
  222. data/vendor/webrat/spec/integration/rails/script/performance/request +3 -0
  223. data/vendor/webrat/spec/integration/rails/script/plugin +3 -0
  224. data/vendor/webrat/spec/integration/rails/script/process/inspector +3 -0
  225. data/vendor/webrat/spec/integration/rails/script/process/reaper +3 -0
  226. data/vendor/webrat/spec/integration/rails/script/process/spawner +3 -0
  227. data/vendor/webrat/spec/integration/rails/script/runner +3 -0
  228. data/vendor/webrat/spec/integration/rails/script/server +3 -0
  229. data/vendor/webrat/spec/integration/rails/test/integration/button_click_test.rb +80 -0
  230. data/vendor/webrat/spec/integration/rails/test/integration/fill_in_test.rb +24 -0
  231. data/vendor/webrat/spec/integration/rails/test/integration/link_click_test.rb +27 -0
  232. data/vendor/webrat/spec/integration/rails/test/integration/webrat_test.rb +97 -0
  233. data/vendor/webrat/spec/integration/rails/test/test_helper.rb +25 -0
  234. data/vendor/webrat/spec/integration/sinatra/Rakefile +5 -0
  235. data/vendor/webrat/spec/integration/sinatra/classic_app.rb +64 -0
  236. data/vendor/webrat/spec/integration/sinatra/modular_app.rb +16 -0
  237. data/vendor/webrat/spec/integration/sinatra/test/classic_app_test.rb +37 -0
  238. data/vendor/webrat/spec/integration/sinatra/test/modular_app_test.rb +18 -0
  239. data/vendor/webrat/spec/integration/sinatra/test/test_helper.rb +16 -0
  240. data/vendor/webrat/spec/private/core/configuration_spec.rb +116 -0
  241. data/vendor/webrat/spec/private/core/field_spec.rb +85 -0
  242. data/vendor/webrat/spec/private/core/link_spec.rb +24 -0
  243. data/vendor/webrat/spec/private/core/logging_spec.rb +10 -0
  244. data/vendor/webrat/spec/private/core/session_spec.rb +195 -0
  245. data/vendor/webrat/spec/private/mechanize/mechanize_session_spec.rb +81 -0
  246. data/vendor/webrat/spec/private/merb/attaches_file_spec.rb +93 -0
  247. data/vendor/webrat/spec/private/merb/merb_session_spec.rb +61 -0
  248. data/vendor/webrat/spec/private/nokogiri_spec.rb +77 -0
  249. data/vendor/webrat/spec/private/rails/attaches_file_spec.rb +81 -0
  250. data/vendor/webrat/spec/private/rails/rails_session_spec.rb +112 -0
  251. data/vendor/webrat/spec/private/selenium/application_servers/rails_spec.rb +26 -0
  252. data/vendor/webrat/spec/public/basic_auth_spec.rb +24 -0
  253. data/vendor/webrat/spec/public/check_spec.rb +191 -0
  254. data/vendor/webrat/spec/public/choose_spec.rb +118 -0
  255. data/vendor/webrat/spec/public/click_area_spec.rb +106 -0
  256. data/vendor/webrat/spec/public/click_button_spec.rb +496 -0
  257. data/vendor/webrat/spec/public/click_link_spec.rb +511 -0
  258. data/vendor/webrat/spec/public/fill_in_spec.rb +209 -0
  259. data/vendor/webrat/spec/public/locators/field_by_xpath_spec.rb +19 -0
  260. data/vendor/webrat/spec/public/locators/field_labeled_spec.rb +172 -0
  261. data/vendor/webrat/spec/public/locators/field_with_id_spec.rb +16 -0
  262. data/vendor/webrat/spec/public/matchers/contain_spec.rb +114 -0
  263. data/vendor/webrat/spec/public/matchers/have_selector_spec.rb +142 -0
  264. data/vendor/webrat/spec/public/matchers/have_tag_spec.rb +39 -0
  265. data/vendor/webrat/spec/public/matchers/have_xpath_spec.rb +136 -0
  266. data/vendor/webrat/spec/public/reload_spec.rb +10 -0
  267. data/vendor/webrat/spec/public/save_and_open_spec.rb +70 -0
  268. data/vendor/webrat/spec/public/select_date_spec.rb +112 -0
  269. data/vendor/webrat/spec/public/select_datetime_spec.rb +137 -0
  270. data/vendor/webrat/spec/public/select_spec.rb +249 -0
  271. data/vendor/webrat/spec/public/select_time_spec.rb +100 -0
  272. data/vendor/webrat/spec/public/selenium/application_server_factory_spec.rb +49 -0
  273. data/vendor/webrat/spec/public/selenium/application_servers/external_spec.rb +12 -0
  274. data/vendor/webrat/spec/public/selenium/selenium_session_spec.rb +37 -0
  275. data/vendor/webrat/spec/public/set_hidden_field_spec.rb +5 -0
  276. data/vendor/webrat/spec/public/submit_form_spec.rb +5 -0
  277. data/vendor/webrat/spec/public/visit_spec.rb +58 -0
  278. data/vendor/webrat/spec/public/within_spec.rb +177 -0
  279. data/vendor/webrat/spec/rcov.opts +1 -0
  280. data/vendor/webrat/spec/spec.opts +2 -0
  281. data/vendor/webrat/spec/spec_helper.rb +50 -0
  282. metadata +474 -0
data/Capfile ADDED
@@ -0,0 +1,3 @@
1
+ load 'deploy' if respond_to?(:namespace) # cap2 differentiator
2
+ Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
3
+ load 'config/deploy'
data/MIT-LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2009 Winton Welsh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,69 @@
1
+ Sum
2
+ ===
3
+
4
+ A budgeting app built on Sinatra. Use it at [sumapp.com](http://sumapp.com).
5
+
6
+ The flow
7
+ --------
8
+
9
+ Fill out a form containing your total monthly bills, income, and desired savings. When you use your credit card or the ATM, send an email to [sum@sumapp.com](mailto:sum@sumapp.com) with the dollar amount. Every midnight, you will receive an email with budgeting metrics for the day.
10
+
11
+ Take a look at the [read email feature](http://github.com/winton/sum/blob/master/features/read_email.feature) to see example budget email scenarios.
12
+
13
+ Development setup
14
+ -----------------
15
+
16
+ Install the sum gem for its dependencies:
17
+
18
+ <pre>
19
+ gem sources -a http://gems.github.com
20
+ sudo gem install winton-sum
21
+ </pre>
22
+
23
+ Fork the [Sum repository](http://github.com/winton/sum) on [GitHub](http://github.com).
24
+
25
+ <pre>
26
+ git clone git@github.com:YOUR_NAME/sum.git
27
+ cd sum
28
+ </pre>
29
+
30
+ Create your database:
31
+
32
+ <pre>
33
+ mysql -uroot -e "create database sum;"
34
+ </pre>
35
+
36
+ Copy and edit the example config files:
37
+
38
+ <pre>
39
+ cp config/database.example.yml config/database.yml
40
+ cp config/mail.example.yml config/mail.yml
41
+ mate config/database.yml config/mail.yml
42
+ </pre>
43
+
44
+ Migrate the database:
45
+
46
+ <pre>
47
+ rake db:migrate
48
+ </pre>
49
+
50
+ Start the application with shotgun:
51
+
52
+ <pre>
53
+ shotgun
54
+ </pre>
55
+
56
+ Architecture
57
+ ------------
58
+
59
+ The application consists of a simple form that updates the user table and a non-public-facing action that doubles as a background job. The background action is designed to be <code>curl</code>ed by cron every minute. It handles email and time-sensitive user updates.
60
+
61
+ Running the test suite
62
+ ----------------------
63
+
64
+ Sum uses [Cucumber](http://github.com/aslakhellesoy/cucumber), [Webrat](http://github.com/brynary/webrat), and [email-spec](http://github.com/bmabey/email-spec) for functional tests and [rspec](http://github.com/dchelimsky/rspec) for unit tests:
65
+
66
+ <pre>
67
+ rake features
68
+ rake spec
69
+ </pre>
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+
3
+ gems = [
4
+ [ 'cucumber', '=0.3.93' ],
5
+ [ 'rspec', '=1.2.8' ],
6
+ [ 'winton-active_wrapper', '=0.1.9' ]
7
+ ]
8
+
9
+ gems.each do |name, version|
10
+ begin
11
+ gem name, version
12
+ rescue Exception
13
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/#{name}/lib"
14
+ end
15
+ end
16
+
17
+ require 'rake'
18
+ require 'active_wrapper/tasks'
19
+ require 'cucumber/rake/task'
20
+ require 'rake/gempackagetask'
21
+ require 'spec/rake/spectask'
22
+ require 'gemspec'
23
+
24
+ ActiveWrapper::Tasks.new(
25
+ :base => File.dirname(__FILE__),
26
+ :env => ENV['ENV']
27
+ )
28
+
29
+ desc "Generate gemspec"
30
+ task :gemspec do
31
+ File.open("#{Dir.pwd}/#{GEM_NAME}.gemspec", 'w') do |f|
32
+ f.write(GEM_SPEC.to_ruby)
33
+ end
34
+ end
35
+
36
+ desc "Install gem"
37
+ task :install do
38
+ Rake::Task['gem'].invoke
39
+ `sudo gem uninstall #{GEM_NAME} -x`
40
+ `sudo gem install pkg/#{GEM_NAME}*.gem`
41
+ `rm -Rf pkg`
42
+ end
43
+
44
+ Cucumber::Rake::Task.new do |t|
45
+ t.cucumber_opts = "--format pretty"
46
+ end
47
+
48
+ Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
49
+ pkg.gem_spec = GEM_SPEC
50
+ end
51
+
52
+ Spec::Rake::SpecTask.new do |t|
53
+ t.rcov = true
54
+ t.spec_opts = ["--format", "specdoc", "--colour"]
55
+ t.spec_files = FileList["spec/**/*_spec.rb"]
56
+ end
data/config.ru ADDED
@@ -0,0 +1,3 @@
1
+ require "#{File.dirname(__FILE__)}/lib/sum"
2
+
3
+ run Application
@@ -0,0 +1,10 @@
1
+ development: &dev
2
+ adapter: mysql
3
+ database: sum
4
+ username: root
5
+ password:
6
+ host: localhost
7
+ production:
8
+ <<: *dev
9
+ test:
10
+ <<: *dev
@@ -0,0 +1,36 @@
1
+ set :ubistrano, {
2
+ :application => :sum_beta,
3
+ :platform => :sinatra, # :php, :rails, :sinatra
4
+ :repository => 'git@github.com:winton/sum.git',
5
+
6
+ :ec2 => {
7
+ :access_key => '',
8
+ :secret_key => ''
9
+ },
10
+
11
+ :mysql => {
12
+ :root_password => '',
13
+ :app_password => ''
14
+ },
15
+
16
+ :production => {
17
+ :domains => [ 'beta.sumapp.com' ],
18
+ :host => '174.129.231.24'
19
+ },
20
+
21
+ :staging => {
22
+ :domains => [ 'staging.beta.sumapp.com' ],
23
+ :host => '174.129.231.24'
24
+ }
25
+ }
26
+
27
+ require 'ubistrano'
28
+
29
+ after "deploy:update_code", "deploy:update_crontab"
30
+
31
+ namespace :deploy do
32
+ desc "Update the crontab file"
33
+ task :update_crontab, :roles => :web do
34
+ run "cd #{release_path} && whenever --update-crontab #{application}"
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ winton-active_wrapper:
2
+ repo: git@github.com:winton/active_wrapper.git
3
+ path: vendor
4
+ email-spec:
5
+ repo: git://github.com/bmabey/email-spec.git
6
+ path: vendor
7
+ winton-fetcher:
8
+ repo: git@github.com:winton/fetcher.git
9
+ path: vendor
10
+ winton-secret_key:
11
+ repo: git@github.com:winton/secret_key.git
12
+ path: vendor
13
+ sinatra:
14
+ repo: git://github.com/sinatra/sinatra.git
15
+ path: vendor
16
+ webrat:
17
+ repo: git://github.com/brynary/webrat.git
18
+ path: vendor
@@ -0,0 +1,32 @@
1
+ development:
2
+ imap:
3
+ password: password
4
+ port: 993
5
+ server: imap.gmail.com
6
+ ssl: true
7
+ use_login: true
8
+ username: test@sumapp.com
9
+ smtp:
10
+ address: smtp.gmail.com
11
+ authentication: :plain
12
+ domain: sumapp.com
13
+ password:
14
+ port: 587
15
+ enable_starttls_auto: true
16
+ user_name: test@sumapp.com
17
+ production:
18
+ imap:
19
+ password: password
20
+ port: 993
21
+ server: imap.gmail.com
22
+ ssl: true
23
+ use_login: true
24
+ username: sum@sumapp.com
25
+ smtp:
26
+ address: smtp.gmail.com
27
+ authentication: :plain
28
+ domain: sumapp.com
29
+ password:
30
+ port: 587
31
+ enable_starttls_auto: true
32
+ user_name: sum@sumapp.com
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ gem 'winton-secret_key', '=0.1.0'
5
+ rescue Exception
6
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/winton-secret_key/lib"
7
+ end
8
+
9
+ require 'secret_key'
10
+ key = SecretKey.new(File.dirname(__FILE__)).read
11
+
12
+ every 1.minute do
13
+ puts @environment
14
+ case @environment
15
+ when 'development'
16
+ command "curl http://localhost:#{@port}/cron"
17
+ else
18
+ command "curl https://sumapp.com/#{key}"
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ class Users < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.column :flash, :string
5
+ t.column :recent_transactions, :string, :limit => 1024
6
+ t.column :savings_goal, :decimal, :precision => 10, :scale => 2, :default => 0
7
+ t.column :send_now, :boolean, :default => false
8
+ t.column :spending_goal, :decimal, :precision => 10, :scale => 2, :default => 0
9
+ t.column :spent_this_month, :decimal, :precision => 10, :scale => 2, :default => 0
10
+ t.column :spent_today, :decimal, :precision => 10, :scale => 2, :default => 0
11
+ t.column :temporary_spending_cut, :decimal, :precision => 10, :scale => 2, :default => 0
12
+ t.column :timezone_offset, :integer, :default => 0
13
+ t.column :reset_at, :datetime
14
+ t.column :send_at, :datetime
15
+ t.timestamps
16
+ end
17
+ end
18
+
19
+ def self.down
20
+ drop_table :users
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ class UserEmails < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :user_emails do |t|
4
+ t.column :active, :boolean, :default => true
5
+ t.column :email, :string
6
+ t.column :failures, :integer, :default => 0
7
+ t.column :user_id, :integer
8
+ t.timestamps
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :user_emails
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ Feature: Front page
2
+
3
+ As a user
4
+ I want to enter my information into a form
5
+ In order to submit that information
6
+
7
+ Scenario: I visit the front page
8
+ When I visit the front page
9
+ Then I should see a form
10
+ And I should see a savings text field
11
+ And I should see an income text field
12
+ And I should see a bills text field
13
+ And I should see an email text field
14
+ And I should see a tos checkbox
15
+ And I should see a submit button
@@ -0,0 +1,445 @@
1
+ Feature: Read email
2
+
3
+ As a user
4
+ I want to read a budget email
5
+ In order to know how much I can spend
6
+
7
+ Background:
8
+ Given today is June 1, 2009
9
+ And I have created an account with these attributes:
10
+ | savings | income | bills |
11
+ | 1000 | 5000 | 2500 |
12
+ And my email queue is empty
13
+
14
+ Scenario: I see some static information every day
15
+ When the background job runs
16
+ And I open the email
17
+ Then I should see "Today's budget" in the subject
18
+ And I should see "Fiscal month: 06/01/09 to 07/01/09 (30 days)" in the email
19
+
20
+
21
+ # Day 1
22
+
23
+ Scenario: I see budget information for day 1, having spent nothing today
24
+ When the background job runs
25
+ And I open the email
26
+ Then I should see in the email:
27
+ """
28
+ You can spend $50.00 today.
29
+
30
+ If you don't spend anything today, you will have
31
+ $51.72 to spend each day.
32
+ $362.07 to spend each week.
33
+ $1500.00 to spend over the next 29 days.
34
+
35
+ You have saved $50.00 more than expected for a 1 day period.
36
+ """
37
+ # days in month = 30
38
+ # spending money / days in month = 50
39
+ # days left = 29
40
+ # spending money / days left = 51.72
41
+ # spending money / days left * 7 = 362.07
42
+
43
+ Scenario: I see budget information for day 1, having deposited money
44
+ Given it is day 1
45
+ And I have deposited $1.00
46
+ When the background job runs
47
+ And I open the email
48
+ Then I should see in the email:
49
+ """
50
+ You can spend $51.00 today.
51
+
52
+ If you don't spend anything today, you will have
53
+ $51.76 to spend each day.
54
+ $362.31 to spend each week.
55
+ $1501.00 to spend over the next 29 days.
56
+
57
+ You have saved $51.00 more than expected for a 1 day period.
58
+ """
59
+ # (spending money / days in month) - spent today = 51
60
+ # spending money + 1 = 1501
61
+
62
+ Scenario: I see budget information for day 1, having spent under budget
63
+ Given it is day 1
64
+ And today I have spent $49.00
65
+ When the background job runs
66
+ And I open the email
67
+ Then I should see in the email:
68
+ """
69
+ You can spend $1.00 today.
70
+
71
+ If you only spend $49.00 today, you will have
72
+ $50.03 to spend each day.
73
+ $350.24 to spend each week.
74
+ $1451.00 to spend over the next 29 days.
75
+
76
+ You have saved $1.00 more than expected for a 1 day period.
77
+ """
78
+
79
+ Scenario: I see budget information for day 1, having spent exactly my budget
80
+ Given it is day 1
81
+ And today I have spent $50.00
82
+ When the background job runs
83
+ And I open the email
84
+ Then I should see in the email:
85
+ """
86
+ You have spent your budget for today.
87
+
88
+ If you only spend $50.00 today, you will have
89
+ $50.00 to spend each day.
90
+ $350.00 to spend each week.
91
+ $1450.00 to spend over the next 29 days.
92
+
93
+ You have spent exactly as expected for a 1 day period.
94
+ """
95
+
96
+ Scenario: I see budget information for day 1, having spent over budget
97
+ Given it is day 1
98
+ And today I have spent $51.00
99
+ When the background job runs
100
+ And I open the email
101
+ Then I should see in the email:
102
+ """
103
+ You have spent your budget for today, plus an additional $1.00.
104
+
105
+ If you only spend $51.00 today, you will have
106
+ $49.97 to spend each day.
107
+ $349.76 to spend each week.
108
+ $1449.00 to spend over the next 29 days.
109
+
110
+ You have spent $1.00 more than expected for a 1 day period.
111
+ """
112
+
113
+ Scenario: I see budget information for day 1, having spent all of my spending money
114
+ Given it is day 1
115
+ And today I have spent $1500.00
116
+ When the background job runs
117
+ And I open the email
118
+ Then I should see in the email:
119
+ """
120
+ You should not spend any money for the next 29 days if you want to continue to save $1000.00.
121
+
122
+ If you wish to spend into your savings this month, you can afford to spend
123
+ $34.48 each day.
124
+ $241.38 each week.
125
+ $1000.00 over the next 29 days.
126
+
127
+ You have spent $1450.00 more than expected for a 1 day period.
128
+ """
129
+ # total left = 1000
130
+ # total left / days left = 34.48
131
+ # total left / days left * 7 = 241.38
132
+
133
+ Scenario: I see budget information for day 1, having spent all of my savings
134
+ Given it is day 1
135
+ And today I have spent $2500.00
136
+ When the background job runs
137
+ And I open the email
138
+ Then I should see in the email:
139
+ """
140
+ You should not spend any money for the next 29 days if you want to avoid going into debt.
141
+
142
+ You have spent $2450.00 more than expected for a 1 day period.
143
+ """
144
+
145
+ Scenario: I see budget information for day 1, having gone into debt
146
+ Given it is day 1
147
+ And today I have spent $2501.00
148
+ When the background job runs
149
+ And I open the email
150
+ Then I should see in the email:
151
+ """
152
+ You need to earn $0.03 today.
153
+
154
+ Continue to do so for the next 29 days to avoid going into debt.
155
+
156
+ If you do not, Sum will temporarily decrease your spending money for next month to account for the debt.
157
+
158
+ You have spent $2451.00 more than expected for a 1 day period.
159
+ """
160
+ # total left = -1
161
+ # total left / days left including today * -1 = 0.03
162
+
163
+ # Day 15
164
+
165
+ Scenario: I see budget information for day 15, having spent nothing today
166
+ Given it is day 15
167
+ And before today I spent $700.00
168
+ And today I have spent $0.00
169
+ When the background job runs
170
+ And I open the email
171
+ Then I should see in the email:
172
+ """
173
+ You can spend $50.00 today.
174
+
175
+ If you don't spend anything today, you will have
176
+ $53.33 to spend each day.
177
+ $373.33 to spend each week.
178
+ $800.00 to spend over the next 15 days.
179
+
180
+ You have saved $50.00 more than expected for a 15 day period.
181
+ """
182
+
183
+ Scenario: I see budget information for day 15, having deposited money
184
+ Given it is day 15
185
+ And I have deposited $1.00
186
+ And before today I spent $700.00
187
+ When the background job runs
188
+ And I open the email
189
+ Then I should see in the email:
190
+ """
191
+ You can spend $51.00 today.
192
+
193
+ If you don't spend anything today, you will have
194
+ $53.40 to spend each day.
195
+ $373.80 to spend each week.
196
+ $801.00 to spend over the next 15 days.
197
+
198
+ You have saved $51.00 more than expected for a 15 day period.
199
+ """
200
+
201
+ Scenario: I see budget information for day 15, having spent under budget
202
+ Given it is day 15
203
+ And before today I spent $700.00
204
+ And today I have spent $49.00
205
+ When the background job runs
206
+ And I open the email
207
+ Then I should see in the email:
208
+ """
209
+ You can spend $1.00 today.
210
+
211
+ If you only spend $49.00 today, you will have
212
+ $50.07 to spend each day.
213
+ $350.47 to spend each week.
214
+ $751.00 to spend over the next 15 days.
215
+
216
+ You have saved $1.00 more than expected for a 15 day period.
217
+ """
218
+
219
+ Scenario: I see budget information for day 15, having spent exactly my budget
220
+ Given it is day 15
221
+ And before today I spent $700.00
222
+ And today I have spent $50.00
223
+ When the background job runs
224
+ And I open the email
225
+ Then I should see in the email:
226
+ """
227
+ You have spent your budget for today.
228
+
229
+ If you only spend $50.00 today, you will have
230
+ $50.00 to spend each day.
231
+ $350.00 to spend each week.
232
+ $750.00 to spend over the next 15 days.
233
+
234
+ You have spent exactly as expected for a 15 day period.
235
+ """
236
+
237
+ Scenario: I see budget information for day 15, having spent over budget
238
+ Given it is day 15
239
+ And before today I spent $700.00
240
+ And today I have spent $51.00
241
+ When the background job runs
242
+ And I open the email
243
+ Then I should see in the email:
244
+ """
245
+ You have spent your budget for today, plus an additional $1.00.
246
+
247
+ If you only spend $51.00 today, you will have
248
+ $49.93 to spend each day.
249
+ $349.53 to spend each week.
250
+ $749.00 to spend over the next 15 days.
251
+
252
+ You have spent $1.00 more than expected for a 15 day period.
253
+ """
254
+
255
+ Scenario: I see budget information for day 15, having spent all of my spending money
256
+ Given it is day 15
257
+ And before today I spent $700.00
258
+ And today I have spent $800.00
259
+ When the background job runs
260
+ And I open the email
261
+ Then I should see in the email:
262
+ """
263
+ You should not spend any money for the next 15 days if you want to continue to save $1000.00.
264
+
265
+ If you wish to spend into your savings this month, you can afford to spend
266
+ $66.67 each day.
267
+ $466.67 each week.
268
+ $1000.00 over the next 15 days.
269
+
270
+ You have spent $750.00 more than expected for a 15 day period.
271
+ """
272
+
273
+ Scenario: I see budget information for day 15, having spent all of my savings
274
+ Given it is day 15
275
+ And before today I spent $700.00
276
+ And today I have spent $1050.00
277
+ When the background job runs
278
+ And I open the email
279
+ Then I should see in the email:
280
+ """
281
+ You should not spend any money for the next 15 days if you want to continue to save $750.00.
282
+
283
+ If you wish to spend into your savings this month, you can afford to spend
284
+ $50.00 each day.
285
+ $350.00 each week.
286
+ $750.00 over the next 15 days.
287
+
288
+ You have spent $1000.00 more than expected for a 15 day period.
289
+ """
290
+
291
+ Scenario: I see budget information for day 15, having gone into debt
292
+ Given it is day 15
293
+ And before today I spent $700.00
294
+ And today I have spent $1801.00
295
+ When the background job runs
296
+ And I open the email
297
+ Then I should see in the email:
298
+ """
299
+ You need to earn $0.06 today.
300
+
301
+ Continue to do so for the next 15 days to avoid going into debt.
302
+
303
+ If you do not, Sum will temporarily decrease your spending money for next month to account for the debt.
304
+
305
+ You have spent $1751.00 more than expected for a 15 day period.
306
+ """
307
+
308
+ # Day 30
309
+
310
+ Scenario: I see budget information for day 30, having spent nothing today
311
+ Given it is day 30
312
+ And before today I spent $1450.00
313
+ And today I have spent $0.00
314
+ When the background job runs
315
+ And I open the email
316
+ Then I should see in the email:
317
+ """
318
+ You can spend $50.00 today.
319
+
320
+ You have saved $50.00 more than expected for a 30 day period.
321
+ """
322
+
323
+ Scenario: I see budget information for day 30, having deposited money
324
+ Given it is day 30
325
+ And I have deposited $1.00
326
+ And before today I spent $1450.00
327
+ When the background job runs
328
+ And I open the email
329
+ Then I should see in the email:
330
+ """
331
+ You can spend $51.00 today.
332
+
333
+ You have saved $51.00 more than expected for a 30 day period.
334
+ """
335
+
336
+ Scenario: I see budget information for day 30, having spent under budget
337
+ Given it is day 30
338
+ And before today I spent $1450.00
339
+ And today I have spent $49.00
340
+ When the background job runs
341
+ And I open the email
342
+ Then I should see in the email:
343
+ """
344
+ You can spend $1.00 today.
345
+
346
+ You have saved $1.00 more than expected for a 30 day period.
347
+ """
348
+
349
+ Scenario: I see budget information for day 30, having spent exactly my budget
350
+ Given it is day 30
351
+ And before today I spent $1450.00
352
+ And today I have spent $50.00
353
+ When the background job runs
354
+ And I open the email
355
+ Then I should see in the email:
356
+ """
357
+ You should not spend any money if you want to continue to save $1000.00.
358
+
359
+ You have spent exactly as expected for a 30 day period.
360
+ """
361
+
362
+ Scenario: I see budget information for day 30, having spent over budget
363
+ Given it is day 30
364
+ And before today I spent $1450.00
365
+ And today I have spent $51.00
366
+ When the background job runs
367
+ And I open the email
368
+ Then I should see in the email:
369
+ """
370
+ You should not spend any money if you want to continue to save $999.00.
371
+
372
+ You have spent $1.00 more than expected for a 30 day period.
373
+ """
374
+
375
+ Scenario: I see budget information for day 30, having spent all of my savings
376
+ Given it is day 30
377
+ And before today I spent $1450.00
378
+ And today I have spent $1050.00
379
+ When the background job runs
380
+ And I open the email
381
+ Then I should see in the email:
382
+ """
383
+ You should not spend any money if you want to avoid going into debt.
384
+
385
+ You have spent $1000.00 more than expected for a 30 day period.
386
+ """
387
+
388
+ Scenario: I see budget information for day 30, having gone into debt
389
+ Given it is day 30
390
+ And before today I spent $1450.00
391
+ And today I have spent $1051.00
392
+ When the background job runs
393
+ And I open the email
394
+ Then I should see in the email:
395
+ """
396
+ You need to earn $1.00 today.
397
+
398
+ If you do not, Sum will temporarily decrease your spending money for next month to account for the debt.
399
+
400
+ You have spent $1001.00 more than expected for a 30 day period.
401
+ """
402
+
403
+
404
+ # Transactions
405
+
406
+ Scenario: I see my last transaction
407
+ Given today I have spent $1.00
408
+ When the background job runs
409
+ And I open the email
410
+ Then I should see in the email:
411
+ """
412
+ Last transaction:
413
+ $-1.00
414
+ """
415
+
416
+ Scenario: I see my last two transactions
417
+ Given today I have spent $1.00
418
+ And today I have spent $2.00
419
+ When the background job runs
420
+ And I open the email
421
+ Then I should see in the email:
422
+ """
423
+ Last 2 transactions:
424
+ $-2.00
425
+ $-1.00
426
+ """
427
+
428
+ Scenario: I see my last five transactions
429
+ Given today I have spent $1.00
430
+ And today I have spent $2.00
431
+ And today I have spent $3.00
432
+ And today I have spent $4.00
433
+ And today I have spent $5.00
434
+ And today I have spent $6.00
435
+ When the background job runs
436
+ And I open the email
437
+ Then I should see in the email:
438
+ """
439
+ Last 5 transactions:
440
+ $-6.00
441
+ $-5.00
442
+ $-4.00
443
+ $-3.00
444
+ $-2.00
445
+ """