roda-component 0.1.11 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (307) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +1 -1
  3. data/lib/roda/component.rb +107 -4
  4. data/lib/roda/component/dom.rb +9 -15
  5. data/lib/roda/component/events.rb +28 -7
  6. data/lib/roda/component/form.rb +126 -13
  7. data/lib/roda/component/form/validations.rb +18 -6
  8. data/lib/roda/component/titleize.rb +97 -0
  9. data/lib/roda/component/version.rb +1 -1
  10. data/test/dummy/.bowerrc +1 -1
  11. data/test/dummy/app.rb +31 -13
  12. data/test/dummy/bower.json +1 -1
  13. data/test/dummy/components/form.rb +46 -0
  14. data/test/dummy/components/forms/address.rb +11 -0
  15. data/test/dummy/components/forms/form.rb +14 -0
  16. data/test/dummy/components/layout.rb +2 -2
  17. data/test/dummy/components/login.rb +6 -6
  18. data/test/dummy/config.ru +1 -14
  19. data/test/dummy/dummy.db +0 -0
  20. data/test/dummy/models/user.rb +15 -13
  21. data/test/dummy/public/{chat/bower → bower}/font-awesome/.bower.json +0 -0
  22. data/test/dummy/public/{chat/bower → bower}/font-awesome/.gitignore +0 -0
  23. data/test/dummy/public/{chat/bower → bower}/font-awesome/.npmignore +0 -0
  24. data/test/dummy/public/{chat/bower → bower}/font-awesome/bower.json +0 -0
  25. data/test/dummy/public/{chat/bower → bower}/font-awesome/css/font-awesome.css +0 -0
  26. data/test/dummy/public/{chat/bower → bower}/font-awesome/css/font-awesome.min.css +0 -0
  27. data/test/dummy/public/{chat/bower → bower}/font-awesome/fonts/FontAwesome.otf +0 -0
  28. data/test/dummy/public/{chat/bower → bower}/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  29. data/test/dummy/public/{chat/bower → bower}/font-awesome/fonts/fontawesome-webfont.svg +0 -0
  30. data/test/dummy/public/{chat/bower → bower}/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  31. data/test/dummy/public/{chat/bower → bower}/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  32. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/bordered-pulled.less +0 -0
  33. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/core.less +0 -0
  34. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/extras.less +0 -0
  35. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/fixed-width.less +0 -0
  36. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/font-awesome.less +0 -0
  37. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/icons.less +0 -0
  38. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/larger.less +0 -0
  39. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/list.less +0 -0
  40. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/mixins.less +0 -0
  41. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/path.less +0 -0
  42. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/rotated-flipped.less +0 -0
  43. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/spinning.less +0 -0
  44. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/stacked.less +0 -0
  45. data/test/dummy/public/{chat/bower → bower}/font-awesome/less/variables.less +0 -0
  46. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_bordered-pulled.scss +0 -0
  47. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_core.scss +0 -0
  48. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_extras.scss +0 -0
  49. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_fixed-width.scss +0 -0
  50. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_icons.scss +0 -0
  51. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_larger.scss +0 -0
  52. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_list.scss +0 -0
  53. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_mixins.scss +0 -0
  54. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_path.scss +0 -0
  55. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_rotated-flipped.scss +0 -0
  56. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_spinning.scss +0 -0
  57. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_stacked.scss +0 -0
  58. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/_variables.scss +0 -0
  59. data/test/dummy/public/{chat/bower → bower}/font-awesome/scss/font-awesome.scss +0 -0
  60. data/test/dummy/public/{chat/bower → bower}/jScrollPane/.bower.json +0 -0
  61. data/test/dummy/public/{chat/bower → bower}/jScrollPane/.gitignore +0 -0
  62. data/test/dummy/public/{chat/bower → bower}/jScrollPane/.gitmodules +0 -0
  63. data/test/dummy/public/{chat/bower → bower}/jScrollPane/GPL-LICENSE.txt +0 -0
  64. data/test/dummy/public/{chat/bower → bower}/jScrollPane/MIT-LICENSE.txt +0 -0
  65. data/test/dummy/public/{chat/bower → bower}/jScrollPane/README.md +0 -0
  66. data/test/dummy/public/{chat/bower → bower}/jScrollPane/ajax.html +0 -0
  67. data/test/dummy/public/{chat/bower → bower}/jScrollPane/ajax_content.html +0 -0
  68. data/test/dummy/public/{chat/bower → bower}/jScrollPane/anchors.html +0 -0
  69. data/test/dummy/public/{chat/bower → bower}/jScrollPane/api.html +0 -0
  70. data/test/dummy/public/{chat/bower → bower}/jScrollPane/arrow_hover.html +0 -0
  71. data/test/dummy/public/{chat/bower → bower}/jScrollPane/arrow_positions.html +0 -0
  72. data/test/dummy/public/{chat/bower → bower}/jScrollPane/arrows.html +0 -0
  73. data/test/dummy/public/{chat/bower → bower}/jScrollPane/auto_reinitialise.html +0 -0
  74. data/test/dummy/public/{chat/bower → bower}/jScrollPane/basic.html +0 -0
  75. data/test/dummy/public/{chat/bower → bower}/jScrollPane/bower.json +0 -0
  76. data/test/dummy/public/{chat/bower → bower}/jScrollPane/build/.gitignore +0 -0
  77. data/test/dummy/public/{chat/bower → bower}/jScrollPane/build/Gruntfile.js +0 -0
  78. data/test/dummy/public/{chat/bower → bower}/jScrollPane/build/package.json +0 -0
  79. data/test/dummy/public/{chat/bower → bower}/jScrollPane/caps.html +0 -0
  80. data/test/dummy/public/{chat/bower → bower}/jScrollPane/changelog.html +0 -0
  81. data/test/dummy/public/{chat/bower → bower}/jScrollPane/destroy.html +0 -0
  82. data/test/dummy/public/{chat/bower → bower}/jScrollPane/drag_size.html +0 -0
  83. data/test/dummy/public/{chat/bower → bower}/jScrollPane/dynamic_content.html +0 -0
  84. data/test/dummy/public/{chat/bower → bower}/jScrollPane/dynamic_height.html +0 -0
  85. data/test/dummy/public/{chat/bower → bower}/jScrollPane/dynamic_width.html +0 -0
  86. data/test/dummy/public/{chat/bower → bower}/jScrollPane/events.html +0 -0
  87. data/test/dummy/public/{chat/bower → bower}/jScrollPane/faqs.html +0 -0
  88. data/test/dummy/public/{chat/bower → bower}/jScrollPane/fixed_width.html +0 -0
  89. data/test/dummy/public/{chat/bower → bower}/jScrollPane/focus.html +0 -0
  90. data/test/dummy/public/{chat/bower → bower}/jScrollPane/fullpage_scroll.html +0 -0
  91. data/test/dummy/public/{chat/bower → bower}/jScrollPane/iframe.html +0 -0
  92. data/test/dummy/public/{chat/bower → bower}/jScrollPane/iframe2.html +0 -0
  93. data/test/dummy/public/{chat/bower → bower}/jScrollPane/iframe_content1.html +0 -0
  94. data/test/dummy/public/{chat/bower → bower}/jScrollPane/iframe_content2.html +0 -0
  95. data/test/dummy/public/{chat/bower → bower}/jScrollPane/iframe_content3.html +0 -0
  96. data/test/dummy/public/{chat/bower → bower}/jScrollPane/iframe_content4.html +0 -0
  97. data/test/dummy/public/{chat/bower → bower}/jScrollPane/image.html +0 -0
  98. data/test/dummy/public/{chat/bower → bower}/jScrollPane/image/logo.png +0 -0
  99. data/test/dummy/public/{chat/bower → bower}/jScrollPane/image2.html +0 -0
  100. data/test/dummy/public/{chat/bower → bower}/jScrollPane/index.html +0 -0
  101. data/test/dummy/public/{chat/bower → bower}/jScrollPane/invisibles.html +0 -0
  102. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/after.html +0 -0
  103. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/before.html +0 -0
  104. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/brs_main.css +0 -0
  105. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/index.html +0 -0
  106. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/jquery.mousewheel.js +0 -0
  107. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/jscrollpane-2b3.css +0 -0
  108. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/jscrollpane-2b3.js +0 -0
  109. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/11/native.html +0 -0
  110. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/after.html +0 -0
  111. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/after_reinit.html +0 -0
  112. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/before.html +0 -0
  113. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/before_reinit.html +0 -0
  114. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/brs_main.css +0 -0
  115. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/index.html +0 -0
  116. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/jquery.mousewheel.js +0 -0
  117. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/jscrollpane-2b3.css +0 -0
  118. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/jscrollpane-2b3.js +0 -0
  119. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/native.html +0 -0
  120. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/12/wrapped.html +0 -0
  121. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/7/after.html +0 -0
  122. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/7/before.html +0 -0
  123. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/7/index.html +0 -0
  124. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/7/jscrollpane-2b1.css +0 -0
  125. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/7/jscrollpane-2b2.js +0 -0
  126. data/test/dummy/public/{chat/bower → bower}/jScrollPane/issues/7/native.html +0 -0
  127. data/test/dummy/public/{chat/bower → bower}/jScrollPane/known_issues.html +0 -0
  128. data/test/dummy/public/{chat/bower → bower}/jScrollPane/less_basic.html +0 -0
  129. data/test/dummy/public/{chat/bower → bower}/jScrollPane/mwheel_intent.html +0 -0
  130. data/test/dummy/public/{chat/bower → bower}/jScrollPane/override_animate.html +0 -0
  131. data/test/dummy/public/{chat/bower → bower}/jScrollPane/runeimp.html +0 -0
  132. data/test/dummy/public/{chat/bower → bower}/jScrollPane/runeimp2.html +0 -0
  133. data/test/dummy/public/{chat/bower → bower}/jScrollPane/script/demo.js +0 -0
  134. data/test/dummy/public/{chat/bower → bower}/jScrollPane/script/jquery.jscrollpane.js +0 -0
  135. data/test/dummy/public/{chat/bower → bower}/jScrollPane/script/jquery.jscrollpane.min.js +0 -0
  136. data/test/dummy/public/{chat/bower → bower}/jScrollPane/script/jquery.mousewheel.js +0 -0
  137. data/test/dummy/public/{chat/bower → bower}/jScrollPane/script/mwheelIntent.js +0 -0
  138. data/test/dummy/public/{chat/bower → bower}/jScrollPane/scroll_on_left.html +0 -0
  139. data/test/dummy/public/{chat/bower → bower}/jScrollPane/scroll_to.html +0 -0
  140. data/test/dummy/public/{chat/bower → bower}/jScrollPane/scroll_to_animate.html +0 -0
  141. data/test/dummy/public/{chat/bower → bower}/jScrollPane/settings.html +0 -0
  142. data/test/dummy/public/{chat/bower → bower}/jScrollPane/short.html +0 -0
  143. data/test/dummy/public/{chat/bower → bower}/jScrollPane/style/demo.css +0 -0
  144. data/test/dummy/public/{chat/bower → bower}/jScrollPane/style/jquery.jscrollpane.css +0 -0
  145. data/test/dummy/public/{chat/bower → bower}/jScrollPane/themes/lozenge/image/ui-icons_222222_256x240.png +0 -0
  146. data/test/dummy/public/{chat/bower → bower}/jScrollPane/themes/lozenge/image/ui-icons_888888_256x240.png +0 -0
  147. data/test/dummy/public/{chat/bower → bower}/jScrollPane/themes/lozenge/image/ui-icons_cd0a0a_256x240.png +0 -0
  148. data/test/dummy/public/{chat/bower → bower}/jScrollPane/themes/lozenge/index.html +0 -0
  149. data/test/dummy/public/{chat/bower → bower}/jScrollPane/themes/lozenge/style/jquery.jscrollpane.lozenge.css +0 -0
  150. data/test/dummy/public/{chat/bower → bower}/jScrollPane/v1.html +0 -0
  151. data/test/dummy/public/{chat/bower → bower}/jquery/.bower.json +0 -0
  152. data/test/dummy/public/{chat/bower → bower}/jquery/MIT-LICENSE.txt +0 -0
  153. data/test/dummy/public/{chat/bower → bower}/jquery/bower.json +0 -0
  154. data/test/dummy/public/{chat/bower → bower}/jquery/dist/jquery.js +0 -0
  155. data/test/dummy/public/{chat/bower → bower}/jquery/dist/jquery.min.js +0 -0
  156. data/test/dummy/public/{chat/bower → bower}/jquery/dist/jquery.min.map +0 -0
  157. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax.js +0 -0
  158. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/jsonp.js +0 -0
  159. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/load.js +0 -0
  160. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/parseJSON.js +0 -0
  161. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/parseXML.js +0 -0
  162. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/script.js +0 -0
  163. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/var/nonce.js +0 -0
  164. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/var/rquery.js +0 -0
  165. data/test/dummy/public/{chat/bower → bower}/jquery/src/ajax/xhr.js +0 -0
  166. data/test/dummy/public/{chat/bower → bower}/jquery/src/attributes.js +0 -0
  167. data/test/dummy/public/{chat/bower → bower}/jquery/src/attributes/attr.js +0 -0
  168. data/test/dummy/public/{chat/bower → bower}/jquery/src/attributes/classes.js +0 -0
  169. data/test/dummy/public/{chat/bower → bower}/jquery/src/attributes/prop.js +0 -0
  170. data/test/dummy/public/{chat/bower → bower}/jquery/src/attributes/support.js +0 -0
  171. data/test/dummy/public/{chat/bower → bower}/jquery/src/attributes/val.js +0 -0
  172. data/test/dummy/public/{chat/bower → bower}/jquery/src/callbacks.js +0 -0
  173. data/test/dummy/public/{chat/bower → bower}/jquery/src/core.js +0 -0
  174. data/test/dummy/public/{chat/bower → bower}/jquery/src/core/access.js +0 -0
  175. data/test/dummy/public/{chat/bower → bower}/jquery/src/core/init.js +0 -0
  176. data/test/dummy/public/{chat/bower → bower}/jquery/src/core/parseHTML.js +0 -0
  177. data/test/dummy/public/{chat/bower → bower}/jquery/src/core/ready.js +0 -0
  178. data/test/dummy/public/{chat/bower → bower}/jquery/src/core/var/rsingleTag.js +0 -0
  179. data/test/dummy/public/{chat/bower → bower}/jquery/src/css.js +0 -0
  180. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/addGetHookIf.js +0 -0
  181. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/curCSS.js +0 -0
  182. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/defaultDisplay.js +0 -0
  183. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/hiddenVisibleSelectors.js +0 -0
  184. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/support.js +0 -0
  185. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/swap.js +0 -0
  186. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/var/cssExpand.js +0 -0
  187. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/var/getStyles.js +0 -0
  188. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/var/isHidden.js +0 -0
  189. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/var/rmargin.js +0 -0
  190. data/test/dummy/public/{chat/bower → bower}/jquery/src/css/var/rnumnonpx.js +0 -0
  191. data/test/dummy/public/{chat/bower → bower}/jquery/src/data.js +0 -0
  192. data/test/dummy/public/{chat/bower → bower}/jquery/src/data/Data.js +0 -0
  193. data/test/dummy/public/{chat/bower → bower}/jquery/src/data/accepts.js +0 -0
  194. data/test/dummy/public/{chat/bower → bower}/jquery/src/data/var/data_priv.js +0 -0
  195. data/test/dummy/public/{chat/bower → bower}/jquery/src/data/var/data_user.js +0 -0
  196. data/test/dummy/public/{chat/bower → bower}/jquery/src/deferred.js +0 -0
  197. data/test/dummy/public/{chat/bower → bower}/jquery/src/deprecated.js +0 -0
  198. data/test/dummy/public/{chat/bower → bower}/jquery/src/dimensions.js +0 -0
  199. data/test/dummy/public/{chat/bower → bower}/jquery/src/effects.js +0 -0
  200. data/test/dummy/public/{chat/bower → bower}/jquery/src/effects/Tween.js +0 -0
  201. data/test/dummy/public/{chat/bower → bower}/jquery/src/effects/animatedSelector.js +0 -0
  202. data/test/dummy/public/{chat/bower → bower}/jquery/src/event.js +0 -0
  203. data/test/dummy/public/{chat/bower → bower}/jquery/src/event/ajax.js +0 -0
  204. data/test/dummy/public/{chat/bower → bower}/jquery/src/event/alias.js +0 -0
  205. data/test/dummy/public/{chat/bower → bower}/jquery/src/event/support.js +0 -0
  206. data/test/dummy/public/{chat/bower → bower}/jquery/src/exports/amd.js +0 -0
  207. data/test/dummy/public/{chat/bower → bower}/jquery/src/exports/global.js +0 -0
  208. data/test/dummy/public/{chat/bower → bower}/jquery/src/intro.js +0 -0
  209. data/test/dummy/public/{chat/bower → bower}/jquery/src/jquery.js +0 -0
  210. data/test/dummy/public/{chat/bower → bower}/jquery/src/manipulation.js +0 -0
  211. data/test/dummy/public/{chat/bower → bower}/jquery/src/manipulation/_evalUrl.js +0 -0
  212. data/test/dummy/public/{chat/bower → bower}/jquery/src/manipulation/support.js +0 -0
  213. data/test/dummy/public/{chat/bower → bower}/jquery/src/manipulation/var/rcheckableType.js +0 -0
  214. data/test/dummy/public/{chat/bower → bower}/jquery/src/offset.js +0 -0
  215. data/test/dummy/public/{chat/bower → bower}/jquery/src/outro.js +0 -0
  216. data/test/dummy/public/{chat/bower → bower}/jquery/src/queue.js +0 -0
  217. data/test/dummy/public/{chat/bower → bower}/jquery/src/queue/delay.js +0 -0
  218. data/test/dummy/public/{chat/bower → bower}/jquery/src/selector-native.js +0 -0
  219. data/test/dummy/public/{chat/bower → bower}/jquery/src/selector-sizzle.js +0 -0
  220. data/test/dummy/public/{chat/bower → bower}/jquery/src/selector.js +0 -0
  221. data/test/dummy/public/{chat/bower → bower}/jquery/src/serialize.js +0 -0
  222. data/test/dummy/public/{chat/bower → bower}/jquery/src/sizzle/dist/sizzle.js +0 -0
  223. data/test/dummy/public/{chat/bower → bower}/jquery/src/sizzle/dist/sizzle.min.js +0 -0
  224. data/test/dummy/public/{chat/bower → bower}/jquery/src/sizzle/dist/sizzle.min.map +0 -0
  225. data/test/dummy/public/{chat/bower → bower}/jquery/src/traversing.js +0 -0
  226. data/test/dummy/public/{chat/bower → bower}/jquery/src/traversing/findFilter.js +0 -0
  227. data/test/dummy/public/{chat/bower → bower}/jquery/src/traversing/var/rneedsContext.js +0 -0
  228. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/arr.js +0 -0
  229. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/class2type.js +0 -0
  230. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/concat.js +0 -0
  231. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/hasOwn.js +0 -0
  232. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/indexOf.js +0 -0
  233. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/pnum.js +0 -0
  234. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/push.js +0 -0
  235. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/rnotwhite.js +0 -0
  236. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/slice.js +0 -0
  237. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/strundefined.js +0 -0
  238. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/support.js +0 -0
  239. data/test/dummy/public/{chat/bower → bower}/jquery/src/var/toString.js +0 -0
  240. data/test/dummy/public/{chat/bower → bower}/jquery/src/wrap.js +0 -0
  241. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/.bower.json +0 -0
  242. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/README.md +0 -0
  243. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/bower.json +0 -0
  244. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Bold/OpenSans-Bold.eot +0 -0
  245. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Bold/OpenSans-Bold.svg +0 -0
  246. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Bold/OpenSans-Bold.ttf +0 -0
  247. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Bold/OpenSans-Bold.woff +0 -0
  248. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/BoldItalic/OpenSans-BoldItalic.eot +0 -0
  249. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/BoldItalic/OpenSans-BoldItalic.svg +0 -0
  250. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/BoldItalic/OpenSans-BoldItalic.ttf +0 -0
  251. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/BoldItalic/OpenSans-BoldItalic.woff +0 -0
  252. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBold/OpenSans-ExtraBold.eot +0 -0
  253. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBold/OpenSans-ExtraBold.svg +0 -0
  254. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBold/OpenSans-ExtraBold.ttf +0 -0
  255. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBold/OpenSans-ExtraBold.woff +0 -0
  256. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBoldItalic/OpenSans-ExtraBoldItalic.eot +0 -0
  257. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBoldItalic/OpenSans-ExtraBoldItalic.svg +0 -0
  258. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBoldItalic/OpenSans-ExtraBoldItalic.ttf +0 -0
  259. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/ExtraBoldItalic/OpenSans-ExtraBoldItalic.woff +0 -0
  260. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Italic/OpenSans-Italic.eot +0 -0
  261. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Italic/OpenSans-Italic.svg +0 -0
  262. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Italic/OpenSans-Italic.ttf +0 -0
  263. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Italic/OpenSans-Italic.woff +0 -0
  264. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Light/OpenSans-Light.eot +0 -0
  265. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Light/OpenSans-Light.svg +0 -0
  266. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Light/OpenSans-Light.ttf +0 -0
  267. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Light/OpenSans-Light.woff +0 -0
  268. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/LightItalic/OpenSans-LightItalic.eot +0 -0
  269. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/LightItalic/OpenSans-LightItalic.svg +0 -0
  270. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/LightItalic/OpenSans-LightItalic.ttf +0 -0
  271. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/LightItalic/OpenSans-LightItalic.woff +0 -0
  272. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Regular/OpenSans-Regular.eot +0 -0
  273. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Regular/OpenSans-Regular.svg +0 -0
  274. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Regular/OpenSans-Regular.ttf +0 -0
  275. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Regular/OpenSans-Regular.woff +0 -0
  276. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Semibold/OpenSans-Semibold.eot +0 -0
  277. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Semibold/OpenSans-Semibold.svg +0 -0
  278. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Semibold/OpenSans-Semibold.ttf +0 -0
  279. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/Semibold/OpenSans-Semibold.woff +0 -0
  280. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/SemiboldItalic/OpenSans-SemiboldItalic.eot +0 -0
  281. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/SemiboldItalic/OpenSans-SemiboldItalic.svg +0 -0
  282. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/SemiboldItalic/OpenSans-SemiboldItalic.ttf +0 -0
  283. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/fonts/SemiboldItalic/OpenSans-SemiboldItalic.woff +0 -0
  284. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/index.html +0 -0
  285. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/open-sans.css +0 -0
  286. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/open-sans.less +0 -0
  287. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/open-sans.scss +0 -0
  288. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/package.json +0 -0
  289. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_Bold.scss +0 -0
  290. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_BoldItalic.scss +0 -0
  291. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_ExtraBold.scss +0 -0
  292. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_ExtraBoldItalic.scss +0 -0
  293. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_Italic.scss +0 -0
  294. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_Light.scss +0 -0
  295. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_LightItalic.scss +0 -0
  296. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_Regular.scss +0 -0
  297. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_Semibold.scss +0 -0
  298. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_SemiboldItalic.scss +0 -0
  299. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_mixins.scss +0 -0
  300. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/_variables.scss +0 -0
  301. data/test/dummy/public/{chat/bower → bower}/open-sans-fontface/sass/open-sans.scss +0 -0
  302. data/test/dummy/public/form/README.txt +7 -0
  303. data/test/dummy/public/form/css/style.css +80 -0
  304. data/test/dummy/public/form/index.html +58 -0
  305. data/test/dummy/public/form/js/index.js +24 -0
  306. data/test/dummy/public/form/license.txt +9 -0
  307. metadata +579 -564
