sproutit-sproutcore 1.0.0.20090407205609

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 (382) hide show
  1. data/Buildfile +115 -0
  2. data/History.txt +7 -0
  3. data/README.txt +102 -0
  4. data/Rakefile +54 -0
  5. data/Todo.txt +1 -0
  6. data/bin/sc-build +13 -0
  7. data/bin/sc-build-number +13 -0
  8. data/bin/sc-gen +13 -0
  9. data/bin/sc-init +13 -0
  10. data/bin/sc-manifest +13 -0
  11. data/bin/sc-server +13 -0
  12. data/bin/sproutcore +14 -0
  13. data/buildtasks/build.rake +73 -0
  14. data/buildtasks/entry.rake +59 -0
  15. data/buildtasks/manifest.rake +469 -0
  16. data/buildtasks/render.rake +21 -0
  17. data/buildtasks/target.rake +42 -0
  18. data/gen/Buildfile +16 -0
  19. data/gen/app/Buildfile +36 -0
  20. data/gen/app/README +1 -0
  21. data/gen/app/USAGE +13 -0
  22. data/gen/app/templates/apps/{target_name}/core.js +27 -0
  23. data/gen/app/templates/apps/{target_name}/english.lproj/loading.rhtml +9 -0
  24. data/gen/app/templates/apps/{target_name}/english.lproj/main_page.js +22 -0
  25. data/gen/app/templates/apps/{target_name}/english.lproj/strings.js +15 -0
  26. data/gen/app/templates/apps/{target_name}/main.js +30 -0
  27. data/gen/controller/Buildfile +18 -0
  28. data/gen/controller/README +1 -0
  29. data/gen/controller/USAGE +13 -0
  30. data/gen/controller/templates/controllers/{filename}.js +18 -0
  31. data/gen/controller/templates/tests/controllers/{filename}.js +15 -0
  32. data/gen/framework/Buildfile +36 -0
  33. data/gen/framework/README +1 -0
  34. data/gen/framework/USAGE +13 -0
  35. data/gen/framework/templates/frameworks/{target_name}/core.js +21 -0
  36. data/gen/framework/templates/frameworks/{target_name}/english.lproj/strings.js +15 -0
  37. data/gen/language/Buildfile +26 -0
  38. data/gen/language/README +1 -0
  39. data/gen/language/USAGE +13 -0
  40. data/gen/language/templates/{filename}/strings.js +14 -0
  41. data/gen/model/Buildfile +16 -0
  42. data/gen/model/README +1 -0
  43. data/gen/model/USAGE +15 -0
  44. data/gen/model/templates/fixtures/{filename}.js +35 -0
  45. data/gen/model/templates/models/{filename}.js +19 -0
  46. data/gen/model/templates/tests/models/{filename}.js +15 -0
  47. data/gen/project/Buildfile +45 -0
  48. data/gen/project/INIT +3 -0
  49. data/gen/project/README +1 -0
  50. data/gen/project/USAGE +2 -0
  51. data/gen/project/templates/{filename}/Buildfile +7 -0
  52. data/gen/project/templates/{filename}/README +7 -0
  53. data/gen/test/Buildfile +25 -0
  54. data/gen/test/README +1 -0
  55. data/gen/test/USAGE +12 -0
  56. data/gen/test/templates/tests/{filename}.js +15 -0
  57. data/gen/theme/Buildfile +36 -0
  58. data/gen/theme/README +1 -0
  59. data/gen/theme/USAGE +13 -0
  60. data/gen/theme/templates/themes/{target_name}/english.lproj/strings.js +15 -0
  61. data/gen/view/Buildfile +18 -0
  62. data/gen/view/README +1 -0
  63. data/gen/view/USAGE +13 -0
  64. data/gen/view/templates/tests/views/{filename}.js +15 -0
  65. data/gen/view/templates/views/{filename}.js +18 -0
  66. data/lib/sproutcore.rb +130 -0
  67. data/lib/sproutcore/builders/base.rb +79 -0
  68. data/lib/sproutcore/builders/combine.rb +37 -0
  69. data/lib/sproutcore/builders/html.rb +177 -0
  70. data/lib/sproutcore/builders/javascript.rb +55 -0
  71. data/lib/sproutcore/builders/minify.rb +67 -0
  72. data/lib/sproutcore/builders/sass.rb +55 -0
  73. data/lib/sproutcore/builders/strings.rb +44 -0
  74. data/lib/sproutcore/builders/stylesheet.rb +40 -0
  75. data/lib/sproutcore/builders/test.rb +58 -0
  76. data/lib/sproutcore/builders/test_index.rb +26 -0
  77. data/lib/sproutcore/buildfile.rb +483 -0
  78. data/lib/sproutcore/buildfile/build_task.rb +33 -0
  79. data/lib/sproutcore/buildfile/buildfile_dsl.rb +162 -0
  80. data/lib/sproutcore/buildfile/cloneable.rb +34 -0
  81. data/lib/sproutcore/buildfile/early_time.rb +29 -0
  82. data/lib/sproutcore/buildfile/invocation_chain.rb +64 -0
  83. data/lib/sproutcore/buildfile/namespace.rb +33 -0
  84. data/lib/sproutcore/buildfile/string_ext.rb +198 -0
  85. data/lib/sproutcore/buildfile/task.rb +304 -0
  86. data/lib/sproutcore/buildfile/task_arguments.rb +86 -0
  87. data/lib/sproutcore/buildfile/task_manager.rb +193 -0
  88. data/lib/sproutcore/deprecated/view_helper.rb +662 -0
  89. data/lib/sproutcore/helpers/capture_helper.rb +30 -0
  90. data/lib/sproutcore/helpers/cssmin.rb +202 -0
  91. data/lib/sproutcore/helpers/dom_id_helper.rb +21 -0
  92. data/lib/sproutcore/helpers/entry_sorter.rb +105 -0
  93. data/lib/sproutcore/helpers/packed_optimizer.rb +102 -0
  94. data/lib/sproutcore/helpers/static_helper.rb +268 -0
  95. data/lib/sproutcore/helpers/tag_helper.rb +117 -0
  96. data/lib/sproutcore/helpers/text_helper.rb +303 -0
  97. data/lib/sproutcore/models/generator.rb +493 -0
  98. data/lib/sproutcore/models/hash_struct.rb +109 -0
  99. data/lib/sproutcore/models/manifest.rb +342 -0
  100. data/lib/sproutcore/models/manifest_entry.rb +232 -0
  101. data/lib/sproutcore/models/project.rb +270 -0
  102. data/lib/sproutcore/models/target.rb +403 -0
  103. data/lib/sproutcore/rack/builder.rb +269 -0
  104. data/lib/sproutcore/rack/dev.rb +51 -0
  105. data/lib/sproutcore/rack/docs.rb +24 -0
  106. data/lib/sproutcore/rack/proxy.rb +92 -0
  107. data/lib/sproutcore/rack/service.rb +141 -0
  108. data/lib/sproutcore/rack/test_runner.rb +24 -0
  109. data/lib/sproutcore/render_engines/erubis.rb +55 -0
  110. data/lib/sproutcore/render_engines/haml.rb +39 -0
  111. data/lib/sproutcore/tools.rb +311 -0
  112. data/lib/sproutcore/tools/build.rb +80 -0
  113. data/lib/sproutcore/tools/build_number.rb +23 -0
  114. data/lib/sproutcore/tools/gen.rb +99 -0
  115. data/lib/sproutcore/tools/init.rb +40 -0
  116. data/lib/sproutcore/tools/manifest.rb +84 -0
  117. data/lib/sproutcore/tools/server.rb +45 -0
  118. data/spec/buildtasks/build/copy_spec.rb +60 -0
  119. data/spec/buildtasks/build/spec_helper.rb +36 -0
  120. data/spec/buildtasks/manifest/catalog_spec.rb +48 -0
  121. data/spec/buildtasks/manifest/hide_buildfiles_spec.rb +125 -0
  122. data/spec/buildtasks/manifest/localize_spec.rb +97 -0
  123. data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +246 -0
  124. data/spec/buildtasks/manifest/prepare_build_tasks/css_spec.rb +87 -0
  125. data/spec/buildtasks/manifest/prepare_build_tasks/html_spec.rb +175 -0
  126. data/spec/buildtasks/manifest/prepare_build_tasks/javascript_spec.rb +65 -0
  127. data/spec/buildtasks/manifest/prepare_build_tasks/minify_spec.rb +70 -0
  128. data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +152 -0
  129. data/spec/buildtasks/manifest/prepare_build_tasks/sass_spec.rb +98 -0
  130. data/spec/buildtasks/manifest/prepare_build_tasks/strings_spec.rb +64 -0
  131. data/spec/buildtasks/manifest/prepare_build_tasks/tests_spec.rb +163 -0
  132. data/spec/buildtasks/manifest/prepare_spec.rb +43 -0
  133. data/spec/buildtasks/manifest/spec_helper.rb +35 -0
  134. data/spec/buildtasks/target_spec.rb +214 -0
  135. data/spec/fixtures/builder_tests/Buildfile +15 -0
  136. data/spec/fixtures/builder_tests/apps/combine_test/a.js +1 -0
  137. data/spec/fixtures/builder_tests/apps/combine_test/b.js +1 -0
  138. data/spec/fixtures/builder_tests/apps/combine_test/c.js +1 -0
  139. data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/a.css +1 -0
  140. data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/b.css +1 -0
  141. data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/c.css +1 -0
  142. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/bar1_sample.rhtml +2 -0
  143. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/erb_sample.html.erb +1 -0
  144. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/icons/image.png +0 -0
  145. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/image.jpg +0 -0
  146. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/rhtml_sample.rhtml +1 -0
  147. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/strings.js +4 -0
  148. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/style.css +0 -0
  149. data/spec/fixtures/builder_tests/apps/html_test/french.lproj/french-icons/fr.png +0 -0
  150. data/spec/fixtures/builder_tests/apps/html_test/french.lproj/strings.js +4 -0
  151. data/spec/fixtures/builder_tests/apps/html_test/lib/layout_template.rhtml +1 -0
  152. data/spec/fixtures/builder_tests/apps/html_test/scripts.js +0 -0
  153. data/spec/fixtures/builder_tests/apps/javascript_test/sc_static.js +15 -0
  154. data/spec/fixtures/builder_tests/apps/javascript_test/sc_super.js +4 -0
  155. data/spec/fixtures/builder_tests/apps/javascript_test/strings.js +7 -0
  156. data/spec/fixtures/builder_tests/apps/sass_test/sample.sass +3 -0
  157. data/spec/fixtures/builder_tests/apps/strings_test/lproj/strings.js +8 -0
  158. data/spec/fixtures/builder_tests/apps/stylesheet_test/build_directives.css +9 -0
  159. data/spec/fixtures/builder_tests/apps/stylesheet_test/sc_static.css +12 -0
  160. data/spec/fixtures/builder_tests/apps/test_test/lib/alt_layout.rhtml +1 -0
  161. data/spec/fixtures/builder_tests/apps/test_test/lib/test_layout.rhtml +3 -0
  162. data/spec/fixtures/builder_tests/apps/test_test/tests/qunit_test.js +1 -0
  163. data/spec/fixtures/builder_tests/apps/test_test/tests/qunit_test2.js +1 -0
  164. data/spec/fixtures/builder_tests/apps/test_test/tests/rhtml_test.rhtml +4 -0
  165. data/spec/fixtures/builder_tests/frameworks/debug/core.js +0 -0
  166. data/spec/fixtures/builder_tests/frameworks/debug/english.lproj/dummy.css +0 -0
  167. data/spec/fixtures/builder_tests/frameworks/qunit/core.js +0 -0
  168. data/spec/fixtures/builder_tests/frameworks/qunit/english.lproj/dummy.css +0 -0
  169. data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/req_style_1.css +0 -0
  170. data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/strings.js +4 -0
  171. data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/test.rhtml +1 -0
  172. data/spec/fixtures/builder_tests/frameworks/req_target_1/req_js_1.js +0 -0
  173. data/spec/fixtures/builder_tests/frameworks/req_target_2/english.lproj/req_style_2.css +0 -0
  174. data/spec/fixtures/builder_tests/frameworks/req_target_2/english.lproj/test.rhtml +1 -0
  175. data/spec/fixtures/builder_tests/frameworks/req_target_2/javascript.js +1 -0
  176. data/spec/fixtures/builder_tests/frameworks/req_target_2/lib/alt_layout.rhtml +0 -0
  177. data/spec/fixtures/builder_tests/frameworks/req_target_2/req_js_2.js +0 -0
  178. data/spec/fixtures/builder_tests/themes/sample_theme/Buildfile +1 -0
  179. data/spec/fixtures/buildfiles/basic/Buildfile +16 -0
  180. data/spec/fixtures/buildfiles/basic/task_module.rake +6 -0
  181. data/spec/fixtures/buildfiles/installed/Buildfile +7 -0
  182. data/spec/fixtures/buildfiles/installed/Buildfile2 +5 -0
  183. data/spec/fixtures/buildfiles/project_test/Buildfile +4 -0
  184. data/spec/fixtures/buildfiles/project_test/not_project/Buildfile +2 -0
  185. data/spec/fixtures/buildfiles/project_test/not_project/child/PLACEHOLDER +0 -0
  186. data/spec/fixtures/entry_for_project/Buildfile +1 -0
  187. data/spec/fixtures/entry_for_project/apps/test_app/entry.txt +0 -0
  188. data/spec/fixtures/entry_for_project/apps/test_app/frameworks/nested/PLACEHOLDER +0 -0
  189. data/spec/fixtures/entry_for_project/frameworks/shared/PLACEHOLDER +0 -0
  190. data/spec/fixtures/find_targets/custom/Buildfile +8 -0
  191. data/spec/fixtures/find_targets/custom/bars/bar1/bars/bar1/PLACEHOLDER +0 -0
  192. data/spec/fixtures/find_targets/custom/bars/bar1/bars/bar2/PLACEHOLDER +0 -0
  193. data/spec/fixtures/find_targets/custom/bars/bar1/foos/foo1/PLACEHOLDER +0 -0
  194. data/spec/fixtures/find_targets/custom/bars/bar1/foos/foo2/PLACEHOLDER +0 -0
  195. data/spec/fixtures/find_targets/custom/foos/custom_foos/Buildfile +5 -0
  196. data/spec/fixtures/find_targets/custom/foos/custom_foos/custom_foodir/foo1/PLACEHOLDER +0 -0
  197. data/spec/fixtures/find_targets/custom/foos/custom_foos/custom_foodir/foo2/PLACEHOLDER +0 -0
  198. data/spec/fixtures/find_targets/custom/foos/custom_foos/foos/not_foo1/PLACEHOLDER +0 -0
  199. data/spec/fixtures/find_targets/custom/foos/foo1/bars/bar1/PLACEHOLDER +0 -0
  200. data/spec/fixtures/find_targets/custom/foos/foo1/bars/bar2/PLACEHOLDER +0 -0
  201. data/spec/fixtures/find_targets/nested/Buildfile +8 -0
  202. data/spec/fixtures/find_targets/nested/apps/app1/Buildfile +1 -0
  203. data/spec/fixtures/find_targets/nested/apps/app1/apps/nested_app/PLACEHOLDER +0 -0
  204. data/spec/fixtures/find_targets/standard/Apps/app1/frameworks/framework1/PLACEHOLDER +0 -0
  205. data/spec/fixtures/find_targets/standard/Apps/app1/frameworks/framework2/PLACEHOLDER +0 -0
  206. data/spec/fixtures/find_targets/standard/clients/client1/PLACEHOLDER +0 -0
  207. data/spec/fixtures/find_targets/standard/frameworks/framework1/frameworks/framework1/PLACEHOLDER +0 -0
  208. data/spec/fixtures/find_targets/standard/frameworks/framework2/PLACEHOLDER +0 -0
  209. data/spec/fixtures/find_targets/standard/themes/theme1/PLACEHOLDER +0 -0
  210. data/spec/fixtures/find_targets/standard/themes/theme2/PLACEHOLDER +0 -0
  211. data/spec/fixtures/languages/apps/caps_long_names/English.lproj/PLACEHOLDER +0 -0
  212. data/spec/fixtures/languages/apps/caps_long_names/FreNCH.lproj/PLACEHOLDER +0 -0
  213. data/spec/fixtures/languages/apps/caps_long_names/UnknOWN.lproj/PLACEHOLDER +0 -0
  214. data/spec/fixtures/languages/apps/long_names/english.lproj/PLACEHOLDER +0 -0
  215. data/spec/fixtures/languages/apps/long_names/french.lproj/PLACEHOLDER +0 -0
  216. data/spec/fixtures/languages/apps/long_names/german.lproj/PLACEHOLDER +0 -0
  217. data/spec/fixtures/languages/apps/long_names/italian.lproj/PLACEHOLDER +0 -0
  218. data/spec/fixtures/languages/apps/long_names/japanese.lproj/PLACEHOLDER +0 -0
  219. data/spec/fixtures/languages/apps/long_names/spanish.lproj/PLACEHOLDER +0 -0
  220. data/spec/fixtures/languages/apps/long_names/unknown.lproj/PLACEHOLDER +0 -0
  221. data/spec/fixtures/languages/apps/no_names/PLACEHOLDER +0 -0
  222. data/spec/fixtures/languages/apps/short_names/de.lproj/PLACEHOLDER +0 -0
  223. data/spec/fixtures/languages/apps/short_names/en-CA.lproj/PLACEHOLDER +0 -0
  224. data/spec/fixtures/languages/apps/short_names/en-GB.lproj/PLACEHOLDER +0 -0
  225. data/spec/fixtures/languages/apps/short_names/en-US.lproj/PLACEHOLDER +0 -0
  226. data/spec/fixtures/languages/apps/short_names/en.lproj/PLACEHOLDER +0 -0
  227. data/spec/fixtures/languages/apps/short_names/es.lproj/PLACEHOLDER +0 -0
  228. data/spec/fixtures/languages/apps/short_names/foo.lproj/PLACEHOLDER +0 -0
  229. data/spec/fixtures/languages/apps/short_names/fr.lproj/PLACEHOLDER +0 -0
  230. data/spec/fixtures/languages/apps/short_names/it.lproj/PLACEHOLDER +0 -0
  231. data/spec/fixtures/languages/apps/short_names/ja.lproj/PLACEHOLDER +0 -0
  232. data/spec/fixtures/ordered_entries/apps/no_requires/1.js +1 -0
  233. data/spec/fixtures/ordered_entries/apps/no_requires/B.js +1 -0
  234. data/spec/fixtures/ordered_entries/apps/no_requires/a.js +1 -0
  235. data/spec/fixtures/ordered_entries/apps/no_requires/a/a.js +1 -0
  236. data/spec/fixtures/ordered_entries/apps/no_requires/a/b.js +1 -0
  237. data/spec/fixtures/ordered_entries/apps/no_requires/b/a.js +1 -0
  238. data/spec/fixtures/ordered_entries/apps/no_requires/c.js +1 -0
  239. data/spec/fixtures/ordered_entries/apps/no_requires/core.js +1 -0
  240. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/B.css +0 -0
  241. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a.css +0 -0
  242. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a/a.css +0 -0
  243. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a/b.css +0 -0
  244. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/b/a.css +0 -0
  245. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/c.css +0 -0
  246. data/spec/fixtures/ordered_entries/apps/no_requires/lproj/strings.js +1 -0
  247. data/spec/fixtures/ordered_entries/apps/no_requires/utils.js +1 -0
  248. data/spec/fixtures/ordered_entries/apps/with_requires/a.js +2 -0
  249. data/spec/fixtures/ordered_entries/apps/with_requires/b.js +3 -0
  250. data/spec/fixtures/ordered_entries/apps/with_requires/c.js +2 -0
  251. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/a.css +2 -0
  252. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/b.css +2 -0
  253. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/c.css +2 -0
  254. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/d.js +1 -0
  255. data/spec/fixtures/real_world/Buildfile +12 -0
  256. data/spec/fixtures/real_world/apps/account/README +1 -0
  257. data/spec/fixtures/real_world/apps/calendar/README +1 -0
  258. data/spec/fixtures/real_world/apps/contacts/README_BEFORE_EDITING +1 -0
  259. data/spec/fixtures/real_world/apps/files/README +1 -0
  260. data/spec/fixtures/real_world/apps/mail/README +1 -0
  261. data/spec/fixtures/real_world/apps/mobile_photos/README +1 -0
  262. data/spec/fixtures/real_world/apps/photos/README +1 -0
  263. data/spec/fixtures/real_world/apps/uploader/README +1 -0
  264. data/spec/fixtures/real_world/frameworks/core_files/PLACEHOLDER +0 -0
  265. data/spec/fixtures/real_world/frameworks/core_photos/PLACEHOLDER +0 -0
  266. data/spec/fixtures/real_world/frameworks/shared/PLACEHOLDER +0 -0
  267. data/spec/fixtures/real_world/frameworks/sproutcore/Buildfile +26 -0
  268. data/spec/fixtures/real_world/frameworks/sproutcore/README +1 -0
  269. data/spec/fixtures/real_world/frameworks/sproutcore/apps/docs/PLACEHOLDER +0 -0
  270. data/spec/fixtures/real_world/frameworks/sproutcore/apps/test_runner/PLACEHOLDER +0 -0
  271. data/spec/fixtures/real_world/frameworks/sproutcore/core.js +0 -0
  272. data/spec/fixtures/real_world/frameworks/sproutcore/debug/debug-resource.html +0 -0
  273. data/spec/fixtures/real_world/frameworks/sproutcore/debug/sample_debug.js +0 -0
  274. data/spec/fixtures/real_world/frameworks/sproutcore/demo2.js +0 -0
  275. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/debug/sample_debug-loc.js +0 -0
  276. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo.css +4 -0
  277. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo.html +1 -0
  278. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo2.sass +0 -0
  279. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.haml +0 -0
  280. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.html.erb +1 -0
  281. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.rhtml +0 -0
  282. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/fixtures/sample_fixtures-loc.js +0 -0
  283. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/has_require.css +4 -0
  284. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/no_require.css +1 -0
  285. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/no_sc_resource.rhtml +1 -0
  286. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/protocols/sample-loc.js +0 -0
  287. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/sc_resource.css +6 -0
  288. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/sc_resource.rhtml +3 -0
  289. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/strings.js +1 -0
  290. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/tests/sample-loc.js +0 -0
  291. data/spec/fixtures/real_world/frameworks/sproutcore/fixtures/sample-json-fixture.json +1 -0
  292. data/spec/fixtures/real_world/frameworks/sproutcore/fixtures/sample_fixtures.js +0 -0
  293. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/application/PLACEHOLDER +0 -0
  294. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/costello/core.js +0 -0
  295. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/data_store/PLACEHOLDER +0 -0
  296. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/debug/PLACEHOLDER +0 -0
  297. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/desktop/PLACEHOLDER +0 -0
  298. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/empty_theme/PLACEHOLDER +0 -0
  299. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/foundation/PLACEHOLDER +0 -0
  300. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/mobile/PLACEHOLDER +0 -0
  301. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/qunit/PLACEHOLDER +0 -0
  302. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/uploader/PLACEHOLDER +0 -0
  303. data/spec/fixtures/real_world/frameworks/sproutcore/french.lproj/french-resource.js +0 -0
  304. data/spec/fixtures/real_world/frameworks/sproutcore/french.lproj/strings.js +1 -0
  305. data/spec/fixtures/real_world/frameworks/sproutcore/german.lproj/german-resource.js +0 -0
  306. data/spec/fixtures/real_world/frameworks/sproutcore/german.lproj/strings.js +0 -0
  307. data/spec/fixtures/real_world/frameworks/sproutcore/has_require.js +4 -0
  308. data/spec/fixtures/real_world/frameworks/sproutcore/lib/index.html +1 -0
  309. data/spec/fixtures/real_world/frameworks/sproutcore/no_require.js +1 -0
  310. data/spec/fixtures/real_world/frameworks/sproutcore/protocols/sample.js +0 -0
  311. data/spec/fixtures/real_world/frameworks/sproutcore/sc_resource.js +6 -0
  312. data/spec/fixtures/real_world/frameworks/sproutcore/tests/nested/sample1.js +0 -0
  313. data/spec/fixtures/real_world/frameworks/sproutcore/tests/nested/sample2.js +0 -0
  314. data/spec/fixtures/real_world/frameworks/sproutcore/tests/sample.js +0 -0
  315. data/spec/fixtures/real_world/frameworks/sproutcore/tests/sample.rhtml +1 -0
  316. data/spec/fixtures/real_world/frameworks/sproutcore/themes/standard_theme/README +0 -0
  317. data/spec/fixtures/real_world/frameworks/sproutcore/views/view.js +1 -0
  318. data/spec/fixtures/real_world/generators/sample_custom/Buildfile +0 -0
  319. data/spec/fixtures/real_world/generators/sample_custom/templates/{filename}.js +1 -0
  320. data/spec/fixtures/recursive_project/Buildfile +8 -0
  321. data/spec/fixtures/recursive_project/frameworks/sproutcore/frameworks/costello/PLACEHOLDER +0 -0
  322. data/spec/lib/builders/combine_spec.rb +67 -0
  323. data/spec/lib/builders/html_spec.rb +577 -0
  324. data/spec/lib/builders/javascript_spec.rb +81 -0
  325. data/spec/lib/builders/sass_spec.rb +43 -0
  326. data/spec/lib/builders/spec_helper.rb +30 -0
  327. data/spec/lib/builders/strings_spec.rb +52 -0
  328. data/spec/lib/builders/stylesheet_spec.rb +63 -0
  329. data/spec/lib/builders/test_index_spec.rb +44 -0
  330. data/spec/lib/builders/test_spec.rb +135 -0
  331. data/spec/lib/buildfile/config_for_spec.rb +81 -0
  332. data/spec/lib/buildfile/define_spec.rb +59 -0
  333. data/spec/lib/buildfile/dup_spec.rb +65 -0
  334. data/spec/lib/buildfile/invoke_spec.rb +130 -0
  335. data/spec/lib/buildfile/load_spec.rb +49 -0
  336. data/spec/lib/buildfile/task/dup_spec.rb +55 -0
  337. data/spec/lib/buildfile/task_defined_spec.rb +17 -0
  338. data/spec/lib/buildfile_commands/build_task_spec.rb +19 -0
  339. data/spec/lib/buildfile_commands/config_spec.rb +97 -0
  340. data/spec/lib/buildfile_commands/import_spec.rb +17 -0
  341. data/spec/lib/buildfile_commands/namespace_spec.rb +18 -0
  342. data/spec/lib/buildfile_commands/proxies_spec.rb +38 -0
  343. data/spec/lib/buildfile_commands/replace_task_spec.rb +29 -0
  344. data/spec/lib/buildfile_commands/task_spec.rb +36 -0
  345. data/spec/lib/helpers/packing_optimizer/optimize_spec.rb +26 -0
  346. data/spec/lib/models/hash_struct/deep_clone_spec.rb +27 -0
  347. data/spec/lib/models/hash_struct/has_options_spec.rb +32 -0
  348. data/spec/lib/models/hash_struct/hash_spec.rb +64 -0
  349. data/spec/lib/models/hash_struct/merge_spec.rb +26 -0
  350. data/spec/lib/models/hash_struct/method_missing.rb +41 -0
  351. data/spec/lib/models/manifest/add_entry_spec.rb +36 -0
  352. data/spec/lib/models/manifest/add_transform_spec.rb +90 -0
  353. data/spec/lib/models/manifest/build_spec.rb +78 -0
  354. data/spec/lib/models/manifest/entry_for_spec.rb +94 -0
  355. data/spec/lib/models/manifest/find_entry.rb +84 -0
  356. data/spec/lib/models/manifest/prepare_spec.rb +62 -0
  357. data/spec/lib/models/manifest_entry/cacheable_url_spec.rb +31 -0
  358. data/spec/lib/models/manifest_entry/prepare_spec.rb +54 -0
  359. data/spec/lib/models/project/add_target_spec.rb +44 -0
  360. data/spec/lib/models/project/buildfile_spec.rb +35 -0
  361. data/spec/lib/models/project/find_targets_for_spec.rb +77 -0
  362. data/spec/lib/models/project/load_nearest_project_spec.rb +23 -0
  363. data/spec/lib/models/project/target_for_spec.rb +33 -0
  364. data/spec/lib/models/project/targets_spec.rb +62 -0
  365. data/spec/lib/models/target/compute_build_number_spec.rb +125 -0
  366. data/spec/lib/models/target/config_spec.rb +30 -0
  367. data/spec/lib/models/target/expand_required_targets_spec.rb +48 -0
  368. data/spec/lib/models/target/installed_languages_spec.rb +47 -0
  369. data/spec/lib/models/target/lproj_for_spec.rb +38 -0
  370. data/spec/lib/models/target/manifest_for_spec.rb +42 -0
  371. data/spec/lib/models/target/parent_target_spec.rb +21 -0
  372. data/spec/lib/models/target/prepare_spec.rb +53 -0
  373. data/spec/lib/models/target/required_targets_spec.rb +119 -0
  374. data/spec/lib/models/target/target_for_spec.rb +56 -0
  375. data/spec/lib/tools/build_number_spec.rb +28 -0
  376. data/spec/lib/tools/gen_spec.rb +207 -0
  377. data/spec/lib/tools/tools_spec.rb +78 -0
  378. data/spec/spec_helper.rb +138 -0
  379. data/sproutcore.gemspec +47 -0
  380. data/vendor/github_gem_lint.rb +22 -0
  381. data/vendor/yui-compressor/yuicompressor-2.4.2.jar +0 -0
  382. metadata +1084 -0
