trellis 0.0.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 (292) hide show
  1. data/.git/COMMIT_EDITMSG +1 -0
  2. data/.git/HEAD +1 -0
  3. data/.git/config +9 -0
  4. data/.git/description +1 -0
  5. data/.git/hooks/applypatch-msg.sample +15 -0
  6. data/.git/hooks/commit-msg.sample +24 -0
  7. data/.git/hooks/post-commit.sample +8 -0
  8. data/.git/hooks/post-receive.sample +15 -0
  9. data/.git/hooks/post-update.sample +8 -0
  10. data/.git/hooks/pre-applypatch.sample +14 -0
  11. data/.git/hooks/pre-commit.sample +18 -0
  12. data/.git/hooks/pre-rebase.sample +169 -0
  13. data/.git/hooks/prepare-commit-msg.sample +36 -0
  14. data/.git/hooks/update.sample +107 -0
  15. data/.git/index +0 -0
  16. data/.git/info/exclude +6 -0
  17. data/.git/logs/HEAD +1 -0
  18. data/.git/logs/refs/heads/master +1 -0
  19. data/.git/logs/refs/remotes/origin/master +1 -0
  20. data/.git/objects/01/2368d4dd3162ed570e6c1423ecb07a6a35f8c0 +0 -0
  21. data/.git/objects/02/238c88c522b88e71529495460cc5c2c8abe110 +0 -0
  22. data/.git/objects/07/03d3b123189f1da72d3889d9340e681841c96d +0 -0
  23. data/.git/objects/0b/3935fdbf3ce8d37e6cb2a6ec94edaccb1c58dc +0 -0
  24. data/.git/objects/0f/ecd1ad35f280c874ce070b7a97a4c1f86ace54 +0 -0
  25. data/.git/objects/12/61911b063b7e6883c5e9db7ab4396183f6f2b5 +0 -0
  26. data/.git/objects/13/0b7e2d214ec198d07aeed5b462597670ce85ad +0 -0
  27. data/.git/objects/13/214d40f02b63f1233fd7881b1df9bb75d9c0a7 +0 -0
  28. data/.git/objects/14/7522bdc96af0b578935d81c1a7347b40573940 +0 -0
  29. data/.git/objects/16/a7fde706fe92b3548254d29722f5ca311ce63b +2 -0
  30. data/.git/objects/16/fe580663cc29d141bf416dff5bf21bf4ea42a3 +0 -0
  31. data/.git/objects/17/2d55806918bafb4e701515edb27147a4ef6498 +0 -0
  32. data/.git/objects/17/686ef1882ed6c3c07b781fab18739b1c5e48bd +0 -0
  33. data/.git/objects/17/824053c41a2cb523303e20cac42f66f9ca2134 +0 -0
  34. data/.git/objects/18/68448c5f88bc73b2fef6b8f3aa75b663013a3a +1 -0
  35. data/.git/objects/1a/6d9051dc4c7db10bdcf5e17c5cb717b072d30a +0 -0
  36. data/.git/objects/1a/8bdb445fb5668dcd1309cb9de5dedf846c822f +0 -0
  37. data/.git/objects/1b/6fa67b70358fdb82eabcd7ef3bc46d518d0426 +0 -0
  38. data/.git/objects/1c/f6510ed08a274fb3292d40557cebb3e7090767 +0 -0
  39. data/.git/objects/1d/e661ce11d5335e5dd7968ef5698e2eb164eb52 +0 -0
  40. data/.git/objects/1e/301ee672d6040e734af6d0a5491185129d7b67 +0 -0
  41. data/.git/objects/21/2201d46200f13d23d8199bd4a7ecc9165205b3 +0 -0
  42. data/.git/objects/22/f687370babdf38d224d2a9ff039707e8d0aac1 +0 -0
  43. data/.git/objects/24/202d2de95dc31f5f1802e629a16ad45b723267 +0 -0
  44. data/.git/objects/25/4c5b6b8d211d8b9f07310fd342fd7b078a81af +0 -0
  45. data/.git/objects/25/5832653fde9e9748855347fe7bce013aa0a6c7 +0 -0
  46. data/.git/objects/25/806945c89b4b5adb8c8676c308e4653361109c +5 -0
  47. data/.git/objects/26/cb8deaca28bf275769cbb71b88e53a44adb9ca +0 -0
  48. data/.git/objects/2e/1c13fb5f154f89cccd4c645540a2ec8ee0f57a +0 -0
  49. data/.git/objects/2f/437425b2c085bb529379e409d6b4bc7f9f9fba +0 -0
  50. data/.git/objects/2f/7cf4a25937b71f3de52e70c7a47cb2f7da07ea +0 -0
  51. data/.git/objects/31/31bdbbac6e6e7e24fd6ea84332461f67e6e5b6 +0 -0
  52. data/.git/objects/32/02e02de478263912b729283eabc7cdab8a8037 +0 -0
  53. data/.git/objects/32/839ed9baa8c800d9033655d1ee995499825fb5 +0 -0
  54. data/.git/objects/34/2e479c61796125b0e025b483401ec12f3c5824 +2 -0
  55. data/.git/objects/36/7acb017aebab3f627a5208c25885c21fa7fdbb +0 -0
  56. data/.git/objects/38/12910d2e2a8e6eeb79a399d20751675d3fbe77 +0 -0
  57. data/.git/objects/38/3fe1e1a07537db6e0f39c6005b7b4e40fa5ccd +0 -0
  58. data/.git/objects/39/5ef328bc94df9191f4d80a77503a5be9f1f673 +0 -0
  59. data/.git/objects/39/dc76dc096fdf737e9f4f7062b78cbf4ab9628f +0 -0
  60. data/.git/objects/3b/09eb196140b471556ed4c3987c0bc739733617 +0 -0
  61. data/.git/objects/3b/9f97f1bf55834832a92bab1ab900918dc49570 +0 -0
  62. data/.git/objects/40/5ffd3925b39dd905638ec24640dd8efa27cc1b +0 -0
  63. data/.git/objects/42/4a5f37c6fe3a7cac54b0f85688c1cce7da9cdf +0 -0
  64. data/.git/objects/44/9e23363d4e20da6a67be7ab78fc610b4e9852a +0 -0
  65. data/.git/objects/48/90bb5d241cecaa30b28148c920b833056c7508 +0 -0
  66. data/.git/objects/4c/558c43676410a2c0a7d5c7b94fca18923a9af1 +0 -0
  67. data/.git/objects/4f/bf572175dd5245104679ff8f676f6a34466c29 +0 -0
  68. data/.git/objects/53/8583edf00c9f656b32149a0e69e030371737c5 +0 -0
  69. data/.git/objects/5b/8a2c4bf7bb56ae199414096dbda21fd4b8cd63 +0 -0
  70. data/.git/objects/5c/9fe8ff998d56dc076657ef88ee9f784ec160f9 +0 -0
  71. data/.git/objects/5f/8441f9d3507587940f31af04dd9fd5911d2812 +0 -0
  72. data/.git/objects/61/4f2752550e63b793e31dcf98b5ea26c32eb0be +0 -0
  73. data/.git/objects/65/801b0fabdfd673861dbb2ac134d438ab2cc5e4 +3 -0
  74. data/.git/objects/66/2746dffaddbcd311f4ac070b5b9bddcd8894a7 +0 -0
  75. data/.git/objects/69/1ed3b65603a0d0dabfb66720e7163664be78f5 +0 -0
  76. data/.git/objects/69/3dc3b6037df8c1c8dfda916d649e7975481124 +0 -0
  77. data/.git/objects/69/67538427b3a371b33c01c72726ac7e67a04c39 +0 -0
  78. data/.git/objects/69/6cd6c3508e2adcc68829497884ff3d4b774dc2 +0 -0
  79. data/.git/objects/69/6ee93917040308ccd0bfc245d63c1cbeb23dae +0 -0
  80. data/.git/objects/6a/3ac317746a8a2cfc88f3580bbd2b03dc0d50b5 +0 -0
  81. data/.git/objects/6d/6ba435839a65b96cb3f7584a40dd3767a0990f +0 -0
  82. data/.git/objects/6f/32c190a651a82caeefeb55eb797fc5daf05426 +0 -0
  83. data/.git/objects/71/fecbce1bec36eaf4de655763d4bb7ceffc991d +0 -0
  84. data/.git/objects/72/01d78a9e90347fd2f7790f9e8665587791d853 +0 -0
  85. data/.git/objects/73/6c97d6a551363ba4a4956408ba228fcab7f91f +0 -0
  86. data/.git/objects/75/3264271e935e18f4eada49aa1818c6fb9e6abb +0 -0
  87. data/.git/objects/75/cc7d390b4a565495cbdc5a08d4479e7e8dbfeb +0 -0
  88. data/.git/objects/76/7c0c96f68b9ccec34ae538045a63bbe4f97d3e +0 -0
  89. data/.git/objects/7b/47fe9b1ab9f2692b9706054de706430089ecd4 +0 -0
  90. data/.git/objects/7c/73144ef6847d1bc2315aee0925c0e5cf5c09d1 +0 -0
  91. data/.git/objects/7d/9c177c155386606aa1024d5cb020a097eea64a +0 -0
  92. data/.git/objects/7d/e9a3296007c09ba9b7bc1802e1c5d9ad1d5dbb +0 -0
  93. data/.git/objects/7e/b73d408147c8d84881109189a9724292abcb8d +0 -0
  94. data/.git/objects/82/0e3bf5449a8f6b8eb637a653dcb2dc387b393c +0 -0
  95. data/.git/objects/83/a10ce96763cb30d0b81425e9372f89c52f4c9f +0 -0
  96. data/.git/objects/83/bdb801e78444d59225f83cf2f336f114cd23cf +0 -0
  97. data/.git/objects/85/24e583e822abc18b1ffdd0aa356c9c079b5548 +0 -0
  98. data/.git/objects/88/b46cc8de5237897d4ba5023d3efdc83fec4f7c +0 -0
  99. data/.git/objects/89/5eea66474f48a2cbf643ad0ef266fd26f9d2a3 +0 -0
  100. data/.git/objects/8a/539c5fc35d5b770d67241d2ccb6c6855df6a43 +0 -0
  101. data/.git/objects/8a/82cdbfb20cc58fdffe4e13b70b55977deb92c6 +0 -0
  102. data/.git/objects/8b/2b537442e4ac8719ffa71b3ae0c707133df06f +0 -0
  103. data/.git/objects/8c/2a45f0c81284a081f8d7980e9a54210a9dd52c +0 -0
  104. data/.git/objects/8e/10355b4ab312450031966107ef6a65b13833ee +0 -0
  105. data/.git/objects/90/f6c76767ae725f15fa6790dcb6afd1719fa0e2 +0 -0
  106. data/.git/objects/92/92b696aba9840c089241c68d4c6ca14be38a64 +2 -0
  107. data/.git/objects/93/e03faa6906892434fa9f2ad230c4ececc64055 +0 -0
  108. data/.git/objects/93/eca2e28d80fda45d327fdfc6c39ae690af41cd +0 -0
  109. data/.git/objects/95/92fe63bd5403654730cdec76cbf116ef83bbff +0 -0
  110. data/.git/objects/96/bc6dd471c1c188a2eb4308bcc3ca4e6c8faac8 +0 -0
  111. data/.git/objects/96/d855221a9ff4fba57d2b70680b57bd5115cfde +0 -0
  112. data/.git/objects/97/4ec92b2b9eb01a73fb57dc78d463cafdda69a7 +0 -0
  113. data/.git/objects/97/b7ec01ebe089832a0972d20b0a40d54456985c +2 -0
  114. data/.git/objects/97/fa6aaae4cfc7654feb39971e7aee5c41ed1679 +1 -0
  115. data/.git/objects/98/18351c6685a1df5d23686e6346d11d261cc6f0 +0 -0
  116. data/.git/objects/98/3d3b7e55cbbce2f2a52c6e6d830a5dca7b4d42 +0 -0
  117. data/.git/objects/a2/e551049a5388112c21e7f3a4a720bf720601fa +0 -0
  118. data/.git/objects/a3/7066bea4fae90d34761a3ebb3f8a348d94eba1 +0 -0
  119. data/.git/objects/a6/c99619145d1b9b75aa15ca04fbe4db0b2ee79b +0 -0
  120. data/.git/objects/aa/e2234d0a8c1485096c7a7de039eceb4e019e13 +0 -0
  121. data/.git/objects/b0/3b52f4269c9719e02ecd6caabd8aff5adf3aac +0 -0
  122. data/.git/objects/b1/50dbbd89f825302a9e82d8fdd66f238b8e6fc4 +0 -0
  123. data/.git/objects/b4/545fbe393aaa18f1c48de4d9f68f64ebf09ddc +0 -0
  124. data/.git/objects/b6/8691404e088692929d40b988b993f815d76756 +0 -0
  125. data/.git/objects/b7/daa3d844ef5f5cd917084e48edd82e67f8f476 +0 -0
  126. data/.git/objects/bf/4232281dba5a0f17e8b5c16b9cbd0f52274fa8 +0 -0
  127. data/.git/objects/bf/9b84d10f19188beffe5ee99e6f5af035ba77d3 +2 -0
  128. data/.git/objects/c0/06e664b306145de804f74ab27550d62a92f58a +1 -0
  129. data/.git/objects/c2/2806b16775f66453012d085d21fb8698521ea9 +0 -0
  130. data/.git/objects/c2/7f6559350f7adb19d43742b55b2f91d07b6550 +0 -0
  131. data/.git/objects/c3/833df21ed674a77374fd40ed931f900134d38b +0 -0
  132. data/.git/objects/c3/b9da021eb76abad0b17ad6c4be75595e41a7e6 +0 -0
  133. data/.git/objects/c5/ca6b7e3561ea88aba1712aabb984d1167787f1 +0 -0
  134. data/.git/objects/c6/39f65e1f43c75b7f57c5ab28623d295dfb2afd +0 -0
  135. data/.git/objects/c8/6f05fa5539f8e69e94850d1cd917919ff581be +0 -0
  136. data/.git/objects/cb/5e499c8dcc6f2901014a08e74f04b7a4bd6596 +0 -0
  137. data/.git/objects/cc/1289d25eeee6ba31b4e406372cb9abf969fc6f +0 -0
  138. data/.git/objects/cc/784e1b49111c42fa7eae10ad0e788186e672d1 +0 -0
  139. data/.git/objects/cc/bcae254dad515d7ef01a461980544e4483c41e +0 -0
  140. data/.git/objects/ce/bfadad3a7c17148998e04275e6d0d1cf1b2e1e +0 -0
  141. data/.git/objects/ce/f3335522abdc47a2c94be70455dcfaa9a51a27 +0 -0
  142. data/.git/objects/cf/6add7ea568d3d90d6a1f8afb0898b0119b14ff +0 -0
  143. data/.git/objects/d0/719abd196033fec0e4cbe2af68a26ca435389c +0 -0
  144. data/.git/objects/d1/ce63ff0eaec4dba419e5547f99195dfb9b5b20 +0 -0
  145. data/.git/objects/d2/c3feb3c74b892cca0049c0aced6ce786a784c8 +0 -0
  146. data/.git/objects/d7/8a91da13413419da71b539a3c6c7df4d8a2458 +0 -0
  147. data/.git/objects/d8/c7bed2b1357d54b6935536a8a51a3c73d535b4 +0 -0
  148. data/.git/objects/da/c3a1277ccb4806011e33eb32c5609cc45c46f2 +0 -0
  149. data/.git/objects/dc/3f920f4b937d0e2ac512f0bb31d477624c46d5 +0 -0
  150. data/.git/objects/dc/6c389abf5bd0d7b9be2a6bb6af90738a410e59 +0 -0
  151. data/.git/objects/df/07c423309f39c4d5570b0fad65dff23af654ae +0 -0
  152. data/.git/objects/e2/93a5c9e2257b9f3fcb8fa5d5c8147a2f07ba1b +0 -0
  153. data/.git/objects/e4/8464df56bf487e96e21ea99487330266dae3c9 +0 -0
  154. data/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  155. data/.git/objects/e8/0c13a1d1f62756fb9f1e4fb9f7512e5b8c5816 +0 -0
  156. data/.git/objects/e9/b8ade2125b2615ddc4c8043940c40ed36215c9 +0 -0
  157. data/.git/objects/ed/59a7599a81ef7ea7c230c3332345a3484febf8 +0 -0
  158. data/.git/objects/f0/d2a74226877e9a28af814d0d3727e4a3487439 +0 -0
  159. data/.git/objects/f0/f3ba7264f1d8e60eb93a907092af98fdc248e4 +0 -0
  160. data/.git/objects/f1/cdcc7ff2dc6298318523b2c4e425f515a58bf7 +3 -0
  161. data/.git/objects/f4/45c96f406f4d2f9839b3fe2fe8bc7f943ab469 +0 -0
  162. data/.git/objects/f7/eef7ce0a650d6f0c85dc4982f481da7bd44362 +0 -0
  163. data/.git/objects/f9/0abf96c5059ce1ba115f73bc9cf33e4663b3e1 +0 -0
  164. data/.git/objects/f9/cf7ae572eaebbd63f809f16a5ef14b9c679790 +0 -0
  165. data/.git/objects/fc/300df53fe2c9467210e2fd4c2f34e1c9c7c89a +0 -0
  166. data/.git/objects/fc/45546596ab8debc4c9f49a7abc7fc14a7bcc7f +4 -0
  167. data/.git/objects/fc/68d89167f69614eda17bd56dfccb4355d818e6 +0 -0
  168. data/.git/objects/fc/e4c02bb132e73b3cb957da182db7b75a47c242 +0 -0
  169. data/.git/objects/fe/be283da7f326344013f1fc847c12c8f29ab6ef +0 -0
  170. data/.git/refs/heads/master +1 -0
  171. data/.git/refs/remotes/origin/master +1 -0
  172. data/.gitignore +5 -0
  173. data/History.txt +4 -0
  174. data/License.txt +22 -0
  175. data/Manifest.txt +291 -0
  176. data/README +156 -0
  177. data/README.txt +156 -0
  178. data/Rakefile +4 -0
  179. data/config/hoe.rb +83 -0
  180. data/config/requirements.rb +15 -0
  181. data/examples/crud_components/html/address_view_edit.xhtml +19 -0
  182. data/examples/crud_components/html/addresses.xhtml +20 -0
  183. data/examples/crud_components/html/images/destroy.gif +0 -0
  184. data/examples/crud_components/html/images/edit.gif +0 -0
  185. data/examples/crud_components/html/images/field-error-marker.gif +0 -0
  186. data/examples/crud_components/html/images/sort-asc.png +0 -0
  187. data/examples/crud_components/html/images/sort-desc.png +0 -0
  188. data/examples/crud_components/html/images/sortable.png +0 -0
  189. data/examples/crud_components/html/style/trellis.css +416 -0
  190. data/examples/crud_components/source/crud_components.rb +77 -0
  191. data/examples/crud_components/source/domain.rb +6 -0
  192. data/examples/examples.txt +65 -0
  193. data/examples/flickr/source/flickr.rb +87 -0
  194. data/examples/guest_book/source/guest_book.rb +61 -0
  195. data/examples/hangman/html/game_over.xhtml +23 -0
  196. data/examples/hangman/html/guess.xhtml +47 -0
  197. data/examples/hangman/html/images/a_disabled.png +0 -0
  198. data/examples/hangman/html/images/a_enabled.png +0 -0
  199. data/examples/hangman/html/images/b_disabled.png +0 -0
  200. data/examples/hangman/html/images/b_enabled.png +0 -0
  201. data/examples/hangman/html/images/c_disabled.png +0 -0
  202. data/examples/hangman/html/images/c_enabled.png +0 -0
  203. data/examples/hangman/html/images/d_disabled.png +0 -0
  204. data/examples/hangman/html/images/d_enabled.png +0 -0
  205. data/examples/hangman/html/images/e_disabled.png +0 -0
  206. data/examples/hangman/html/images/e_enabled.png +0 -0
  207. data/examples/hangman/html/images/f_disabled.png +0 -0
  208. data/examples/hangman/html/images/f_enabled.png +0 -0
  209. data/examples/hangman/html/images/g_disabled.png +0 -0
  210. data/examples/hangman/html/images/g_enabled.png +0 -0
  211. data/examples/hangman/html/images/h_disabled.png +0 -0
  212. data/examples/hangman/html/images/h_enabled.png +0 -0
  213. data/examples/hangman/html/images/i_disabled.png +0 -0
  214. data/examples/hangman/html/images/i_enabled.png +0 -0
  215. data/examples/hangman/html/images/j_disabled.png +0 -0
  216. data/examples/hangman/html/images/j_enabled.png +0 -0
  217. data/examples/hangman/html/images/k_disabled.png +0 -0
  218. data/examples/hangman/html/images/k_enabled.png +0 -0
  219. data/examples/hangman/html/images/l_disabled.png +0 -0
  220. data/examples/hangman/html/images/l_enabled.png +0 -0
  221. data/examples/hangman/html/images/m_disabled.png +0 -0
  222. data/examples/hangman/html/images/m_enabled.png +0 -0
  223. data/examples/hangman/html/images/n_disabled.png +0 -0
  224. data/examples/hangman/html/images/n_enabled.png +0 -0
  225. data/examples/hangman/html/images/o_disabled.png +0 -0
  226. data/examples/hangman/html/images/o_enabled.png +0 -0
  227. data/examples/hangman/html/images/p_disabled.png +0 -0
  228. data/examples/hangman/html/images/p_enabled.png +0 -0
  229. data/examples/hangman/html/images/q_disabled.png +0 -0
  230. data/examples/hangman/html/images/q_enabled.png +0 -0
  231. data/examples/hangman/html/images/r_disabled.png +0 -0
  232. data/examples/hangman/html/images/r_enabled.png +0 -0
  233. data/examples/hangman/html/images/s_disabled.png +0 -0
  234. data/examples/hangman/html/images/s_enabled.png +0 -0
  235. data/examples/hangman/html/images/t_disabled.png +0 -0
  236. data/examples/hangman/html/images/t_enabled.png +0 -0
  237. data/examples/hangman/html/images/u_disabled.png +0 -0
  238. data/examples/hangman/html/images/u_enabled.png +0 -0
  239. data/examples/hangman/html/images/v_disabled.png +0 -0
  240. data/examples/hangman/html/images/v_enabled.png +0 -0
  241. data/examples/hangman/html/images/w_disabled.png +0 -0
  242. data/examples/hangman/html/images/w_enabled.png +0 -0
  243. data/examples/hangman/html/images/x_disabled.png +0 -0
  244. data/examples/hangman/html/images/x_enabled.png +0 -0
  245. data/examples/hangman/html/images/y_disabled.png +0 -0
  246. data/examples/hangman/html/images/y_enabled.png +0 -0
  247. data/examples/hangman/html/images/z_disabled.png +0 -0
  248. data/examples/hangman/html/images/z_enabled.png +0 -0
  249. data/examples/hangman/html/resources/word_list.txt +35 -0
  250. data/examples/hangman/html/start.xhtml +18 -0
  251. data/examples/hangman/html/style/hangman.css +27 -0
  252. data/examples/hangman/source/hangman.rb +71 -0
  253. data/examples/hilo/html/game_over.xhtml +15 -0
  254. data/examples/hilo/html/guess.xhtml +18 -0
  255. data/examples/hilo/html/start.xhtml +17 -0
  256. data/examples/hilo/source/hilo.rb +51 -0
  257. data/examples/simplest/html/start.xhtml +0 -0
  258. data/examples/simplest/source/simplest.rb +39 -0
  259. data/examples/stateful_counters/html/counters.xhtml +49 -0
  260. data/examples/stateful_counters/html/style/main.css +441 -0
  261. data/examples/stateful_counters/source/stateful_counters.rb +68 -0
  262. data/lib/trellis/core_components.rb +870 -0
  263. data/lib/trellis/jquery.rb +38 -0
  264. data/lib/trellis/logging.rb +60 -0
  265. data/lib/trellis/trellis.rb +701 -0
  266. data/lib/trellis/utils.rb +209 -0
  267. data/lib/trellis/version.rb +35 -0
  268. data/lib/trellis.rb +27 -0
  269. data/script/console +10 -0
  270. data/script/destroy +14 -0
  271. data/script/generate +14 -0
  272. data/script/txt2html +82 -0
  273. data/setup.rb +1585 -0
  274. data/tasks/deployment.rake +34 -0
  275. data/tasks/environment.rake +7 -0
  276. data/tasks/rspec.rake +21 -0
  277. data/tasks/website.rake +17 -0
  278. data/test/application_spec.rb +79 -0
  279. data/test/component_spec.rb +88 -0
  280. data/test/core_extensions_spec.rb +108 -0
  281. data/test/fixtures/application_spec_applications.rb +51 -0
  282. data/test/page_spec.rb +46 -0
  283. data/test/renderer_spec.rb +12 -0
  284. data/test/router_spec.rb +52 -0
  285. data/test/spec.opts +1 -0
  286. data/test/spec_helper.rb +16 -0
  287. data/website/index.html +124 -0
  288. data/website/index.txt +119 -0
  289. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  290. data/website/stylesheets/screen.css +138 -0
  291. data/website/template.html.erb +48 -0
  292. metadata +512 -0