@@ -97,7 +97,7 @@ class Roda
97
97
  # when the validation fails.
98
98
  def assert_format(att, format, error = [att, :format])
99
99
  if assert_present(att, error)
100
- assert(send(att).to_s.match(format), error)
100
+ assert(_attributes.send(att).to_s.match(format), error)
101
101
  end
102
102
  end
103
103
 
@@ -107,8 +107,20 @@ class Roda
107
107
  # @param [Symbol] att The attribute you wish to verify the presence of.
108
108
  # @param [Array<Symbol, Symbol>] error The error that should be returned
109
109
  # when the validation fails.
110
- def assert_present(att, error = [att, :not_present])
111
- assert(!send(att).to_s.empty?, error)
110
+ def assert_present(att, error = [att, :not_present], klass = false)
111
+ if error.is_a? Class
112
+ error_new = klass.dup
113
+ klass = error
114
+ error = error_new || [att, :not_present]
115
+
116
+ options = {}
117
+ options[:key] = _options[:key] if _options.key? :key
118
+
119
+ f = klass.new(_attributes.send(att), options)
120
+ assert(f.valid?, [att, f.errors])
121
+ else
122
+ assert(!_attributes.send(att).to_s.empty?, error)
123
+ end
112
124
  end
113
125
 
114
126
  # Checks if all the characters of an attribute is a digit.
