droom 0.0.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (459) hide show
  1. data/README.md +2 -2
  2. data/app/assets/images/droom/Untitled-1.png +0 -0
  3. data/app/assets/images/droom/acceptance.png +0 -0
  4. data/app/assets/images/droom/add.png +0 -0
  5. data/app/assets/images/droom/asterisk.png +0 -0
  6. data/app/assets/images/droom/asterisk_up.png +0 -0
  7. data/app/assets/images/droom/checkboxes.png +0 -0
  8. data/app/assets/images/droom/clear.png +0 -0
  9. data/app/assets/images/droom/cog.png +0 -0
  10. data/app/assets/images/droom/copy.png +0 -0
  11. data/app/assets/images/droom/copy_to_dropbox.png +0 -0
  12. data/app/assets/images/droom/crosses.png +0 -0
  13. data/app/assets/images/droom/dark_scrap_icons.png +0 -0
  14. data/app/assets/images/droom/defilter.png +0 -0
  15. data/app/assets/images/droom/delete.png +0 -0
  16. data/app/assets/images/droom/dropbox.png +0 -0
  17. data/app/assets/images/droom/dropbox_button.png +0 -0
  18. data/app/assets/images/droom/dropbox_button_small.png +0 -0
  19. data/app/assets/images/droom/edit.png +0 -0
  20. data/app/assets/images/droom/expand.png +0 -0
  21. data/app/assets/images/droom/expander.png +0 -0
  22. data/app/assets/images/droom/folder.png +0 -0
  23. data/app/assets/images/droom/image_box.png +0 -0
  24. data/app/assets/images/droom/in_dropbox.png +0 -0
  25. data/app/assets/images/droom/integration_buttons.png +0 -0
  26. data/app/assets/images/droom/leftright.png +0 -0
  27. data/app/assets/images/droom/manage.png +0 -0
  28. data/app/assets/images/droom/medium_object_icons.png +0 -0
  29. data/app/assets/images/droom/object_icons.png +0 -0
  30. data/app/assets/images/droom/playable.png +0 -0
  31. data/app/assets/images/droom/revoke.png +0 -0
  32. data/app/assets/images/droom/scrap_icons.png +0 -0
  33. data/app/assets/images/droom/search.png +0 -0
  34. data/app/assets/images/droom/small_asterisk.png +0 -0
  35. data/app/assets/images/droom/small_cog.png +0 -0
  36. data/app/assets/images/droom/small_download.png +0 -0
  37. data/app/assets/images/droom/small_image_box.png +0 -0
  38. data/app/assets/images/droom/smalladd.png +0 -0
  39. data/app/assets/images/droom/smallarrows.png +0 -0
  40. data/app/assets/images/droom/smallcopy.png +0 -0
  41. data/app/assets/images/droom/smalldelete.png +0 -0
  42. data/app/assets/images/droom/smalldownload.png +0 -0
  43. data/app/assets/images/droom/smalledit.png +0 -0
  44. data/app/assets/images/droom/smallmanage.png +0 -0
  45. data/app/assets/images/droom/star.png +0 -0
  46. data/app/assets/images/droom/symbols.png +0 -0
  47. data/app/assets/images/droom/tinyarrows.png +0 -0
  48. data/app/assets/images/droom/to_dropbox.png +0 -0
  49. data/app/assets/images/droom/twisty.png +0 -0
  50. data/app/assets/images/droom/youtube_icon.png +0 -0
  51. data/app/assets/javascripts/droom.js.coffee +62 -65
  52. data/app/assets/javascripts/droom/actions.js.coffee +492 -0
  53. data/app/assets/javascripts/droom/ajax.js.coffee +84 -0
  54. data/app/assets/javascripts/droom/{lib/extensions.js.coffee → extensions.js.coffee} +22 -15
  55. data/app/assets/javascripts/droom/lib/ZeroClipboard.js +334 -0
  56. data/app/assets/javascripts/droom/lib/ZeroClipboard.swf +0 -0
  57. data/app/assets/javascripts/droom/lib/ZeroClipboard10.swf +0 -0
  58. data/app/assets/javascripts/droom/map.js.coffee +124 -86
  59. data/app/assets/javascripts/droom/popups.js.coffee +252 -0
  60. data/app/assets/javascripts/droom/stream.js.coffee +92 -0
  61. data/app/assets/javascripts/droom/utilities.js.coffee +120 -0
  62. data/app/assets/javascripts/droom/widgets.js.coffee +1209 -0
  63. data/app/assets/stylesheets/_definitions.css.sass +189 -0
  64. data/app/assets/stylesheets/droom.css.sass +1429 -376
  65. data/app/assets/stylesheets/lib/_kalendae.css.sass +2 -5
  66. data/app/assets/stylesheets/lib/_popups.css.sass +86 -0
  67. data/app/assets/stylesheets/lib/_toolbar.css.sass +17 -11
  68. data/app/controllers/droom/agenda_categories_controller.rb +24 -0
  69. data/app/controllers/droom/calendars_controller.rb +29 -0
  70. data/app/controllers/droom/confirmations_controller.rb +38 -0
  71. data/app/controllers/droom/dashboard_controller.rb +2 -0
  72. data/app/controllers/droom/documents_controller.rb +42 -51
  73. data/app/controllers/droom/dropbox_tokens_controller.rb +61 -0
  74. data/app/controllers/droom/engine_controller.rb +8 -23
  75. data/app/controllers/droom/events_controller.rb +39 -27
  76. data/app/controllers/droom/folders_controller.rb +106 -0
  77. data/app/controllers/droom/group_invitations_controller.rb +15 -7
  78. data/app/controllers/droom/groups_controller.rb +4 -4
  79. data/app/controllers/droom/invitations_controller.rb +37 -9
  80. data/app/controllers/droom/memberships_controller.rb +5 -3
  81. data/app/controllers/droom/organisations_controller.rb +53 -0
  82. data/app/controllers/droom/people_controller.rb +46 -27
  83. data/app/controllers/droom/preferences_controller.rb +34 -0
  84. data/app/controllers/droom/scraps_controller.rb +77 -0
  85. data/app/controllers/droom/search_controller.rb +66 -0
  86. data/app/controllers/droom/suggestions_controller.rb +22 -35
  87. data/app/controllers/droom/users_controller.rb +69 -0
  88. data/app/controllers/droom/venues_controller.rb +1 -2
  89. data/app/controllers/droom/youtube_controller.rb +22 -0
  90. data/app/helpers/droom/droom_helper.rb +93 -5
  91. data/app/models/droom/agenda_category.rb +8 -1
  92. data/app/models/droom/calendar.rb +19 -0
  93. data/app/models/droom/category.rb +4 -6
  94. data/app/models/droom/document.rb +103 -64
  95. data/app/models/droom/document_attachment.rb +13 -47
  96. data/app/models/droom/dropbox_document.rb +41 -0
  97. data/app/models/droom/dropbox_token.rb +29 -0
  98. data/app/models/droom/event.rb +125 -90
  99. data/app/models/droom/folder.rb +134 -0
  100. data/app/models/droom/group.rb +68 -27
  101. data/app/models/droom/group_invitation.rb +12 -3
  102. data/app/models/droom/invitation.rb +50 -8
  103. data/app/models/droom/mailing_list_membership.rb +128 -0
  104. data/app/models/droom/membership.rb +71 -9
  105. data/app/models/droom/organisation.rb +22 -0
  106. data/app/models/droom/page.rb +3 -3
  107. data/app/models/droom/person.rb +180 -166
  108. data/app/models/droom/personal_folder.rb +31 -0
  109. data/app/models/droom/preference.rb +35 -0
  110. data/app/models/droom/recurrence_rule.rb +1 -1
  111. data/app/models/droom/scrap.rb +110 -0
  112. data/app/models/droom/tag.rb +120 -0
  113. data/app/models/droom/tagging.rb +13 -0
  114. data/app/models/droom/user.rb +211 -0
  115. data/app/models/droom/user_action_observer.rb +12 -0
  116. data/app/models/droom/venue.rb +56 -39
  117. data/app/views/cropper/uploads/_crop.html.haml +46 -0
  118. data/app/views/devise/mailer/confirmation_instructions.en.html.haml +24 -0
  119. data/app/views/devise/mailer/reset_password_instructions.en.html.haml +21 -0
  120. data/app/views/devise/passwords/edit.html.haml +31 -0
  121. data/app/views/devise/passwords/new.html.haml +21 -0
  122. data/app/views/devise/sessions/new.html.haml +33 -0
  123. data/app/views/droom/agenda_categories/_form.html.haml +22 -0
  124. data/app/views/droom/agenda_categories/new.html.haml +6 -0
  125. data/app/views/droom/calendars/_action_menu.html.haml +6 -0
  126. data/app/views/droom/calendars/index.html.haml +12 -0
  127. data/app/views/droom/calendars/show.rss.builder +0 -0
  128. data/app/views/droom/confirmations/show.html.haml +40 -0
  129. data/app/views/droom/dashboard/_calendar.html.haml +1 -0
  130. data/app/views/droom/dashboard/_folders.html.haml +5 -0
  131. data/app/views/droom/dashboard/_future_events.html.haml +18 -0
  132. data/app/views/droom/dashboard/_invitations.html.haml +1 -0
  133. data/app/views/droom/dashboard/_past_events.haml +14 -0
  134. data/app/views/droom/dashboard/_quicksearch.html.haml +6 -0
  135. data/app/views/droom/dashboard/_stream.html.haml +9 -0
  136. data/app/views/droom/dashboard/index.html.haml +5 -3
  137. data/app/views/droom/documents/_action_menu.html.haml +7 -0
  138. data/app/views/droom/documents/_documents.html.haml +5 -0
  139. data/app/views/droom/documents/_documents_list.html.haml +6 -6
  140. data/app/views/droom/documents/_documents_table.html.haml +29 -10
  141. data/app/views/droom/documents/_form.html.haml +3 -7
  142. data/app/views/droom/documents/_listing.html.haml +5 -6
  143. data/app/views/droom/documents/_pagination.html.haml +8 -0
  144. data/app/views/droom/documents/_search_result.html.haml +14 -0
  145. data/app/views/droom/documents/_suggested.html.haml +0 -5
  146. data/app/views/droom/documents/_table_document.html.haml +2 -8
  147. data/app/views/droom/documents/edit.html.haml +7 -1
  148. data/app/views/droom/documents/index.html.haml +10 -27
  149. data/app/views/droom/documents/new.html.haml +10 -1
  150. data/app/views/droom/dropbox_tokens/new.html.haml +6 -0
  151. data/app/views/droom/events/_action_menu.html.haml +15 -0
  152. data/app/views/droom/events/_event.html.haml +67 -57
  153. data/app/views/droom/events/_form.html.haml +15 -4
  154. data/app/views/droom/events/_invitations.html.haml +4 -19
  155. data/app/views/droom/events/_search_result.html.haml +19 -0
  156. data/app/views/droom/events/_views.html.haml +2 -2
  157. data/app/views/droom/events/edit.html.haml +7 -1
  158. data/app/views/droom/events/index.html.haml +27 -10
  159. data/app/views/droom/events/index.rss.builder +1 -1
  160. data/app/views/droom/events/new.html.haml +6 -1
  161. data/app/views/droom/events/show.html.haml +5 -1
  162. data/app/views/droom/folders/_action_menu.html.haml +14 -0
  163. data/app/views/droom/folders/_attachments.html.haml +3 -0
  164. data/app/views/droom/folders/_contents.html.haml +6 -0
  165. data/app/views/droom/folders/_folder.html.haml +20 -0
  166. data/app/views/droom/folders/_folders.html.haml +2 -0
  167. data/app/views/droom/folders/_form.html.haml +18 -0
  168. data/app/views/droom/folders/edit.html.haml +6 -0
  169. data/app/views/droom/folders/index.html.haml +32 -0
  170. data/app/views/droom/folders/new.html.haml +6 -0
  171. data/app/views/droom/folders/show.html.haml +1 -0
  172. data/app/views/droom/group_invitations/_form.html.haml +15 -4
  173. data/app/views/droom/group_invitations/new.html.haml +6 -1
  174. data/app/views/droom/groups/_action_menu.haml +11 -0
  175. data/app/views/droom/groups/_form.html.haml +10 -11
  176. data/app/views/droom/groups/_group.html.haml +9 -10
  177. data/app/views/droom/groups/_groups.html.haml +2 -3
  178. data/app/views/droom/groups/_search_result.html.haml +16 -0
  179. data/app/views/droom/groups/_suggested.html.haml +7 -0
  180. data/app/views/droom/groups/edit.html.haml +7 -1
  181. data/app/views/droom/groups/index.html.haml +6 -3
  182. data/app/views/droom/groups/new.html.haml +7 -0
  183. data/app/views/droom/groups/show.html.haml +1 -1
  184. data/app/views/droom/invitations/_created.html.haml +1 -1
  185. data/app/views/droom/invitations/_form.html.haml +9 -5
  186. data/app/views/droom/invitations/_invitation.html.haml +9 -0
  187. data/app/views/droom/invitations/_invitations.html.haml +8 -0
  188. data/app/views/droom/invitations/new.html.haml +6 -1
  189. data/app/views/droom/memberships/_member.html.haml +1 -1
  190. data/app/views/droom/memberships/_membership_toggle.html.haml +10 -0
  191. data/app/views/droom/memberships/new.html.haml +10 -0
  192. data/app/views/droom/organisations/_action_menu.html.haml +7 -0
  193. data/app/views/droom/organisations/_form.html.haml +25 -0
  194. data/app/views/droom/organisations/_organisation.html.haml +23 -0
  195. data/app/views/droom/organisations/_organisations.html.haml +2 -0
  196. data/app/views/droom/organisations/_suggested.html.haml +7 -0
  197. data/app/views/droom/organisations/edit.html.haml +6 -0
  198. data/app/views/droom/organisations/index.html.haml +20 -0
  199. data/app/views/droom/organisations/new.html.haml +6 -0
  200. data/app/views/droom/organisations/show.html.haml +1 -0
  201. data/app/views/droom/pages/_full_page.html.haml +1 -1
  202. data/app/views/droom/pages/index.html.haml +1 -1
  203. data/app/views/droom/panels/_account.html.haml +16 -0
  204. data/app/views/droom/panels/_admin.html.haml +23 -0
  205. data/app/views/droom/panels/_devices.html.haml +19 -0
  206. data/app/views/droom/panels/_dropbox.html.haml +17 -0
  207. data/app/views/droom/panels/_email.html.haml +16 -0
  208. data/app/views/droom/panels/_keywords.html.haml +3 -0
  209. data/app/views/droom/panels/_networks.html.haml +3 -0
  210. data/app/views/droom/panels/_readers.html.haml +18 -0
  211. data/app/views/droom/panels/_rss.html.haml +10 -0
  212. data/app/views/droom/panels/_search.html.haml +16 -0
  213. data/app/views/droom/panels/_vcal.html.haml +3 -0
  214. data/app/views/droom/people/_action_menu.html.haml +22 -0
  215. data/app/views/droom/people/_form.html.haml +69 -39
  216. data/app/views/droom/people/_listing.html.haml +19 -0
  217. data/app/views/droom/people/_memberships.html.haml +24 -0
  218. data/app/views/droom/people/_people.html.haml +5 -29
  219. data/app/views/droom/people/_person.html.haml +30 -32
  220. data/app/views/droom/people/_search_result.html.haml +14 -0
  221. data/app/views/droom/people/_suggested.html.haml +10 -5
  222. data/app/views/droom/people/edit.html.haml +9 -1
  223. data/app/views/droom/people/index.html.haml +6 -9
  224. data/app/views/droom/people/new.html.haml +6 -1
  225. data/app/views/droom/people/show.html.haml +1 -1
  226. data/app/views/droom/preferences/_checkbox.html.haml +7 -0
  227. data/app/views/droom/preferences/_radio_set.html.haml +9 -0
  228. data/app/views/droom/scraps/_action_menu.html.haml +9 -0
  229. data/app/views/droom/scraps/_caption.html.haml +9 -0
  230. data/app/views/droom/scraps/_credit.html.haml +10 -0
  231. data/app/views/droom/scraps/_form.html.haml +77 -0
  232. data/app/views/droom/scraps/_heading.html.haml +16 -0
  233. data/app/views/droom/scraps/_scrap.html.haml +17 -0
  234. data/app/views/droom/scraps/_search_result.html.haml +5 -0
  235. data/app/views/droom/scraps/_stream.html.haml +4 -0
  236. data/app/views/droom/scraps/_suggested.html.haml +5 -0
  237. data/app/views/droom/scraps/_thumb.html.haml +5 -0
  238. data/app/views/droom/scraps/edit.html.haml +2 -0
  239. data/app/views/droom/scraps/full/_document.html.haml +6 -0
  240. data/app/views/droom/scraps/full/_event.html.haml +20 -0
  241. data/app/views/droom/scraps/full/_image.html.haml +4 -0
  242. data/app/views/droom/scraps/full/_link.html.haml +5 -0
  243. data/app/views/droom/scraps/full/_quote.html.haml +8 -0
  244. data/app/views/droom/scraps/full/_text.html.haml +4 -0
  245. data/app/views/droom/scraps/full/_video.html.haml +4 -0
  246. data/app/views/droom/scraps/index.atom.builder +26 -0
  247. data/app/views/droom/scraps/index.html.haml +15 -0
  248. data/app/views/droom/scraps/new.html.haml +2 -0
  249. data/app/views/droom/scraps/show.html.haml +4 -0
  250. data/app/views/droom/scraps/thumbs/_document.html.haml +9 -0
  251. data/app/views/droom/scraps/thumbs/_event.html.haml +17 -0
  252. data/app/views/droom/scraps/thumbs/_image.html.haml +6 -0
  253. data/app/views/droom/scraps/thumbs/_link.html.haml +10 -0
  254. data/app/views/droom/scraps/thumbs/_quote.html.haml +8 -0
  255. data/app/views/droom/scraps/thumbs/_text.html.haml +8 -0
  256. data/app/views/droom/scraps/thumbs/_video.html.haml +6 -0
  257. data/app/views/droom/shared/_controls.html.haml +5 -3
  258. data/app/views/droom/shared/_excerpt.html.haml +2 -0
  259. data/app/views/droom/shared/_navigation.html.haml +4 -4
  260. data/app/views/droom/shared/_panels.html.haml +4 -0
  261. data/app/views/droom/shared/_quicklinks.html.haml +5 -0
  262. data/app/views/droom/shared/_search_form.html.haml +2 -1
  263. data/app/views/droom/shared/_search_results.html.haml +9 -0
  264. data/app/views/droom/shared/_suggestions.html.haml +13 -9
  265. data/app/views/droom/shared/_title_excerpt.haml +2 -0
  266. data/app/views/droom/shared/search.html.haml +18 -0
  267. data/app/views/droom/users/_confirmation.html.haml +2 -0
  268. data/app/views/droom/users/_dropbox_status.html.haml +2 -0
  269. data/app/views/droom/users/_form.html.haml +34 -0
  270. data/app/views/droom/users/_user.html.haml +2 -0
  271. data/app/views/droom/users/_user_or_person.html.haml +63 -0
  272. data/app/views/droom/users/_users_table.html.haml +17 -0
  273. data/app/views/droom/users/edit.html.haml +10 -0
  274. data/app/views/droom/users/index.html.haml +42 -0
  275. data/app/views/droom/users/unwelcome.en.html.haml +17 -0
  276. data/app/views/droom/venues/_search_result.html.haml +17 -0
  277. data/app/views/droom/youtube/show.html.haml +12 -0
  278. data/app/views/kaminari/_paginator.html.haml +16 -0
  279. data/app/views/layouts/droom/application.html.haml +17 -17
  280. data/app/views/layouts/droom/email.html.haml +59 -0
  281. data/config/cucumber.yml +8 -0
  282. data/config/initializers/devise.rb +240 -0
  283. data/config/locales/devise.en.yml +58 -0
  284. data/config/locales/en.yml +203 -32
  285. data/config/routes.rb +55 -16
  286. data/config/sunspot.yml +17 -0
  287. data/db/migrate/20121120115447_tags.rb +18 -0
  288. data/db/migrate/20130116155331_dropbox_tokens.rb +9 -0
  289. data/db/migrate/20130118103540_folders.rb +47 -0
  290. data/db/migrate/20130124113715_preferences.rb +10 -0
  291. data/db/migrate/20130125132427_users_in_droom.rb +58 -0
  292. data/db/migrate/20130129142307_mobile_phones.rb +7 -0
  293. data/db/migrate/20130130120124_folder_ancestry_to_parents.rb +7 -0
  294. data/db/migrate/20130131161430_mailing_lists.rb +37 -0
  295. data/db/migrate/20130207091903_preference_uuids.rb +5 -0
  296. data/db/migrate/20130207123614_stream.rb +13 -0
  297. data/db/migrate/20130208035113_access_token_secret.rb +5 -0
  298. data/db/migrate/20130214145014_person_image.rb +9 -0
  299. data/db/migrate/20130217152644_devise_confirmable.rb +13 -0
  300. data/db/migrate/20130225095328_create_droom_calendars.rb +20 -0
  301. data/db/migrate/20130226092944_give_scraps_document.rb +5 -0
  302. data/db/migrate/20130228083509_privateness.rb +9 -0
  303. data/db/migrate/20130228134143_store_metadata.rb +5 -0
  304. data/db/migrate/20130305150648_folders_nicely.rb +6 -0
  305. data/db/migrate/20130305151936_niceties.rb +7 -0
  306. data/db/migrate/20130308103807_create_droom_dropbox_documents.rb +11 -0
  307. data/db/migrate/20130308154552_dropbox_documents_changed.rb +5 -0
  308. data/db/migrate/20130326135738_user_titles.rb +5 -0
  309. data/lib/droom.rb +189 -22
  310. data/lib/droom/engine.rb +6 -1
  311. data/lib/droom/folders.rb +106 -0
  312. data/lib/droom/lazy_hash.rb +73 -0
  313. data/lib/droom/{helpers.rb → model_helpers.rb} +1 -1
  314. data/lib/droom/monkeys.rb +21 -2
  315. data/lib/droom/routing.rb +13 -0
  316. data/lib/droom/searchability.rb +25 -0
  317. data/lib/droom/taggability.rb +60 -0
  318. data/lib/droom/title_case.rb +101 -0
  319. data/lib/droom/user_config.rb +67 -0
  320. data/lib/droom/validators.rb +6 -0
  321. data/lib/droom/version.rb +1 -1
  322. data/lib/generators/droom/install/templates/droom_initializer.rb +0 -1
  323. data/lib/tasks/cucumber.rake +65 -0
  324. data/spec/datasets/calendar_events_dataset.rb +44 -0
  325. data/spec/datasets/calendar_pages_dataset.rb +8 -0
  326. data/spec/datasets/calendar_sites_dataset.rb +6 -0
  327. data/spec/datasets/calendars_dataset.rb +34 -0
  328. data/spec/datasets/documents_dataset.rb +28 -0
  329. data/spec/datasets/recurrence_dataset.rb +7 -0
  330. data/spec/dummy/README.rdoc +261 -0
  331. data/spec/dummy/Rakefile +7 -0
  332. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  333. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  334. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  335. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  336. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  337. data/spec/dummy/config.ru +4 -0
  338. data/spec/dummy/config/application.rb +65 -0
  339. data/spec/dummy/config/boot.rb +10 -0
  340. data/spec/dummy/config/database.yml +21 -0
  341. data/spec/dummy/config/environment.rb +5 -0
  342. data/spec/dummy/config/environments/development.rb +38 -0
  343. data/spec/dummy/config/environments/production.rb +67 -0
  344. data/spec/dummy/config/environments/test.rb +38 -0
  345. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  346. data/spec/dummy/config/initializers/inflections.rb +15 -0
  347. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  348. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  349. data/spec/dummy/config/initializers/session_store.rb +8 -0
  350. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  351. data/spec/dummy/config/locales/en.yml +7 -0
  352. data/spec/dummy/config/routes.rb +3 -0
  353. data/spec/dummy/config/sunspot.yml +17 -0
  354. data/spec/dummy/db/migrate/20120911070124_create_droom_data.droom.rb +135 -0
  355. data/spec/dummy/db/migrate/20120921094604_agenda_sections.droom.rb +14 -0
  356. data/spec/dummy/db/migrate/20120921094605_add_postal_address_to_people.droom.rb +11 -0
  357. data/spec/dummy/db/migrate/20121102064845_give_groups_descriptions.droom.rb +6 -0
  358. data/spec/dummy/db/migrate/20121102064846_more_names.droom.rb +6 -0
  359. data/spec/dummy/db/migrate/20121102064847_event_agenda_sections.droom.rb +14 -0
  360. data/spec/dummy/db/migrate/20121102064848_create_group_invitations.droom.rb +11 -0
  361. data/spec/dummy/db/migrate/20121102064849_give_people_positions.droom.rb +6 -0
  362. data/spec/dummy/db/migrate/20121102064850_help_pages.droom.rb +14 -0
  363. data/spec/dummy/db/migrate/20121102064851_category_slugs.droom.rb +6 -0
  364. data/spec/dummy/db/migrate/20121102064852_document_links.droom.rb +15 -0
  365. data/spec/dummy/db/migrate/20121102064853_people_visibility.droom.rb +8 -0
  366. data/spec/dummy/db/migrate/20121102094820_shy_people.droom.rb +6 -0
  367. data/spec/dummy/db/migrate/20121102095927_visibility_defaults.droom.rb +10 -0
  368. data/spec/dummy/db/migrate/20130121122352_tags.droom.rb +19 -0
  369. data/spec/dummy/db/migrate/20130121122353_dropbox.droom.rb +10 -0
  370. data/spec/dummy/db/migrate/20130121122354_folders.droom.rb +48 -0
  371. data/spec/dummy/db/migrate/20130128142902_memberships_expire.droom.rb +6 -0
  372. data/spec/dummy/db/migrate/20130128142903_give_documents_extracted_text.droom.rb +6 -0
  373. data/spec/dummy/db/migrate/20130128142904_preferences.droom.rb +11 -0
  374. data/spec/dummy/db/migrate/20130128142905_users_in_droom.droom.rb +59 -0
  375. data/spec/dummy/db/migrate/20130130120631_folder_ancestry_to_parents.droom.rb +8 -0
  376. data/spec/dummy/db/migrate/20130201103737_mobile_phones.droom.rb +8 -0
  377. data/spec/dummy/db/migrate/20130201103738_mailing_lists.droom.rb +38 -0
  378. data/spec/dummy/db/schema.rb +327 -0
  379. data/spec/dummy/log/sunspot-solr-test.log.1 +222 -0
  380. data/spec/dummy/public/404.html +26 -0
  381. data/spec/dummy/public/422.html +26 -0
  382. data/spec/dummy/public/500.html +25 -0
  383. data/spec/dummy/public/favicon.ico +0 -0
  384. data/spec/dummy/public/system/droom/documents/files/000/000/001/original/frog.png +0 -0
  385. data/spec/dummy/public/system/droom/documents/files/000/000/001/original/rat.png +0 -0
  386. data/spec/dummy/script/rails +6 -0
  387. data/spec/dummy/solr/conf/admin-extra.html +31 -0
  388. data/spec/dummy/solr/conf/elevate.xml +36 -0
  389. data/spec/dummy/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  390. data/spec/dummy/solr/conf/protwords.txt +21 -0
  391. data/spec/dummy/solr/conf/schema.xml +238 -0
  392. data/spec/dummy/solr/conf/scripts.conf +24 -0
  393. data/spec/dummy/solr/conf/solrconfig.xml +934 -0
  394. data/spec/dummy/solr/conf/spellings.txt +2 -0
  395. data/spec/dummy/solr/conf/stopwords.txt +58 -0
  396. data/spec/dummy/solr/conf/synonyms.txt +31 -0
  397. data/spec/dummy/solr/data/development/index/segments.gen +0 -0
  398. data/spec/dummy/solr/data/development/index/segments_1 +0 -0
  399. data/spec/dummy/solr/data/development/spellchecker/segments.gen +0 -0
  400. data/spec/dummy/solr/data/development/spellchecker/segments_1 +0 -0
  401. data/spec/dummy/solr/data/test/index/segments.gen +0 -0
  402. data/spec/dummy/solr/data/test/index/segments_3nu +0 -0
  403. data/spec/dummy/solr/data/test/spellchecker/segments.gen +0 -0
  404. data/spec/dummy/solr/data/test/spellchecker/segments_1 +0 -0
  405. data/spec/dummy/webdav/1/group/rat.png +0 -0
  406. data/spec/dummy/webdav/1/unattached/rat.png +0 -0
  407. data/spec/dummy/webdav/group/rat.png +0 -0
  408. data/spec/dummy/webdav/unattached/rat.png +0 -0
  409. data/spec/dummy/webdav/unattached/rat_v1.png +0 -0
  410. data/spec/factories/agenda_categories.rb +5 -0
  411. data/spec/factories/categories.rb +5 -0
  412. data/spec/factories/documents.rb +16 -0
  413. data/spec/factories/events.rb +41 -0
  414. data/spec/factories/folders.rb +5 -0
  415. data/spec/factories/groups.rb +6 -0
  416. data/spec/factories/memberships.rb +5 -0
  417. data/spec/factories/people.rb +16 -0
  418. data/spec/factories/recurrence_rules.rb +25 -0
  419. data/spec/factories/user.rb +6 -0
  420. data/spec/fixtures/dummy.ics +59 -0
  421. data/spec/fixtures/images/frog.png +0 -0
  422. data/spec/fixtures/images/rat.png +0 -0
  423. data/spec/fixtures/ny.ics +36 -0
  424. data/spec/lib/droom/folders_spec.rb +46 -0
  425. data/spec/lib/droom/lazy_hash_spec.rb +33 -0
  426. data/spec/models/droom/agenda_category_spec.rb +15 -0
  427. data/spec/models/droom/document_spec.rb +76 -0
  428. data/spec/models/droom/event_spec.rb +154 -0
  429. data/spec/models/droom/folder_spec.rb +5 -0
  430. data/spec/models/droom/ical_spec.rb +62 -0
  431. data/spec/models/droom/mailing_list_membership_spec.rb +81 -0
  432. data/spec/models/droom/membership_spec.rb +22 -0
  433. data/spec/models/droom/person_spec.rb +72 -0
  434. data/spec/models/droom/recurrence_rule_spec.rb +81 -0
  435. data/spec/models/droom/user_spec.rb +27 -0
  436. data/spec/models/droom/visibility_spec.rb +6 -0
  437. data/spec/spec_helper.rb +119 -0
  438. data/spec/support/matchers/content_type_matcher.rb +17 -0
  439. metadata +591 -36
  440. data/app/assets/javascripts/droom/calendar.js.coffee +0 -121
  441. data/app/assets/javascripts/droom/drag_sort.js.coffee +0 -22
  442. data/app/assets/javascripts/droom/forms.js.coffee +0 -746
  443. data/app/assets/javascripts/droom/sort.js.coffee +0 -126
  444. data/app/assets/javascripts/droom/suggester.js.coffee +0 -230
  445. data/app/controllers/droom/document_attachments_controller.rb +0 -19
  446. data/app/models/droom/document_link.rb +0 -31
  447. data/app/models/droom/personal_document.rb +0 -98
  448. data/app/views/droom/dashboard/_marginalia.html.haml +0 -3
  449. data/app/views/droom/dashboard/_my_future_events.html.haml +0 -9
  450. data/app/views/droom/dashboard/_my_group_documents.html.haml +0 -6
  451. data/app/views/droom/dashboard/_my_past_events.haml +0 -6
  452. data/app/views/droom/documents/_event_document_form.html.haml +0 -15
  453. data/app/views/droom/errors/bang.html.haml +0 -12
  454. data/app/views/droom/errors/not_allowed.html.haml +0 -12
  455. data/app/views/droom/errors/not_found.html.haml +0 -12
  456. data/app/views/droom/events/_attachment.html.haml +0 -1
  457. data/app/views/droom/events/_attachment_list.html.haml +0 -4
  458. data/app/views/droom/invitations/_attending_people.html.haml +0 -8
  459. data/config/initializers/dav.rb +0 -2