@@ -0,0 +1,870 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright &169;2001-2008 Integrallis Software, LLC.
5
+ # All Rights Reserved.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #++
26
+
27
+ require 'trellis/trellis'
28
+ require 'paginator'
29
+
30
+ module Trellis
31
+ module CoreComponents
32
+
33
+ # Component that triggers an action on the server with a subsequent full
34
+ # page refresh
35
+ #
36
+ class ActionLink < Trellis::Component
37
+ render do |tag|
38
+ source = tag.attr['tid']
39
+ context = tag.attr['context']
40
+ target_page = tag.attr['page'] || tag.globals.page.class.name
41
+ url_root = tag.globals.page.class.url_root
42
+
43
+ if context
44
+ value = tag.locals.instance_eval(context) || tag.globals.instance_eval(context)
45
+ end
46
+
47
+ if source
48
+ if context
49
+ id = "#{source}_#{value}"
50
+ href = "#{url_root}/#{target_page}.select_#{source}/#{value}"
51
+ else
52
+ id = "#{source}"
53
+ href = "#{url_root}/#{target_page}.select_#{source}"
54
+ end
55
+ else
56
+ if context
57
+ id = "action_link_#{value}"
58
+ href = "#{url_root}/#{target_page}.select/#{value}"
59
+ else
60
+ id = "action_link"
61
+ href = "#{url_root}/#{target_page}.select"
62
+ end
63
+ end
64
+
65
+ builder = Builder::XmlMarkup.new
66
+ text = builder.a('${contents}', "href" => href, "id" => id)
67
+ text.replace_ant_style_property('contents', tag.expand)
68
+ end
69
+ end
70
+
71
+ #
72
+ #
73
+ #
74
+ class Loop < Trellis::Component
75
+ render do |tag|
76
+ value_name = tag.attr['value']
77
+ value = "#{value_name}="
78
+ # make value available by name to the page
79
+ source = tag.attr['source']
80
+ start, finish = source.split('..')
81
+ content = ''
82
+ (start..finish).each do |n|
83
+ tag.locals.send(value.to_sym, n)
84
+ content << tag.expand
85
+ end
86
+ content
87
+ end
88
+ end
89
+
90
+ #
91
+ #
92
+ #
93
+ class Each < Trellis::Component
94
+ render do |tag|
95
+ value_name = tag.attr['value']
96
+ value = "#{value_name}="
97
+ # make value available by name to the page
98
+ source = tag.attr['source']
99
+ begin
100
+ iterator = tag.locals.instance_eval(source) || tag.globals.instance_eval(source)
101
+ rescue NoMethodError
102
+ iterator = []
103
+ end
104
+ content = ''
105
+ iterator.each do |n|
106
+ tag.locals.send(value.to_sym, n)
107
+ content << tag.expand
108
+ end if iterator
109
+ content
110
+ end
111
+ end
112
+
113
+ #
114
+ #
115
+ #
116
+ class Value < Trellis::Component
117
+ render do |tag|
118
+ name = tag.attr['name']
119
+ unless name.include?('.')
120
+ value = tag.locals.send(name.to_sym) || tag.globals.send(name.to_sym)
121
+ else
122
+ target_name, method = name.split('.')
123
+ target = tag.locals.send(target_name.to_sym) || tag.globals.send(target_name.to_sym)
124
+ value = target.send(method.to_sym) if target
125
+ end
126
+ value
127
+ end
128
+ end
129
+
130
+ #
131
+ #
132
+ #
133
+ class PageLink < Trellis::Component
134
+ render do |tag|
135
+ url_root = tag.globals.page.class.url_root
136
+ page_name = tag.attr['tpage']
137
+ id = tag.attr['tid'] || page_name
138
+ href = "#{url_root}/#{page_name}"
139
+ contents = tag.expand
140
+ builder = Builder::XmlMarkup.new
141
+ builder.a(contents, "href" => href, "id" => "page_link_#{id}")
142
+ end
143
+ end
144
+
145
+ #
146
+ #
147
+ #
148
+ class Img < Trellis::Component
149
+ render do |tag|
150
+ attrs = tag.attr.exclude_keys('src', 'alt')
151
+
152
+ # resolve the ${} variables
153
+ attrs['src'] = Utils.expand_properties_in_tag(tag.attr['src'], tag)
154
+ attrs['alt'] = Utils.expand_properties_in_tag(tag.attr['alt'], tag)
155
+
156
+ builder = Builder::XmlMarkup.new
157
+ builder.img(attrs)
158
+ end
159
+ end
160
+
161
+ #
162
+ #
163
+ #
164
+ class Remove < Trellis::Component
165
+ render do |tag|
166
+ # do nothing
167
+ end
168
+ end
169
+
170
+ #
171
+ #
172
+ #
173
+ class If < Trellis::Component
174
+ render do |tag|
175
+ # resolve the ${} variables
176
+ test = Utils.expand_properties_in_tag(tag.attr['test'], tag)
177
+
178
+ # TODO I'm suspecting this is buggy! plus should we catch exceptions?
179
+ local = tag.locals.instance_eval(test)
180
+ global = tag.globals.instance_eval(test)
181
+
182
+ result = false
183
+ if local
184
+ result = local
185
+ elsif global
186
+ result = global
187
+ end
188
+
189
+ content = ''
190
+ if result
191
+ content << tag.expand
192
+ else
193
+ # find the else tag and expand it
194
+ end
195
+ content
196
+ end
197
+ end
198
+
199
+ #
200
+ #
201
+ #
202
+ class Unless < Trellis::Component
203
+ render do |tag|
204
+ # resolve the ${} variables
205
+ test = Utils.expand_properties_in_tag(tag.attr['test'], tag)
206
+
207
+ local = tag.locals.instance_eval(test)
208
+ global = tag.globals.instance_eval(test)
209
+
210
+ result = false
211
+ if local
212
+ result = !local
213
+ elsif global
214
+ result = !global
215
+ end
216
+
217
+ content = ''
218
+ if result
219
+ content << tag.expand
220
+ else
221
+ # find the else tag and expand it
222
+ end
223
+ content
224
+ end
225
+ end
226
+
227
+ #
228
+ #
229
+ #
230
+ class Button < Trellis::Component
231
+ render do |tag|
232
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
233
+ attrs['name'] = "#{tag.attr['tid']}"
234
+ contents = tag.expand
235
+ builder = Builder::XmlMarkup.new
236
+ builder.button(contents, attrs)
237
+ end
238
+ end
239
+
240
+ #
241
+ #
242
+ #
243
+ class Form < Trellis::Component
244
+ render do |tag|
245
+ url_root = tag.globals.page.class.url_root
246
+ form_name = tag.attr['tid']
247
+ on_behalf = tag.attr['on_behalf']
248
+ method = tag.attr['method'] || 'GET'
249
+ tag.locals.form_name = form_name
250
+ href = "#{url_root}/#{tag.globals.page.class.name}.submit_#{(on_behalf ? on_behalf : form_name)}"
251
+ builder = Builder::XmlMarkup.new
252
+ builder.form("name" => form_name, "action" => href, "method" => method) do |form|
253
+ form << tag.expand
254
+ end
255
+ end
256
+ end
257
+
258
+ #
259
+ #
260
+ #
261
+ class Submit < Trellis::Component
262
+ tag_name "submit"
263
+
264
+ contained_in "form"
265
+
266
+ render do |tag|
267
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
268
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
269
+ attrs['type'] = 'submit'
270
+ builder = Builder::XmlMarkup.new
271
+ builder.input(attrs)
272
+ end
273
+ end
274
+
275
+ #
276
+ #
277
+ #
278
+ class CheckBox < Trellis::Component
279
+ tag_name "check_box"
280
+
281
+ contained_in "form"
282
+
283
+ render do |tag|
284
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
285
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
286
+ attrs['type'] = 'checkbox'
287
+ builder = Builder::XmlMarkup.new
288
+ builder.input(attrs)
289
+ end
290
+ end
291
+
292
+ #
293
+ #
294
+ #
295
+ class TextField < Trellis::Component
296
+ tag_name "text_field"
297
+
298
+ contained_in "form"
299
+
300
+ render do |tag|
301
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type', 'value')
302
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
303
+ value = tag.attr['value']
304
+ literal = tag.attr['literal'] =~ /^(y|yes|true)$/
305
+ if value
306
+ if !literal
307
+ resolved_value = ''
308
+ unless value.include?('.')
309
+ resolved_value = tag.locals.send(value.to_sym) || tag.globals.send(value.to_sym)
310
+ else
311
+ target_name, method = value.split('.')
312
+ target = tag.locals.send(target_name.to_sym) || tag.globals.send(target_name.to_sym)
313
+ resolved_value = target.send(method.to_sym) if target
314
+ end
315
+ attrs['value'] = resolved_value if resolved_value
316
+ else
317
+ attrs['value'] = value
318
+ end
319
+ end
320
+
321
+ attrs['type'] = 'text'
322
+ builder = Builder::XmlMarkup.new
323
+ builder.input(attrs)
324
+ end
325
+ end
326
+
327
+ #
328
+ #
329
+ #
330
+ class TextArea < Trellis::Component
331
+ tag_name "text_area"
332
+
333
+ contained_in "form"
334
+
335
+ render do |tag|
336
+ attrs = tag.attr.exclude_keys('tid', 'name', 'keep_contents')
337
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
338
+ keep_contents = tag.attr['keep_contents']
339
+ if keep_contents
340
+ contents = keep_contents =~ /^(y|yes|true)$/ ? tag.expand : ''
341
+ else
342
+ contents = ''
343
+ end
344
+ builder = Builder::XmlMarkup.new
345
+ builder.textarea(contents, attrs)
346
+ end
347
+ end
348
+
349
+ #
350
+ #
351
+ #
352
+ class Password < Trellis::Component
353
+ tag_name "password"
354
+
355
+ contained_in "form"
356
+
357
+ render do |tag|
358
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
359
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
360
+ attrs['type'] = 'password'
361
+ builder = Builder::XmlMarkup.new
362
+ builder.input(attrs)
363
+ end
364
+ end
365
+
366
+ #
367
+ #
368
+ #
369
+ class Hidden < Trellis::Component
370
+ tag_name "hidden"
371
+
372
+ contained_in "form"
373
+
374
+ render do |tag|
375
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type', 'value')
376
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
377
+ value = tag.attr['value']
378
+ literal = tag.attr['literal'] =~ /^(y|yes|true)$/
379
+ if value
380
+ if !literal
381
+ resolved_value = ''
382
+ unless value.include?('.')
383
+ resolved_value = tag.locals.send(value.to_sym) || tag.globals.send(value.to_sym)
384
+ else
385
+ target_name, method = value.split('.')
386
+ target = tag.locals.send(target_name.to_sym) || tag.globals.send(target_name.to_sym)
387
+ resolved_value = target.send(method.to_sym)
388
+ end
389
+ attrs['value'] = resolved_value
390
+ else
391
+ attrs['value'] = value
392
+ end
393
+ end
394
+
395
+ attrs['type'] = 'hidden'
396
+ builder = Builder::XmlMarkup.new
397
+ builder.input(attrs)
398
+ end
399
+ end
400
+
401
+ #
402
+ #
403
+ #
404
+ class Select < Trellis::Component
405
+ tag_name "select"
406
+
407
+ contained_in "form"
408
+
409
+ render do |tag|
410
+ attrs = tag.attr.exclude_keys('tid', 'select_if', 'selected_value', 'source')
411
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
412
+ expression = tag.attr['source'] # something we can iterate over
413
+ selected = Utils.evaluate_tag_attribute('select_if', tag)
414
+ selected_value = Utils.evaluate_tag_attribute('selected_value', tag)
415
+ value_accessor = Utils.expand_properties_in_tag(tag.attr['value'], tag)
416
+
417
+ builder = Builder::XmlMarkup.new
418
+ builder.select(attrs) do
419
+ if expression.include? '..'
420
+ start, finish = expression.split('..')
421
+ (start..finish).each do |item|
422
+ value = value_accessor ? item.instance_eval(value_accessor) : item
423
+ unless item == selected
424
+ builder.option item, :value => value
425
+ else
426
+ builder.option item, selected => (selected_value.nil? ? 'yes' : selected_value), :value => value
427
+ end
428
+ end
429
+ else
430
+ source = Utils.evaluate_tag_attribute('source', tag)
431
+ if source.respond_to? :each
432
+ source.each do |item|
433
+ value = value_accessor ? item.instance_eval(value_accessor) : item
434
+ unless item == selected
435
+ builder.option item, :value => value
436
+ else
437
+ builder.option item, :selected => (selected_value.nil? ? 'yes' : selected_value), :value => value
438
+ end
439
+ end
440
+ elsif source.respond_to? :each_pair
441
+ source.each_pair do |name,value|
442
+ unless value == selected
443
+ builder.option name, :value => value
444
+ else
445
+ builder.option name, :selected => (selected_value.nil? ? value : selected_value), :value => value
446
+ end
447
+ end
448
+ end
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+
455
+ # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
456
+ # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
457
+ # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
458
+ # onto the HTML as an HTML element attribute as in the example shown.
459
+ #
460
+ # ==== Examples
461
+ # label(:post, :title)
462
+ # #=> <label for="post_title">Title</label>
463
+ #
464
+ # label(:post, :title, "A short title")
465
+ # #=> <label for="post_title">A short title</label>
466
+ #
467
+ # label(:post, :title, "A short title", :class => "title_label")
468
+ # #=> <label for="post_title" class="title_label">A short title</label>
469
+ class Label < Trellis::Component
470
+ render do |tag|
471
+ target = "#{tag.locals.form_name}_#{tag.attr['for']}"
472
+ contents = tag.expand
473
+ builder = Builder::XmlMarkup.new
474
+ builder.label(contents, :for => target)
475
+ end
476
+ end
477
+
478
+ # Creates a file upload field. If you are using file uploads then you will also need
479
+ # to set the multipart option for the form tag:
480
+ #
481
+ # <%= form_tag { :action => "post" }, { :multipart => true } %>
482
+ # <label for="file">File to Upload</label> <%= file_field_tag "file" %>
483
+ # <%= submit_tag %>
484
+ # <%= end_form_tag %>
485
+ #
486
+ # The specified URL will then be passed a File object containing the selected file, or if the field
487
+ # was left blank, a StringIO object.
488
+ #
489
+ # ==== Options
490
+ # * Creates standard HTML attributes for the tag.
491
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
492
+ #
493
+ # ==== Examples
494
+ # file_field_tag 'attachment'
495
+ # # => <input id="attachment" name="attachment" type="file" />
496
+ #
497
+ # file_field_tag 'avatar', :class => 'profile-input'
498
+ # # => <input class="profile-input" id="avatar" name="avatar" type="file" />
499
+ #
500
+ # file_field_tag 'picture', :disabled => true
501
+ # # => <input disabled="disabled" id="picture" name="picture" type="file" />
502
+ #
503
+ # file_field_tag 'resume', :value => '~/resume.doc'
504
+ # # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
505
+ #
506
+ # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg'
507
+ # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
508
+ #
509
+ # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html'
510
+ # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
511
+ class File < Trellis::Component
512
+
513
+ render do |tag|
514
+
515
+ end
516
+ end
517
+
518
+ # TODO: Need radio button group and a standalone radio button
519
+
520
+
521
+ #
522
+ #
523
+ #
524
+ class Grid < Trellis::Component
525
+ is_stateful
526
+
527
+ tag_name "grid"
528
+
529
+ attr_accessor :source
530
+ field :page_position, :persistent => true
531
+ attr_reader :properties
532
+ attr_reader :commands
533
+ attr_reader :counter_method
534
+ attr_reader :counter_method_arguments
535
+ attr_reader :retrieve_block
536
+ attr_reader :sort_properties
537
+ field :sorted_by, :persistent => true
538
+ field :sort_direction, :persistent => true, :default_value => :ascending
539
+
540
+ def initialize
541
+ @properties = []
542
+ @sort_properties = []
543
+ @commands = []
544
+ end
545
+
546
+ # must be called before rendering
547
+ def columns(*syms)
548
+ syms.each do |sym|
549
+ @properties << sym
550
+ end
551
+ end
552
+
553
+ def add_command(options=[])
554
+ # extract options
555
+ name = options[:name] if options
556
+ page = options[:page] if options
557
+ context = options[:context] if options
558
+ image = options[:image] if options
559
+
560
+ @commands << lambda do |tag, object|
561
+ tid = tag.attr['tid']
562
+ value = object.send(context.to_sym)
563
+ url_root = tag.globals.page.class.url_root
564
+ page = tag.globals.page.class.name unless page
565
+
566
+ href = "#{url_root}/#{page}.#{name}_#{tid}/#{value}"
567
+
568
+ %{
569
+ <a href="#{href}">
570
+ <img src='#{image}'/>
571
+ </a>
572
+ }
573
+ end
574
+ end
575
+
576
+ def sort_by_all_except(*syms)
577
+ @sort_properties = @properties.reject { |property| syms.includes?(property)}
578
+ end
579
+
580
+ def sort_by(*syms)
581
+ unless syms.first == :all
582
+ @sort_properties = syms
583
+ else
584
+ @sort_properties = @properties
585
+ end
586
+ end
587
+
588
+ def size_accessor(symbol, args)
589
+ @counter_method, @counter_method_arguments = symbol, args
590
+ end
591
+
592
+ def retrieve_method(&block)
593
+ @retrieve_block = block
594
+ end
595
+
596
+ # event handlers
597
+
598
+ def on_page(page)
599
+ @page_position = page.to_i # must use the cohersion built in capabilities
600
+ end
601
+
602
+ def on_sort(property)
603
+ to_sym = property.to_sym
604
+ if @sort_properties.include?(to_sym)
605
+ if @sorted_by != to_sym
606
+ @sorted_by = to_sym
607
+ else
608
+ @sort_direction = @sort_direction == :ascending ? :descending : :ascending
609
+ end
610
+ end
611
+ end
612
+
613
+ render do |tag|
614
+ url_root = tag.globals.page.class.url_root
615
+ # get the page object
616
+ page = tag.globals.page
617
+ # get the tag properties
618
+ tid = tag.attr['tid']
619
+ rows_per_page = tag.attr['rows_per_page'] || 3
620
+ # get the instance of the component from the page
621
+ grid = page.send("grid_#{tid}")
622
+ # get the properties or columns
623
+ properties = grid.properties
624
+ # get the sort properties
625
+ sort_properties = grid.sort_properties
626
+ # current page
627
+ current_page = grid.page_position || 1
628
+ # sort information
629
+ sorted_by = grid.sorted_by
630
+ sort_direction = grid.sort_direction
631
+ commands = grid.commands
632
+ # get the source from either the tag or the component instance itself
633
+ source = tag.attr['source'] || grid.source
634
+ # get the array or collection that we can iterate over
635
+ iterator = tag.locals.send(source.to_sym) || tag.globals.send(source.to_sym)
636
+
637
+ builder = Builder::XmlMarkup.new
638
+
639
+ # build the table
640
+ builder.div(:class => "t-data-grid") {
641
+
642
+ # configure pagination
643
+ #TODO need to implement page ranging
644
+ range = tag.attr['page_range'] || 5
645
+ available_rows = 0
646
+ unless grid.counter_method
647
+ available_rows = iterator.length
648
+ else
649
+ unless grid.counter_method_arguments
650
+ available_rows = iterator.send(grid.counter_method)
651
+ else
652
+ available_rows = iterator.send(grid.counter_method, grid.counter_method_arguments)
653
+ end
654
+ end
655
+
656
+ # configure the pager
657
+ pager = Paginator.new(available_rows, rows_per_page) do |offset, per_page|
658
+ rows = nil
659
+ unless grid.retrieve_method
660
+ if sorted_by
661
+ iterator = iterator.sort_by { |row| row.send(sorted_by) }
662
+ iterator.reverse! if sort_direction == :descending
663
+ end
664
+ rows = iterator[offset, per_page]
665
+ else
666
+ rows = grid.retrieve_method.call
667
+ end
668
+ rows
669
+ end
670
+
671
+ # get the current page from the pager
672
+ rows = pager.page(current_page)
673
+
674
+ # render the pager control if necessary
675
+ unless pager.number_of_pages < 2
676
+ builder.div(:class => "t-data-grid-pager") {
677
+ # loop over the pages
678
+ (1..pager.number_of_pages).each do |page_num|
679
+ if page_num == current_page
680
+ builder.span("#{page_num}", :class => "current")
681
+ else
682
+ builder.a("#{page_num}", :href => "#{url_root}/#{page.class.name}.page_grid#{tid}/#{page_num}", :title => "Go to page #{page_num}")
683
+ end
684
+ end
685
+ }
686
+ end
687
+
688
+ # build the html
689
+ builder.table(:class => "t-data-grid") {
690
+ # header
691
+ builder.thead {
692
+ builder.tr {
693
+ properties.each_index { |index|
694
+ property = properties[index]
695
+ field_name = property.to_s.humanize
696
+
697
+ if properties.length == index + 1
698
+ css_class = "#{field_name} t-last"
699
+ elsif index == 0
700
+ css_class = "#{field_name} t-first"
701
+ else
702
+ css_class = "#{field_name}"
703
+ end
704
+
705
+ sort_image = 'sortable.png'
706
+ if property == sorted_by
707
+ if sort_direction == :ascending
708
+ sort_image = 'sort-desc.png'
709
+ elsif sort_direction == :descending
710
+ sort_image = 'sort-asc.png'
711
+ end
712
+ end
713
+
714
+ unless sort_properties.include?(property)
715
+ builder.th(field_name, :class => css_class)
716
+ else
717
+ builder.th(:class => css_class) {
718
+ builder.a(field_name, :href => "#{url_root}/#{page.class.name}.sort_grid#{tid}/#{property}")
719
+ builder.a(:href => "#{url_root}/#{page.class.name}.sort_grid#{tid}/#{property}") {
720
+ builder.img(:alt => "[Sortable]", :class => "t-sort-icon", :id => "#{property}:sort", :src => "#{url_root}/images/#{sort_image}", :name => "#{property}:sort")
721
+ }
722
+ }
723
+ end
724
+ }
725
+ # add columns for commands
726
+ if commands && !commands.empty?
727
+ (1..commands.size).each do
728
+ builder.th
729
+ end
730
+ end
731
+ }
732
+ }
733
+
734
+ # body
735
+ builder.tbody {
736
+ # data
737
+ index = 0
738
+ rows.each do |item|
739
+ if (rows.last_item_number - rows.first_item_number) == index + 1
740
+ css_class = "t-last"
741
+ elsif index == 0
742
+ css_class = "t-first"
743
+ else
744
+ css_class = nil
745
+ end
746
+ index = index + 1
747
+
748
+ if css_class
749
+ builder.tr(:class => css_class) {
750
+ properties.each { |property|
751
+ field_value = item.send(property)
752
+ builder.td("#{field_value}", :class => "#{property}")
753
+ }
754
+ # add columns for commands
755
+ if commands && !commands.empty?
756
+ commands.each { |command|
757
+ builder.td { |td| td << command.call(tag, item)}
758
+ }
759
+ end
760
+ }
761
+ else
762
+ builder.tr {
763
+ properties.each { |property|
764
+ field_value = item.send(property)
765
+ builder.td("#{field_value}", :class => "#{property}")
766
+ }
767
+ # add columns for commands
768
+ if commands && !commands.empty?
769
+ commands.each { |command|
770
+ builder.td { |td| td << command.call(tag, item)}
771
+ }
772
+ end
773
+ }
774
+ end
775
+ end if rows
776
+ }
777
+ }
778
+ }
779
+ end
780
+ end
781
+
782
+ class ObjectEditor < Trellis::Component
783
+ is_stateful
784
+
785
+ depends_on :form, :submit
786
+
787
+ field :model, :persistent => true
788
+ attr_reader :retrieve_block
789
+ attr_reader :properties
790
+ attr_accessor :submit_text
791
+ attr_reader :submit_block
792
+
793
+ def initialize
794
+ @properties = []
795
+ end
796
+
797
+ # must be called before rendering
798
+ def fields(*syms)
799
+ syms.each do |sym|
800
+ @properties << sym
801
+ end
802
+ end
803
+
804
+ def on_submit(&block)
805
+ # populate the object with values from the session
806
+ if page.params
807
+ properties.each do |property|
808
+ value = page.params[property.to_sym]
809
+ @model.instance_variable_set("@#{property}".to_sym, value)
810
+ end
811
+ end
812
+ if block_given?
813
+ @submit_block = block
814
+ else
815
+ @submit_block.call(@model)
816
+ end
817
+ end
818
+
819
+ render do |tag|
820
+ url_root = tag.globals.page.class.url_root
821
+ # get the page object
822
+ page = tag.globals.page
823
+ # get the tag properties
824
+ tid = tag.attr['tid']
825
+ # get the instance of the component from the page
826
+ editor = page.send("object_editor_#{tid}")
827
+ # get the properties or columns
828
+ properties = editor.properties
829
+ # get the source from either the tag or the component instance itself
830
+ source = tag.attr['model'] || editor.model
831
+ submit_text = tag.attr['submit_text'] || editor.submit_text || "Submit"
832
+
833
+ builder = Builder::XmlMarkup.new
834
+
835
+ # the editor encloses a form
836
+ form = tag.render("form", "tid" => "form_#{tid}", "method" => "post", "on_behalf" => "object_editor#{tid}") do
837
+ builder.div(:class => "t-beaneditor") {
838
+ properties.each { |property|
839
+ field_name = property.to_s.humanize
840
+ field_value = source.send(property)
841
+ builder.div(:class => "t-beaneditor-row") {
842
+ builder.label(field_name, :for => property, :id => "#{property}:label")
843
+ builder.input(:id => property, :name => property, :type => "text", :value => field_value)
844
+ builder.img(:alt => "[Error]", :class => "t-error-icon t-invisible", :id => "#{property}:icon", :src => "#{url_root}/images/field-error-marker.gif", :name => "#{property}:icon")
845
+ }
846
+ } if source
847
+ builder.div(:class => "t-beaneditor-row") {
848
+ builder << tag.render("submit", "tid" => "submit", "name" => "whats_the_name", "value" => submit_text)
849
+ }
850
+ }
851
+ end
852
+ form
853
+ end
854
+
855
+ end
856
+
857
+ class Div < Trellis::Component
858
+ render do |tag|
859
+ attrs = tag.attr.exclude_keys('id', 'title')
860
+ attrs['id'] = Utils.expand_properties_in_tag(tag.attr['id'], tag) if tag.attr['id']
861
+ attrs['title'] = Utils.expand_properties_in_tag(tag.attr['title'], tag) if tag.attr['title']
862
+ builder = Builder::XmlMarkup.new
863
+ builder.div(attrs) { |div|
864
+ div << tag.expand
865
+ }
866
+ end
867
+ end
868
+
869
+ end
870
+ end