@@ -139,12 +151,12 @@ class Roda
139
151
  end
140
152
 
141
153
  def assert_member(att, set, err = [att, :not_valid])
142
- assert(set.include?(send(att)), err)
154
+ assert(set.include?(_attributes.send(att)), err)
143
155
  end
144
156
 
145
157
  def assert_length(att, range, error = [att, :not_in_range])
146
158
  if assert_present(att, error)
147
- val = send(att).to_s
159
+ val = _attributes.send(att).to_s
148
160
  assert range.include?(val.length), error
149
161
  end
150
162
  end
@@ -173,7 +185,7 @@ class Roda
173
185
  # @param [Array<Symbol, Symbol>] error The error that should be returned
174
186
  # when the validation fails.
175
187
  def assert_equal(att, value, error = [att, :not_equal])
176
- assert value === send(att), error
188
+ assert value === _attributes.send(att), error
177
189
  end
178
190
 
179
191
  # The grand daddy of all assertions. If you want to build custom
@@ -0,0 +1,97 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Adds String#titleize for creating properly capitalized titles.
3
+ # It can be called as Titleize.titleize or "a string".titleize.
4
+ #
5
+ # titlecase is included as an alias for titleize.
6
+ #
7
+ # If loaded in a Rails environment, it modifies Inflector.titleize.
8
+ module Titleize
9
+ SMALL_WORDS = %w{a an and as at but by en for if in of on or the to v v. via vs vs.}
10
+
11
+ extend self
12
+
13
+ # Capitalizes most words to create a nicer looking title string.
14
+ #
15
+ # The list of "small words" which are not capped comes from
16
+ # the New York Times Manual of Style, plus 'vs' and 'v'.
17
+ #
18
+ # "notes on a scandal" # => "Notes on a Scandal"
19
+ # "the good german" # => "The Good German"
20
+ def titleize(title)
21
+ title = title.dup
22
+ title.downcase! unless title[/[[:lower:]]/] # assume all-caps need fixing
23
+
24
+ phrases(title).map do |phrase|
25
+ words = phrase.split
26
+ words.map do |word|
27
+ def word.capitalize
28
+ # like String#capitalize, but it starts with the first letter
29
+ self.sub(/[[:alpha:]].*/) {|subword| subword.capitalize}
30
+ end
31
+
32
+ case word
33
+ when /[[:alpha:]]\.[[:alpha:]]/ # words with dots in, like "example.com"
34
+ word
35
+ when /[-‑]/ # hyphenated word (regular and non-breaking)
36
+ word.split(/([-‑])/).map do |part|
37
+ SMALL_WORDS.include?(part) ? part : part.capitalize
38
+ end.join
39
+ when /^[[:alpha:]].*[[:upper:]]/ # non-first letter capitalized already
40
+ word
41
+ when /^[[:digit:]]/ # first character is a number
42
+ word
43
+ when words.first, words.last
44
+ word.capitalize
45
+ when *(SMALL_WORDS + SMALL_WORDS.map {|small| small.capitalize })
46
+ word.downcase
47
+ else
48
+ word.capitalize
49
+ end
50
+ end.join(" ")
51
+ end.join(" ")
52
+ end
53
+
54
+ # Splits a title into an array based on punctuation.
55
+ #
56
+ # "simple title" # => ["simple title"]
57
+ # "more complicated: titling" # => ["more complicated:", "titling"]
58
+ def phrases(title)
59
+ phrases = title.scan(/.+?(?:[:.;?!] |$)/).map {|phrase| phrase.strip }
60
+
61
+ # rejoin phrases that were split on the '.' from a small word
62
+ if phrases.size > 1
63
+ phrases[0..-2].each_with_index do |phrase, index|
64
+ if SMALL_WORDS.include?(phrase.split.last.downcase)
65
+ phrases[index] << " " + phrases.slice!(index + 1)
66
+ end
67
+ end
68
+ end
69
+
70
+ phrases
71
+ end
72
+ end
73
+
74
+ class String
75
+ # Capitalizes most words to create a nicer looking title string.
76
+ #
77
+ # The list of "small words" which are not capped comes from
78
+ # the New York Times Manual of Style, plus 'vs' and 'v'.
79
+ #
80
+ # titleize is also aliased as titlecase.
81
+ #
82
+ # "notes on a scandal" # => "Notes on a Scandal"
83
+ # "the good german" # => "The Good German"
84
+ def titleize(opts={})
85
+ if defined? ActiveSupport
86
+ ActiveSupport::Inflector.titleize(self, opts)
87
+ else
88
+ Titleize.titleize(self)
89
+ end
90
+ end
91
+ alias_method :titlecase, :titleize
92
+
93
+ def titleize!
94
+ replace(titleize)
95
+ end
96
+ alias_method :titlecase!, :titleize!
97
+ end
@@ -1,5 +1,5 @@
1
1
  class Roda
