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.
- data/Buildfile +115 -0
- data/History.txt +7 -0
- data/README.txt +102 -0
- data/Rakefile +54 -0
- data/Todo.txt +1 -0
- data/bin/sc-build +13 -0
- data/bin/sc-build-number +13 -0
- data/bin/sc-gen +13 -0
- data/bin/sc-init +13 -0
- data/bin/sc-manifest +13 -0
- data/bin/sc-server +13 -0
- data/bin/sproutcore +14 -0
- data/buildtasks/build.rake +73 -0
- data/buildtasks/entry.rake +59 -0
- data/buildtasks/manifest.rake +469 -0
- data/buildtasks/render.rake +21 -0
- data/buildtasks/target.rake +42 -0
- data/gen/Buildfile +16 -0
- data/gen/app/Buildfile +36 -0
- data/gen/app/README +1 -0
- data/gen/app/USAGE +13 -0
- data/gen/app/templates/apps/{target_name}/core.js +27 -0
- data/gen/app/templates/apps/{target_name}/english.lproj/loading.rhtml +9 -0
- data/gen/app/templates/apps/{target_name}/english.lproj/main_page.js +22 -0
- data/gen/app/templates/apps/{target_name}/english.lproj/strings.js +15 -0
- data/gen/app/templates/apps/{target_name}/main.js +30 -0
- data/gen/controller/Buildfile +18 -0
- data/gen/controller/README +1 -0
- data/gen/controller/USAGE +13 -0
- data/gen/controller/templates/controllers/{filename}.js +18 -0
- data/gen/controller/templates/tests/controllers/{filename}.js +15 -0
- data/gen/framework/Buildfile +36 -0
- data/gen/framework/README +1 -0
- data/gen/framework/USAGE +13 -0
- data/gen/framework/templates/frameworks/{target_name}/core.js +21 -0
- data/gen/framework/templates/frameworks/{target_name}/english.lproj/strings.js +15 -0
- data/gen/language/Buildfile +26 -0
- data/gen/language/README +1 -0
- data/gen/language/USAGE +13 -0
- data/gen/language/templates/{filename}/strings.js +14 -0
- data/gen/model/Buildfile +16 -0
- data/gen/model/README +1 -0
- data/gen/model/USAGE +15 -0
- data/gen/model/templates/fixtures/{filename}.js +35 -0
- data/gen/model/templates/models/{filename}.js +19 -0
- data/gen/model/templates/tests/models/{filename}.js +15 -0
- data/gen/project/Buildfile +45 -0
- data/gen/project/INIT +3 -0
- data/gen/project/README +1 -0
- data/gen/project/USAGE +2 -0
- data/gen/project/templates/{filename}/Buildfile +7 -0
- data/gen/project/templates/{filename}/README +7 -0
- data/gen/test/Buildfile +25 -0
- data/gen/test/README +1 -0
- data/gen/test/USAGE +12 -0
- data/gen/test/templates/tests/{filename}.js +15 -0
- data/gen/theme/Buildfile +36 -0
- data/gen/theme/README +1 -0
- data/gen/theme/USAGE +13 -0
- data/gen/theme/templates/themes/{target_name}/english.lproj/strings.js +15 -0
- data/gen/view/Buildfile +18 -0
- data/gen/view/README +1 -0
- data/gen/view/USAGE +13 -0
- data/gen/view/templates/tests/views/{filename}.js +15 -0
- data/gen/view/templates/views/{filename}.js +18 -0
- data/lib/sproutcore.rb +130 -0
- data/lib/sproutcore/builders/base.rb +79 -0
- data/lib/sproutcore/builders/combine.rb +37 -0
- data/lib/sproutcore/builders/html.rb +177 -0
- data/lib/sproutcore/builders/javascript.rb +55 -0
- data/lib/sproutcore/builders/minify.rb +67 -0
- data/lib/sproutcore/builders/sass.rb +55 -0
- data/lib/sproutcore/builders/strings.rb +44 -0
- data/lib/sproutcore/builders/stylesheet.rb +40 -0
- data/lib/sproutcore/builders/test.rb +58 -0
- data/lib/sproutcore/builders/test_index.rb +26 -0
- data/lib/sproutcore/buildfile.rb +483 -0
- data/lib/sproutcore/buildfile/build_task.rb +33 -0
- data/lib/sproutcore/buildfile/buildfile_dsl.rb +162 -0
- data/lib/sproutcore/buildfile/cloneable.rb +34 -0
- data/lib/sproutcore/buildfile/early_time.rb +29 -0
- data/lib/sproutcore/buildfile/invocation_chain.rb +64 -0
- data/lib/sproutcore/buildfile/namespace.rb +33 -0
- data/lib/sproutcore/buildfile/string_ext.rb +198 -0
- data/lib/sproutcore/buildfile/task.rb +304 -0
- data/lib/sproutcore/buildfile/task_arguments.rb +86 -0
- data/lib/sproutcore/buildfile/task_manager.rb +193 -0
- data/lib/sproutcore/deprecated/view_helper.rb +662 -0
- data/lib/sproutcore/helpers/capture_helper.rb +30 -0
- data/lib/sproutcore/helpers/cssmin.rb +202 -0
- data/lib/sproutcore/helpers/dom_id_helper.rb +21 -0
- data/lib/sproutcore/helpers/entry_sorter.rb +105 -0
- data/lib/sproutcore/helpers/packed_optimizer.rb +102 -0
- data/lib/sproutcore/helpers/static_helper.rb +268 -0
- data/lib/sproutcore/helpers/tag_helper.rb +117 -0
- data/lib/sproutcore/helpers/text_helper.rb +303 -0
- data/lib/sproutcore/models/generator.rb +493 -0
- data/lib/sproutcore/models/hash_struct.rb +109 -0
- data/lib/sproutcore/models/manifest.rb +342 -0
- data/lib/sproutcore/models/manifest_entry.rb +232 -0
- data/lib/sproutcore/models/project.rb +270 -0
- data/lib/sproutcore/models/target.rb +403 -0
- data/lib/sproutcore/rack/builder.rb +269 -0
- data/lib/sproutcore/rack/dev.rb +51 -0
- data/lib/sproutcore/rack/docs.rb +24 -0
- data/lib/sproutcore/rack/proxy.rb +92 -0
- data/lib/sproutcore/rack/service.rb +141 -0
- data/lib/sproutcore/rack/test_runner.rb +24 -0
- data/lib/sproutcore/render_engines/erubis.rb +55 -0
- data/lib/sproutcore/render_engines/haml.rb +39 -0
- data/lib/sproutcore/tools.rb +311 -0
- data/lib/sproutcore/tools/build.rb +80 -0
- data/lib/sproutcore/tools/build_number.rb +23 -0
- data/lib/sproutcore/tools/gen.rb +99 -0
- data/lib/sproutcore/tools/init.rb +40 -0
- data/lib/sproutcore/tools/manifest.rb +84 -0
- data/lib/sproutcore/tools/server.rb +45 -0
- data/spec/buildtasks/build/copy_spec.rb +60 -0
- data/spec/buildtasks/build/spec_helper.rb +36 -0
- data/spec/buildtasks/manifest/catalog_spec.rb +48 -0
- data/spec/buildtasks/manifest/hide_buildfiles_spec.rb +125 -0
- data/spec/buildtasks/manifest/localize_spec.rb +97 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +246 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/css_spec.rb +87 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/html_spec.rb +175 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/javascript_spec.rb +65 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/minify_spec.rb +70 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +152 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/sass_spec.rb +98 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/strings_spec.rb +64 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/tests_spec.rb +163 -0
- data/spec/buildtasks/manifest/prepare_spec.rb +43 -0
- data/spec/buildtasks/manifest/spec_helper.rb +35 -0
- data/spec/buildtasks/target_spec.rb +214 -0
- data/spec/fixtures/builder_tests/Buildfile +15 -0
- data/spec/fixtures/builder_tests/apps/combine_test/a.js +1 -0
- data/spec/fixtures/builder_tests/apps/combine_test/b.js +1 -0
- data/spec/fixtures/builder_tests/apps/combine_test/c.js +1 -0
- data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/a.css +1 -0
- data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/b.css +1 -0
- data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/c.css +1 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/bar1_sample.rhtml +2 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/erb_sample.html.erb +1 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/icons/image.png +0 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/image.jpg +0 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/rhtml_sample.rhtml +1 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/strings.js +4 -0
- data/spec/fixtures/builder_tests/apps/html_test/english.lproj/style.css +0 -0
- data/spec/fixtures/builder_tests/apps/html_test/french.lproj/french-icons/fr.png +0 -0
- data/spec/fixtures/builder_tests/apps/html_test/french.lproj/strings.js +4 -0
- data/spec/fixtures/builder_tests/apps/html_test/lib/layout_template.rhtml +1 -0
- data/spec/fixtures/builder_tests/apps/html_test/scripts.js +0 -0
- data/spec/fixtures/builder_tests/apps/javascript_test/sc_static.js +15 -0
- data/spec/fixtures/builder_tests/apps/javascript_test/sc_super.js +4 -0
- data/spec/fixtures/builder_tests/apps/javascript_test/strings.js +7 -0
- data/spec/fixtures/builder_tests/apps/sass_test/sample.sass +3 -0
- data/spec/fixtures/builder_tests/apps/strings_test/lproj/strings.js +8 -0
- data/spec/fixtures/builder_tests/apps/stylesheet_test/build_directives.css +9 -0
- data/spec/fixtures/builder_tests/apps/stylesheet_test/sc_static.css +12 -0
- data/spec/fixtures/builder_tests/apps/test_test/lib/alt_layout.rhtml +1 -0
- data/spec/fixtures/builder_tests/apps/test_test/lib/test_layout.rhtml +3 -0
- data/spec/fixtures/builder_tests/apps/test_test/tests/qunit_test.js +1 -0
- data/spec/fixtures/builder_tests/apps/test_test/tests/qunit_test2.js +1 -0
- data/spec/fixtures/builder_tests/apps/test_test/tests/rhtml_test.rhtml +4 -0
- data/spec/fixtures/builder_tests/frameworks/debug/core.js +0 -0
- data/spec/fixtures/builder_tests/frameworks/debug/english.lproj/dummy.css +0 -0
- data/spec/fixtures/builder_tests/frameworks/qunit/core.js +0 -0
- data/spec/fixtures/builder_tests/frameworks/qunit/english.lproj/dummy.css +0 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/req_style_1.css +0 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/strings.js +4 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/test.rhtml +1 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_1/req_js_1.js +0 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_2/english.lproj/req_style_2.css +0 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_2/english.lproj/test.rhtml +1 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_2/javascript.js +1 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_2/lib/alt_layout.rhtml +0 -0
- data/spec/fixtures/builder_tests/frameworks/req_target_2/req_js_2.js +0 -0
- data/spec/fixtures/builder_tests/themes/sample_theme/Buildfile +1 -0
- data/spec/fixtures/buildfiles/basic/Buildfile +16 -0
- data/spec/fixtures/buildfiles/basic/task_module.rake +6 -0
- data/spec/fixtures/buildfiles/installed/Buildfile +7 -0
- data/spec/fixtures/buildfiles/installed/Buildfile2 +5 -0
- data/spec/fixtures/buildfiles/project_test/Buildfile +4 -0
- data/spec/fixtures/buildfiles/project_test/not_project/Buildfile +2 -0
- data/spec/fixtures/buildfiles/project_test/not_project/child/PLACEHOLDER +0 -0
- data/spec/fixtures/entry_for_project/Buildfile +1 -0
- data/spec/fixtures/entry_for_project/apps/test_app/entry.txt +0 -0
- data/spec/fixtures/entry_for_project/apps/test_app/frameworks/nested/PLACEHOLDER +0 -0
- data/spec/fixtures/entry_for_project/frameworks/shared/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/Buildfile +8 -0
- data/spec/fixtures/find_targets/custom/bars/bar1/bars/bar1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/bars/bar1/bars/bar2/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/bars/bar1/foos/foo1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/bars/bar1/foos/foo2/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/foos/custom_foos/Buildfile +5 -0
- data/spec/fixtures/find_targets/custom/foos/custom_foos/custom_foodir/foo1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/foos/custom_foos/custom_foodir/foo2/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/foos/custom_foos/foos/not_foo1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/foos/foo1/bars/bar1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/custom/foos/foo1/bars/bar2/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/nested/Buildfile +8 -0
- data/spec/fixtures/find_targets/nested/apps/app1/Buildfile +1 -0
- data/spec/fixtures/find_targets/nested/apps/app1/apps/nested_app/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/Apps/app1/frameworks/framework1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/Apps/app1/frameworks/framework2/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/clients/client1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/frameworks/framework1/frameworks/framework1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/frameworks/framework2/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/themes/theme1/PLACEHOLDER +0 -0
- data/spec/fixtures/find_targets/standard/themes/theme2/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/caps_long_names/English.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/caps_long_names/FreNCH.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/caps_long_names/UnknOWN.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/english.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/french.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/german.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/italian.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/japanese.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/spanish.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/long_names/unknown.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/no_names/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/de.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/en-CA.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/en-GB.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/en-US.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/en.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/es.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/foo.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/fr.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/it.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/languages/apps/short_names/ja.lproj/PLACEHOLDER +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/1.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/B.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/a.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/a/a.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/a/b.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/b/a.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/c.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/core.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/B.css +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a.css +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a/a.css +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a/b.css +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/b/a.css +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/c.css +0 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/lproj/strings.js +1 -0
- data/spec/fixtures/ordered_entries/apps/no_requires/utils.js +1 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/a.js +2 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/b.js +3 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/c.js +2 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/a.css +2 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/b.css +2 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/c.css +2 -0
- data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/d.js +1 -0
- data/spec/fixtures/real_world/Buildfile +12 -0
- data/spec/fixtures/real_world/apps/account/README +1 -0
- data/spec/fixtures/real_world/apps/calendar/README +1 -0
- data/spec/fixtures/real_world/apps/contacts/README_BEFORE_EDITING +1 -0
- data/spec/fixtures/real_world/apps/files/README +1 -0
- data/spec/fixtures/real_world/apps/mail/README +1 -0
- data/spec/fixtures/real_world/apps/mobile_photos/README +1 -0
- data/spec/fixtures/real_world/apps/photos/README +1 -0
- data/spec/fixtures/real_world/apps/uploader/README +1 -0
- data/spec/fixtures/real_world/frameworks/core_files/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/core_photos/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/shared/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/Buildfile +26 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/README +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/apps/docs/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/apps/test_runner/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/core.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/debug/debug-resource.html +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/debug/sample_debug.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/demo2.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/debug/sample_debug-loc.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo.css +4 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo.html +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo2.sass +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.haml +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.html.erb +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.rhtml +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/fixtures/sample_fixtures-loc.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/has_require.css +4 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/no_require.css +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/no_sc_resource.rhtml +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/protocols/sample-loc.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/sc_resource.css +6 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/sc_resource.rhtml +3 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/strings.js +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/tests/sample-loc.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/fixtures/sample-json-fixture.json +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/fixtures/sample_fixtures.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/application/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/costello/core.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/data_store/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/debug/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/desktop/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/empty_theme/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/foundation/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/mobile/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/qunit/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/uploader/PLACEHOLDER +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/french.lproj/french-resource.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/french.lproj/strings.js +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/german.lproj/german-resource.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/german.lproj/strings.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/has_require.js +4 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/lib/index.html +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/no_require.js +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/protocols/sample.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/sc_resource.js +6 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/tests/nested/sample1.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/tests/nested/sample2.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/tests/sample.js +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/tests/sample.rhtml +1 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/themes/standard_theme/README +0 -0
- data/spec/fixtures/real_world/frameworks/sproutcore/views/view.js +1 -0
- data/spec/fixtures/real_world/generators/sample_custom/Buildfile +0 -0
- data/spec/fixtures/real_world/generators/sample_custom/templates/{filename}.js +1 -0
- data/spec/fixtures/recursive_project/Buildfile +8 -0
- data/spec/fixtures/recursive_project/frameworks/sproutcore/frameworks/costello/PLACEHOLDER +0 -0
- data/spec/lib/builders/combine_spec.rb +67 -0
- data/spec/lib/builders/html_spec.rb +577 -0
- data/spec/lib/builders/javascript_spec.rb +81 -0
- data/spec/lib/builders/sass_spec.rb +43 -0
- data/spec/lib/builders/spec_helper.rb +30 -0
- data/spec/lib/builders/strings_spec.rb +52 -0
- data/spec/lib/builders/stylesheet_spec.rb +63 -0
- data/spec/lib/builders/test_index_spec.rb +44 -0
- data/spec/lib/builders/test_spec.rb +135 -0
- data/spec/lib/buildfile/config_for_spec.rb +81 -0
- data/spec/lib/buildfile/define_spec.rb +59 -0
- data/spec/lib/buildfile/dup_spec.rb +65 -0
- data/spec/lib/buildfile/invoke_spec.rb +130 -0
- data/spec/lib/buildfile/load_spec.rb +49 -0
- data/spec/lib/buildfile/task/dup_spec.rb +55 -0
- data/spec/lib/buildfile/task_defined_spec.rb +17 -0
- data/spec/lib/buildfile_commands/build_task_spec.rb +19 -0
- data/spec/lib/buildfile_commands/config_spec.rb +97 -0
- data/spec/lib/buildfile_commands/import_spec.rb +17 -0
- data/spec/lib/buildfile_commands/namespace_spec.rb +18 -0
- data/spec/lib/buildfile_commands/proxies_spec.rb +38 -0
- data/spec/lib/buildfile_commands/replace_task_spec.rb +29 -0
- data/spec/lib/buildfile_commands/task_spec.rb +36 -0
- data/spec/lib/helpers/packing_optimizer/optimize_spec.rb +26 -0
- data/spec/lib/models/hash_struct/deep_clone_spec.rb +27 -0
- data/spec/lib/models/hash_struct/has_options_spec.rb +32 -0
- data/spec/lib/models/hash_struct/hash_spec.rb +64 -0
- data/spec/lib/models/hash_struct/merge_spec.rb +26 -0
- data/spec/lib/models/hash_struct/method_missing.rb +41 -0
- data/spec/lib/models/manifest/add_entry_spec.rb +36 -0
- data/spec/lib/models/manifest/add_transform_spec.rb +90 -0
- data/spec/lib/models/manifest/build_spec.rb +78 -0
- data/spec/lib/models/manifest/entry_for_spec.rb +94 -0
- data/spec/lib/models/manifest/find_entry.rb +84 -0
- data/spec/lib/models/manifest/prepare_spec.rb +62 -0
- data/spec/lib/models/manifest_entry/cacheable_url_spec.rb +31 -0
- data/spec/lib/models/manifest_entry/prepare_spec.rb +54 -0
- data/spec/lib/models/project/add_target_spec.rb +44 -0
- data/spec/lib/models/project/buildfile_spec.rb +35 -0
- data/spec/lib/models/project/find_targets_for_spec.rb +77 -0
- data/spec/lib/models/project/load_nearest_project_spec.rb +23 -0
- data/spec/lib/models/project/target_for_spec.rb +33 -0
- data/spec/lib/models/project/targets_spec.rb +62 -0
- data/spec/lib/models/target/compute_build_number_spec.rb +125 -0
- data/spec/lib/models/target/config_spec.rb +30 -0
- data/spec/lib/models/target/expand_required_targets_spec.rb +48 -0
- data/spec/lib/models/target/installed_languages_spec.rb +47 -0
- data/spec/lib/models/target/lproj_for_spec.rb +38 -0
- data/spec/lib/models/target/manifest_for_spec.rb +42 -0
- data/spec/lib/models/target/parent_target_spec.rb +21 -0
- data/spec/lib/models/target/prepare_spec.rb +53 -0
- data/spec/lib/models/target/required_targets_spec.rb +119 -0
- data/spec/lib/models/target/target_for_spec.rb +56 -0
- data/spec/lib/tools/build_number_spec.rb +28 -0
- data/spec/lib/tools/gen_spec.rb +207 -0
- data/spec/lib/tools/tools_spec.rb +78 -0
- data/spec/spec_helper.rb +138 -0
- data/sproutcore.gemspec +47 -0
- data/vendor/github_gem_lint.rb +22 -0
- data/vendor/yui-compressor/yuicompressor-2.4.2.jar +0 -0
- 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
|