@@ -0,0 +1,403 @@
1
+ # ===========================================================================
2
+ # Project: Abbot - SproutCore Build Tools
3
+ # Copyright: ©2009 Apple, Inc.
4
+ # portions copyright @2006-2009 Sprout Systems, Inc.
5
+ # and contributors
6
+ # ===========================================================================
7
+
8
+ module SC
9
+
10
+ # Defines a build target in a project. A build target is a component that
11
+ # you might want to build separately such as an application or library.
12
+ #
13
+ # Targets make require other targets through their dependencies property.
14
+ # The property should name the other targets using either a relative or
15
+ # absolute target name. An absolute target name begins with a forward
16
+ # slash (i.e. /sproutcore), where a relative begins with the target name
17
+ # itself.
18
+ #
19
+ # When you create a target you must pass at least the following two
20
+ # parameters:
21
+ #
22
+ # target_name:: the name of the target, must start with a forward-slash
23
+ # target_type:: a task namespace that can be used to invoke tasks for
24
+ # building the manifest and other commands
25
+ #
26
+ # In addition to the above required parameters, you may also pass options.
27
+ # These options will be simply set on the target and may be used by the
28
+ # build tasks. At least two options, however, have special meaning and
29
+ # they should be set at some point before the task is used:
30
+ #
31
+ # project:: This should point to the project the owns the target. If you
32
+ # create a target using the Project#add_target method, this will be set
33
+ # for you.
34
+ # source_root:: This should contain the absolute path to the source
35
+ # directory for the target. If you do not set this option, then the
36
+ # Target will not be able to load a Buildfile you define. It is not
37
+ # strictly necessary to set this option however, as some targets may be
38
+ # entirely synthesized from other sources.
39
+ #
40
+ class Target < HashStruct
41
+
42
+ def initialize(target_name, target_type, opts={})
43
+ # Add target_name & type of options so they can be enumerated as part
44
+ # of the hash
45
+ opts = opts.merge :target_name => target_name,
46
+ :target_type => target_type
47
+
48
+ # Remove the project since we do not want to enumerate that as part of
49
+ # the hash
50
+ @project = opts.delete(:project)
51
+ super(opts)
52
+ end
53
+
54
+ attr_reader :project
55
+
56
+ def inspect
57
+ "SC::Target(#{target_name})"
58
+ end
59
+
60
+ # Invoke this method to make sure the basic paths for the target have
61
+ # been prepared. This method is called on the target before it is
62
+ # returned from any call target_for(). It will invoke the target:prepare
63
+ # build task.
64
+ #
65
+ # You can call this method as often as you want; it will only execute
66
+ # once.
67
+ #
68
+ # === Returns
69
+ # Target
70
+ def prepare!
71
+ if !@is_prepared
72
+ @is_prepared = true
73
+ if buildfile.task_defined? 'target:prepare'
74
+ buildfile.invoke 'target:prepare',
75
+ :target => self, :project => self.project, :config => self.config
76
+ end
77
+ end
78
+ return self
79
+ end
80
+
81
+ # Used for unit testing
82
+ def prepared?; @is_prepared || false; end
83
+
84
+ ######################################################
85
+ # CONFIG
86
+ #
87
+
88
+ # Returns the buildfile for this target. The buildfile is a clone of
89
+ # the parent target buildfile with any buildfile found in the current
90
+ # target's source_root merged over top of it.
91
+ #
92
+ # === Returns
93
+ # Buildfile
94
+ #
95
+ def buildfile
96
+ @buildfile ||= parent_target.buildfile.dup.for_target(self).load!(self.source_root)
97
+ end
98
+
99
+ # Returns the config for the current project. The config is computed by
100
+ # taking the merged config settings from the build file given the current
101
+ # build mode, then merging any environmental configs (set in SC::env)
102
+ # over the top.
103
+ #
104
+ # This is the config hash you should use to control how items are built.
105
+ def config
106
+ return @config ||= buildfile.config_for(target_name, SC.build_mode).merge(SC.env)
107
+ end
108
+
109
+ # Clears the cached config, reloading it from the buildfile again. This
110
+ # is mostly used for unit testing.
111
+ def reload_config!
112
+ @config= nil
113
+ @is_prepared = false
114
+ prepare!
115
+ return self
116
+ end
117
+
118
+
119
+ ######################################################
120
+ ## COMPUTED HELPER PROPERTIES
121
+ ##
122
+
123
+ # Returns all of the targets required by this target. This will use the
124
+ # "required" config, resolving the target names using target_for().
125
+ #
126
+ # You may pass some additional options in which will select the set
127
+ # of targets you want returned.
128
+ #
129
+ # === Options
130
+ # :debug:: if true, config.debug_required will be included
131
+ # :test:: if true, config.test_required will also be included
132
+ #
133
+ # === Returns
134
+ # Array of Targets
135
+ #
136
+ def required_targets(opts={})
137
+
138
+ # compute cache key for these options
139
+ key = [:debug, :test, :theme].map do |k|
140
+ opts[k] ? k : nil
141
+ end
142
+ key = key.compact.join('.')
143
+
144
+ # Return cache value if found
145
+ ret = (@required_targets ||= {})[key]
146
+ return ret unless ret.nil?
147
+
148
+ # else compute return value, respecting options
149
+ ret = [config.required]
150
+ if opts[:debug] && config.debug_required
151
+ ret << config.debug_required
152
+ end
153
+ if opts[:test] && config.test_required
154
+ ret << config.test_required
155
+ end
156
+ if opts[:theme] && self.loads_theme? && config.theme
157
+ # verify theme is a theme target type - note that if no matching
158
+ # target is found, we'll just let this go through so the standard
159
+ # not found warning can show.
160
+ t = target_for(config.theme)
161
+ if t && t.target_type != :theme
162
+ SC.logger.warn "Target #{config.theme} was set as theme for #{target_name} but it is not a theme."
163
+ else
164
+ ret << config.theme
165
+ end
166
+ end
167
+
168
+ ret = ret.flatten.compact.map do |n|
169
+ if (t = target_for(n)).nil?
170
+ SC.logger.warn "Could not find target #{n} that is required by #{target_name}"
171
+ end
172
+ t
173
+ end
174
+ ret = ret.compact.uniq
175
+
176
+ @required_targets[key] = ret
177
+ return ret
178
+ end
179
+
180
+ # Returns true if this target can load a theme. Default returns true
181
+ # only if the target_type == :app, but you can override this by setting
182
+ # the value yourself.
183
+ def loads_theme?;
184
+ ret = self[:loads_theme]
185
+ ret.nil? ? (target_type == :app) : ret
186
+ end
187
+
188
+ # Returns the expanded list of required targets, ordered as they actually
189
+ # need to be loaded.
190
+ #
191
+ # This method takes the same options as required_targets to select the
192
+ # targets to include.
193
+ #
194
+ # === Returns
195
+ # Array of targets
196
+ #
197
+ def expand_required_targets(opts ={})
198
+ _expand_required_targets(opts, [self])
199
+ end
200
+
201
+ def _expand_required_targets(opts, seen)
202
+ ret = []
203
+ required_targets(opts).each do |target|
204
+ next if seen.include?(target)
205
+ seen << target # avoid loading again
206
+
207
+ # add required targets, if not already seend...
208
+ target._expand_required_targets(opts, seen).each do |required|
209
+ ret << required
210
+ seen << required
211
+ end
212
+
213
+ # then add my own target...
214
+ ret << target
215
+ end
216
+ return ret.uniq.compact
217
+ end
218
+
219
+ # Returns true if the passed path appears to be a target directory
220
+ # according to the target's current config.
221
+ def target_directory?(path, root_path=nil)
222
+ root_path = self.source_root if root_path.nil?
223
+ @target_names ||= self.config.target_types.keys
224
+ path = path.to_s.sub /^#{Regexp.escape root_path}\//, ''
225
+ @target_names.each do |name|
226
+ return true if path =~ /^#{Regexp.escape name.to_s}/
227
+ end
228
+ return false
229
+ end
230
+
231
+ # Computes a unique build number for this target. The build number is
232
+ # gauranteed to change anytime the contents of any source file changes
233
+ # or anytime the build number of a required target changes. Although
234
+ # this will generate long strings, it will automatically ensure that
235
+ # resources are properly cached when deployed.
236
+ #
237
+ # Note that this method does NOT set the build_number on the receiver;
238
+ # it just calculates a value and returns it. You usually call this
239
+ # method just to set the build_number on the target.
240
+ #
241
+ # === Returns
242
+ # A build number string
243
+ #
244
+ def compute_build_number(seen=nil)
245
+
246
+ # Look for a global build_numbers hash and try that
247
+ if (build_numbers = config.build_numbers)
248
+ build_number = build_numbers[target_name.to_s] || build_numbers[target_name.to_sym]
249
+ end
250
+
251
+ # Otherwise, use config build number specifically for this target, if
252
+ # specified
253
+ build_number ||= config.build_number
254
+
255
+ # Otherwise, actually compute a build number.
256
+ if build_number.nil?
257
+ require 'digest/md5'
258
+
259
+ # Computes the build number based on the contents of the
260
+ # files. It is not as fast as using an mtime, but it will remain
261
+ # constant from one machine to the next so it can be used when
262
+ # deploying across multiple build machines, etc.
263
+ digests = Dir.glob(File.join(source_root, '**', '*')).map do |path|
264
+ allowed = File.exists?(path) && !File.directory?(path)
265
+ allowed = allowed && !target_directory?(path)
266
+ allowed ? Digest::SHA1.hexdigest(File.read(path)) : nil
267
+ end
268
+ digests.compact!
269
+
270
+ # Get all required targets and add in their build number.
271
+ # Note the "seen" variable passed here will avoid circular references
272
+ # causing infinite loops. Normally this should not be necessary, but
273
+ # we put this here to gaurd against misconfigured projects
274
+ seen ||= []
275
+ required_targets.each do |ct|
276
+ next if seen.include?(ct)
277
+ seen << ct
278
+ digests << (ct.build_number || ct.compute_build_number(seen))
279
+ end
280
+
281
+ # Finally digest the complete string - tada! build number
282
+ build_number = Digest::SHA1.hexdigest(digests.join)
283
+ end
284
+
285
+ return build_number.to_s
286
+ end
287
+
288
+ ######################################################
289
+ # MANIFEST
290
+ #
291
+
292
+ # An array of manifests for the target. You can retrieve the manifest
293
+ # for a particular variation using the method manifest_for().
294
+ def manifests; @manifests ||= []; end
295
+
296
+ # Returns the manifest matching the variation options. If no matching
297
+ # manifest can be found, a new manifest will be created and prepared.
298
+ def manifest_for(variation={})
299
+ ret = manifests.find { |m| m.has_options?(variation) }
300
+ if ret.nil?
301
+ ret = Manifest.new(self, variation)
302
+ @manifests << ret
303
+ end
304
+ return ret
305
+ end
306
+
307
+ ######################################################
308
+ # TARGET METHODS
309
+ #
310
+
311
+ # Finds the parent target for this target. The parent target is the
312
+ # target with a higher-level of hierarchy.
313
+ #
314
+ # For example, the parent of 'sproutcore/foundation' is 'sproutcore'
315
+ #
316
+ # If your target is a top-level target, then this will return the project
317
+ # itself. If your target does not yet belong to a project, this will
318
+ # return nil
319
+ #
320
+ def parent_target
321
+ return @parent_target unless @parent_target.nil?
322
+ return nil if project.nil?
323
+ tname = target_name
324
+ while @parent_target.nil?
325
+ tname = tname.to_s.sub(/\/[^\/]+$/,'')
326
+ @parent_target = (tname.size>0) ? project.target_for(tname) : project
327
+ end
328
+ @parent_target
329
+ end
330
+
331
+ # Find a target relative to the receiver target. If you pass a regular
332
+ # target name, this method will start by looking for direct children of
333
+ # the receiver, then it will move up the hierarchy, looking in the parent
334
+ # target and on up until it reaches the top-level library.
335
+ #
336
+ # This 'scoped' search model allows you to next targets and then to
337
+ # properly reference them.
338
+ #
339
+ # To absolutely reference a target (i.e. to name its part beginning from
340
+ # the project root, begin with a forward-slash.)
341
+ #
342
+ # === Params
343
+ # target_name:: the name of the target
344
+ #
345
+ # === Returns
346
+ # found target or nil if no match could be found
347
+ #
348
+ def target_for(target_name)
349
+ if target_name =~ /^\// # absolute target
350
+ ret = project.target_for(target_name)
351
+ else # relative target...
352
+
353
+ # look for any targets that are children of this target
354
+ ret = project.target_for([self.target_name, target_name].join('/'))
355
+
356
+ # Ask my parent target to look for the target, and so on.
357
+ if ret.nil?
358
+ ret = parent_target.nil? ? project.target_for(target_name) : parent_target.target_for(target_name)
359
+ end
360
+ end
361
+ return ret
362
+ end
363
+
364
+ ######################################################
365
+ # LANGUAGE HELPER METHODS
366
+ #
367
+
368
+ LONG_LANGUAGE_MAP = { :english => :en, :french => :fr, :german => :de, :japanese => :ja, :spanish => :es, :italian => :it }
369
+ SHORT_LANGUAGE_MAP = { :en => :english, :fr => :french, :de => :german, :ja => :japanese, :es => :spanish, :it => :italian }
370
+
371
+ # Returns the language codes for any languages actually found in this
372
+ # target.
373
+ def installed_languages
374
+ ret = Dir.glob(File.join(source_root, '*.lproj')).map do |path|
375
+ next unless path =~ /\/([^\/]+)\.lproj/
376
+ (LONG_LANGUAGE_MAP[$1.downcase.to_sym] || $1).to_s
377
+ end
378
+ ret << config.preferred_language.to_s if config.preferred_language
379
+ ret.compact.uniq.sort { |a,b| a.downcase <=> b.downcase }.map { |l| l.to_sym }
380
+ end
381
+
382
+ # Returns project-relative path to the lproj directory for the named
383
+ # short language code
384
+ def lproj_for(language_code)
385
+
386
+ # try code as passed
387
+ ret = "#{language_code}.lproj"
388
+ return ret if File.directory? File.join(source_root, ret)
389
+
390
+ # try long language too...
391
+ language_code = SHORT_LANGUAGE_MAP[language_code.to_s.downcase.to_sym]
392
+ if language_code
393
+ ret = "#{language_code}.lproj"
394
+ return ret if File.directory? File.join(source_root, ret)
395
+ end
396
+
397
+ # None found
398
+ return nil
399
+ end
400
+
401
+ end
402
+
403
+ end
@@ -0,0 +1,269 @@
1
+ # ===========================================================================
2
+ # Project: Abbot - SproutCore Build Tools
3
+ # Copyright: ©2009 Apple, Inc.
4
+ # portions copyright @2006-2009 Sprout Systems, Inc.
5
+ # and contributors
6
+ # ===========================================================================
7
+
8
+ require 'thread'
9
+
10
+ module SC
11
+ module Rack
12
+
13
+ # A Rack application for serving dynamically-built SproutCore projects.
14
+ # Most of the time you will use this application as part of the sc-server
15
+ # command to dynamically build your SproutCore project while you develop
16
+ # it.
17
+ #
18
+ # If you are deploying some Ruby-based infrastructure in your production
19
+ # environment, you could also use this application to dynamically build
20
+ # new versions of your SproutCore apps when you deploy them. This would
21
+ # allow you to potentially bypass the pre-deployment build step using
22
+ # sc-build.
23
+ #
24
+ # While this model is supported by the Rack adaptor, it is generally
25
+ # recommended that you instead build you app without using this adaptor
26
+ # since the build step will help catch possible errors in your code before
27
+ # you go live with your project. Sometimes, however, dynamically building
28
+ # content is useful, and that is what this adaptor is for.
29
+ #
30
+ # === Using This Application
31
+ #
32
+ # When you instantiate a builder, you must provide one or more projects
33
+ # that contain the resources you want to load. Each incoming request url
34
+ # will be mapped to an entriy in a project manifest. The entry is then
35
+ # built and the resulting file returned. Once a file has been built, it
36
+ # will not be rebuilt unless the source file it represents has changed.
37
+ #
38
+ # In addition to dynamically building entries, the Builder can also
39
+ # forwards requests onto an SC::Rack::Proxy app to handle proxies
40
+ # requests.
41
+ #
42
+ # === Config Settings
43
+ #
44
+ # This app respects several options that you can name in your config file
45
+ # (in addition to proxy configs), that can affect the app performance.
46
+ # Normally reasonable defaults for these settings are built into the
47
+ # SproutCore buildfile, but you may choose to override them if you are
48
+ # deploying into a production environment.
49
+ #
50
+ # :reload_project:: If set to true, then the builder will reload the
51
+ # projects to look for changed files before servicing incoming
52
+ # requests. You will generally want this option while working in
53
+ # debug mode, but you may want to disable it for production, since it
54
+ # can slow down performance.
55
+ #
56
+ # :use_cached_headers:: If set to true, then the builder will return
57
+ # static assets with an "Expires: <10-years>" header attached. This
58
+ # will yield excellent performance in production systems but it may
59
+ # interfere with loading the most recent copies of files when in
60
+ # development mode.
61
+ #
62
+ # :combine_javascript:: If set, the generated html will reference a
63
+ # combined version of the javascript for elgible targets. This will
64
+ # yield better performance in production, but slows down load time in
65
+ # development mode.
66
+ #
67
+ # :combine_stylesheets:: Ditto to combine_javascript
68
+ #
69
+ class Builder
70
+
71
+ # used to set expires header.
72
+ ONE_YEAR = 365 * 24 * 60 * 60
73
+
74
+ # When you create a new builder, pass in one or more projects you want
75
+ # the builder to monitor for changes.
76
+ def initialize(project)
77
+ @project = project
78
+ @last_reload_time = Time.now
79
+ end
80
+
81
+ # Main entry point for this Rack application. Returns 404 if no
82
+ # matching entry could be found in the project.
83
+ def call(env)
84
+
85
+ # define local variables so they will survive the mutext contexts
86
+ # below...
87
+ ret = url = target = language = cacheable = manifest = entry = nil
88
+ build_path = nil
89
+
90
+ project_mutex.synchronize do
91
+ reload_project! # if needed
92
+
93
+ # collect some standard info
94
+ url = env['PATH_INFO']
95
+
96
+ # look for a matching target
97
+ target = target_for(url)
98
+ ret = not_found("No matching target") if target.nil?
99
+
100
+ # normalize url to resolve to entry & extract the language
101
+ if ret.nil?
102
+ url, language, cacheable = normalize_url(url, target)
103
+ ret = not_found("Target requires language") if language.nil?
104
+ end
105
+
106
+ # lookup manifest
107
+ if ret.nil?
108
+ language = language.to_s.downcase.to_sym # normalize
109
+ manifest = target.manifest_for(:language => language).build!
110
+
111
+ # lookup entry by url
112
+ unless entry = manifest.entries.find { |e| e.url == url }
113
+ ret = not_found("No matching entry in target")
114
+ end
115
+ end
116
+
117
+ if ret.nil?
118
+ # Clean the entry so it will rebuild if we are serving an html
119
+ # file
120
+ entry.clean! if [:html, :test].include?(entry.entry_type)
121
+
122
+ # Now build entry and return a file object
123
+ build_path = entry.build!.build_path
124
+ end
125
+
126
+ # Update last reload time. This way if any other requests are
127
+ # waiting, they won't rebuild their manifest.
128
+ @last_reload_time = Time.now
129
+ end
130
+
131
+ return ret unless ret.nil?
132
+
133
+ unless File.file?(build_path) && File.readable?(build_path)
134
+ return not_found("File could not build (entry: #{entry.filename} - build_path: #{build_path}")
135
+ end
136
+
137
+ SC.logger.info "Serving #{target.target_name.to_s.sub(/^\//,'')}:#{entry.filename}"
138
+
139
+ # define response headers
140
+ file_size = File.size(build_path)
141
+ headers = {
142
+ "Last-Modified" => File.mtime(build_path).httpdate,
143
+ "Etag" => File.mtime(build_path).to_i.to_s,
144
+ "Content-Type" => mime_type(build_path),
145
+ "Content-Length" => file_size.to_s,
146
+ "Expires" => (cacheable ? (Time.now + ONE_YEAR) : Time.now).httpdate
147
+ }
148
+ [200, headers, File.open(build_path, 'rb')]
149
+ end
150
+
151
+ attr_reader :project
152
+
153
+ protected
154
+
155
+ # Mutex used while updating the project and retrieving the entry to
156
+ # build.
157
+ def project_mutex; @project_mutex ||= Mutex.new; end
158
+
159
+ # Mutex used while building an entry...
160
+ def build_mutex; @build_mutex ||= Mutex.new; end
161
+
162
+ # Invoked when a resource cannot be found for some reason
163
+ def not_found(reason)
164
+ reason = "<html><body><p>#{reason}</p></body></html>"
165
+ return [404, {
166
+ "Content-Type" => "text/html",
167
+ "Content-Length" => reason.size.to_s
168
+ }, reason]
169
+ end
170
+
171
+ # Reloads the project if reloading is enabled. At maximum this will
172
+ # reload the project every 5 seconds.
173
+ def reload_project!
174
+ # don't reload if no project or is disabled
175
+ return if @project.nil? || !@project.config.reload_project
176
+
177
+ # reload after a delay of 5 sec
178
+ reload_delay = (Time.now - @last_reload_time)
179
+
180
+ if reload_delay > 5
181
+ SC.logger.info "Rebuilding project manifest"
182
+ @project.reload!
183
+ end
184
+ end
185
+
186
+ def target_for(url)
187
+
188
+ # get targets
189
+ targets = project.targets.values.dup
190
+ targets.each { |t| t.prepare! }
191
+
192
+ # split the url into parts. pop parts until we find a matching
193
+ # target. This ensures that we end up with the deepest matching
194
+ # target.
195
+ url_parts = url.split '/'
196
+ ret = nil
197
+ while url_parts.size>0 && ret.nil?
198
+ url = url_parts.join '/'
199
+ ret = targets.find { |t| t.url_root == url || t.index_root == url }
200
+ url_parts.pop
201
+ end
202
+ return ret
203
+ end
204
+
205
+ # Helper method. This will normalize a URL into one that can map
206
+ # directly to an entry in the bundle. If the URL is of a format that
207
+ # cannot be converted, returns the url. In particular, this will look
208
+ # for all the different ways you can request an index.html file and
209
+ # convert it to a canonical form
210
+ #
211
+ # Returns the normalized url, the language extracted from the url and
212
+ # a boolean indicating whether the url is considered cacheable or not.
213
+ # any url beginning with the target's url_root is considered cacheable
214
+ # and will therefore be returned with an expires <10years> header set.
215
+ #
216
+ # === Params
217
+ # url:: the url to normalize
218
+ # target:: the suspected target url
219
+ #
220
+ # === Returns
221
+ # [normalized url, matched language, cacheable]
222
+ #
223
+ def normalize_url(url, target)
224
+
225
+ cacheable = true
226
+
227
+ # match
228
+ # /foo - /foo/index.html
229
+ # /foo/en - /foo/en/index.html
230
+ # /foo/en/build_number - /foo/en/build_number/index.html
231
+ # /foo/en/CURRENT/resource-name
232
+ matched = url.match(/^#{Regexp.escape target.index_root}(\/([^\/\.]+))?(\/([^\/\.]+))?(\/(.*))?$/)
233
+ unless matched.nil?
234
+ matched_language = matched[2] || target.config.preferred_language
235
+
236
+ matched_build_number = matched[4]
237
+ if matched_build_number.blank? || matched_build_number == 'current'
238
+ matched_build_number = target.build_number
239
+ end
240
+
241
+ resource_name = matched[6]
242
+ resource_name = 'index.html' if resource_name.blank?
243
+
244
+ # convert to url root based
245
+ url = [target.url_root, matched_language, matched_build_number,
246
+ resource_name] * '/'
247
+ cacheable = false # index_root based urls are not cacheable
248
+
249
+ # otherwise, just get the language -- url_root-based urls must be
250
+ # fully qualified
251
+ else
252
+ matched = url.match(/^#{Regexp.escape target.url_root}\/([^\/\.]+)/)
253
+ matched_language = matched ? matched[1] : nil
254
+ end
255
+
256
+ return [url, matched_language, cacheable]
257
+ end
258
+
259
+ # Returns the mime type. Basically this is the Rack mime mapper with
260
+ # a few bug fixes.
261
+ def mime_type(build_path)
262
+ ext = File.extname(build_path)
263
+ (ext == '.js') ? 'text/javascript' : ::Rack::Mime.mime_type(ext, 'text/plain')
264
+ end
265
+
266
+ end
267
+
268
+ end
269
+ end