2
2
  class Component
3
- VERSION = "0.1.11"
3
+ VERSION = "0.1.15"
4
4
  end
5
5
  end
@@ -1,3 +1,3 @@
1
1
  {
2
- "directory" : "public/chat/bower"
2
+ "directory" : "public/bower"
3
3
  }
@@ -34,6 +34,8 @@ class TestApp < Roda
34
34
  end
35
35
  end
36
36
 
37
+ require_relative 'models/user'
38
+
37
39
  # use BetterErrors::Middleware
38
40
  use Shield::Middleware, "/login"
39
41
  use Rack::Session::Cookie,
@@ -43,21 +45,33 @@ class TestApp < Roda
43
45
  plugin :csrf, header: 'X-CSRF-TOKEN', skip: ['POST:/faye']
44
46
  plugin :component, { path: 'components', token: '687^*&SAD876asd87as6d*&8asd', debug: true }
45
47
  plugin :assets, {
46
- path: "#{path}/../public/chat",
48
+ path: "#{path}/../public",
47
49
  css_dir: '',
48
50
  js_dir: '',
49
- css: [
50
- 'bower/open-sans-fontface/open-sans.css',
51
- 'bower/font-awesome/css/font-awesome.css',
52
- 'bower/jScrollPane/style/jquery.jscrollpane.css',
53
- 'css/style.css',
54
- 'css/login.scss'
55
- ],
56
- js: [
57
- 'bower/jquery/dist/jquery.js',
58
- 'bower/jScrollPane/script/jquery.mousewheel.js',
59
- 'bower/jScrollPane/script/jquery.jscrollpane.js'
60
- ]
51
+ group_subdirs: false,
52
+ css: {
53
+ chat: [
54
+ 'bower/open-sans-fontface/open-sans.css',
55
+ 'bower/font-awesome/css/font-awesome.css',
56
+ 'bower/jScrollPane/style/jquery.jscrollpane.css',
57
+ 'chat/css/style.css',
58
+ 'chat/css/login.scss'
59
+ ],
60
+ form: [
61
+ 'form/css/style.css'
62
+ ]
63
+ },
64
+ js: {
65
+ chat: [
66
+ 'bower/jquery/dist/jquery.js',
67
+ 'bower/jScrollPane/script/jquery.mousewheel.js',
68
+ 'chat/bower/jScrollPane/script/jquery.jscrollpane.js'
69
+ ],
70
+ form: [
71
+ 'bower/jquery/dist/jquery.js',
72
+ 'form/js/index.js'
73
+ ]
74
+ }
61
75
  }