@@ -2,7 +2,12 @@ module Droom
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Droom
4
4
  initializer "droom.integration" do
5
- ActiveRecord::Base.send :include, Droom::Helpers
5
+ ActiveRecord::Base.send :include, Droom::ModelHelpers
6
+ ActiveRecord::Base.send :include, Droom::Taggability
7
+ ActiveRecord::Base.send :include, Droom::Folders
8
+ ActiveSupport.on_load :action_controller do
9
+ helper Droom::DroomHelper
10
+ end
6
11
  end
7
12
  end
8
13
  end
@@ -0,0 +1,106 @@
1
+ # Documents in droom are never attached directly to an object. Instead they are always attached to a folder,
2
+ # and the folder is usually attached to an object. The folder itself is a normal ActiveRecord object with a
3
+ # name and some tree behaviours that allow us to present a filing system.
4
+ #
5
+ # This file defines the interface by which we declare that an object has a folder and would like to receive documents.
6
+ #
7
+ # It is also possible to have a loose folder: in that case it is considered public and available to everyone.
8
+ #
9
+ module Droom
10
+ module Folders
11
+
12
+ def self.included(base)
13
+ base.extend FolderClassMethods
14
+ end
15
+
16
+ module FolderClassMethods
17
+ def has_folder?
18
+ false
19
+ end
20
+
21
+ # In most cases all you need is a `has_folder` line in the model class definition.
22
+ #
23
+ # class Thing < ActiveRecord::Base
24
+ # has_folder
25
+ # end
26
+ #
27
+ # This will bring in the folder and document associations and all the instance methods
28
+ # involved in adding and removing documents. The folder itself is lazy-loaded: the first
29
+ # time it is requested, it will be created.
30
+ #
31
+ # You can specify that the folder should be created inside another folder. For example, the
32
+ # document folder associated with an agenda_category is always created within the folder of
33
+ # its event, so that the filing system reflects the organisation of the event. To achieve this,
34
+ # pass in the name of an associate as the `:within` option:
35
+ #
36
+ # class Subthing < ActiveRecord::Base
37
+ # belongs_to :thing
38
+ # has_folder :within => :thing
39
+ # end
40
+ #
41
+ def has_folder( options={} )
42
+ return if has_folder?
43
+ has_one :folder, :as => :holder, :class_name => "Droom::Folder"
44
+ has_many :documents, :through => :folder, :class_name => "Droom::Document"
45
+ # The :within option is stored as a class variable that will be consulted when the folder is created.
46
+ class_variable_set(:"@@parent_folder_holder", options[:within])
47
+
48
+ class_eval {
49
+ extend Droom::Folders::FolderedClassMethods
50
+ include Droom::Folders::FolderedInstanceMethods
51
+ alias_method_chain :folder, :lazy_load
52
+ }
53
+ end
54
+ end
55
+
56
+ module FolderedClassMethods
57
+ # The has_folder? method is used to prevent multiple calls to has_folder, but might possibly be useful elsewhere.
58
+ def has_folder?
59
+ true
60
+ end
61
+ end
62
+
63
+ module FolderedInstanceMethods
64
+ #
65
+ # Folders are lazy-created. That is, when we need it, we make it. This is achieved by chaining the `:folder`
66
+ # association method and creating a folder if none exists. This method definition must occur after the association
67
+ # has been defined.
68
+ #
69
+ def folder_with_lazy_load
70
+ folder_without_lazy_load || self.create_folder(:parent => get_parent_folder)
71
+ end
72
+
73
+ def all_documents
74
+ Droom::Document.in_folders(folder.family)
75
+ end
76
+
77
+ #
78
+ # Here we refer to the class variable defined during `has_folder` configuration. If it exists, we will put our folder
79
+ # inside that of the named associate. The containing folder might be created as a side effect.
80
+ #
81
+ def get_parent_folder
82
+ pfh = self.class.class_variable_get(:"@@parent_folder_holder")
83
+ if pfh && holder = send(pfh.to_sym)
84
+ holder.folder
85
+ else
86
+ # otherwise we want a root folder like /Events
87
+ Droom::Folder.find_or_create_by_slug_and_parent_id(self.class.to_s.titlecase.split('/').last.pluralize, nil)
88
+ end
89
+ end
90
+
91
+ # Create a new document in our folder, with the supplied properties.
92
+ #
93
+ def add_document(attributes)
94
+ folder.documents.create(attributes)
95
+ end
96
+
97
+ # Move an existing document into our folder.
98
+ #
99
+ def receive_document(doc)
100
+ folder.documents << doc
101
+ end
102
+
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,73 @@
1
+ # This is the mechanism by which droom's preferences system is bolted onto the host app's user class.
2
+ #
3
+ module Droom
4
+ # LazyHash is a recursive hash descendant that accepts keys in the form `key:subkey:subsubkey` and populates
5
+ # nested hashes accordingly.
6
+ #
7
+ # In droom this mechanism is used to manage the default values that sit behind user preferences. The motive
8
+ # is to match the `User#pref(key)` interface, with concatenated keys designating namespaced values:
9
+ #
10
+ # LazyHash.set("key:subkey:otherkey:something", "hippopotamus")
11
+ # => {:key => {:subkey => {:otherkey => {:something => "hippopotamus"}}}}
12
+ #
13
+ # LazyHash.get("key:subkey:otherkey") => {:something => "hippopotamus"}
14
+ # LazyHash.get("key:subkey:otherkey:something") => "hippopotamus"
15
+ #
16
+ # An unset value will always return nil. A set value may be false. You may want to test for the difference.
17
+ #
18
+ # NB. keys are always symbolized.
19
+ #
20
+ class LazyHash < Hash
21
+
22
+ def initialize(*args)
23
+ hash = args.extract_options!
24
+ super
25
+ hash.each_pair do |k,v|
26
+ self[k] = v.is_a?(Hash) ? Droom::LazyHash.new(v) : v
27
+ end
28
+ end
29
+
30
+ def inspect
31
+ "lazyhash: #{super}"
32
+ end
33
+
34
+ # *get* will return the value in the named bucket. The bucket is designated
35
+ # by a key that can either be simple or the dot.separated path to a nested hash.
36
+ #
37
+ def get(path)
38
+ key, subkeys = split_path(path)
39
+ if subkeys.any?
40
+ if self[key].is_a?(Droom::LazyHash)
41
+ self[key].get(subkeys)
42
+ else
43
+ nil
44
+ end
45
+ elsif self.key?(key)
46
+ self[key]
47
+ else
48
+ nil
49
+ end
50
+ end
51
+
52
+ # *set* will set the value at the named bucket. Note that you should only ever do this from an
53
+ # initializer or some other thread-global event that can be relied upon always to have run. Never call
54
+ # LazyHash#set at runtime unless what you want is a local, non-thread-global nested hash construction.
55
+ #
56
+ def set(path, value)
57
+ key, subkeys = split_path(path)
58
+ if subkeys.any?
59
+ self[key] ||= Droom::LazyHash.new({})
60
+ self[key].set(subkeys, value)
61
+ else
62
+ self[key] = value
63
+ end
64
+ end
65
+
66
+ #
67
+ def split_path(key)
68
+ keys = key.is_a?(Array) ? key : key.to_s.split('.')
69
+ keys.any? ? [keys.shift.to_sym, keys] : [nil, []]
70
+ end
71
+
72
+ end
73
+ end
@@ -1,5 +1,5 @@
1
1
  module Droom