62
76
 
63
77
  def current_user
@@ -80,6 +94,10 @@ class TestApp < Roda
80
94
 
81
95
  r.root { component(:chat, js: true) }
82
96
 
97
+ r.on('form') do
98
+ component(:form, js: true)
99
+ end
100
+
83
101
  r.on('login') { component(:login, js: true) }
84
102
  r.on('logout') { component(:login, call: :logout) }
85
103
 
@@ -10,7 +10,7 @@
10
10
  "**/.*",
11
11
  "node_modules",
12
12
  "bower_components",
13
- "public/chat/bower",
13
+ "public/bower",
14
14
  "test",
15
15
  "tests"
16
16
  ],
@@ -1,3 +1,49 @@
1
+ require_relative 'forms/form'
2
+
1
3
  class Form < Roda::Component
4
+ comp_name :form
5
+ comp_html "../public/form/index.html"
6
+ comp_setup do |dom|
7
+ # remove hard coded links as we are adding them in using the assets plugin.
8
+ dom.css('head > link').remove
9
+ # add require css and javascript
10
+ dom.at_css('head').add_child assets([:css, :form])
11
+ dom.at_css('html').add_child assets([:js, :form])
12
+ dom.at_css('html').add_child <<-EOF
13
+ <script type="text/javascript" src="/assets/components/roda/component.js"></script>
14
+ <script type="text/javascript" src="/faye/client.js"></script>
15
+ EOF
16
+ end
17
+
18
+ def display
19
+ return unless server?
20
+
21
+ dom.at_css('head').add_child csrf_metatag
22
+
23
+ data = {
24
+ name: 'test',
25
+ address: {
26
+ zip: 90036
27
+ }
28
+ }
29
+
30
+ form_dom = dom.find('#form')
31
+ form = Forms::Login.new(data, key: :user, dom: form_dom)
32
+
33
+ form.render_values
34
+
35
+ render_fields data, dom: dom.find('.profile')
36
+
37
+ dom
38
+ end
39
+
40
+ on :form, '#form', Forms::Login, key: :user do |form, el, evt|
41
+ render_fields form, dom: dom.find('.profile')
2
42
 
43
+ if form.valid?
44
+ puts 'yay, vaild!'
45
+ else
46
+ form.display_errors
47
+ end
48
+ end
3
49
  end
@@ -0,0 +1,11 @@
1
+ module Forms
2
+ class Address < Roda::Component::Form
3
+ attr_accessor :line1, :line2, :city, :state, :zip
4
+
5
+ def validate
6
+ assert_present :line1
7
+ assert_present :city
8
+ assert_present :zip
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ require 'roda/component/form'
2
+ require_relative 'address'
3
+
4
+ module Forms
5
+ class Login < Roda::Component::Form
6
+ attr_accessor :name, :email, :phone, :address
7
+
8
+ def validate
9
+ assert_present :name
10
+ assert_present :email
11
+ assert_present :address, Forms::Address
12
+ end
13
+ end
14
+ end
@@ -5,8 +5,8 @@ class LayoutComp < Roda::Component
5
5
  # remove hard coded links as we are adding them in using the assets plugin.
6
6
  dom.css('head > link').remove
7
7
  # add require css and javascript
8
- dom.at_css('head').add_child assets(:css)
9
- dom.at_css('html').add_child assets(:js)
8
+ dom.at_css('head').add_child assets([:css, :chat])
9
+ dom.at_css('html').add_child assets([:js, :chat])
10
10
  dom.at_css('html').add_child <<-EOF