2
- module Helpers
2
+ module ModelHelpers
3
3
  def self.included(base)
4
4
  base.extend Droom::ClassMethods
5
5
  base.send :include, Droom::InstanceMethods
@@ -1,5 +1,4 @@
1
1
  class Array
2
-
3
2
  def to_ics
4
3
  to_rical.to_s
5
4
  end
@@ -11,5 +10,25 @@ class Array
11
10
  end
12
11
  end
13
12
  end
14
-
15
13
  end
14
+
15
+ class Time
16
+ def ceil(seconds = 60)
17
+ Time.at((self.to_f / seconds).ceil * seconds)
18
+ end
19
+
20
+ def floor(seconds = 60)
21
+ Time.at((self.to_f / seconds).floor * seconds)
22
+ end
23
+ end
24
+
25
+ class DateTime
26
+ def ceil(seconds = 60)
27
+ Time.at(self.to_f).ceil(seconds).to_datetime
28
+ end
29
+
30
+ def floor(seconds = 60)
31
+ Time.at(self.to_f).floor(seconds).to_datetime
32
+ end
33
+ end
34
+
@@ -0,0 +1,13 @@
1
+ module ActionDispatch::Routing
2
+
3
+ class Mapper
4
+ def droom
5
+ droom_at('/')
6
+ end
7
+
8
+ def droom_at(prefix="/")
9
+ prefix = "#{prefix}/" unless prefix.last == '/'
10
+ mount Droom::Engine => "#{prefix}droom/", :as => :droom
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'droom/title_case'
2
+
3
+ STOPWORDS = %w{ a about above after again against all am and any are aren't as at be because been before being below between both by can't cannot could couldn't did didn't do does doesn't doing don't down during each few for from further had hadn't has hasn't have haven't having he he'd he'll he's her here here's hers herself himself his how how's i i'd i'll i'm i've if in into is isn't it it's its itself let's me more most mustn't my myself no nor not of off once only other ought our ours ourselves out over own same shan't she she'd she'll she's should shouldn't some such than that that's the their theirs them themselves then there there's these they they'd they'll they're they've this those through too under until up very was wasn't we we'd we'll we're we've were weren't what what's when when's where where's which while who who's whom why why's with won't would wouldn't you you'd you'll you're you've your yours yourself yourselves }
4
+ STOPWORD_RE = %r{\b(#{STOPWORDS.join('|')})\b\s* }
5
+ SMALL_WORDS = %w( is a an and as at but by en for if in of on or the to v[.]? via vs[.]? )
6
+ SMALL_RE = %r{\b(#{SMALL_WORDS.join('|')})\b}io
7
+
8
+ SMALL_WORDS.join('|')
9
+
10
+ class String
11
+ include TitleCase
12
+
13
+ def searchable
14
+ downcase.remove_stopwords.flatten
15
+ end
16
+
17
+ def flatten
18
+ split(/[^\w\d\']{1,}/).join(' ')
19
+ end
20
+
21
+ def remove_stopwords
22
+ gsub(STOPWORD_RE, '')
23
+ end
24
+
25
+ end
@@ -0,0 +1,60 @@
1
+ module Droom
2
+ module Taggability
3
+
4
+ def self.included(base)
5
+ base.extend TaggableClassMethods
6
+ end
7
+
8
+ module TaggableClassMethods
9
+ def has_tags?
10
+ false
11
+ end
12
+
13
+ def has_tags
14
+ return if has_tags?
15
+ has_many :taggings, :as => :taggee, :class_name => "Droom::Tagging"
16
+ has_many :tags, :through => :taggings, :class_name => "Droom::Tag"
17
+
18
+ class_eval {
19
+ extend Droom::Taggability::TaggedClassMethods
20
+ include Droom::Taggability::TaggedInstanceMethods
21
+ }
22
+ end
23
+ end
24
+
25
+ module TaggedClassMethods
26
+ def has_tags?
27
+ true
28
+ end
29
+ end
30
+
31
+ module TaggedInstanceMethods
32
+ def add_tag(word=nil)
33
+ self.tags << Tag.for(word) if word && !word.blank?
34
+ end
35
+
36
+ def remove_tag(word=nil)
37
+ tag = Tag.find_by_title(word) if word && !word.blank?
38
+ self.tags.delete(tag) if tag
39
+ end
40
+
41
+ def keywords
42
+ self.tags.map {|t| t.name}.join(', ')
43
+ end
44
+
45
+ def keywords_before_type_cast # for form_helper
46
+ keywords
47
+ end
48
+
49
+ def keywords=(somewords="")
50
+ if somewords.blank?
51
+ self.tags.clear
52
+ else
53
+ self.tags = Tag.from_list(somewords)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Ruby Title Case
4
+ # A Ruby implementation of John Gruber's Title Case
5
+ # http://daringfireball.net/2008/05/title_case
6
+ #
7
+ # Copyright (c) Paul Mucur (http://mucur.name), 2008.
8
+ # Dual-licensed under the BSD (BSD-LICENSE.txt) and GPL (GPL-LICENSE.txt)
9
+ # licenses.
10
+ #
11
+ # This program is free software; you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation; either version 2 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+
21
+ module TitleCase
22
+
23
+ # A regular expression to match small words that should not be
24
+ # titleized.
25
+ SMALL_WORDS_RE = /^(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\.?|via|vs\.?)$/
26
+
27
+ def titleize_if_appropriate(word)
28
+ if word
29
+ # If a word does not contain a full-stop within itself and it doesn't
30
+ # contain any capital letters apart from its first letter, titleize it.
31
+ if !word[/\w\.\w/] && !word[/^.+[A-Z]/]
32
+
33
+ # Capitalise the first *word* character (therefore avoiding problems
34
+ # where the first character is some punctuation).
35
+ word[/^\W*\w/] &&= word[/^\W*\w/].upcase
36
+ end
37
+
38
+ word
39
+ else
40
+ ""
41
+ end
42
+ end
43
+
44
+ def title_case
45
+
46
+ # Keep track when a colon has been used at the end of a word.
47
+ colon_preceding = false
48
+
49
+ # Split the string by any whitespace and then inspect each word
50
+ # at a time.
51
+ words = split(/\s+/).map do |word|
52
+ title_cased_word = if colon_preceding
53
+
54
+ # If there was a colon preceding this word then titleize
55
+ # it even if it is a small word.
56
+ colon_preceding = false
57
+ titleize_if_appropriate(word)
58
+ elsif word.downcase[SMALL_WORDS_RE]
59
+
60
+ # If this is a small word, make it lowercase.
61
+ word.downcase
62
+ else
63
+
64
+ # In all other cases, titleize the word.
65
+ titleize_if_appropriate(word)
66
+ end
67
+
68
+ # If this word ends in a colon, set the flag so that the
69
+ # following word can be titleized.
70
+ colon_preceding = true if word[/:$/]
71
+
72
+ title_cased_word
73
+ end
74
+
75
+ # Always capitalise the first and last words.
76
+ words[0] = titleize_if_appropriate(words[0])
77
+ words[-1] = titleize_if_appropriate(words[-1])
78
+
79
+ words.join(" ")
80
+ end
81
+ end
82
+
83
+ if $0 == __FILE__
84
+
85
+ # If this file is being executed, read in from the command line
86
+ # and STDIN.
87
+ input = ARGV.first || STDIN.read
88
+
89
+ # ARGV.first is frozen and so cannot be extended by default.
90
+ input = input.dup if input.frozen?
91
+
92
+ if input.empty?
93
+
94
+ # If no input was given, print simple usage instructions
95
+ puts "usage: ruby title_case.rb [TEXT_TO_TITLE_CASE]"
96
+ else
97
+ input.extend(TitleCase)
98
+
99
+ puts input.title_case
100
+ end
101
+ end
@@ -0,0 +1,67 @@
1
+ # This is a recursive hash that accepst keys in the form `key:subkey:subsubkey` and
2
+ # populates nested hash objects accordingly. It allows both the standard []= and a more
3
+ # convenient method-call notation.
4
+ #
5
+ #
6
+ #
7
+ # Call new with an existing hash to populate the LazyHash with defaults.
8
+
9
+ module Droom
10
+
11
+ class PreferencesHash < Hash
12
+ def get(path)
13
+ key, subkeys = split_path(path)
14
+ if subkeys.any?
15
+ self[key.to_sym].get(subkeys)
16
+ else
17
+ if self[key].is_a? Droom::Preference
18
+ self[key].value
19
+ else
20
+ self[key]
21
+ end
22
+ end
23
+ end
24
+
25
+ def set(path, value)
26
+ key, subkeys = split_path(path)
27
+ if subkeys.any?
28
+ self[key] ||= LazyHash.new({})
29
+ self[key].set(subkeys, value)
30
+ else
31
+ if self[key].is_a? Droom::Preference
32
+ self[key].set(value)
33
+ else
34
+ self[key] = value
35
+ end
36
+ end
37
+ end
38
+
39
+ def has_path?(path)
40
+ key, subkeys = split_path(path)
41
+ if subkeys.any?
42
+ self[key.to_sym].has_path?(subkeys)
43
+ else
44
+ self.has_key?(key.to_sym)
45
+ end
46
+ end
47
+
48
+ def split_path(key)
49
+ keys = path.is_a?(Array) ? path : path.to_s.split(':')
50
+ [keys.shift, keys]
51
+ end
52
+
53
+ def method_missing(method_name, *args, &blk)
54
+ return self.get(method_name, &blk) if has_path?(method_name)
55
+ match = method_name.to_s.match(/(.*?)([?=]?)$/)
56
+ case match[2]
57
+ when "="
58
+ self.set(match[1], args.first)
59
+ when "?"
60
+ !!self[match[1]]
61
+ else
62
+ default(method_name, *args, &blk)
63
+ end
64
+ end
65
+ end
66
+
67
+ end