11
11
  <script type="text/javascript" src="/assets/components/roda/component.js"></script>
12
12
  <script type="text/javascript" src="/faye/client.js"></script>
@@ -19,7 +19,7 @@ class LoginComponent < Roda::Component
19
19
  end
20
20
 
21
21
  def logout
22
- super(Models::User)
22
+ super(TestApp::Models::User)
23
23
  request.redirect 'login'
24
24
  end
25
25
 
@@ -30,18 +30,18 @@ class LoginComponent < Roda::Component
30
30
 
31
31
  if signup
32
32
  if form.valid?
33
- Models::User.create form.slice(:first_name, :last_name, :email, :password)
34
- login(Models::User, form.email, form.password)
33
+ TestApp::Models::User.create form.slice(:first_name, :last_name, :email, :password)
34
+ login(TestApp::Models::User, form.email, form.password)
35
35
  {success: true}
36
36
  else
37
37
  {success: false, errors: form.errors}
38
38
  end
39
39
  else
40
- user = Models::User.where(email: form.email).first
40
+ user = TestApp::Models::User.where(email: form.email).first
41
41
 
42
42
  return {success: false, reason: 'Account doesn\'t exist.'} unless user
43
43
 
44
- if login(Models::User, form.email, form.password)
44
+ if login(TestApp::Models::User, form.email, form.password)
45
45
  {success: true}
46
46
  else
47
47
  {success: false, errors: { email: ['Email and Password combination is incorrect.']}}
@@ -50,7 +50,7 @@ class LoginComponent < Roda::Component
50
50
  end
51
51
  end
52
52
 
53
- on :form, 'form.main-form', class: Forms::Login do |form, el, evt|
53
+ on :form, 'form.main-form', Forms::Login do |form, el, evt|
54
54
  if form.valid?
55
55
  login_with form.attributes do |res|
56
56
  if res['success']
@@ -1,19 +1,6 @@
1
1
  require 'pry'
2
2
  require 'awesome_print'
3
- require 'rack/unreloader'
4
3
  require './constants'
5
4
  require './app'
6
5
 
7
- Unreloader = Rack::Unreloader.new(:logger=>Logger.new($stdout)){TestApp}
8
- Unreloader.require './app.rb'
9
- Unreloader.require './components'
10
- Unreloader.require './models'
11
- Unreloader.require '../../lib/roda/component/dom.rb'
12
- Unreloader.require '../../lib/roda/component/faye.rb'
13
- Unreloader.require '../../lib/roda/component/form.rb'
14
- Unreloader.require '../../lib/roda/component/events.rb'
15
- Unreloader.require '../../lib/roda/component/form/validations.rb'
16
- Unreloader.require '../../lib/roda/component.rb'
17
- Unreloader.require '../../lib/roda/plugins/component.rb'
18
-
19
- run Unreloader
6
+ run TestApp
Binary file
@@ -1,18 +1,20 @@
1
- module Models
2
- class User < Sequel::Model
3
- include Shield::Model
1
+ class TestApp
2
+ module Models
3
+ class User < Sequel::Model
4
+ include Shield::Model
4
5
 
5
- def full_name
6
- "#{first_name} #{last_name}"
7
- end
6
+ def full_name
7
+ "#{first_name} #{last_name}"
8
+ end
8
9
 
9
- class << self
10
- def fetch email
11
- # TODO: Case insensitive emails? Force lowercase?
12
- if user = Models::User.where(email: email)
13
- user.first
14
- else
15
- false
10
+ class << self
11
+ def fetch email
12
+ # TODO: Case insensitive emails? Force lowercase?
13
+ if user = Models::User.where(email: email)
14
+ user.first
15
+ else
16
+ false
17
+ end
16
18
  end
17
19
  end
18
20
  end