diff --git a/bundles/ruby-on-rails.tmbundle/.gitignore b/bundles/ruby-on-rails.tmbundle/.gitignore new file mode 100644 index 000000000..8d7880ee6 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/.gitignore @@ -0,0 +1,3 @@ +config/*.yml +website/dist +.DS_Store diff --git a/bundles/ruby-on-rails.tmbundle/ABOUT b/bundles/ruby-on-rails.tmbundle/ABOUT new file mode 100644 index 000000000..341ea0409 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/ABOUT @@ -0,0 +1,36 @@ +== Rails Bundle 1.0 -> 2.0 SOON +This bundle is (c) 2006 syncPEOPLE, LLC. +As of version 1.0, it is distributed under the terms of the MIT License. + +http://syncpeople.com/ - We develop social interaction tools for online and real-world conferences and events. + +== Maintained by == +Dr Nic Williams +drnicwilliams@gmail.com +http://drnicwilliams.com + +== Created by == +Duane Johnson +duane.johnson@gmail.com +http://blog.inquirylabs.com/ + +== Contributors (no doubt missing many) == +Sami Samhuri +James Edward Grey II +Michael Sheets +Allan Odgaard +Cliff Matthews +Kent Siblev +Stephen Touset +Lawrence Pit +Dean Strelau +Daniel Kristensen +Simon Jefford +James Deville +Stephen Bannasch +Tom Morris +David Lowenfels +Sam Granieri +Jacob Swanner +Steve Ross +Charles Roper \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/CHANGELOG b/bundles/ruby-on-rails.tmbundle/CHANGELOG new file mode 100644 index 000000000..bc9c44101 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/CHANGELOG @@ -0,0 +1,109 @@ +[Edge] Trundle to Rails 2.0 Bundle +Ruby on Rails bundle maintenance has been taken over by Dr Nic Williams from its initial legendary creator Duane Johnson. +See git branch two_point_ooh for initial drive to upgrade bundle for Rails 2.0 conventions (http://github.com/drnic/ruby-on-rails-tmbundle/tree/two_point_ooh) + +Added so far: + * Snippets/Commands for: + * Tests + * assert_select (ass) + * assert_difference/assert_no_difference (asd/asnd) + * GET+POST test method stubs (deftg+deftp) + * posts(:) + Alt+Esc allows you to select a posts.yml fixture + * Controllers + * respond_to (rest) and respond_to(html) (Shift+Cmd+H) + * 'Go To View' within a respond_to will use the format/wants type as the default + * REMOVED: render_component snippets + * loggers - pass a block instead of raw string to save time if + logging not used (e.g. production) [Stephen Touset] + * redirect_to for resource paths (rep, repp, renp, renpp) + * render :update (ru) [Simon Jefford] + * Views + * form_for and form_for(with errors) (ff,ffe) + * Various form_for helpers in a drop-down list, e.g. f.text_field (f.) + * Various form_for helpers with own tab completion, e.g. f.text_field (fftf) + * link_to for resource paths (lip, lipp, linp, linpp) + * <% end -%> (end) + * for-loop (for) + * link_to(@model.name, @model) (ltm) + * Layouts + * javascript_include_tag - jit + * stylesheet_link_tag - slt + * Models + * has_many :through (hmt) + * association snippets give better defaults (e.g. bt + hm) + * validates_format_of (vf,vfif) [Dean Strelau] + * validates_numericality_of (vn,vnif) [Tom Morris] + * before/after callbacks (prefix bef/aft + 1st letter of words) [Sam Granieri] + * Migrations + * Sexy Migrations now available as "t." snippets and regular snippets [Lawrence Pit] + * Migration classes have own textmate scope + * Add/Remove Columns - the 'down' statements are in reverse + order [Lawrence Pit, Daniel Kristensen] + * Added 'Redo Last Migration' [Simon Jefford] + * Functional Tests + * assert_redirect_to for resource paths (artp, artpp, artnp, artnpp) + * assert(assigns(:model)) (asg) + * Routes + * named routes, resources snippets (map, mapr, maprs) + * catch_all (mapca) [Sam Granieri] + * Active Support + * cattr_accessor/mattr_accessor (crw/mrw) [Jacob Swanner] + * returning (returning) [Jacob Swanner] + * Language/Syntax + * New keywords: rescue_from, alias_method_chain, alias_attribute, delegate, respond_to [David Lowenfels, Jacob Swanner] + * Added rb as a valid Rails file type [James Deville] + * Commands + * Auto-completion for Foxy Fixtures + * html.erb is the default for new templates (backwards compatibility + is being worked on too) + * 'Go To XXX' - available for wide range of permutations. + Fixtures -> Models; Models -> Controllers, etc + * haml is a valid file extension [Steve Ross] + * specify alternate default file extensions for: [Steve Ross] + * javascript - ENV['RAILS_JS_EXT'] + * stylesheet - ENV['RAILS_CSS_EXT'] + * view - ENV['RAILS_VIEW_EXT'] + * 'Call Generate Script' now accesses all a project's generators + * Plugins + * Footnote + * footnote-edge uses .erb for templates [Stephen Bannasch] + * Internals + * Rakefile - test runner [Steve Ross] + * Added generator_test + * Removed unnecessary misc_test + * script/clean_bundle_file_names - rename non-os-agnostic file names [Charles Roper] + * Haml support [Lawrence Pit] + +For thoughts and patches, email drnicwilliams@gmail.com + +* BLACKHOLE OF CHANGELOG * +... + +* END OF BLACKHOLE * +2006-09-28 Added "Rake Migrate to Version", changed "rake migrate" to "rake db:migrate" to remove warning message. +2006-02-22 Changed key bindings to TextMate conventions (http://macromates.com/textmate/manual/key_bindings#conventions) +2006-02-22 Footnotes can be turned off for specific pages: + e.g. render :action => 'whatever', :footnotes => false [Duane] +2006-02-22 intelligent go to file now asks for a view file name if it doesn't exist before going there [Duane] +2006-02-22 Fixed that intelligent go to file was not recognizing view files other than rhtml [Duane] +2006-02-21 Generated files open automatically [Duane, Sami Samhuri] +2006-02-21 Generate migrations works (has to be chdir to rails_root to work) [Duane] +2006-02-21 Fixed that the footnotes plugin was doubling the /controllers/ segment [Duane, Cliff Matthews] +2006-02-21 Fixed that the footnotes plugin was rendering for rxml and rjs pages [Duane] + +0.9.1 Released + +2006-02-21 Fixed that the footnotes plugin was breaking on redirects [Duane] + +0.9 Released + +2006-02-20 Added Migration Snippets [Sami Samhuri] +2006-02-20 Added Rails Engines plugin [Duane] +2006-02-20 Added double quotes around call to CocoaDialog [Kent Siblev] +2006-02-20 Intelligent Go To File will now go to the file nearest the caret for stylesheet and javascript tags with multiple files [Cliff Matthews] +2006-02-20 Added plugin install script bound to ctrl-option-command-\ [Duane] +2006-02-20 Added 'plugins' directory to Support, and included textmate_footnotes plugin. [Duane] +2006-02-20 More intelligent argument regexps for javascripts and stylesheets [Cliff Matthews] +2006-02-20 Generate command is now a background process [Duane] + +0.8 Released \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Capfile b/bundles/ruby-on-rails.tmbundle/Capfile new file mode 100644 index 000000000..c36d48d21 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Capfile @@ -0,0 +1,3 @@ +load 'deploy' if respond_to?(:namespace) # cap2 differentiator +Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) } +load 'config/deploy' \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Autocomplete Foreign Key Fixture Reference (habtm).tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Autocomplete Foreign Key Fixture Reference (habtm).tmCommand new file mode 100644 index 000000000..91226b6d9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Autocomplete Foreign Key Fixture Reference (habtm).tmCommand @@ -0,0 +1,25 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" "${TM_BUNDLE_SUPPORT}/bin/fixture_auto_complete.rb" preserve + fallbackInput + line + input + selection + keyEquivalent + ~$ + name + Autocomplete Foreign Key Fixture Reference (habtm) + output + insertAsSnippet + scope + source.yaml + uuid + 275C0B86-F735-49B6-8A22-218A8F4CC2E0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Autocomplete Foreign Key Fixture Reference.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Autocomplete Foreign Key Fixture Reference.tmCommand new file mode 100644 index 000000000..885e3ae88 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Autocomplete Foreign Key Fixture Reference.tmCommand @@ -0,0 +1,25 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/fixture_auto_complete.rb" + fallbackInput + line + input + selection + keyEquivalent + ~ + name + Autocomplete Foreign Key Fixture Reference + output + insertAsSnippet + scope + source.yaml, meta.rails.unit_test, meta.rails.functional_test + uuid + 0BCF0EE2-35EE-4959-A771-E74D55271D5A + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Clone Development DB to Test DB.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Clone Development DB to Test DB.tmCommand new file mode 100644 index 000000000..254a9a6f5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Clone Development DB to Test DB.tmCommand @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:test:clone + + input + none + keyEquivalent + ^| + name + Clone Development DB to Test DB + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 6F2AB859-46E3-4FF5-A9A7-E9A813AB5DE1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/DB Schema Dump.plist b/bundles/ruby-on-rails.tmbundle/Commands/DB Schema Dump.plist new file mode 100644 index 000000000..b1f115057 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/DB Schema Dump.plist @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:schema:dump + + input + none + keyEquivalent + ^| + name + Dump DB to schema.rb + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 310C901C-EF32-4E88-938A-804ABBF8C428 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/DB Schema Import.plist b/bundles/ruby-on-rails.tmbundle/Commands/DB Schema Import.plist new file mode 100644 index 000000000..5ef3bf494 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/DB Schema Import.plist @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:schema:load + + input + none + keyEquivalent + ^| + name + Load schema.rb to DB + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 6DEF923E-2347-46EC-AFBE-183D08E63DC1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Generate Quick Migration.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Generate Quick Migration.tmCommand new file mode 100644 index 000000000..82cd82399 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Generate Quick Migration.tmCommand @@ -0,0 +1,25 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/generate_quick_migration.rb" + fallbackInput + word + input + none + keyEquivalent + ^M + name + Quick Migration + output + discard + scope + source.ruby.rails, source.yaml, text.html.ruby + uuid + D696FA2C-785A-4B73-A2F6-F750904DD7C2 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Generate.plist b/bundles/ruby-on-rails.tmbundle/Commands/Generate.plist new file mode 100644 index 000000000..da6b64f7b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Generate.plist @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/generate.rb" &>/dev/null & + + input + none + keyEquivalent + ^| + name + Call Generate Script + output + discard + scope + source.ruby.rails, source.yaml + uuid + 4904EDC7-5ED3-4132-AAB2-C2AD87C97EFE + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Alternate File.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To Alternate File.plist new file mode 100644 index 000000000..4752c78e5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Alternate File.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" controller + input + none + keyEquivalent + ~$@ + name + Go to Controller + output + showAsTooltip + scope + text.html.ruby, source.ruby.rails.embedded.html, meta.rails.helper, meta.rails.functional_test, source.js, source.css, source.yaml, meta.rails.model, meta.rails.unit_test, text.haml + uuid + 9453F0B3-B946-445F-BDB0-B01DE70732FC + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To File on This Line.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To File on This Line.plist new file mode 100644 index 000000000..1d1cdf0fb --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To File on This Line.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_file_on_current_line.rb" + input + none + keyEquivalent + ~@ + name + File on Current Line + output + showAsTooltip + scope + source.ruby.rails, text.html.ruby, source.ruby.rails.embedded.html, text.haml + uuid + 09BB96F2-75FD-48A7-8314-B5B56B09B477 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To File.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To File.plist new file mode 100644 index 000000000..dbbe3f4b8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To File.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + saveActiveFile + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" + input + none + keyEquivalent + ~@ + name + Alternate File + output + showAsTooltip + scope + source.ruby.rails, text.html.ruby, source.ruby.rails.embedded.html, source.yaml, text.haml, source.css, source.js, source.sass + uuid + 0CCC8443-40F3-4BAB-9440-D737562B5F45 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Functional Test.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To Functional Test.plist new file mode 100644 index 000000000..70a3ad76f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Functional Test.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" functional_test + input + none + keyEquivalent + ~$@ + name + Go to Functional Test + output + showAsTooltip + scope + meta.rails.controller, meta.rails.helper, meta.rails.model, source.yaml, meta.rails.unit_test + uuid + DFE393BE-0764-49FE-B464-6350A50921E6 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Helper.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To Helper.plist new file mode 100644 index 000000000..8f9de159a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Helper.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" helper + input + none + keyEquivalent + ~$@ + name + Go to Helper + output + showAsTooltip + scope + text.html.ruby, source.ruby.rails.embedded.html, meta.rails.controller, meta.rails.model, meta.rails.unit_test, meta.rails.functional_test, text.haml + uuid + 51C9C27A-D931-49F9-B6D8-C0E7ABEC992D + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Javascript.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To Javascript.plist new file mode 100644 index 000000000..11e28b633 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Javascript.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" javascript + input + none + keyEquivalent + ~$@ + name + Go to Javascript + output + showAsTooltip + scope + text.html.ruby, source.ruby.rails.embedded.html, meta.rails.controller, meta.rails.helper, text.haml + uuid + B078346F-61D8-4E75-9427-80720FBC67F7 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Model.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Go To Model.tmCommand new file mode 100644 index 000000000..14cc7a96d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Model.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" model + input + none + keyEquivalent + ~$@ + name + Go to Model + output + showAsTooltip + scope + text.html.ruby, source.ruby.rails.embedded.html, meta.rails.helper, meta.rails.unit_test, source.js, source.css, source.yaml, meta.rails.controller, meta.rails.functional_test, text.haml + uuid + C7151BF3-7068-4344-9B09-86F3BF4A9C63 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Stylesheet.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To Stylesheet.plist new file mode 100644 index 000000000..c635608b0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Stylesheet.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" stylesheet + input + none + keyEquivalent + ~$@ + name + Go to Stylesheet + output + showAsTooltip + scope + text.html.ruby, source.ruby.rails.embedded.html, meta.rails.controller, meta.rails.helper, text.haml + uuid + B207BBD4-D6AA-41E9-9530-27210F2D7B66 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To Unit Test.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To Unit Test.plist new file mode 100644 index 000000000..55cd62f2f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To Unit Test.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" unit_test + input + none + keyEquivalent + ~$@ + name + Go to Unit Test + output + showAsTooltip + scope + meta.rails.controller, meta.rails.helper, meta.rails.model, meta.rails.functional_test, source.yaml + uuid + BDBB15A4-2824-4BEC-93A5-7475F9C46A39 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go To View.plist b/bundles/ruby-on-rails.tmbundle/Commands/Go To View.plist new file mode 100644 index 000000000..76ddc6f9e --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go To View.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" view + input + none + keyEquivalent + ~$@ + name + Go to View + output + showAsTooltip + scope + meta.rails.controller, meta.rails.mailer, source.js, source.css + uuid + EE862691-A624-4797-90CF-EDD39EFB2D8E + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Go to Fixture.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Go to Fixture.tmCommand new file mode 100644 index 000000000..f7235f48e --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Go to Fixture.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/go_to_alternate_file.rb" fixture + input + none + keyEquivalent + ~$@ + name + Go to Fixture + output + showAsTooltip + scope + meta.rails.controller, meta.rails.helper, meta.rails.model, meta.rails.unit_test, meta.rails.functional_test + uuid + 638D94A4-BDFC-4FE9-8909-9934F3FD2899 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Install Bundled Plugin.plist b/bundles/ruby-on-rails.tmbundle/Commands/Install Bundled Plugin.plist new file mode 100644 index 000000000..a0dd47da2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Install Bundled Plugin.plist @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/list_plugins.rb" + input + none + keyEquivalent + ^| + name + Install Plugin + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 46ECE243-0448-4A64-A223-27CC21E7704D + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Load Fixtures (Test DB).tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Load Fixtures (Test DB).tmCommand new file mode 100644 index 000000000..ad2f54449 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Load Fixtures (Test DB).tmCommand @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:fixtures:load -v RAILS_ENV -a test + + input + none + keyEquivalent + ^| + name + Load Fixtures (Test DB) + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + F758BFD1-00CA-4742-BE71-032580080F5C + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Load Fixtures.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Load Fixtures.tmCommand new file mode 100644 index 000000000..38e248146 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Load Fixtures.tmCommand @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:fixtures:load + + input + none + keyEquivalent + ^| + name + Load Fixtures (Development DB) + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 5EEA0C71-B34B-4408-953B-F47AAD343CCC + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Make Selection in to Partial.plist b/bundles/ruby-on-rails.tmbundle/Commands/Make Selection in to Partial.plist new file mode 100644 index 000000000..0d9d2230b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Make Selection in to Partial.plist @@ -0,0 +1,26 @@ + + + + + beforeRunningCommand + saveModifiedFiles + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/create_partial_from_selection.rb" + + fallbackInput + line + input + selection + keyEquivalent + ^H + name + Create Partial From Selection + output + replaceSelectedText + scope + source.ruby.rails, text.html.ruby, text.haml + uuid + 1DD8A214-1C97-45BA-ADEE-8F888DDE8570 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Migrate to Previous Version.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Migrate to Previous Version.tmCommand new file mode 100644 index 000000000..eebc5b41a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Migrate to Previous Version.tmCommand @@ -0,0 +1,29 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" + +# Find the previous version number from the schema.rb file +export PREVIOUS=`grep 'Schema\.define' "$TM_PROJECT_DIRECTORY/db/schema.rb" | ruby -e 'print $stdin.read.scan(/\d+/).first.to_i - 1'` + +# Migrate database to the previous version +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:migrate -v VERSION -a $PREVIOUS + + input + none + keyEquivalent + ^| + name + Migrate to Previous Version + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 9A1AE6BA-8350-4AB7-B5BD-969A7E64CF29 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Rake Migrate to Version.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Rake Migrate to Version.tmCommand new file mode 100644 index 000000000..523171498 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Rake Migrate to Version.tmCommand @@ -0,0 +1,28 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" + +# Find the current version number from the schema.rb file +export CURRENT=`grep 'Schema\.define' "$TM_PROJECT_DIRECTORY/db/schema.rb" | ruby -e 'print $stdin.read.scan(/\d+/).first'` + +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:migrate -q "Migrate to which version? (Current version: $CURRENT)" -v VERSION + + input + none + keyEquivalent + ^| + name + Migrate to Version ... + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 07C696F8-79F5-4E0B-9EE9-03B693A54ABB + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Rake Migrate.plist b/bundles/ruby-on-rails.tmbundle/Commands/Rake Migrate.plist new file mode 100644 index 000000000..dda96db82 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Rake Migrate.plist @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:migrate + + input + none + keyEquivalent + ^| + name + Migrate to Current + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 985F56D4-82ED-4C45-8250-2ECCFC71957E + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Redo Last Migration.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Redo Last Migration.tmCommand new file mode 100644 index 000000000..04ea67a7b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Redo Last Migration.tmCommand @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" db:migrate:redo + + input + none + keyEquivalent + ^| + name + Redo Last Migration + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + CFDA9F62-D071-4E0F-AD10-66AE0729FFCF + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Show DB Schema.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Show DB Schema.tmCommand new file mode 100644 index 000000000..345b00a9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Show DB Schema.tmCommand @@ -0,0 +1,24 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/show_schema.rb" + + input + none + keyEquivalent + ^@S + name + Show DB Schema for Current Class + output + showAsTooltip + scope + source.ruby.rails + uuid + 1970AE74-3949-40B3-B263-727AA3FF167A + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test All.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test All.tmCommand new file mode 100644 index 000000000..cf7084c3c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test All.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test + input + none + keyEquivalent + ^\ + name + Test All + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + DC549A45-D9B0-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test Functionals.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test Functionals.tmCommand new file mode 100644 index 000000000..19bbec320 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test Functionals.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test:functionals + input + none + keyEquivalent + ^\ + name + Test Functionals + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + F4EA552D-D9B0-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test Integration.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test Integration.tmCommand new file mode 100644 index 000000000..64143d0e7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test Integration.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test:integration + input + none + keyEquivalent + ^\ + name + Test Integration + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 04A30A4D-D9B1-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test Plugins.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test Plugins.tmCommand new file mode 100644 index 000000000..9cbe5f2aa --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test Plugins.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test:plugins + input + none + keyEquivalent + ^\ + name + Test Plugins + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 0D966168-D9B1-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test Recent.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test Recent.tmCommand new file mode 100644 index 000000000..cb0e0e5f3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test Recent.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test:recent + input + none + keyEquivalent + ^\ + name + Test Recent + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 190401C2-D9B1-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test Uncommitted.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test Uncommitted.tmCommand new file mode 100644 index 000000000..c9b58f158 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test Uncommitted.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test:uncommitted + input + none + keyEquivalent + ^\ + name + Test Uncommitted + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 212C3047-D9B1-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/Test Units.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/Test Units.tmCommand new file mode 100644 index 000000000..f77a38ed9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/Test Units.tmCommand @@ -0,0 +1,23 @@ + + + + + beforeRunningCommand + nop + command + RUBYLIB="$TM_BUNDLE_SUPPORT/lib:$RUBYLIB" +"${TM_RUBY:=ruby}" -- "${TM_BUNDLE_SUPPORT}/bin/rake_helper.rb" test:units + input + none + keyEquivalent + ^\ + name + Test Units + output + showAsHTML + scope + source.ruby.rails, source.yaml + uuid + 2C60CBA1-D9B1-11DC-94E9-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Commands/View demo help.tmCommand b/bundles/ruby-on-rails.tmbundle/Commands/View demo help.tmCommand new file mode 100644 index 000000000..c05c29c67 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Commands/View demo help.tmCommand @@ -0,0 +1,27 @@ + + + + + beforeRunningCommand + nop + command + . "$TM_SUPPORT_PATH/lib/webpreview.sh" +html_header "Ruby on Rails Bundle Help" "Ruby on Rails" +cat "$TM_BUNDLE_SUPPORT/../website/demo.html" +html_footer + + + input + none + keyEquivalent + ^h + name + View demo help + output + showAsHTML + scope + source.ruby.rails + uuid + 964436B8-E578-11DC-8177-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/DragCommands/Require Rails File.tmDragCommand b/bundles/ruby-on-rails.tmbundle/DragCommands/Require Rails File.tmDragCommand new file mode 100644 index 000000000..72494dd01 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/DragCommands/Require Rails File.tmDragCommand @@ -0,0 +1,41 @@ + + + + + beforeRunningCommand + nop + command + #!/usr/bin/env ruby + +file_path = ENV['TM_DROPPED_FILE'].gsub(/\.rb$/, '') +full_file_path = File.expand_path(file_path) +relative_path = nil +known_load_path = %w[app/controllers app/models app/helpers app config lib] +known_load_path.each do |path| + if full_file_path =~ %r{^#{File.join(ENV['TM_PROJECT_DIRECTORY'], path)}/(.*)} + relative_path = $1 + break + end +end +if relative_path + puts "require '#{relative_path}'" +else + puts "require File.dirname(__FILE__) + '/#{file_path}'" +end + + draggedFileExtensions + + rb + + input + selection + name + Require Rails File + output + insertAsSnippet + scope + source.ruby.rails + uuid + 56151E60-589F-4CE7-8062-3E087732E2F1 + + diff --git a/bundles/ruby-on-rails.tmbundle/LICENSE b/bundles/ruby-on-rails.tmbundle/LICENSE new file mode 100644 index 000000000..ad7b671f5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2006 syncPEOPLE, LLC. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Column.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Column.tmMacro new file mode 100644 index 000000000..5b9fb7152 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Column.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb add_remove_column + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Add / Remove Column + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mcol + uuid + 18C76913-061C-4D65-866D-67AA3724AFEF + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Index.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Index.tmMacro new file mode 100644 index 000000000..e52e0de03 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Index.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb add_remove_index + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Add / Remove Index + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mind + uuid + 95F83E1D-5B03-424F-8BEC-8AF66C8939BC + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Named Index.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Named Index.tmMacro new file mode 100644 index 000000000..4fab72985 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Named Index.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb add_remove_named_index + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Add / Remove Named Index + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mind + uuid + A7F692C1-778A-48B8-945E-573568BA0403 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Several Columns (marcc).tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Several Columns (marcc).tmMacro new file mode 100644 index 000000000..81cdce6f1 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Several Columns (marcc).tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb add_remove_column_continue + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Add / Remove Several Columns (marcc) + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + marcc + uuid + 27A6C58A-896B-4956-BA81-D671A2EF9C7D + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Several Columns.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Several Columns.tmMacro new file mode 100644 index 000000000..ed6781461 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Several Columns.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb add_remove_column_continue + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Add / Remove Several Columns + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mcol + uuid + 7BC860E6-7561-4E6E-983B-507D7A6F6228 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Unique Index.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Unique Index.tmMacro new file mode 100644 index 000000000..e41228046 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Add %3A Remove Unique Index.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb add_remove_unique_index + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Add / Remove Unique Index + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mind + uuid + 33057A79-677B-4DFB-99D4-1492778BDDC6 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Change : Change Table.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Change : Change Table.tmMacro new file mode 100644 index 000000000..48238093b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Change : Change Table.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb change_change_table + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Change / Change Table + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mtab + uuid + 20FC02C5-32A3-4F20-B163-FF75C9FDFABF + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Create %3A Drop Table.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Create %3A Drop Table.tmMacro new file mode 100644 index 000000000..b8a2e24e3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Create %3A Drop Table.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb create_drop_table + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Create / Drop Table + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mtab + uuid + 25F8F5D8-2BD1-45D8-8B2A-9F2EA4F73AA2 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Drop %3A Create Table.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Drop %3A Create Table.tmMacro new file mode 100644 index 000000000..5d7dfb8d4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Drop %3A Create Table.tmMacro @@ -0,0 +1,48 @@ + + + + + commands + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + insert_add_column_or_create_table.rb + input + selection + output + replaceSelectedText + + command + executeCommandWithOptions: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfLine: + + + name + Drop / Create Table (Second Half) + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + [press tab twice to generate create_table] + uuid + A2135370-67A1-488D-B43C-B4F221127C2F + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Remove %3A Add Column.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Remove %3A Add Column.tmMacro new file mode 100644 index 000000000..195fd3c59 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Remove %3A Add Column.tmMacro @@ -0,0 +1,48 @@ + + + + + commands + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + insert_add_column_or_create_table.rb + input + selection + output + replaceSelectedText + + command + executeCommandWithOptions: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfLine: + + + name + Remove / Add Column (Second Half) + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + [press tab twice to generate add_column] + uuid + 809BCA42-5C49-4B08-B3C4-BB773036C086 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Rename %3A Rename Several Columns (mncc).tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Rename %3A Rename Several Columns (mncc).tmMacro new file mode 100644 index 000000000..d97a9c2f3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Rename %3A Rename Several Columns (mncc).tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb rename_column_continue + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Rename / Rename Several Columns (mncc) + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mncc + uuid + 04A86178-71B1-430A-A06D-DFF7C9A338B5 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Rename %3A Rename Several Columns.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Rename %3A Rename Several Columns.tmMacro new file mode 100644 index 000000000..d35cfc368 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Rename %3A Rename Several Columns.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb rename_column_continue + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Rename / Rename Several Columns + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mcol + uuid + F03162DE-9DB6-417B-9DD7-52D9F11EA736 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Rename Column.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Rename Column.tmMacro new file mode 100644 index 000000000..36cf7651b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Rename Column.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb rename_column + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Rename / Rename Column + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mcol + uuid + AC50762C-DE40-4EB9-9A22-2F6AF2EA4EA3 + + diff --git a/bundles/ruby-on-rails.tmbundle/Macros/Rename Table.tmMacro b/bundles/ruby-on-rails.tmbundle/Macros/Rename Table.tmMacro new file mode 100644 index 000000000..900da4b9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Macros/Rename Table.tmMacro @@ -0,0 +1,44 @@ + + + + + commands + + + command + deleteWordLeft: + + + command + moveToBeginningOfLine: + + + command + moveToEndOfDocumentAndModifySelection: + + + argument + + command + intelligent_migration_snippet.rb rename_table + input + selection + output + insertAsSnippet + + command + executeCommandWithOptions: + + + name + Rename / Rename Table + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + scopeType + local + tabTrigger + mtab + uuid + FD8CC811-2AD3-480F-B975-DF959DC96C67 + + diff --git a/bundles/ruby-on-rails.tmbundle/Preferences/Template (ERB).tmPreferences b/bundles/ruby-on-rails.tmbundle/Preferences/Template (ERB).tmPreferences new file mode 100644 index 000000000..613fe5663 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Preferences/Template (ERB).tmPreferences @@ -0,0 +1,48 @@ + + + + + name + Template (ERB) + scope + text.html.ruby + settings + + shellVariables + + + name + TM_RAILS_TEMPLATE_START_RUBY_EXPR + value + <%= + + + name + TM_RAILS_TEMPLATE_END_RUBY_EXPR + value + %> + + + name + TM_RAILS_TEMPLATE_START_RUBY_INLINE + value + <% + + + name + TM_RAILS_TEMPLATE_END_RUBY_INLINE + value + -%> + + + name + TM_RAILS_TEMPLATE_END_RUBY_BLOCK + value + <% end -%> + + + + uuid + 87EF33FE-E918-11DC-A399-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Preferences/Template (Haml).tmPreferences b/bundles/ruby-on-rails.tmbundle/Preferences/Template (Haml).tmPreferences new file mode 100644 index 000000000..c0ab0b814 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Preferences/Template (Haml).tmPreferences @@ -0,0 +1,48 @@ + + + + + name + Template (Haml) + scope + text.haml + settings + + shellVariables + + + name + TM_RAILS_TEMPLATE_START_RUBY_EXPR + value + = + + + name + TM_RAILS_TEMPLATE_END_RUBY_EXPR + value + + + + name + TM_RAILS_TEMPLATE_START_RUBY_INLINE + value + - + + + name + TM_RAILS_TEMPLATE_END_RUBY_INLINE + value + + + + name + TM_RAILS_TEMPLATE_END_RUBY_BLOCK + value + + + + + uuid + C0FD2646-E924-11DC-A399-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/README b/bundles/ruby-on-rails.tmbundle/README new file mode 100644 index 000000000..8993ecb1c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/README @@ -0,0 +1,38 @@ +TextMate bundle for Ruby on Rails development + +GET IT NOW: with Rails 2.0 support. + +Rails 2.0 (copied from CHANGELOG) + +* Snippets/Commands for: + * Tests + * assert_select (ass) + * assert_difference/assert_no_difference (asd/asnd) + * GET+POST test method stubs (defg+defp) + * Controllers + * respond_to (rst) + * REMOVED: render_component snippets + * loggers - pass a block instead of raw string to save time if logging not used (e.g. production) [thx Stephen Touset] + * redirect_to for resource paths (rep, repp, renp, renpp) + * render :update (ru) [thx Simon Jefford] + * Views + * form_for (ff) + * link_to for resource paths (lip, lipp, linp, linpp) + * <% end -%> (end) + * Models + * has_many :though (hmt) + * association snippets give better defaults (e.g. bt + hm) + * validates_format_of (vf,vfif) [thx Dean Strelau] + * Migrations + * Sexy Migrations now available as "t." snippets [thx Lawrence Pit] + * Migration classes have own textmate scope + * Add/Remove Columns - the 'down' statements are in reverse order [thx Lawrence Pit, Daniel Kristensen] +* Language/Syntax + * New keywords: rescue_from + * Added rb as a valid Rails file type [thx James Deville] +* Commands + * html.erb is the default for new templates (backwards compatibility is being worked on too) +* Plugins + * Footnote + * footnote-edge uses .erb for templates [thx Stephen Bannasch] + diff --git a/bundles/ruby-on-rails.tmbundle/Rakefile b/bundles/ruby-on-rails.tmbundle/Rakefile new file mode 100644 index 000000000..ecab394b6 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Rakefile @@ -0,0 +1,19 @@ +require 'rubygems' +require 'rake' +require 'rake/testtask' + +APP_VERSION="2.0.0" +APP_NAME='Ruby on Rails.tmbundle' +APP_ROOT=File.dirname(__FILE__) + +RUBY_APP='ruby' + +desc "TMBundle Test Task" +task :default => [ :test ] +Rake::TestTask.new { |t| + t.libs << "test" + t.pattern = 'Support/test/*_test.rb' + t.verbose = true + t.warning = false +} +Dir['tasks/**/*.rake'].each { |file| load file } diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/$LABEL.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/$LABEL.tmSnippet new file mode 100644 index 000000000..9016c98dc --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/$LABEL.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + \$LABEL + name + $LABEL + scope + source.yaml + tabTrigger + $L + uuid + 786980D8-FA69-4542-85A3-5E48CFAA6814 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/%3C%= Fixtures_identify(%3Asymbol) %%3E.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/%3C%= Fixtures_identify(%3Asymbol) %%3E.tmSnippet new file mode 100644 index 000000000..f171943ab --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/%3C%= Fixtures_identify(%3Asymbol) %%3E.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}Fixtures.identify(:${1:name})${TM_RAILS_TEMPLATE_END_RUBY_EXPR}$0 + name + <%= Fixtures.identify(:symbol) %> + scope + source.yaml + tabTrigger + fi + uuid + 9671EB7A-89D6-4C23-914F-88CBEE0D177A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/180 rails form_tag.plist b/bundles/ruby-on-rails.tmbundle/Snippets/180 rails form_tag.plist new file mode 100644 index 000000000..af4e443a2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/180 rails form_tag.plist @@ -0,0 +1,18 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_INLINE}form_tag(${1::action => "${5:update}"}${6:, {:${8:class} => "${9:form}"\}}) do${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + $0 +${TM_RAILS_TEMPLATE_END_RUBY_BLOCK} + name + form_tag + scope + text.html.ruby, text.haml + tabTrigger + ft + uuid + F0F6DACA-6A0B-11D9-BDC2-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create binary column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create binary column.tmSnippet new file mode 100644 index 000000000..1415d5095 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create binary column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.binary :${1:title}${2:, :limit => ${3:2}.megabytes} +$0 + name + Table column binary + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcbi + uuid + 5E9B8B0E-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create boolean column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create boolean column.tmSnippet new file mode 100644 index 000000000..80789efac --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create boolean column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.boolean :${1:title} +$0 + name + Table column boolean + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcb + uuid + 967093B4-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create controller class.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create controller class.tmSnippet new file mode 100644 index 000000000..a6889304a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create controller class.tmSnippet @@ -0,0 +1,25 @@ + + + + + content + class ${1:Model}Controller < ApplicationController + before_filter :find_${2:model} + + $0 + + private + def find_${2} + @$2 = ${3:$1}.find(params[:id]) if params[:id] + end +end + name + Create controller class + scope + source.ruby + tabTrigger + cla + uuid + 4B3F798E-E3B6-48C8-8C2F-CB8631011638 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create date column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create date column.tmSnippet new file mode 100644 index 000000000..6964d0474 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create date column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.date :${1:title} +$0 + name + Table column date + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcda + uuid + 56276686-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create datetime column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create datetime column.tmSnippet new file mode 100644 index 000000000..2e69fe4b7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create datetime column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.datetime :${1:title} +$0 + name + Table column datetime + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcdt + uuid + D6CBCA96-D52F-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create decimal column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create decimal column.tmSnippet new file mode 100644 index 000000000..82e9703e4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create decimal column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.decimal :${1:title}${2:${3:, :precision => ${4:10}}${5:, :scale => ${6:2}}} +$0 + name + Table column decimal + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcd + uuid + 93A16768-D52E-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create float column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create float column.tmSnippet new file mode 100644 index 000000000..abd80a958 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create float column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.float :${1:title} +$0 + name + Table column float + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcf + uuid + 8AF989C4-D52E-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create functional test class.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create functional test class.tmSnippet new file mode 100644 index 000000000..3ea76b2c5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create functional test class.tmSnippet @@ -0,0 +1,21 @@ + + + + + content + require File.dirname(__FILE__) + '/../test_helper' + +class ${1:Model}ControllerTest < ActionController::TestCase + deft$0 +end + + name + Create functional test class + scope + source.ruby + tabTrigger + cla + uuid + F60D0630-CBF5-4283-9D20-FA46C787A88D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create integer column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create integer column.tmSnippet new file mode 100644 index 000000000..aafb6e0ed --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create integer column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.integer :${1:title} +$0 + name + Table column integer + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tci + uuid + 729D559E-D52D-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create lock_version column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create lock_version column.tmSnippet new file mode 100644 index 000000000..362518c52 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create lock_version column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.integer :lock_version, :null => false, :default => 0 +$0 + name + Table column lock_version + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcl + uuid + FC2523C1-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create references column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create references column.tmSnippet new file mode 100644 index 000000000..85c7d6429 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create references column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.references :${1:taggable}${2:, :polymorphic => ${3:{ :default => '${4:Photo}' \}}} +$0 + name + Table column(s) references + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcr + uuid + EDA6568B-D533-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create string column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create string column.tmSnippet new file mode 100644 index 000000000..f9a589e06 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create string column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.string :${1:title} +$0 + name + Table column string + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcs + uuid + 377BF814-D52D-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create text column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create text column.tmSnippet new file mode 100644 index 000000000..d76545ad5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create text column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.text :${1:title} +$0 + name + Table column text + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tct + uuid + 6A9D4C30-D52D-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create time column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create time column.tmSnippet new file mode 100644 index 000000000..08baf008d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create time column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.time :${1:title} +$0 + name + Table column time + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcti + uuid + 4F5DDD37-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create timestamp column.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create timestamp column.tmSnippet new file mode 100644 index 000000000..4af537cc8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create timestamp column.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.timestamp :${1:title} +$0 + name + Table column timestamp + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tcts + uuid + 4600CE20-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Create timestamps columns.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Create timestamps columns.tmSnippet new file mode 100644 index 000000000..a59d2fcae --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Create timestamps columns.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.timestamps +$0 + name + Table column timestamps + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tctss + uuid + E0C8FDC4-D532-11DC-BD8E-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Migration Create Column (mcc).plist b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Create Column (mcc).plist new file mode 100644 index 000000000..8d82b60e6 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Create Column (mcc).plist @@ -0,0 +1,17 @@ + + + + + content + t.column ${1:title}, :${2:string} +$0 + name + Create Column in Table + scope + meta.rails.migration.create_table + tabTrigger + mcol + uuid + 7592CA99-75D7-48B6-9133-00B9F148FF43 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Migration Create Column Continue (mccc).plist b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Create Column Continue (mccc).plist new file mode 100644 index 000000000..cbe15cdf9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Create Column Continue (mccc).plist @@ -0,0 +1,17 @@ + + + + + content + t.column ${1:title}, :${2:string} +mccc$0 + name + Create Several Columns in Table + scope + meta.rails.migration.create_table + tabTrigger + mccc + uuid + 67FD2F8F-5F25-45F2-A451-2F39977A9EDE + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Migration Drop Create Table (mdct).plist b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Drop Create Table (mdct).plist new file mode 100644 index 000000000..465ca0c7f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Drop Create Table (mdct).plist @@ -0,0 +1,16 @@ + + + + + content + drop_table :${1:table}${2: [press tab twice to generate create_table]} + name + Drop / Create Table + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + tabTrigger + mtab + uuid + 20375601-B13F-4314-B8E4-362706566636 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Migration Remove and Add Column (mrac).plist b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Remove and Add Column (mrac).plist new file mode 100644 index 000000000..c94410f6f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Migration Remove and Add Column (mrac).plist @@ -0,0 +1,16 @@ + + + + + content + remove_column :${1:table}, :${2:column}${3: [press tab twice to generate add_column]} + name + Remove / Add Column + scope + meta.rails.migration - meta.rails.migration.create_table - meta.rails.migration.change_table + tabTrigger + mcol + uuid + 16A705EB-10DC-42B5-9FF2-377E206421DC + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/RAILS_DEFAULT_LOGGER.debug (rdb).plist b/bundles/ruby-on-rails.tmbundle/Snippets/RAILS_DEFAULT_LOGGER.debug (rdb).plist new file mode 100644 index 000000000..2e03699d7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/RAILS_DEFAULT_LOGGER.debug (rdb).plist @@ -0,0 +1,16 @@ + + + + + content + RAILS_DEFAULT_LOGGER.debug "${1:message}"$0 + name + RAILS_DEFAULT_LOGGER.debug + scope + source.ruby.rails + tabTrigger + rdb + uuid + 7B15B396-1F41-4529-9253-32761E94448C + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Table column(s) rename.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/Table column(s) rename.tmSnippet new file mode 100644 index 000000000..3b0fbb23c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Table column(s) rename.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.rename(:${1:old_column_name}, :${2:new_column_name}) +$0 + name + Table column(s) rename + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + tre + uuid + DF30226E-1111-448A-B669-7CA34EE83909 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Test Assert Redirected To (art).plist b/bundles/ruby-on-rails.tmbundle/Snippets/Test Assert Redirected To (art).plist new file mode 100644 index 000000000..f0f63e285 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Test Assert Redirected To (art).plist @@ -0,0 +1,16 @@ + + + + + content + assert_redirected_to ${2::action => "${1:index}"} + name + assert_redirected_to + scope + source.ruby.rails + tabTrigger + art + uuid + CD60F800-850D-47CF-BE32-3DE665DD5C68 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/Test Assert Response (are).plist b/bundles/ruby-on-rails.tmbundle/Snippets/Test Assert Response (are).plist new file mode 100644 index 000000000..34750af30 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/Test Assert Response (are).plist @@ -0,0 +1,16 @@ + + + + + content + assert_response :${1:success}, @response.body$0 + name + assert_response + scope + source.ruby.rails + tabTrigger + asre + uuid + 2BD82DCB-1F19-4C8F-BC70-C0BBB06A2138 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_create.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_create.tmSnippet new file mode 100644 index 000000000..d335ffec9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_create.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_create + name + after_create + scope + source.ruby.rails + tabTrigger + aftc + uuid + 279D1981-B055-4693-B9AF-5B571A62A6AE + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_destroy.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_destroy.tmSnippet new file mode 100644 index 000000000..874247ca4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_destroy.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_destroy + name + after_destroy + scope + source.ruby.rails + tabTrigger + aftd + uuid + A2F3E8C1-4216-4890-8491-2F8C7534ED03 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_save.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_save.tmSnippet new file mode 100644 index 000000000..b0e6d2db5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_save.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_save + name + after_save + scope + source.ruby.rails + tabTrigger + afts + uuid + 4D1787E3-1583-4CF3-8D99-CC45D7C35EED + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_update.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_update.tmSnippet new file mode 100644 index 000000000..a21a4707c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_update.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_update + name + after_update + scope + source.ruby.rails + tabTrigger + aftu + uuid + 0C9EA1A1-66C5-4E1C-9C30-E1FFE8EC6EAE + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_validation.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_validation.tmSnippet new file mode 100644 index 000000000..97c367ac2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_validation.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_validation + name + after_validation + scope + source.ruby.rails + tabTrigger + aftv + uuid + 44FBD811-70A9-462B-AC56-F975ADAD62AF + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_validation_on_create.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_validation_on_create.tmSnippet new file mode 100644 index 000000000..2dcf7f9a7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_validation_on_create.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_validation_on_create + name + after_validation_on_create + scope + source.ruby.rails + tabTrigger + aftvoc + uuid + BA0DE6C7-EAD3-42C9-8ABB-2B9A5F2FE225 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/after_validation_on_update.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/after_validation_on_update.tmSnippet new file mode 100644 index 000000000..7aa427152 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/after_validation_on_update.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + after_validation_on_update + name + after_validation_on_update + scope + source.ruby.rails + tabTrigger + aftvou + uuid + BCB25D36-2D3F-41E9-B2CF-37D6E883E8D1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert(var = assigns(%3Avar)).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert(var = assigns(%3Avar)).tmSnippet new file mode 100644 index 000000000..002825556 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert(var = assigns(%3Avar)).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + assert(${1:var} = assigns(:${1}), "Cannot find @${1}") +$0 + name + assert(var = assigns(:var)) + scope + source.ruby + tabTrigger + asg + uuid + FE9C4B4E-860D-49F0-AAF7-5582B98F5F54 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_difference.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_difference.tmSnippet new file mode 100644 index 000000000..ac28af8a8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_difference.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + assert_difference "${1:Model}.${2:count}", ${3:1} do + $0 +end + name + assert_difference + scope + source.ruby + tabTrigger + asd + uuid + 30BEA6FB-301C-4460-93EC-FA3404688962 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_no_difference.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_no_difference.tmSnippet new file mode 100644 index 000000000..fa7f7afa5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_no_difference.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + assert_no_difference "${1:Model}.${2:count}" do + $0 +end + name + assert_no_difference + scope + source.ruby + tabTrigger + asnd + uuid + 5C6F4462-70E6-40B4-B3F2-F371656E7784 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (nested path plural).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (nested path plural).tmSnippet new file mode 100644 index 000000000..3ed29c1b3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (nested path plural).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + assert_redirected_to ${10:${2:parent}_${3:child}_path(${4:@}${5:${2}})} + name + assert_redirected_to (nested path plural) + scope + source.ruby.rails + tabTrigger + artnpp + uuid + 4C92C020-7337-4D6E-91EE-7ABF2BFC7F41 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (nested path).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (nested path).tmSnippet new file mode 100644 index 000000000..f833cff21 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (nested path).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + assert_redirected_to ${2:${12:parent}_${13:child}_path(${14:@}${15:${12}}, ${16:@}${17:${13}})} + name + assert_redirected_to (nested path) + scope + source.ruby.rails + tabTrigger + artnp + uuid + 97021C0D-EB65-4046-B688-01F09B3B1615 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (path plural).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (path plural).tmSnippet new file mode 100644 index 000000000..1eb37a6df --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (path plural).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + assert_redirected_to ${10:${2:model}s_path} + name + assert_redirected_to (path plural) + scope + source.ruby.rails + tabTrigger + artpp + uuid + 0249637E-0720-46DA-A8FD-E176A2CC458B + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (path).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (path).tmSnippet new file mode 100644 index 000000000..7094f29f1 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_redirected_to (path).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + assert_redirected_to ${2:${12:model}_path(${13:@}${14:${12}})} + name + assert_redirected_to (path) + scope + source.ruby.rails + tabTrigger + artp + uuid + D33EDCE7-F8AF-48D4-AA7A-852BBF03E31D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_rjs.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_rjs.tmSnippet new file mode 100644 index 000000000..998b4c39a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_rjs.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + assert_rjs :${1:replace}, ${2:"${3:dom id}"} + name + assert_rjs + scope + source.ruby.rails + tabTrigger + asrj + uuid + E0F281EC-5311-41F8-ADD9-2E2D059DA651 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/assert_select.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/assert_select.tmSnippet new file mode 100644 index 000000000..053434dfe --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/assert_select.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + assert_select '${1:path}'${2:, :${3:text} => ${4:'${5:inner_html}'}}${6: do + $0 +end} + name + assert_select + scope + source.ruby.rails + tabTrigger + ass + uuid + DBE14FE8-B415-4DBC-A316-F8DA63FE9FD7 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_create.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_create.tmSnippet new file mode 100644 index 000000000..32cb0118e --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_create.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_create + name + before_create + scope + source.ruby.rails + tabTrigger + befc + uuid + D64D8863-DCB6-4397-B5B0-073E0AE04167 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_destroy.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_destroy.tmSnippet new file mode 100644 index 000000000..1c1769a7c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_destroy.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_destroy + name + before_destroy + scope + source.ruby.rails + tabTrigger + befd + uuid + 3F4B502B-5F68-4687-88E9-6EF3BDF9677D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_save.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_save.tmSnippet new file mode 100644 index 000000000..a0be84a75 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_save.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_save + name + before_save + scope + source.ruby.rails + tabTrigger + befs + uuid + 523BE8A6-0845-493D-A9B6-532F73D21950 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_update.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_update.tmSnippet new file mode 100644 index 000000000..e44a4c50a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_update.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_update + name + before_update + scope + source.ruby.rails + tabTrigger + befu + uuid + 1C20EEBE-B4BA-48C8-9B33-7B5BB00D958C + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_validation.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_validation.tmSnippet new file mode 100644 index 000000000..44c18afe7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_validation.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_validation + name + before_validation + scope + source.ruby.rails + tabTrigger + befv + uuid + A1776279-5396-4FE9-9218-8BF2C88C5271 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_validation_on_create.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_validation_on_create.tmSnippet new file mode 100644 index 000000000..32fb67107 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_validation_on_create.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_validation_on_create + name + before_validation_on_create + scope + source.ruby.rails + tabTrigger + befvoc + uuid + E2CE2E3B-8A61-4866-9AF5-A12F44CF7233 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/before_validation_on_update.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/before_validation_on_update.tmSnippet new file mode 100644 index 000000000..9f384da43 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/before_validation_on_update.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + before_validation_on_update + name + before_validation_on_update + scope + source.ruby.rails + tabTrigger + befvou + uuid + 86CFB156-E72B-440F-9C7D-08A3375C3ADB + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/belongs_to (bt).plist b/bundles/ruby-on-rails.tmbundle/Snippets/belongs_to (bt).plist new file mode 100644 index 000000000..fe00c1b7b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/belongs_to (bt).plist @@ -0,0 +1,16 @@ + + + + + content + belongs_to :${1:object}${2:, :class_name => "${3:${1/[[:alpha:]]+|(_)/(?1::\u$0)/g}}", :foreign_key => "${4:${1}_id}"} + name + belongs_to + scope + source.ruby.rails + tabTrigger + bt + uuid + B8F08BD7-6160-482C-8A3D-CBC6BD2079A4 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/cattr_accessor.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/cattr_accessor.tmSnippet new file mode 100644 index 000000000..80ba8f98c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/cattr_accessor.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + cattr_accessor :${0:attr_names} + name + cattr_accessor + scope + source.ruby.rails + tabTrigger + crw + uuid + F57522B2-9F5F-4DF9-AE46-9478AF019C63 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/def create - resource.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/def create - resource.tmSnippet new file mode 100644 index 000000000..d608ed3ae --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/def create - resource.tmSnippet @@ -0,0 +1,30 @@ + + + + + content + def create + @${1:model} = ${2:${1/[[:alpha:]]+|(_)/(?1::\u$0)/g}}.new(params[:$1]) + $0 + respond_to do |wants| + if @$1.save + flash[:notice] = '$2 was successfully created.' + wants.html { redirect_to(@$1) } + wants.xml { render :xml => @$1, :status => :created, :location => @$1 } + else + wants.html { render :action => "new" } + wants.xml { render :xml => @$1.errors, :status => :unprocessable_entity } + end + end +end + + name + def create - resource + scope + meta.rails.controller + tabTrigger + defcreate + uuid + 54F61419-001F-4B71-83AC-8DC633694AF0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/def get request.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/def get request.tmSnippet new file mode 100644 index 000000000..6da35a463 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/def get request.tmSnippet @@ -0,0 +1,21 @@ + + + + + content + def test_should_get_${1:action} + ${2:@${3:model} = ${4:$3s}(:${5:fixture_name}) + }get :${1}${6:, :id => @$3.to_param} + assert_response :success + $0 +end + name + def test_should_get_action + scope + meta.rails.functional_test + tabTrigger + deftg + uuid + 1C491A76-751F-44EF-8DFB-0A585C7EEFF6 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/def post request.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/def post request.tmSnippet new file mode 100644 index 000000000..bfaf1af0c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/def post request.tmSnippet @@ -0,0 +1,21 @@ + + + + + content + def test_should_post_${1:action} + ${3:@$2 = ${4:$2s}(:${5:fixture_name}) + }post :${1}${6:, :id => @$2.to_param}, :${2:model} => { $0 } + assert_response :redirect + +end + name + def test_should_post_action + scope + meta.rails.functional_test + tabTrigger + deftp + uuid + 8B9CD068-4338-4039-AA06-D839A6C7A9FF + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/end.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/end.tmSnippet new file mode 100644 index 000000000..7d4f6f908 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/end.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + <% end -%> + name + end (ERB) + scope + text.html.ruby + tabTrigger + end + uuid + AC385ABF-96CD-4FCB-80AD-BF37D6EE79D2 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/find(%3Aall).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/find(%3Aall).tmSnippet new file mode 100644 index 000000000..877330cd6 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/find(%3Aall).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + find(:all${1:, :conditions => ['${2:${3:field} = ?}', ${5:true}]}) + name + find(:all) + scope + source.ruby.rails + tabTrigger + fina + uuid + A017AB39-A875-40DC-8ACF-7E3551057CA0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/find(%3Afirst).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/find(%3Afirst).tmSnippet new file mode 100644 index 000000000..3bf5c483b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/find(%3Afirst).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + find(:first${1:, :conditions => ['${2:${3:field} = ?}', ${5:true}]}) + name + find(:first) + scope + source.ruby.rails + tabTrigger + finf + uuid + FE430ECD-5D40-4D95-A73B-F064C73992DE + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/find(id).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/find(id).tmSnippet new file mode 100644 index 000000000..9719cbd39 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/find(id).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + find(${1:id}) + name + find(id) + scope + source.ruby.rails + tabTrigger + fini + uuid + 59CD3A41-8164-4FB4-B462-D7ACE86BCDBF + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/for loop erb.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/for loop erb.tmSnippet new file mode 100644 index 000000000..8fcdd1fd7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/for loop erb.tmSnippet @@ -0,0 +1,23 @@ + + + + + content + <% if !${1:list}.blank? %> + <% for ${2:item} in ${1} %> + $3 + <% end %> +<% else %> + $4 +<% end %> + + name + for loop in rhtml + scope + text.html.ruby + tabTrigger + for + uuid + F7744F07-306C-4951-AB5A-3D69BA5516B7 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for check_box.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for check_box.tmSnippet new file mode 100644 index 000000000..7de811baa --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for check_box.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.check_box :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for check_box + scope + text.html.ruby, text.haml + tabTrigger + ffcb + uuid + F0DB6886-4FFE-45BA-907F-44326AD8142D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for checkbox.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for checkbox.tmSnippet new file mode 100644 index 000000000..c10e72a3d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for checkbox.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.check_box :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.check_box (ffcb) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + F579F9E7-E072-4BCC-BFF9-C8C5BAE7FFA5 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for file_field 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for file_field 2.tmSnippet new file mode 100644 index 000000000..845f0c713 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for file_field 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.file_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for file_field + scope + text.html.ruby, text.haml + tabTrigger + ffff + uuid + C8BA285D-E12E-4AB8-A941-514C963E8226 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for file_field.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for file_field.tmSnippet new file mode 100644 index 000000000..5f74f9ab5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for file_field.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.file_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.file_field (ffff) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + 79BC2303-3D9D-4E21-AF85-73B388B7B56D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for hidden_field 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for hidden_field 2.tmSnippet new file mode 100644 index 000000000..f02c3e1c2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for hidden_field 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.hidden_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for hidden_field + scope + text.html.ruby, text.haml + tabTrigger + ffhf + uuid + 99FEFD9B-5A07-46E3-950D-5C474E42B695 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for hidden_field.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for hidden_field.tmSnippet new file mode 100644 index 000000000..ed8136a8c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for hidden_field.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.hidden_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.hidden_field (ffhf) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + 5DBA8F72-DD6C-4CBF-83FD-76301E159BA9 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for label 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for label 2.tmSnippet new file mode 100644 index 000000000..38a7eb97a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for label 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.label :${1:attribute}${2:, "${3:${1/[[:alpha:]]+|(_)/(?1: :\u$0)/g}}"}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for label + scope + text.html.ruby, text.haml + tabTrigger + ffl + uuid + B31822D9-2048-4D16-B2AF-00E0B4E5C368 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for label.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for label.tmSnippet new file mode 100644 index 000000000..96d17eb29 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for label.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.label :${1:attribute}${2:, "${3:${1/[[:alpha:]]+|(_)/(?1: :\u$0)/g}}"}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.label (ffl) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + 402C251E-595B-4A58-8EB9-41989040F280 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for password_field 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for password_field 2.tmSnippet new file mode 100644 index 000000000..94c30b651 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for password_field 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.password_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for password_field + scope + text.html.ruby, text.haml + tabTrigger + ffpf + uuid + 3379FB35-C664-4255-96C6-6E4B91F12759 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for password_field.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for password_field.tmSnippet new file mode 100644 index 000000000..0b143c956 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for password_field.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.password_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.password_field (ffpf) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + 42289456-C8D1-498C-AE30-5206544B349F + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for radio_box 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for radio_box 2.tmSnippet new file mode 100644 index 000000000..383776b09 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for radio_box 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.radio_box :${1:attribute}, :${2:tag_value}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for radio_box + scope + text.html.ruby, text.haml + tabTrigger + ffrb + uuid + D4282CE1-4171-4B13-9220-3F2718BC2505 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for radio_box.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for radio_box.tmSnippet new file mode 100644 index 000000000..19a1a3769 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for radio_box.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.radio_box :${1:attribute}, :${2:tag_value}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.radio_box (ffrb) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + A95358D2-C68A-4894-8C36-062C9F45848A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for submit 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for submit 2.tmSnippet new file mode 100644 index 000000000..115b56d1a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for submit 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.submit "${1:Submit}"${2:, :disable_with => '${3:$1ing...}'}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for submit + scope + text.html.ruby, text.haml + tabTrigger + ffs + uuid + 3000E569-4E19-4566-B08E-A3FFFAAC9075 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for submit.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for submit.tmSnippet new file mode 100644 index 000000000..8d3ba11e4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for submit.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.submit "${1:Submit}"${2:, :disable_with => '${3:$1ing...}'}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.submit (ffs) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + C315EC5D-A7F3-49CB-9795-21B78BB42FF4 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_area 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_area 2.tmSnippet new file mode 100644 index 000000000..1169dbd76 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_area 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.text_area :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for text_area + scope + text.html.ruby, text.haml + tabTrigger + ffta + uuid + 4C898FA8-D09C-4B28-BE42-14BB4EA4E2B1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_area.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_area.tmSnippet new file mode 100644 index 000000000..74a4b06fa --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_area.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.text_area :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.text_area (ffta) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + 06498926-F84D-466C-8736-B8A0AC586A94 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_field 2.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_field 2.tmSnippet new file mode 100644 index 000000000..1d8643bea --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_field 2.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.text_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + form_for text_field + scope + text.html.ruby, text.haml + tabTrigger + fftf + uuid + F46EE8EE-239C-46D7-980B-3F861B7D9111 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_field.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_field.tmSnippet new file mode 100644 index 000000000..5d97b057f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for text_field.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}f.text_field :${1:attribute}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + f.text_field (fftf) + scope + text.html.ruby, text.haml + tabTrigger + f. + uuid + CC1BCD1C-2479-4335-B511-17B880316A75 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for with errors.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for with errors.tmSnippet new file mode 100644 index 000000000..45d2a624b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for with errors.tmSnippet @@ -0,0 +1,20 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}error_messages_for :${1:model}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + +${TM_RAILS_TEMPLATE_START_RUBY_INLINE}form_for @${2:$1} do |f|${TM_RAILS_TEMPLATE_END_RUBY_INLINE} + $0 +${TM_RAILS_TEMPLATE_END_RUBY_BLOCK} + name + form_for with errors + scope + text.html.ruby, text.haml + tabTrigger + ffe + uuid + 15BDD7B6-5C15-4684-93C7-A05E3D2221AC + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/form_for.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/form_for.tmSnippet new file mode 100644 index 000000000..24f1b0789 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/form_for.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_INLINE}form_for @${1:model} do |f|${TM_RAILS_TEMPLATE_END_RUBY_INLINE} + $0 +${TM_RAILS_TEMPLATE_END_RUBY_BLOCK} + name + form_for + scope + text.html.ruby, text.haml + tabTrigger + ff + uuid + 7D99041D-C3B7-4940-AE64-6B1758CDB47C + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/has_and_belongs_to_many (habtm).plist b/bundles/ruby-on-rails.tmbundle/Snippets/has_and_belongs_to_many (habtm).plist new file mode 100644 index 000000000..15eab69f4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/has_and_belongs_to_many (habtm).plist @@ -0,0 +1,16 @@ + + + + + content + has_and_belongs_to_many :${1:object}${2:, :join_table => "${3:table_name}", :foreign_key => "${4:${1}_id}"} + name + has_and_belongs_to_many + scope + source.ruby.rails + tabTrigger + habtm + uuid + 2AC3AC1F-743B-4A33-863C-C37885073806 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/has_many (hm).plist b/bundles/ruby-on-rails.tmbundle/Snippets/has_many (hm).plist new file mode 100644 index 000000000..d782dc2fc --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/has_many (hm).plist @@ -0,0 +1,16 @@ + + + + + content + has_many :${1:object}s${2:, :class_name => "${1}", :foreign_key => "${4:reference}_id"} + name + has_many + scope + source.ruby.rails + tabTrigger + hm + uuid + F396B7BD-8255-48B1-904A-06E7D7CC2741 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/has_many (through).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/has_many (through).tmSnippet new file mode 100644 index 000000000..709246fe9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/has_many (through).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + has_many :${1:objects}, :through => :${2:join_association}${3:, :source => :${4:${2}_table_foreign_key_to_${1}_table}} + name + has_many (through) + scope + source.ruby.rails + tabTrigger + hmt + uuid + 9D58B6C9-BA52-48B3-B639-D5CB894AF810 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/has_many :dependent => :destroy.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/has_many :dependent => :destroy.tmSnippet new file mode 100644 index 000000000..d4b0b8470 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/has_many :dependent => :destroy.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + has_many :${1:object}s${2:, :class_name => "${1}", :foreign_key => "${4:reference}_id"}, :dependent => :destroy$0 + name + has_many :dependent => :destroy + scope + source.ruby.rails + tabTrigger + hmd + uuid + 3E3AF538-171B-4108-AB92-827AD7E24C77 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/has_one (ho).plist b/bundles/ruby-on-rails.tmbundle/Snippets/has_one (ho).plist new file mode 100644 index 000000000..50e153bbc --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/has_one (ho).plist @@ -0,0 +1,16 @@ + + + + + content + has_one :${1:object}${2:, :class_name => "${3:${1/[[:alpha:]]+|(_)/(?1::\u$0)/g}}", :foreign_key => "${4:${1}_id}"} + name + has_one + scope + source.ruby.rails + tabTrigger + ho + uuid + BD2E4045-54E6-450E-B31B-5E1865CFFBC9 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/image_submit_tag.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/image_submit_tag.tmSnippet new file mode 100644 index 000000000..7bf4aebb7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/image_submit_tag.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}image_submit_tag("${1:agree.png}"${2:${3:, :id => "${4:${1/^(\w+)(\.\w*)?$/$1/}}"}${5:, :name => "${6:${1/^(\w+)(\.\w*)?$/$1/}}"}${7:, :class => "${8:${1/^(\w+)(\.\w*)?$/$1/}-button}"}${9:, :disabled => ${10:false}}})${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + image_submit_tag + scope + text.html.ruby, text.haml + tabTrigger + ist + uuid + 9FB9848E-EA5A-11DC-9DE5-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/javascript_include_tag.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/javascript_include_tag.tmSnippet new file mode 100644 index 000000000..8fccf0e4b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/javascript_include_tag.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}javascript_include_tag ${1::all}${2:, :cache => ${3:true}}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + javascript_include_tag + scope + text.html.ruby + tabTrigger + jit + uuid + FEF49C86-9386-405E-A191-684D1C963E3A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/lia.plist b/bundles/ruby-on-rails.tmbundle/Snippets/lia.plist new file mode 100644 index 000000000..d37084585 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/lia.plist @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to "${1:link text...}", :action => "${2:index}"${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (action) + scope + text.html.ruby, text.haml + tabTrigger + lia + uuid + 9E2B42FE-7BC8-11D9-906A-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/liai.plist b/bundles/ruby-on-rails.tmbundle/Snippets/liai.plist new file mode 100644 index 000000000..810a76b36 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/liai.plist @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to "${1:link text...}", :action => "${2:edit}", :id => ${3:@item}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (action, id) + scope + text.html.ruby, text.haml + tabTrigger + liai + uuid + B4F952F4-7BC8-11D9-906A-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/lic.plist b/bundles/ruby-on-rails.tmbundle/Snippets/lic.plist new file mode 100644 index 000000000..053a24975 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/lic.plist @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to "${1:link text...}", :controller => "${2:items}"${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (controller) + scope + text.html.ruby, text.haml + tabTrigger + lic + uuid + 74590E16-7BCB-11D9-906A-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/lica.plist b/bundles/ruby-on-rails.tmbundle/Snippets/lica.plist new file mode 100644 index 000000000..6f72cd678 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/lica.plist @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to "${1:link text...}", :controller => "${2:items}", :action => "${3:index}"${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (controller, action) + scope + text.html.ruby, text.haml + tabTrigger + lica + uuid + C11C0BF5-7BC8-11D9-906A-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/licai.plist b/bundles/ruby-on-rails.tmbundle/Snippets/licai.plist new file mode 100644 index 000000000..440fc5ca9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/licai.plist @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to "${1:link text...}", :controller => "${2:items}", :action => "${3:edit}", :id => ${4:@item}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (controller, action, id) + scope + text.html.ruby, text.haml + tabTrigger + licai + uuid + D21BE958-7BC8-11D9-906A-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/link_to (nested path plural).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (nested path plural).tmSnippet new file mode 100644 index 000000000..7d0e59517 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (nested path plural).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to ${1:"${2:link text...}"}, ${3:${10:parent}_${11:child}_path(${12:@}${13:${10}})}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (nested path plural) + scope + text.html.ruby, text.haml + tabTrigger + linpp + uuid + 866AAD87-E458-4F2D-9E7C-3CE73EFC047B + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/link_to (nested path).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (nested path).tmSnippet new file mode 100644 index 000000000..19a0ca5d4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (nested path).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to ${1:"${2:link text...}"}, ${3:${12:parent}_${13:child}_path(${14:@}${15:${12}}, ${16:@}${17:${13}})}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (nested path) + scope + text.html.ruby, text.haml + tabTrigger + linp + uuid + 750DEEF9-18A0-40FC-8E54-574CE5EE5565 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/link_to (path plural).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (path plural).tmSnippet new file mode 100644 index 000000000..c51b8d8b2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (path plural).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to ${1:"${2:link text...}"}, ${3:${4:model}s_path}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (path plural) + scope + text.html.ruby, text.haml + tabTrigger + lipp + uuid + 6BA737F0-63D1-4D82-9381-4331E18B12C5 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/link_to (path).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (path).tmSnippet new file mode 100644 index 000000000..fbb80ef82 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/link_to (path).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to ${1:"${2:link text...}"}, ${3:${12:model}_path(${13:@}${14:${12}})}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to (path) + scope + text.html.ruby, text.haml + tabTrigger + lip + uuid + 326B57A7-B4A9-447B-A3D2-0EA74158E1E1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/link_to model.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/link_to model.tmSnippet new file mode 100644 index 000000000..ccd67294b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/link_to model.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}link_to ${1:model}.${2:name}, ${3:${4:$1}_path(${14:$1})}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + link_to model + scope + text.html.ruby, text.haml + tabTrigger + lim + uuid + E5E08AA0-4EDD-4583-BF07-5D6C49E98410 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/logger_debug.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/logger_debug.tmSnippet new file mode 100644 index 000000000..2e76fe07f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/logger_debug.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + logger.debug { "${1:message}" }$0 + name + logger.debug + scope + source.ruby.rails + tabTrigger + logd + uuid + D975E5C1-42C2-40F1-8960-0DA533B18113 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/logger_error.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/logger_error.tmSnippet new file mode 100644 index 000000000..7f88e74f0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/logger_error.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + logger.error { "${1:message}" }$0 + name + logger.error + scope + source.ruby.rails + tabTrigger + loge + uuid + 7053B86A-9B81-4154-AB3C-61B8035C5D33 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/logger_fatal.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/logger_fatal.tmSnippet new file mode 100644 index 000000000..c5dd89fbd --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/logger_fatal.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + logger.fatal { "${1:message}" }$0 + name + logger.fatal + scope + source.ruby.rails + tabTrigger + logf + uuid + 35E95C81-22F7-4C40-8297-ED21086DDA81 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/logger_info.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/logger_info.tmSnippet new file mode 100644 index 000000000..7f6949d68 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/logger_info.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + logger.info { "${1:message}" }$0 + name + logger.info + scope + source.ruby.rails + tabTrigger + logi + uuid + 36E2A3EE-E9CC-4B7F-A4CF-AFAF970B8699 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/logger_warn.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/logger_warn.tmSnippet new file mode 100644 index 000000000..e3a5357ed --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/logger_warn.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + logger.warn { "${1:message}" }$0 + name + logger.warn + scope + source.ruby.rails + tabTrigger + logw + uuid + 38D5CA05-E219-4399-A244-609AF40B1D0B + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/map(&%3Asym_proc).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/map(&%3Asym_proc).tmSnippet new file mode 100644 index 000000000..4cd94e4e3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/map(&%3Asym_proc).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + map(&:${1:id}) + name + map(&:sym_proc) + scope + source.ruby.rails + tabTrigger + mp + uuid + EC605540-C431-4FD0-AD91-D913118DACA7 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/map_catch_all.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/map_catch_all.tmSnippet new file mode 100644 index 000000000..2e925829c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/map_catch_all.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + ${1:map}.catch_all "*${2:anything}", :controller => "${3:default}", :action => "${4:error}" + + name + map.catch_all + scope + meta.rails.routes + tabTrigger + mapca + uuid + F3606586-F905-4A91-92CA-82319239221D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/map_named_route.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/map_named_route.tmSnippet new file mode 100644 index 000000000..334621960 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/map_named_route.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${1:map}.${2:connect} '${3::controller/:action/:id}' + name + map.named_route + scope + meta.rails.routes + tabTrigger + map + uuid + 91C543BF-7BD8-4E3A-B493-AE572C5472A0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/map_resource.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/map_resource.tmSnippet new file mode 100644 index 000000000..82728ad1f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/map_resource.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + ${1:map}.resource :${2:resource}${10: do |${11:$2}| + $0 +end} + name + map.resource + scope + meta.rails.routes + tabTrigger + mapr + uuid + 2183A9A9-17ED-4A4F-ABB6-668EDDD3A6E4 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/map_resources.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/map_resources.tmSnippet new file mode 100644 index 000000000..51920457d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/map_resources.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + ${1:map}.resources :${2:resource}${10: do |${11:$2}| + $0 +end} + name + map.resources + scope + meta.rails.routes + tabTrigger + maprs + uuid + 0FF86C46-0E01-4D03-8232-72CA5BD55706 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/map_with_options.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/map_with_options.tmSnippet new file mode 100644 index 000000000..8e6b11277 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/map_with_options.tmSnippet @@ -0,0 +1,19 @@ + + + + + content + ${1:map}.with_options :${2:controller} => '${3:thing}' do |${4:$3}| + $0 +end + + name + map.with_options + scope + meta.rails.routes + tabTrigger + mapwo + uuid + BD4B90F7-2187-4E75-BFFB-77BE67CB8DAE + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/mattr_accessor.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/mattr_accessor.tmSnippet new file mode 100644 index 000000000..fcf73879f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/mattr_accessor.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + mattr_accessor :${0:attr_names} + name + mattr_accessor + scope + source.ruby.rails + tabTrigger + mrw + uuid + B25B7560-FACB-4A9E-A226-B71C796BD1F3 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/named_scope lambda.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/named_scope lambda.tmSnippet new file mode 100644 index 000000000..bc5329cb8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/named_scope lambda.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + named_scope :name, lambda { |${1:param}| { :conditions => ${3:['${4:${5:field} = ?}', ${6:$1}]} } } + + name + named_scope lambda + scope + source.ruby.rails + tabTrigger + ncl + uuid + 4E286CB4-069E-474C-A970-95216FE7DE95 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/named_scope.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/named_scope.tmSnippet new file mode 100644 index 000000000..111f77d1b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/named_scope.tmSnippet @@ -0,0 +1,17 @@ + + + + + content + named_scope :name${1:, :joins => :${2:table}}, :conditions => ${3:['${4:${5:field} = ?}', ${6:true}]} + + name + named_scope + scope + source.ruby.rails + tabTrigger + nc + uuid + 1CB65A0D-4FEC-4438-9B4F-8B0BD13FB875 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_hide (%2Aids).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_hide (%2Aids).tmSnippet new file mode 100644 index 000000000..338cd3862 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_hide (%2Aids).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.hide ${1:"${2:id(s)}"} + name + page.hide (*ids) + scope + source.ruby.rails.rjs + tabTrigger + hide + uuid + 390A447F-0FA3-4F01-A10C-4F35675E0A43 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_insert_html (position, id, partial).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_insert_html (position, id, partial).tmSnippet new file mode 100644 index 000000000..fdbd61495 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_insert_html (position, id, partial).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.insert_html :${1:top}, ${2:"${3:id}"}, :${4:partial => "${5:template}"} + name + page.insert_html (position, id, partial) + scope + source.ruby.rails.rjs + tabTrigger + ins + uuid + 62BEA590-F4EF-4001-B661-764EDFB92811 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_replace (id, partial).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_replace (id, partial).tmSnippet new file mode 100644 index 000000000..856a9fa5a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_replace (id, partial).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.replace ${1:"${2:id}"}, :${3:partial => "${4:template}"} + name + page.replace (id, partial) + scope + source.ruby.rails.rjs + tabTrigger + rep + uuid + 273E5E76-8D13-4476-9C38-8AF87432CB96 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_replace_html (id, partial).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_replace_html (id, partial).tmSnippet new file mode 100644 index 000000000..a4ec87bef --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_replace_html (id, partial).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.replace_html ${1:"${2:id}"}, :${3:partial => "${4:template}"} + name + page.replace_html (id, partial) + scope + source.ruby.rails.rjs + tabTrigger + reph + uuid + 8B914165-9C66-4FA3-9AD6-1DA41B25F8F1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_show (%2Aids).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_show (%2Aids).tmSnippet new file mode 100644 index 000000000..873be2d0c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_show (%2Aids).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.show ${1:"${2:id(s)}"} + name + page.show (*ids) + scope + source.ruby.rails.rjs + tabTrigger + show + uuid + 5ACBF49D-B5A5-495C-89D8-18AA740D9D02 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_toggle (%2Aids).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_toggle (%2Aids).tmSnippet new file mode 100644 index 000000000..d6ae50117 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_toggle (%2Aids).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.toggle ${1:"${2:id(s)}"} + name + page.toggle (*ids) + scope + source.ruby.rails.rjs + tabTrigger + tog + uuid + 028DA0A4-B310-4BEF-8643-2A22993C21C7 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/page_visual_effect (effect, id).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/page_visual_effect (effect, id).tmSnippet new file mode 100644 index 000000000..4ca7af504 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/page_visual_effect (effect, id).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + page.visual_effect :${1:toggle_slide}, ${2:"${3:DOM ID}"} + name + page.visual_effect (effect, id) + scope + source.ruby.rails.rjs + tabTrigger + vis + uuid + CFDC27A3-58CF-4198-8F93-36360978F0D0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/rails flash.plist b/bundles/ruby-on-rails.tmbundle/Snippets/rails flash.plist new file mode 100644 index 000000000..f2ce527b3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/rails flash.plist @@ -0,0 +1,16 @@ + + + + + content + flash[:${1:notice}] = "${2:Successfully created...}"$0 + name + flash[…] + scope + source.ruby.rails + tabTrigger + flash + uuid + D864896E-8763-11D9-897C-000393CBCE2E + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/rails params.plist b/bundles/ruby-on-rails.tmbundle/Snippets/rails params.plist new file mode 100644 index 000000000..6556cc99f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/rails params.plist @@ -0,0 +1,16 @@ + + + + + content + params[:${1:id}] + keyEquivalent + ^p + name + params[…] + scope + source.ruby.rails + uuid + AC8EDA3E-875B-11D9-897C-000393CBCE2E + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/rails session.plist b/bundles/ruby-on-rails.tmbundle/Snippets/rails session.plist new file mode 100644 index 000000000..29e233e88 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/rails session.plist @@ -0,0 +1,16 @@ + + + + + content + session[:${1:user}] + keyEquivalent + ^j + name + session[…] + scope + source.ruby.rails + uuid + 7B02ABF8-8763-11D9-897C-000393CBCE2E + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/rea.plist b/bundles/ruby-on-rails.tmbundle/Snippets/rea.plist new file mode 100644 index 000000000..a057013b3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/rea.plist @@ -0,0 +1,16 @@ + + + + + content + redirect_to :action => "${1:index}" + name + redirect_to (action) + scope + source.ruby.rails + tabTrigger + rea + uuid + F2F3167C-73B9-11D9-B752-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/reai.plist b/bundles/ruby-on-rails.tmbundle/Snippets/reai.plist new file mode 100644 index 000000000..be95edbdf --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/reai.plist @@ -0,0 +1,16 @@ + + + + + content + redirect_to :action => "${1:show}", :id => ${0:@item} + name + redirect_to (action, id) + scope + source.ruby.rails + tabTrigger + reai + uuid + 2233B484-73BA-11D9-B752-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/rec.plist b/bundles/ruby-on-rails.tmbundle/Snippets/rec.plist new file mode 100644 index 000000000..e766697c7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/rec.plist @@ -0,0 +1,16 @@ + + + + + content + redirect_to :controller => "${1:items}" + name + redirect_to (controller) + scope + source.ruby.rails + tabTrigger + rec + uuid + 053490FE-73BA-11D9-B752-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/reca.plist b/bundles/ruby-on-rails.tmbundle/Snippets/reca.plist new file mode 100644 index 000000000..c647a1916 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/reca.plist @@ -0,0 +1,16 @@ + + + + + content + redirect_to :controller => "${1:items}", :action => "${2:list}" + name + redirect_to (controller, action) + scope + source.ruby.rails + tabTrigger + reca + uuid + 0C137FBF-73BA-11D9-B752-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/recai.plist b/bundles/ruby-on-rails.tmbundle/Snippets/recai.plist new file mode 100644 index 000000000..d0922ab5c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/recai.plist @@ -0,0 +1,16 @@ + + + + + content + redirect_to :controller => "${1:items}", :action => "${2:show}", :id => ${0:@item} + name + redirect_to (controller, action, id) + scope + source.ruby.rails + tabTrigger + recai + uuid + 18D3C1C3-73BA-11D9-B752-000D932CD5BA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (nested path plural).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (nested path plural).tmSnippet new file mode 100644 index 000000000..28e2ccd64 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (nested path plural).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + redirect_to(${2:${10:parent}_${11:child}_path(${12:@}${13:${10}})}) + name + redirect_to (nested path plural) + scope + source.ruby.rails + tabTrigger + renpp + uuid + EF527A27-D1D4-4FD8-BD23-71397881C29A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (nested path).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (nested path).tmSnippet new file mode 100644 index 000000000..09312a71d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (nested path).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + redirect_to(${2:${12:parent}_${13:child}_path(${14:@}${15:${12}}, ${16:@}${17:${13}})}) + name + redirect_to (nested path) + scope + source.ruby.rails + tabTrigger + renp + uuid + 9D7228B3-A6ED-4598-B096-032B3600864F + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (path plural).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (path plural).tmSnippet new file mode 100644 index 000000000..65bd83e62 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (path plural).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + redirect_to(${2:${10:model}s_path}) + name + redirect_to (path plural) + scope + source.ruby.rails + tabTrigger + repp + uuid + AFE06B67-CE98-42A6-93D1-8EC8E3B9F83C + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (path).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (path).tmSnippet new file mode 100644 index 000000000..f6bfdd0a0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/redirect_to (path).tmSnippet @@ -0,0 +1,16 @@ + + + + + content + redirect_to(${2:${12:model}_path(${13:@}${14:${12}})}) + name + redirect_to (path) + scope + source.ruby.rails + tabTrigger + rep + uuid + A909C4C3-8EFE-4E39-9D96-BA8F0ABE6085 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (action)... (ra).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (action)... (ra).plist new file mode 100644 index 000000000..1b50d3f47 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (action)... (ra).plist @@ -0,0 +1,16 @@ + + + + + content + render :action => "${1:action}" + name + render (action) + scope + source.ruby.rails + tabTrigger + ra + uuid + 7B03D38B-7580-41AC-BC2B-3766AB074A43 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (action,layout) (ral).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (action,layout) (ral).plist new file mode 100644 index 000000000..b48d6b23b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (action,layout) (ral).plist @@ -0,0 +1,16 @@ + + + + + content + render :action => "${1:action}", :layout => "${2:layoutname}" + name + render (action, layout) + scope + source.ruby.rails + tabTrigger + ral + uuid + 053F1D6A-B413-43FF-B697-E3120FD0489F + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (file) (rf).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (file) (rf).plist new file mode 100644 index 000000000..7ef855488 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (file) (rf).plist @@ -0,0 +1,16 @@ + + + + + content + render :file => "${1:filepath}" + name + render (file) + scope + source.ruby.rails + tabTrigger + rf + uuid + 7D43B0EA-2C3C-499B-9346-A8E48CBF29CD + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (file,use_full_path) (rfu).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (file,use_full_path) (rfu).plist new file mode 100644 index 000000000..2630b619c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (file,use_full_path) (rfu).plist @@ -0,0 +1,16 @@ + + + + + content + render :file => "${1:filepath}", :use_full_path => ${2:false} + name + render (file, use_full_path) + scope + source.ruby.rails + tabTrigger + rfu + uuid + 2A8FBE48-E196-4019-AE76-BF3ED4B54F47 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (inline) (ri).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (inline) (ri).plist new file mode 100644 index 000000000..1b262bfbc --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (inline) (ri).plist @@ -0,0 +1,16 @@ + + + + + content + render :inline => "${1:<%= 'hello' %>}" + name + render (inline) + scope + source.ruby.rails + tabTrigger + ri + uuid + 64E93A71-6E62-48D9-9694-123080AE6723 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (inline,locals) (ril).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (inline,locals) (ril).plist new file mode 100644 index 000000000..c519e2335 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (inline,locals) (ril).plist @@ -0,0 +1,16 @@ + + + + + content + render :inline => "${1:<%= 'hello' %>}", :locals => { ${2::name} => "${3:value}"$4 } + name + render (inline, locals) + scope + source.ruby.rails + tabTrigger + ril + uuid + 1E5DE984-510C-4992-8AD5-C5FA6D7F2A88 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (inline,type) (rit).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (inline,type) (rit).plist new file mode 100644 index 000000000..a61bf21aa --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (inline,type) (rit).plist @@ -0,0 +1,16 @@ + + + + + content + render :inline => "${1:<%= 'hello' %>}", :type => ${2::rxml} + name + render (inline, type) + scope + source.ruby.rails + tabTrigger + rit + uuid + A8AF8B90-94E8-42E1-8057-DDBA57809F6A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (layout) (rl).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (layout) (rl).plist new file mode 100644 index 000000000..d29c1a344 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (layout) (rl).plist @@ -0,0 +1,16 @@ + + + + + content + render :layout => "${1:layoutname}" + name + render (layout) + scope + source.ruby.rails + tabTrigger + rl + uuid + 3F83272F-62D5-4BCB-BAA3-806083078829 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (nothing) (rn).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (nothing) (rn).plist new file mode 100644 index 000000000..22fa73b86 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (nothing) (rn).plist @@ -0,0 +1,16 @@ + + + + + content + render :nothing => ${1:true} + name + render (nothing) + scope + source.ruby.rails + tabTrigger + rn + uuid + AC8A995F-0034-433C-905D-E5C1F29D6EFF + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (nothing,status) (rns).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (nothing,status) (rns).plist new file mode 100644 index 000000000..bbf95ad27 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (nothing,status) (rns).plist @@ -0,0 +1,16 @@ + + + + + content + render :nothing => ${1:true}, :status => ${2:401} + name + render (nothing, status) + scope + source.ruby.rails + tabTrigger + rns + uuid + 724A68C1-A727-46FF-AF59-288E26B09629 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (partial) (rp).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial) (rp).plist new file mode 100644 index 000000000..5154b7565 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial) (rp).plist @@ -0,0 +1,16 @@ + + + + + content + render :partial => "${1:item}" + name + render (partial) + scope + source.ruby.rails + tabTrigger + rp + uuid + 498168A5-5AF8-4F59-8A2D-B517FAB98CDB + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,collection) (rpc).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,collection) (rpc).plist new file mode 100644 index 000000000..24ff03d9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,collection) (rpc).plist @@ -0,0 +1,16 @@ + + + + + content + render :partial => "${1:item}", :collection => ${2:@$1s} + name + render (partial, collection) + scope + source.ruby.rails + tabTrigger + rpc + uuid + 046FB1B6-9C65-4702-91EC-4AA9878CD949 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,locals) (rpl).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,locals) (rpl).plist new file mode 100644 index 000000000..d13bd8bf9 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,locals) (rpl).plist @@ -0,0 +1,16 @@ + + + + + content + render :partial => "${1:item}", :locals => { :${2:$1} => ${3:@$1}$0 } + name + render (partial, locals) + scope + source.ruby.rails + tabTrigger + rpl + uuid + 6F41AFFD-B3A7-42D0-8A84-D6086C118D92 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,object) (rpo).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,object) (rpo).plist new file mode 100644 index 000000000..f9230a8d1 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,object) (rpo).plist @@ -0,0 +1,16 @@ + + + + + content + render :partial => "${1:item}", :object => ${2:@$1} + name + render (partial, object) + scope + source.ruby.rails + tabTrigger + rpo + uuid + BFAAC8DA-A043-4684-967B-B3E5DAE08C62 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,status) (rps).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,status) (rps).plist new file mode 100644 index 000000000..da70e9839 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (partial,status) (rps).plist @@ -0,0 +1,16 @@ + + + + + content + render :partial => "${1:item}", :status => ${2:500} + name + render (partial, status) + scope + source.ruby.rails + tabTrigger + rps + uuid + CBB06A4E-3A82-45F3-91AA-259F02314B9D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (text) (rt).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (text) (rt).plist new file mode 100644 index 000000000..f113cca3d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (text) (rt).plist @@ -0,0 +1,16 @@ + + + + + content + render :text => "${1:text to render...}" + name + render (text) + scope + source.ruby.rails + tabTrigger + rt + uuid + 67C5082F-5011-434A-8EAA-6B8D3600935F + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (text,layout) (rtl).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (text,layout) (rtl).plist new file mode 100644 index 000000000..f98077913 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (text,layout) (rtl).plist @@ -0,0 +1,16 @@ + + + + + content + render :text => "${1:text to render...}", :layout => "${2:layoutname}" + name + render (text, layout) + scope + source.ruby.rails + tabTrigger + rtl + uuid + A3B09AFE-40B5-4623-8B85-E9F369ECE22D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (text,layout=%3Etrue) (rtlt).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (text,layout=%3Etrue) (rtlt).plist new file mode 100644 index 000000000..038959d0b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (text,layout=%3Etrue) (rtlt).plist @@ -0,0 +1,16 @@ + + + + + content + render :text => "${1:text to render...}", :layout => ${2:true} + name + render (text, layout => true) + scope + source.ruby.rails + tabTrigger + rtlt + uuid + 97C0992D-715F-4322-A3E0-DD4D2B7E2FC2 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (text,status) (rts).plist b/bundles/ruby-on-rails.tmbundle/Snippets/render (text,status) (rts).plist new file mode 100644 index 000000000..f480a3c28 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (text,status) (rts).plist @@ -0,0 +1,16 @@ + + + + + content + render :text => "${1:text to render...}", :status => ${2:401} + name + render (text, status) + scope + source.ruby.rails + tabTrigger + rts + uuid + 4F636977-F7A6-4BF5-B09B-7F087683C3B9 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/render (update).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/render (update).tmSnippet new file mode 100644 index 000000000..e748a5db0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/render (update).tmSnippet @@ -0,0 +1,18 @@ + + + + + content + render :update do |${2:page}| + $2.$0 +end + name + render (update) + scope + source.ruby.rails + tabTrigger + ru + uuid + ECB10C0B-E8B7-4606-ABF5-4A2A26E5AB1A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/respond_to (html).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/respond_to (html).tmSnippet new file mode 100644 index 000000000..a694965f5 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/respond_to (html).tmSnippet @@ -0,0 +1,21 @@ + + + + + content + respond_to do |wants| + wants.html do + $TM_SELECTED_TEXT + end + wants.${1:js} { $0 } +end + keyEquivalent + @H + name + respond_to (html) + scope + meta.rails.controller + uuid + 3BDD0D52-443E-4F5F-AE09-ABCC2ABE9A42 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/respond_to.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/respond_to.tmSnippet new file mode 100644 index 000000000..8d701e350 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/respond_to.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + respond_to do |wants| + wants.${1:html}${2: { $0 \}} +end + name + respond_to + scope + meta.rails.controller + tabTrigger + rest + uuid + B41D3164-EA53-4DDC-850E-27B82B24061F + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/returning do %7Cvariable%7C %E2%80%A6 end.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/returning do %7Cvariable%7C %E2%80%A6 end.tmSnippet new file mode 100644 index 000000000..09ed7a5d8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/returning do %7Cvariable%7C %E2%80%A6 end.tmSnippet @@ -0,0 +1,18 @@ + + + + + content + returning ${1:variable} do${2/(^(?<var>\s*[a-z_][a-zA-Z0-9_]*\s*)(,\g<var>)*,?\s*$)|.*/(?1: |)/}${2:v}${2/(^(?<var>\s*[a-z_][a-zA-Z0-9_]*\s*)(,\g<var>)*,?\s*$)|.*/(?1:|)/} + $0 +end + name + returning do |variable| … end + scope + source.ruby.rails + tabTrigger + returning + uuid + D2783155-23F3-4B90-A317-5BD139471193 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/stylesheet_link_tag.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/stylesheet_link_tag.tmSnippet new file mode 100644 index 000000000..b49a775f3 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/stylesheet_link_tag.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}stylesheet_link_tag {1::all}${2:, :cache => ${3:true}}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + stylesheet_link_tag + scope + text.html.ruby + tabTrigger + slt + uuid + 980C7667-9D60-49FF-AF74-A7B19B379F45 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/submit_tag.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/submit_tag.tmSnippet new file mode 100644 index 000000000..9c6b84fc7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/submit_tag.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + ${TM_RAILS_TEMPLATE_START_RUBY_EXPR}submit_tag "${1:Save changes}"${2:, :id => "${3:submit}"}${4:, :name => "${5:$3}"}${6:, :class => "${7:form_$3}"}${8:, :disabled => ${9:false}}${10:, :disable_with => "${11:Please wait...}"}${TM_RAILS_TEMPLATE_END_RUBY_EXPR} + name + submit_tag + scope + text.html.ruby, text.haml + tabTrigger + st + uuid + D0E29200-E910-11DC-A399-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_binary (tcbi).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_binary (tcbi).tmSnippet new file mode 100644 index 000000000..b0a8b49be --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_binary (tcbi).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.binary :${1:title}${2:, :limit => ${3:2}.megabytes} +t.$0 + name + t.binary (tcbi) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 7CE57C6C-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_boolean (tcb).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_boolean (tcb).tmSnippet new file mode 100644 index 000000000..9310b8359 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_boolean (tcb).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.boolean :${1:title} +t.$0 + name + t.boolean (tcb) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 6BE6F315-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_date (tcda).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_date (tcda).tmSnippet new file mode 100644 index 000000000..c1199ab2f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_date (tcda).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.date :${1:title} +t.$0 + name + t.date (tcda) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 61CF5B32-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_datetime (tcdt).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_datetime (tcdt).tmSnippet new file mode 100644 index 000000000..a8fc8276d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_datetime (tcdt).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.datetime :${1:title} +t.$0 + name + t.datetime (tcdt) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 3458B140-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_decimal (tcd).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_decimal (tcd).tmSnippet new file mode 100644 index 000000000..d2d03e7c2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_decimal (tcd).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.decimal :${1:title}${2:${3:, :precision => ${4:10}}${5:, :scale => ${6:2}}} +t.$0 + name + t.decimal (tcd) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 26C09807-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_float (tcf).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_float (tcf).tmSnippet new file mode 100644 index 000000000..e900c1ef6 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_float (tcf).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.float :${1:title} +t.$0 + name + t.float (tcf) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 1BDC463A-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_integer (tci).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_integer (tci).tmSnippet new file mode 100644 index 000000000..543404934 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_integer (tci).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.integer :${1:title} +t.$0 + name + t.integer (tci) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 0E63B7D5-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_lock_version (tcl).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_lock_version (tcl).tmSnippet new file mode 100644 index 000000000..931303f52 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_lock_version (tcl).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.integer :lock_version, :null => false, :default => 0 +t.$0 + name + t.lock_version (tcl) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + A677FFD4-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_references (tcr).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_references (tcr).tmSnippet new file mode 100644 index 000000000..2dba2cbda --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_references (tcr).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.references :${1:taggable}${2:, :polymorphic => ${3:{ :default => '${4:Photo}' \}}} +t.$0 + name + t.references (tcr) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + B6D9225C-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_rename (tre).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_rename (tre).tmSnippet new file mode 100644 index 000000000..2e8b1c726 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_rename (tre).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.rename(:${1:old_column_name}, :${2:new_column_name}) +t.$0 + name + t.rename (tre) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 9D4E30E2-4A61-4941-B9F3-BEE97552747A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_string (tcs).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_string (tcs).tmSnippet new file mode 100644 index 000000000..e6500a8af --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_string (tcs).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.string :${1:title} +t.$0 + name + t.string (tcs) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + B757F7E5-E4BD-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_text (tct).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_text (tct).tmSnippet new file mode 100644 index 000000000..94acdbd4b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_text (tct).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.text :${1:title} +t.$0 + name + t.text (tct) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + FFE7B820-E4BD-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_time (tcti).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_time (tcti).tmSnippet new file mode 100644 index 000000000..8b70a5889 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_time (tcti).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.time :${1:title} +t.$0 + name + t.time (tcti) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 537BDD48-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_timestamp (tcts).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_timestamp (tcts).tmSnippet new file mode 100644 index 000000000..2dcd886ea --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_timestamp (tcts).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.timestamp :${1:title} +t.$0 + name + t.timestamp (tcts) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 49643690-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/t_timestamps (tctss).tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/t_timestamps (tctss).tmSnippet new file mode 100644 index 000000000..3957742af --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/t_timestamps (tctss).tmSnippet @@ -0,0 +1,17 @@ + + + + + content + t.timestamps +t.$0 + name + t.timestamps (tctss) + scope + meta.rails.migration.create_table, meta.rails.migration.change_table + tabTrigger + t. + uuid + 950B0BF2-E4BE-11DC-A11A-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_acceptance_of if.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_acceptance_of if.tmSnippet new file mode 100644 index 000000000..726d59a38 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_acceptance_of if.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_acceptance_of :${1:terms}${2:${3:, :accept => "${4:1}"}${5:, :message => "${6:You must accept the terms of service}"}}, :if => proc { |obj| ${7:obj.condition?} }} + name + validates_acceptance_of if + scope + source.ruby.rails + tabTrigger + vaoif + uuid + A2477223-AD5A-4723-8052-943CE9BA634D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_acceptance_of.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_acceptance_of.tmSnippet new file mode 100644 index 000000000..0869ab9ea --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_acceptance_of.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_acceptance_of :${1:terms}${2:${3:, :accept => "${4:1}"}${5:, :message => "${6:You must accept the terms of service}"}} + name + validates_acceptance_of + scope + source.ruby.rails + tabTrigger + vao + uuid + 89198999-7E6D-4D97-A20E-45263E1CA993 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_associated (va).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_associated (va).plist new file mode 100644 index 000000000..4827d3b7c --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_associated (va).plist @@ -0,0 +1,16 @@ + + + + + content + validates_associated :${1:attribute}${2:, :on => :${3:create}} + name + validates_associated + scope + source.ruby.rails + tabTrigger + va + uuid + 47944705-F605-4ED4-B4C0-9E823EE25138 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_associated if (vaif).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_associated if (vaif).plist new file mode 100644 index 000000000..a24433d78 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_associated if (vaif).plist @@ -0,0 +1,16 @@ + + + + + content + validates_associated :${1:attribute}${2:, :on => :${3:create}, :if => proc { |obj| ${5:obj.condition?} }} + name + validates_associated if + scope + source.ruby.rails + tabTrigger + vaif + uuid + 85E9264C-5414-4FA0-AC07-F305A798ED46 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_confirmation_of (vc).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_confirmation_of (vc).plist new file mode 100644 index 000000000..6d4711a0e --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_confirmation_of (vc).plist @@ -0,0 +1,16 @@ + + + + + content + validates_confirmation_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:should match confirmation}"} + name + validates_confirmation_of + scope + source.ruby.rails + tabTrigger + vc + uuid + B5893618-D07C-48F1-8867-736D0AAFF0E7 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_confirmation_of if (vcif).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_confirmation_of if (vcif).plist new file mode 100644 index 000000000..b747ad17b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_confirmation_of if (vcif).plist @@ -0,0 +1,16 @@ + + + + + content + validates_confirmation_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:should match confirmation}", :if => proc { |obj| ${5:obj.condition?} }} + name + validates_confirmation_of if + scope + source.ruby.rails + tabTrigger + vcif + uuid + 1354726C-DA64-4CA6-A099-26626A865D8D + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_exclusion_of (ve).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_exclusion_of (ve).plist new file mode 100644 index 000000000..50783f6a1 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_exclusion_of (ve).plist @@ -0,0 +1,16 @@ + + + + + content + validates_exclusion_of :${1:attribute}${2:, :in => ${3:%w( ${4:mov avi} )}, :on => :${5:create}, :message => "${6:extension %s is not allowed}"} + name + validates_exclusion_of + scope + source.ruby.rails + tabTrigger + ve + uuid + 4CC98A56-B60B-4A89-80E0-400C5314A050 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_exclusion_of if (veif).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_exclusion_of if (veif).plist new file mode 100644 index 000000000..5f7079567 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_exclusion_of if (veif).plist @@ -0,0 +1,16 @@ + + + + + content + validates_exclusion_of :${1:attribute}${2:, :in => ${3:%w( ${4:mov avi} )}, :on => :${5:create}, :message => "${6:extension %s is not allowed}"}, :if => proc { |obj| ${7:obj.condition?} }} + name + validates_exclusion_of if + scope + source.ruby.rails + tabTrigger + veif + uuid + 869AB0B7-12DD-440A-905A-BFB1E0E16E1C + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_format_of if.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_format_of if.tmSnippet new file mode 100644 index 000000000..abeb64e8a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_format_of if.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_format_of :${1:attribute}, :with => /${2:^[${3:\w\d}]+\$}/${4:, :on => :${5:create}, :message => "${6:is invalid}"}, :if => proc { |obj| ${7:obj.condition?} }} + name + validates_format_of if + scope + source.ruby.rails + tabTrigger + vfif + uuid + 14BF0586-F2E8-4AB3-BB4B-E49099384403 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_format_of.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_format_of.tmSnippet new file mode 100644 index 000000000..177c31107 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_format_of.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_format_of :${1:attribute}, :with => /${2:^[${3:\w\d}]+\$}/${4:, :on => :${5:create}, :message => "${6:is invalid}"} + name + validates_format_of + scope + source.ruby.rails + tabTrigger + vf + uuid + EB47FBA1-AFB3-42F9-94A4-552D3175C17A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_inclusion_of if.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_inclusion_of if.tmSnippet new file mode 100644 index 000000000..235d87bcf --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_inclusion_of if.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_inclusion_of :${1:attribute}${2:, :in => ${3:%w( ${4:mov avi} )}, :on => :${5:create}, :message => "${6:extension %s is not included in the list}"}, :if => proc { |obj| ${7:obj.condition?} }} + name + validates_inclusion_of if + scope + source.ruby.rails + tabTrigger + viif + uuid + 47FF50AF-E9BF-11DC-8518-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_inclusion_of.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_inclusion_of.tmSnippet new file mode 100644 index 000000000..6aaa9f173 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_inclusion_of.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_inclusion_of :${1:attribute}${2:, :in => ${3:%w( ${4:mov avi} )}, :on => :${5:create}, :message => "${6:extension %s is not included in the list}"} + name + validates_inclusion_of + scope + source.ruby.rails + tabTrigger + vi + uuid + 4611F02E-E9BF-11DC-8518-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_length_of (vl).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_length_of (vl).plist new file mode 100644 index 000000000..433f9b46b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_length_of (vl).plist @@ -0,0 +1,16 @@ + + + + + content + validates_length_of :${1:attribute}, :within => ${2:3..20}${3:, :on => :${4:create}, :message => "${5:must be present}"} + name + validates_length_of + scope + source.ruby.rails + tabTrigger + vl + uuid + 5CE8838A-BF2C-497E-B87A-E90C3BC482E0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_length_of if.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_length_of if.tmSnippet new file mode 100644 index 000000000..025e14658 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_length_of if.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_length_of :${1:attribute}, :within => ${2:3..20}${3:, :on => :${4:create}, :message => "${5:must be present}"}, :if => proc { |obj| ${6:obj.condition?} }} + name + validates_length_of if + scope + source.ruby.rails + tabTrigger + vlif + uuid + EC511A43-D3B7-11DC-BA49-00112475D960 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_numericality_of if.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_numericality_of if.tmSnippet new file mode 100644 index 000000000..675bc4617 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_numericality_of if.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_numericality_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:is not a number}"}, :if => proc { |obj| ${5:obj.condition?} }} + name + validates_numericality_of if + scope + source.ruby.rails + tabTrigger + vnif + uuid + CF506019-E964-4172-A3DA-475AE3B65558 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_numericality_of.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/validates_numericality_of.tmSnippet new file mode 100644 index 000000000..633edbf29 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_numericality_of.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + validates_numericality_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:is not a number}"} + name + validates_numericality_of + scope + source.ruby.rails + tabTrigger + vn + uuid + B21BA16D-7C04-4912-8488-425CDCC332A8 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_presence_of (vp).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_presence_of (vp).plist new file mode 100644 index 000000000..a502de862 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_presence_of (vp).plist @@ -0,0 +1,16 @@ + + + + + content + validates_presence_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:can't be blank}"} + name + validates_presence_of + scope + source.ruby.rails + tabTrigger + vp + uuid + 5DAC28A7-33C8-4DA7-9E85-56618D6BEC9F + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_presence_of if (vpif) 2.plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_presence_of if (vpif) 2.plist new file mode 100644 index 000000000..5dbd39c92 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_presence_of if (vpif) 2.plist @@ -0,0 +1,16 @@ + + + + + content + validates_presence_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:can't be blank}"}, :if => proc { |obj| ${5:obj.condition?} }} + name + validates_presence_of if + scope + source.ruby.rails + tabTrigger + vpif + uuid + F5CBBE16-F5CC-4EDA-8BC6-30281BD7D854 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_uniqueness_of (vu).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_uniqueness_of (vu).plist new file mode 100644 index 000000000..3b96b2f41 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_uniqueness_of (vu).plist @@ -0,0 +1,16 @@ + + + + + content + validates_uniqueness_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:must be unique}"} + name + validates_uniqueness_of + scope + source.ruby.rails + tabTrigger + vu + uuid + F8316545-9AE4-4C7F-87ED-A2C00E6637FA + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/validates_uniqueness_of if (vuif).plist b/bundles/ruby-on-rails.tmbundle/Snippets/validates_uniqueness_of if (vuif).plist new file mode 100644 index 000000000..edd92f65d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/validates_uniqueness_of if (vuif).plist @@ -0,0 +1,16 @@ + + + + + content + validates_uniqueness_of :${1:attribute}${2:, :on => :${3:create}, :message => "${4:must be unique}", :if => proc { |obj| ${6:obj.condition?} }} + name + validates_uniqueness_of if + scope + source.ruby.rails + tabTrigger + vuif + uuid + 43680344-0818-42BF-95B4-58CD2D76545B + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/verify (verify).plist b/bundles/ruby-on-rails.tmbundle/Snippets/verify (verify).plist new file mode 100644 index 000000000..62d69f108 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/verify (verify).plist @@ -0,0 +1,19 @@ + + + + + bundlePath + /Users/tobi/Library/Application Support/TextMate/Bundles/Custom.tmbundle + content + verify :only => [:$1], :method => :post, :render => {:status => 500, :text => "use HTTP-POST"} + + name + verify — render + scope + source.ruby.rails + tabTrigger + verify + uuid + 9ECBF20C-003E-41D9-A881-4BAC0656F9DC + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/verify - redirect (verify).plist b/bundles/ruby-on-rails.tmbundle/Snippets/verify - redirect (verify).plist new file mode 100644 index 000000000..3971e3ff7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/verify - redirect (verify).plist @@ -0,0 +1,19 @@ + + + + + bundlePath + /Users/tobi/Library/Application Support/TextMate/Bundles/Custom.tmbundle + content + verify :only => [:$1], :session => :user, :params => :id, :redirect_to => {:action => '${2:index}'} + + name + verify — redirect + scope + source.ruby.rails + tabTrigger + verify + uuid + 7BBD3F57-57A5-4CD0-8E79-B931021FC110 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/wants_format.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/wants_format.tmSnippet new file mode 100644 index 000000000..ac8def947 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/wants_format.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + wants.${1:js|xml|html}${2: { $0 \}} + name + wants.format + scope + meta.rails.controller + tabTrigger + wants + uuid + 3F26FDB4-ACF9-4856-9312-6A4D78DC8564 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/xhr delete.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/xhr delete.tmSnippet new file mode 100644 index 000000000..aa2d909ba --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/xhr delete.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + xhr :delete, :${1:destroy}, :id => ${2:1}$0 + name + xhr delete + scope + source.ruby.rails + tabTrigger + xdelete + uuid + F1BE0C3D-7203-43E9-BEFB-D1A99CDD31C1 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/xhr get.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/xhr get.tmSnippet new file mode 100644 index 000000000..2bc363728 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/xhr get.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + xhr :get, :${1:show}${2:, :id => ${3:1}}$0 + name + xhr get + scope + source.ruby.rails + tabTrigger + xget + uuid + 78FCF992-D01B-404F-BC54-5EE7B91F999A + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/xhr post.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/xhr post.tmSnippet new file mode 100644 index 000000000..064195d7a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/xhr post.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + xhr :post, :${1:create}, :${2:object} => { $3 } + name + xhr post + scope + source.ruby.rails + tabTrigger + xpost + uuid + 62C3838B-0790-4FC2-8425-F273A57F5D33 + + diff --git a/bundles/ruby-on-rails.tmbundle/Snippets/xhr put.tmSnippet b/bundles/ruby-on-rails.tmbundle/Snippets/xhr put.tmSnippet new file mode 100644 index 000000000..5d1fdd999 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Snippets/xhr put.tmSnippet @@ -0,0 +1,16 @@ + + + + + content + xhr :put, :${1:update}, :id => ${2:1}, :${3:object} => { $4 }$0 + name + xhr put + scope + source.ruby.rails + tabTrigger + xput + uuid + C12C98A5-74E5-4E70-9ADB-8783455D6539 + + diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/create_partial_from_selection.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/create_partial_from_selection.rb new file mode 100755 index 000000000..4eb54a9fa --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/create_partial_from_selection.rb @@ -0,0 +1,94 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +# Copyright: +# (c) 2006 syncPEOPLE, LLC. +# Visit us at http://syncpeople.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Creates a partial from the selected text (asks for the partial name) +# and replaces the text with a "render :partial => [partial_name]" erb fragment. + +require 'rails_bundle_tools' + +current_file = RailsPath.new + +# Make sure we're in a view file +unless current_file.file_type == :view + TextMate.exit_show_tool_tip("The ‘create partial from selection’ action works within view files only.") +end + +# If text is selected, create a partial out of it +if TextMate.selected_text + partial_name = TextMate::UI.request_string( + :title => "Create a partial from the selected text", + :default => "partial", + :prompt => "Name of the new partial: (omit the _ and .html.erb)", + :button1 => 'Create' + ) + + if partial_name + path = current_file.dirname + partial = File.join(path, "_#{partial_name}.html.erb") + + # Create the partial file + if File.exist?(partial) + unless TextMate::UI.request_confirmation( + :button1 => "Overwrite", + :button2 => "Cancel", + :title => "The partial file already exists.", + :prompt => "Do you want to overwrite it?" + ) + TextMate.exit_discard + end + end + + file = File.open(partial, "w") { |f| f.write(TextMate.selected_text) } + TextMate.rescan_project + + # Return the new render :partial line + print "<%= render :partial => '#{partial_name}' %>\n" + else + TextMate.exit_discard + end +else + # Otherwise, toggle inline partials if they exist + + text = "" + partial_block_re = + /\n(.+)\n/m + + # Inline partials exist? + if current_file.buffer =~ partial_block_re + text = current_file.buffer.text + while text =~ partial_block_re + partial_name, partial_text = $1, $2 + File.open(partial_name, "w") { |f| f.write $2 } + text.sub! partial_block_re, '' + end + else + # See if there are any render :partial statements to expand + current_file.buffer.lines.each_with_index do |line, i| + text << line + if line =~ /render[\s\(].*:partial\s*=>\s*['"](.+?)['"]/ + partial_name = $1 + modules = current_file.modules + [current_file.controller_name] + + # Check for absolute path to partial file + if partial_name.include?('/') + pieces = partial_name.split('/') + partial_name = pieces.pop + modules = pieces + end + + partial = File.join(current_file.rails_root, 'app', 'views', modules, "_#{partial_name}.html.erb") + + text << "\n" + text << IO.read(partial).gsub("\r\n", "\n") + text << "\n" + end + end + end + print text + TextMate.exit_replace_document +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/fixture_auto_complete.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/fixture_auto_complete.rb new file mode 100644 index 000000000..04a1a942f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/fixture_auto_complete.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby + +require 'rails_bundle_tools' +require 'yaml' +require File.join(ENV['TM_SUPPORT_PATH'], "lib", "escape") +DIALOG = ENV['DIALOG'] + +def parse_line + current_line = TextMate.current_line + if RailsPath.new.file_type == :fixture + if fixture_finder = current_line.match(/^([\s\t]+)\b([a-z_]+)\:([a-z_]*)/) + [:fixture] + fixture_finder[1..3] + end + else + if fixture_finder = current_line.match(/^([\s\t]+).*\b([a-z_]+)\(\:([a-z_]*)\)/) + [:test] + fixture_finder[1..3] + end + end +end + +def load_referenced_fixture_file(ref) + ref_plural = Inflector.pluralize(ref) + ref_file = File.join(TextMate.project_directory, "test", "fixtures", "#{ref_plural}.yml") + if (!File.exist?(ref_file)) + ref_file = File.join(TextMate.project_directory, "spec", "fixtures", "#{ref_plural}.yml") + end + if (!File.exist?(ref_file)) + puts "Could not find any #{ref} fixtures." + TextMate.exit_show_tool_tip + end + YAML.load_file(ref_file) +end + +def ask_for_fixture_or_exit(fixtures) + require "#{ENV['TM_SUPPORT_PATH']}/lib/osx/plist" + h = fixtures.map do |f| + {'title' => f, 'fixture' => f} + end + pl = {'menuItems' => h}.to_plist + res = OSX::PropertyList::load(`#{e_sh DIALOG} -up #{e_sh pl}`) + TextMate.exit_discard unless res.has_key? 'selectedMenuItem' + res['selectedMenuItem']['fixture'] +end + +def filter_fixtures(fixtures, filter) + if !filter.empty? && ARGV[0] != "preserve" + fixtures.select do |f| + f.include? filter + end + else + fixtures + end +end + +# 'foo' => ':foo' and 'foo-bar' => ':"foo-bar"' +def symbolize_name(name) + name.to_sym.inspect +end + +filetype, start_line_gap, ref, filter = parse_line +filter = "" if filter.nil? +foreign_fixtures = load_referenced_fixture_file(ref).keys +candidates = filter_fixtures(foreign_fixtures, filter) +if candidates.empty? + puts "No match found for #{filter}" + TextMate.exit_show_tool_tip +end +selected_fixture = ask_for_fixture_or_exit(candidates) + +if ARGV[0] == "preserve" + print TextMate.current_line.rstrip + if !filter.empty? + print ", " + else + print " " + end + print selected_fixture +else + if filetype == :fixture + print "#{start_line_gap}#{ref}: #{selected_fixture}" + else + print "#{start_line_gap}@#{Inflector.singularize ref} = #{ref}(#{symbolize_name selected_fixture})" + end +end diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/generate.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/generate.rb new file mode 100755 index 000000000..2a8dff8f0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/generate.rb @@ -0,0 +1,71 @@ +#!/usr/bin/env ruby + +# Copyright: +# (c) 2006 syncPEOPLE, LLC. +# Visit us at http://syncpeople.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Asks what to generate and what name to use, then runs script/generate. + +require 'rails_bundle_tools' +require 'fileutils' +require File.dirname(__FILE__) + "/../lib/rails/generate" + +# Look for (created) files and return an array of them +def files_from_generator_output(output, type = 'create') + output.to_a.map { |line| line.scan(/#{type}\s+([^\s]+)$/).flatten.first }.compact.select { |f| File.exist?(f) and !File.directory?(f) } +end + +Generator.setup + +if choice = TextMate.choose("Generate:", Generator.names.map { |name| Inflector.humanize name }, :title => "Rails Generator") + arguments = TextMate::UI.request_string( + :title => "#{Inflector.humanize Generator.generators[choice].name} Generator", + :default => Generator.generators[choice].default_answer, + :prompt => Generator.generators[choice].question, + :button1 => 'Generate' + ) + + if arguments + options = "" + + case choice + when 0 + options = TextMate::UI.request_string( + :title => "Scaffold Controller Name", + :prompt => "Name the new controller for the scaffold:", + :button1 => 'Continue' + ) + options = "'#{options}'" + when 1 + options = TextMate::UI.request_string( + :title => "Controller Actions", + :default => "index new create edit update destroy", + :prompt => "List any actions you would like created for the controller:", + :button1 => 'Create' + ) + end + + # add the --svn option, if needed + proj_dir = ENV["TM_PROJECT_DIRECTORY"] + if proj_dir and File.exist?(File.join(proj_dir, ".svn")) + options << " --svn" + end + + rails_root = RailsPath.new.rails_root + FileUtils.cd rails_root + command = "script/generate #{Generator.generators[choice].name} #{arguments} #{options}" + $logger.debug "Command: #{command}" + + output = ruby(command) + $logger.debug "Output: #{output}" + TextMate.rescan_project + files = files_from_generator_output(output) + files.each { |f| TextMate.open(File.join(rails_root, f)) } + TextMate::UI.simple_notification( + :title => 'Generator Complete', + :summary => "Done generating #{Generator.generators[choice].name}", + :log => output + ) + end +end diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/generate_quick_migration.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/generate_quick_migration.rb new file mode 100644 index 000000000..eb4fd5147 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/generate_quick_migration.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env ruby + +# Copyright: +# (c) 2006 InquiryLabs, Inc. +# Visit us at http://inquirylabs.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Generates a migration file from the selection or current word. This is much faster than calling upon +# Rails' built-in generate migration code. + +require 'rails_bundle_tools' +require 'fileutils' + +selection = TextMate::UI.request_string( + :title => "Quick Migration Generator", + :default => "CreateUserTable", + :prompt => "Name the new migration:", + :button1 => 'Create' +) + +if selection.size < 3 or selection.size > 255 + print "Please highlight the name of the migration you want to create" + TextMate.exit_show_tool_tip +end + +rails_root = RailsPath.new.rails_root +migration_dir = File.join(rails_root, "db", "migrate") +files = Dir.glob(File.join(migration_dir, "[0-9][0-9][0-9]_*")) +if files.empty? + number = "001" +else + number = File.basename(files[-1])[0..2].succ +end + +if selection =~ /^[a-z]/ or selection.include?("_") + # The selected text is an underscored word + underscored = selection + camelized = underscored.camelize +else + # The selected text is a camelized word + camelized = selection + underscored = camelized.underscore +end + +generated_code = <<-RUBY +class #{camelized} < ActiveRecord::Migration + def self.up + mtab + end + + def self.down + end +end +RUBY + +FileUtils.mkdir_p migration_dir +new_migration_filename = File.join(migration_dir, number + "_" + underscored + ".rb") +File.open(new_migration_filename, "w") { |f| f.write generated_code } +TextMate.rescan_project +TextMate.open(new_migration_filename, 2, 8) diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/go_to_alternate_file.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/go_to_alternate_file.rb new file mode 100755 index 000000000..2ae696c56 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/go_to_alternate_file.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +# Copyright: +# (c) 2006 syncPEOPLE, LLC. +# Visit us at http://syncpeople.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Makes an intelligent decision on which file to go to based on the current line or current context. + +require 'rails_bundle_tools' +require 'rails/command_go_to_file' + +CommandGoToFile.alternate(ARGV) \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/go_to_file_on_current_line.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/go_to_file_on_current_line.rb new file mode 100755 index 000000000..d64dae4b7 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/go_to_file_on_current_line.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby + +# Copyright: +# (c) 2006 syncPEOPLE, LLC. +# Visit us at http://syncpeople.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Makes an intelligent decision on which file to go to based on the current line or current context. + +require 'rails_bundle_tools' +require 'rails/command_go_to_file' + +CommandGoToFile.on_current_line \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/insert_add_column_or_create_table.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/insert_add_column_or_create_table.rb new file mode 100755 index 000000000..3614b4d2d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/insert_add_column_or_create_table.rb @@ -0,0 +1,67 @@ +#!/usr/bin/env ruby +require ENV['TM_BUNDLE_SUPPORT'] + '/lib/rails_bundle_tools' + +def prepend(text, prefix) + text.to_a.map { |line| prefix + line }.join +end + +def unprepend(text, prefix) + text.to_a.map { |line| line.index(prefix) == 0 ? line.sub(prefix, '') : line }.join +end + +buffer = Buffer.new(STDIN.read, 0, 0) + +table_name = column_name = nil +case buffer.lines[0] +when /remove_column\s+:(\w+),\s*:(\w+)/ + table_name, column_name = $1, $2 +when /drop_table\s+:(\w+)/ + table_name = $1 +else + puts "No table or column name specified. (\"#{buffer.lines[0]}\")" + TextMate.exit_show_tool_tip +end + +# Find 'self.down' method +if self_down = buffer.find { /^(\s*)def\s+self\.down\b/ } + indentation = self_down[1] + + # Find the matching create_table clause in the schema.rb file + schema = RailsPath.new('db/schema.rb') + schema.buffer.line_number = schema.buffer.column_number = 0 + if schema.exists? + if insert_text = schema.buffer.find { %r{^(\s*)create_table\s+["']#{table_name}['"]} } + from = insert_text[0] + 1 + insert_text_indentation = insert_text[1] + insert_text_end = schema.buffer.find(:from => from) { %r{^#{insert_text_indentation}end\b} } + + # If a column is specified, get just the column, not the whole create_table + if column_name + to = insert_text_end[0] + if insert_text = schema.buffer.find { %r{^\s*\w+\.(column|primary_key|string|text|integer|float|decimal|datetime|timestamp|time|date|binary|boolean)\s+['"]#{column_name}['"](.*)$} } + column_type = ", :#{insert_text[1]}" unless insert_text[1] == "column" + column_params = insert_text[2] + insert_text = "add_column :#{table_name}, :#{column_name}#{column_type}#{column_params}\n" + + buffer.lines.insert self_down[0] + 1, prepend(insert_text, indentation + " ") + else + puts "The db/schema.rb does not have a column matching \"#{column_name}\" within create_table \"#{table_name}\"." + TextMate.exit_show_tool_tip + end + else + insert_text = unprepend(schema.buffer.lines[insert_text[0]..insert_text_end[0]], insert_text_indentation) + buffer.lines.insert self_down[0] + 1, prepend(insert_text + "\n", indentation + " ") + end + print buffer.lines.join.gsub(/\[press tab twice to generate (create_table|add_column)\]/, "") + else + puts "The db/schema.rb does not have a create_table \"#{table_name}\"." + TextMate.exit_show_tool_tip + end + else + puts "The db/schema.rb file doesn't exist. Can't insert create_table." + TextMate.exit_show_tool_tip + end +else + puts "No self.down method found in below the caret." + TextMate.exit_show_tool_tip +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/intelligent_migration_snippet.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/intelligent_migration_snippet.rb new file mode 100755 index 000000000..95577bc40 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/intelligent_migration_snippet.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby +# +# Copyright (c) 2006 Sami Samhuri +# Distributed under the MIT license +# +# Inserts a migration snippet into 2 places in the document, one +# piece in self.up and one in self.down. + +snippets = { + 'rename_column' => + { :up => 'rename_column :${1:table_name}, :${2:column_name}, :${3:new_column_name}$0', + :down => 'rename_column :$1, :$3, :$2' }, + + 'rename_column_continue' => + { :up => 'rename_column :${1:table_name}, :${2:column_name}, :${3:new_column_name} +mncc$0', + :down => 'rename_column :$1, :$3, :$2' }, + + 'rename_table' => + { :up => 'rename_table :${1:old_table_name}, :${2:new_table_name}$0', + :down => 'rename_table :$2, :$1' }, + + 'rename_table_continue' => + { :up => 'rename_table :${1:old_table_name}, :${2:new_table_name} +mntc$0', + :down => 'rename_table :$2, :$1' }, + + 'add_remove_column' => + { :up => 'add_column :${1:table_name}, :${2:column_name}, :${3:string}$0', + :down => 'remove_column :$1, :$2' }, + + 'add_remove_column_continue' => + { :up => 'add_column :${1:table_name}, :${2:column_name}, :${3:string} +marcc$0', + :down => 'remove_column :$1, :$2' }, + + 'create_drop_table' => + { :up => 'create_table :${1:table_name}, :force => true do |t| + t.$0 + t.timestamps +end', + :down => 'drop_table :$1' }, + + 'change_change_table' => + { :up => 'change_table :${1:table_name} do |t| + t.$0 +end', + :down => 'change_table :$1 do |t| +end' }, + + 'add_remove_index' => + { :up => 'add_index :${1:table_name}, :${2:column_name}$0', + :down => 'remove_index :$1, :$2' }, + + 'add_remove_unique_index' => + { :up => 'add_index :${1:table_name}, ${2:[:${3:column_name}${4:, :${5:column_name}}]}, :unique => true$0', + :down => 'remove_index :$1, :column => $2' }, + + 'add_remove_named_index' => + { :up => 'add_index :${1:table_name}, [:${2:column_name}${3:, :${4:column_name}}], :name => "${5:index_name}"${6:, :unique => true}$0', + :down => 'remove_index :$1, :name => :$5' } +} + +def indent(code) + spaces = ' ' * (2 * ENV['TM_TAB_SIZE'].to_i) + lines = code.to_a.collect { |s| spaces + s } + lines.to_s + "\n" +end + +def insert_migration(snippet, text) + lines = text.to_a + + up_code = indent(snippet[:up]) + down_code = indent(snippet[:down]) + + # insert the self.up part of the snippet + lines[0] = up_code + + # find the beginning of self.down and insert down code, this is hardly robust. + # assuming self.down is after self.up in the class + lines.each_with_index do |line, i| + if line =~ /^\s*def\s+self\.down\b/ + lines[i, 1] = [lines[i], down_code] + break + end + end + lines.to_s +end + +snippet = ARGV.shift + +# escape chars that are special in snippets +text = STDIN.read.gsub('[\$\`\\]', '\\\\\1') + +if snippets.has_key? snippet + output = insert_migration(snippets[snippet], text) +else + # return the unmodified text + output = text +end + +print output \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/list_plugins.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/list_plugins.rb new file mode 100644 index 000000000..384783f43 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/list_plugins.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby + +# Copyright: +# (c) 2006 InquiryLabs, Inc. +# Visit us at http://inquirylabs.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Retrieves plugin data from agilewebdevelopment.com and allows you to install directly. + +if RUBY_VERSION =~ /^1\.6\./ then + puts <<-HTML +

Sorry, but this function requires Ruby 1.8.

+

If you do have Ruby 1.8 installed (default for Tiger users) then you need to setup the path variable in ~/.MacOSX/environment.plist.

+

For detailed instructions see the manual (scroll down to the paragraph starting with Important.)

+ HTML + abort +end + +require ENV['TM_SUPPORT_PATH'] + '/lib/escape' # we use e_sh in the rhtml template +require 'rails_bundle_tools' +require "erb" +include ERB::Util +#require 'fileutils' + +root = RailsPath.new.rails_root + +script = File.join(root, "script", "plugin") +enable_install = true +unless File.exist? script + TextMate::UI.alert(:warning, "Plugin Script Not Found", "The 'plugin' script was not found in #{script}. Plugins will not be able to be installed.", 'OK') + enable_install = false +end + +$tags = [ + { :label => "FIXME", :color => "#A00000", :regexp => /FIX ?ME[\s,:]+(\S.*)$/i }, + { :label => "TODO", :color => "#CF830D", :regexp => /TODO[\s,:]+(\S.*)$/i }, + { :label => "CHANGED", :color => "#008000", :regexp => /CHANGED[\s,:]+(\S.*)$/ }, + { :label => "RADAR", :color => "#0090C8", :regexp => /(.*<)ra?dar:\/(?:\/problem|)\/([&0-9]+)(>.*)$/, :trim_if_empty => true }, +] + +template_file = "#{TextMate.bundle_support}/templates/list_plugins.rhtml" +print ERB.new(File.open(template_file), 0, '<>').result diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/rake_helper.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/rake_helper.rb new file mode 100755 index 000000000..2b6a455c4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/rake_helper.rb @@ -0,0 +1,128 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +# Copyright: +# (c) 2006 InquiryLabs, Inc. +# Visit us at http://inquirylabs.com/ +# Author: Duane Johnson (duane.johnson@gmail.com) +# Description: +# Runs 'rake' and executes a particular task + +require 'optparse' +require 'rails_bundle_tools' +require "#{ENV["TM_SUPPORT_PATH"]}/lib/escape" +require "#{ENV["TM_SUPPORT_PATH"]}/lib/web_preview" + +$RAKEMATE_VERSION = "$Revision$" + +Dir.chdir TextMate.project_directory + +options = {} + +task = ARGV.shift + +OptionParser.new do |opts| + opts.banner = "Usage: rake_helper.rb [options]" + + opts.separator "" + opts.separator "Rake helper options:" + + opts.on("-q", "--question [QUESTION TEXT]", "Ask a question before running rake.") do |question| + options[:question] = question + end + + opts.on("-a", "--answer [ANSWER TEXT]", "Default answer for the question.") do |answer| + options[:answer] = answer + end + + opts.on("-v", "--variable [VARIABLE]", "Variable to assign the ANSWER to.") do |variable| + options[:variable] = variable + end + + opts.on("-t", "--title [TITLE TEXT]", "Title of pop-up window.") do |title| + options[:title] = title + end +end.parse! + +if options[:question] + unless options[:answer] = TextMate::UI.request_string( + :title => options[:title] || "Rake", + :default => options[:answer] || "", + :prompt => options[:question], + :button1 => 'Continue' + ) + TextMate.exit_discard + end +end + +command = "rake #{task}" +command += " #{options[:variable]}=#{options[:answer]}" if options[:variable] && options[:answer] + +# puts "#{options.inspect}
" + +reports = { + "migrate" => "Migration Report", + "db:migrate" => "Migration Report" +} + +puts html_head(:window_title => "#{task} — RakeMate", :page_title => 'RakeMate', :sub_title => 'Rake') +puts <<-HTML +
#{reports[task] || "Rake Report"}
+
#{command}
+
+
RakeMate r#{$RAKEMATE_VERSION[/\d+/]}
+
+
+HTML + +$stdout.flush + +output = `#{command}` +lines = output.to_a +# Remove the test output from rake output +lines.pop if lines[-1] =~ /0 tests, 0 assertions, 0 failures, 0 errors/ + +report = "" + +case task +when "db:migrate", "migrate" + inside_table = false + lines.each do |line| + case line + when /^==\s+/ + # Replace == headings with

+ line.gsub!(/^==\s+([^=]+)[=\s]*$/, "\\1") + # Replace parenthetical times with time class + line.gsub!(/(\([\d\.]+s\))/, "\\1") + # Show details inside table + if !inside_table + line << "" + else + line << "
" + end + inside_table = !inside_table + when /^--\s+(.+)$/ + # Show command inside table cell + line = "#{$1}" + when /^\s+->(.+)$/ + # Show execution time inside table cell + line = "#{$1}\n" + else + line += "
" + end + report << line + end + report << "" if inside_table +else + report += lines.join("
") +end + +report += "
Done
" +puts report + +puts <<-HTML +
+
+ + +HTML diff --git a/bundles/ruby-on-rails.tmbundle/Support/bin/show_schema.rb b/bundles/ruby-on-rails.tmbundle/Support/bin/show_schema.rb new file mode 100644 index 000000000..621560558 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/bin/show_schema.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +# encoding: utf-8 + +require "#{ENV['TM_SUPPORT_PATH']}/lib/progress" +TextMate.call_with_progress(:title => "Contacting database", :message => "Fetching database schema…") do + + project = ENV['TM_PROJECT_DIRECTORY'] + word = ENV['TM_CURRENT_WORD'] + + require "#{project}/config/boot" + require "#{project}/config/environment" + + if word.blank? + STDOUT << "Place cursor on class name (or variation) to show its schema" + exit + end + + klass = word.camelcase.singularize.constantize rescue nil + if klass and klass.class == Class and klass.ancestors.include?(ActiveRecord::Base) + columns = klass.columns_hash + + data = [] + data += [%w[column primary sql_type default]] + data += [%w[------ ------- -------- -------]] + data += columns.collect { |col, attrs| [col, attrs.primary.to_s, attrs.sql_type.to_s, attrs.default.to_s] } + + STDOUT << data.inject('') do |output, array| + output + array.inject('') { |row_str, value| row_str + value.ljust(20) } + "\n" + end + elsif klass and klass.class == Class and not klass.ancestors.include?(ActiveRecord::Base) + STDOUT << "'#{word}' is not an Active Record derived class" + else + STDOUT << "'#{word}' was not recognised as a class" + end + +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/images/home.gif b/bundles/ruby-on-rails.tmbundle/Support/images/home.gif new file mode 100644 index 000000000..e7d6b4ff6 Binary files /dev/null and b/bundles/ruby-on-rails.tmbundle/Support/images/home.gif differ diff --git a/bundles/ruby-on-rails.tmbundle/Support/images/install.gif b/bundles/ruby-on-rails.tmbundle/Support/images/install.gif new file mode 100644 index 000000000..2f1ae91ad Binary files /dev/null and b/bundles/ruby-on-rails.tmbundle/Support/images/install.gif differ diff --git a/bundles/ruby-on-rails.tmbundle/Support/images/rails.png b/bundles/ruby-on-rails.tmbundle/Support/images/rails.png new file mode 100644 index 000000000..995a82326 Binary files /dev/null and b/bundles/ruby-on-rails.tmbundle/Support/images/rails.png differ diff --git a/bundles/ruby-on-rails.tmbundle/Support/images/svn.png b/bundles/ruby-on-rails.tmbundle/Support/images/svn.png new file mode 100644 index 000000000..47e067692 Binary files /dev/null and b/bundles/ruby-on-rails.tmbundle/Support/images/svn.png differ diff --git a/bundles/ruby-on-rails.tmbundle/Support/javascripts/prototype.js b/bundles/ruby-on-rails.tmbundle/Support/javascripts/prototype.js new file mode 100644 index 000000000..6385503a1 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/javascripts/prototype.js @@ -0,0 +1,4221 @@ +/* Prototype JavaScript framework, version 1.6.0.2 + * (c) 2005-2008 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.6.0.2', + + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, + MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + document.createElement('div').__proto__ && + document.createElement('div').__proto__ !== + document.createElement('form').__proto__ + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +/* Based on Alex Arnell's inheritance implementation. */ +var Class = { + create: function() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + var subclass = function() { }; + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0; i < properties.length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + + return klass; + } +}; + +Class.Methods = { + addMethods: function(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) + properties.push("toString", "valueOf"); + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames().first() == "$super") { + var method = value, value = Object.extend((function(m) { + return function() { return ancestor[m].apply(this, arguments) }; + })(property).wrap(method), { + valueOf: function() { return method }, + toString: function() { return method.toString() } + }); + } + this.prototype[property] = value; + } + + return this; + } +}; + +var Abstract = { }; + +Object.extend = function(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; +}; + +Object.extend(Object, { + inspect: function(object) { + try { + if (Object.isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + + toJSON: function(object) { + var type = typeof object; + switch (type) { + case 'undefined': + case 'function': + case 'unknown': return; + case 'boolean': return object.toString(); + } + + if (object === null) return 'null'; + if (object.toJSON) return object.toJSON(); + if (Object.isElement(object)) return; + + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (!Object.isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + }, + + toQueryString: function(object) { + return $H(object).toQueryString(); + }, + + toHTML: function(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({ }, object); + }, + + isElement: function(object) { + return object && object.nodeType == 1; + }, + + isArray: function(object) { + return object != null && typeof object == "object" && + 'splice' in object && 'join' in object; + }, + + isHash: function(object) { + return object instanceof Hash; + }, + + isFunction: function(object) { + return typeof object == "function"; + }, + + isString: function(object) { + return typeof object == "string"; + }, + + isNumber: function(object) { + return typeof object == "number"; + }, + + isUndefined: function(object) { + return typeof object == "undefined"; + } +}); + +Object.extend(Function.prototype, { + argumentNames: function() { + var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); + return names.length == 1 && !names[0] ? [] : names; + }, + + bind: function() { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } + }, + + bindAsEventListener: function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [event || window.event].concat(args)); + } + }, + + curry: function() { + if (!arguments.length) return this; + var __method = this, args = $A(arguments); + return function() { + return __method.apply(this, args.concat($A(arguments))); + } + }, + + delay: function() { + var __method = this, args = $A(arguments), timeout = args.shift() * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + }, + + wrap: function(wrapper) { + var __method = this; + return function() { + return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); + } + }, + + methodize: function() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + return __method.apply(null, [this].concat($A(arguments))); + }; + } +}); + +Function.prototype.defer = Function.prototype.delay.curry(0.01); + +Date.prototype.toJSON = function() { + return '"' + this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z"'; +}; + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + } finally { + this.currentlyExecuting = false; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var self = arguments.callee; + self.text.data = this; + return self.div.innerHTML; + }, + + unescapeHTML: function() { + var div = new Element('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? (div.childNodes.length > 1 ? + $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : + div.childNodes[0].nodeValue) : ''; + }, + + toQueryParams: function(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + }, + + toArray: function() { + return this.split(''); + }, + + succ: function() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + }, + + times: function(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + }, + + camelize: function() { + var parts = this.split('-'), len = parts.length; + if (len == 1) return parts[0]; + + var camelized = this.charAt(0) == '-' + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) + : parts[0]; + + for (var i = 1; i < len; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + + return camelized; + }, + + capitalize: function() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + }, + + underscore: function() { + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); + }, + + dasherize: function() { + return this.gsub(/_/,'-'); + }, + + inspect: function(useDoubleQuotes) { + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }, + + toJSON: function() { + return this.inspect(true); + }, + + unfilterJSON: function(filter) { + return this.sub(filter || Prototype.JSONFilter, '#{1}'); + }, + + isJSON: function() { + var str = this; + if (str.blank()) return false; + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + }, + + include: function(pattern) { + return this.indexOf(pattern) > -1; + }, + + startsWith: function(pattern) { + return this.indexOf(pattern) === 0; + }, + + endsWith: function(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + }, + + empty: function() { + return this == ''; + }, + + blank: function() { + return /^\s*$/.test(this); + }, + + interpolate: function(object, pattern) { + return new Template(this, pattern).evaluate(object); + } +}); + +if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(//g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +}; + +String.prototype.parseQuery = String.prototype.toQueryParams; + +Object.extend(String.prototype.escapeHTML, { + div: document.createElement('div'), + text: document.createTextNode('') +}); + +with (String.prototype.escapeHTML) div.appendChild(text); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return ''; + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = { + each: function(iterator, context) { + var index = 0; + iterator = iterator.bind(context); + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + }, + + all: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function(iterator, context) { + iterator = iterator.bind(context); + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(filter, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(filter); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator(value, index)); + }); + return results; + }, + + include: function(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator, context) { + iterator = iterator.bind(context); + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator, context) { + iterator = iterator.bind(context); + return this.map(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + size: function() { + return this.toArray().length; + }, + + inspect: function() { + return '#'; + } +}; + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + filter: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray, + every: Enumerable.all, + some: Enumerable.any +}); +function $A(iterable) { + if (!iterable) return []; + if (iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +if (Prototype.Browser.WebKit) { + $A = function(iterable) { + if (!iterable) return []; + if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && + iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; + }; +} + +Array.from = $A; + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(Object.isArray(value) ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + }, + + intersect: function(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + }, + + clone: function() { + return [].concat(this); + }, + + size: function() { + return this.length; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + }, + + toJSON: function() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +// use native browser JS 1.6 implementation if available +if (Object.isFunction(Array.prototype.forEach)) + Array.prototype._each = Array.prototype.forEach; + +if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; +}; + +if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; + var n = this.slice(0, i).reverse().indexOf(item); + return (n < 0) ? n : i - n - 1; +}; + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.Browser.Opera){ + Array.prototype.concat = function() { + var array = []; + for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); + for (var i = 0, length = arguments.length; i < length; i++) { + if (Object.isArray(arguments[i])) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + }; +} +Object.extend(Number.prototype, { + toColorPart: function() { + return this.toPaddedString(2, 16); + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + }, + + toPaddedString: function(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + }, + + toJSON: function() { + return isFinite(this) ? this.toString() : 'null'; + } +}); + +$w('abs round ceil floor').each(function(method){ + Number.prototype[method] = Math[method].methodize(); +}); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + return { + initialize: function(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + }, + + _each: function(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + set: function(key, value) { + return this._object[key] = value; + }, + + get: function(key) { + return this._object[key]; + }, + + unset: function(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + }, + + toObject: function() { + return Object.clone(this._object); + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + index: function(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + }, + + merge: function(object) { + return this.clone().update(object); + }, + + update: function(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) + return values.map(toQueryPair.curry(key)).join('&'); + } + return toQueryPair(key, values); + }).join('&'); + }, + + inspect: function() { + return '#'; + }, + + toJSON: function() { + return Object.toJSON(this.toObject()); + }, + + clone: function() { + return new Hash(this); + } + } +})()); + +Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; +Hash.from = $H; +var ObjectRange = Class.create(Enumerable, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); + +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isString(this.options.parameters)) + this.options.parameters = this.options.parameters.toQueryParams(); + else if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); + +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.clone(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Object.toQueryString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300); + }, + + getStatus: function() { + try { + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + json = decodeURIComponent(escape(json)); + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); +} + +if (Prototype.BrowserFeatures.XPath) { + document._getElementsByXPath = function(expression, parentElement) { + var results = []; + var query = document.evaluate(expression, $(parentElement) || document, + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, length = query.snapshotLength; i < length; i++) + results.push(Element.extend(query.snapshotItem(i))); + return results; + }; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Node) var Node = { }; + +if (!Node.ELEMENT_NODE) { + // DOM level 2 ECMAScript Language Binding + Object.extend(Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); +} + +(function() { + var element = this.Element; + this.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (Prototype.Browser.IE && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + }; + Object.extend(this.Element, element || { }); +}).call(window); + +Element.cache = { }; + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; + }, + + hide: function(element) { + $(element).style.display = 'none'; + return element; + }, + + show: function(element) { + $(element).style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + content = Object.toHTML(content); + element.innerHTML = content.stripScripts(); + content.evalScripts.bind(content).defer(); + return element; + }, + + replace: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + element.parentNode.replaceChild(content, element); + return element; + }, + + insert: function(element, insertions) { + element = $(element); + + if (Object.isString(insertions) || Object.isNumber(insertions) || + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) + insertions = {bottom:insertions}; + + var content, insert, tagName, childNodes; + + for (var position in insertions) { + content = insertions[position]; + position = position.toLowerCase(); + insert = Element._insertionTranslations[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + insert(element, content); + continue; + } + + content = Object.toHTML(content); + + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); + + content.evalScripts.bind(content).defer(); + } + + return element; + }, + + wrap: function(element, wrapper, attributes) { + element = $(element); + if (Object.isElement(wrapper)) + $(wrapper).writeAttribute(attributes || { }); + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); + else wrapper = new Element('div', wrapper); + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + wrapper.appendChild(element); + return wrapper; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + return $(element).select("*"); + }, + + firstDescendant: function(element) { + element = $(element).firstChild; + while (element && element.nodeType != 1) element = element.nextSibling; + return $(element); + }, + + immediateDescendants: function(element) { + if (!(element = $(element).firstChild)) return []; + while (element && element.nodeType != 1) element = element.nextSibling; + if (element) return [element].concat($(element).nextSiblings()); + return []; + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + if (Object.isString(selector)) + selector = new Selector(selector); + return selector.match($(element)); + }, + + up: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(element.parentNode); + var ancestors = element.ancestors(); + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? element.descendants()[expression] : + element.select(expression)[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + select: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + adjacent: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = element.readAttribute('id'), self = arguments.callee; + if (id) return id; + do { id = 'anonymous_element_' + self.counter++ } while ($(id)); + element.writeAttribute('id', id); + return id; + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + var t = Element._attributeTranslations.read; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + if (name.include(':')) { + return (!element.attributes || !element.attributes[name]) ? null : + element.attributes[name].value; + } + } + return element.getAttribute(name); + }, + + writeAttribute: function(element, name, value) { + element = $(element); + var attributes = { }, t = Element._attributeTranslations.write; + + if (typeof name == 'object') attributes = name; + else attributes[name] = Object.isUndefined(value) ? true : value; + + for (var attr in attributes) { + name = t.names[attr] || attr; + value = attributes[attr]; + if (t.values[attr]) name = t.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + return element; + }, + + getHeight: function(element) { + return $(element).getDimensions().height; + }, + + getWidth: function(element) { + return $(element).getDimensions().width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + return (elementClassName.length > 0 && (elementClassName == className || + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + if (!element.hasClassName(className)) + element.className += (element.className ? ' ' : '') + className; + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + element.className = element.className.replace( + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + return element[element.hasClassName(className) ? + 'removeClassName' : 'addClassName'](className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + var node = element.firstChild; + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + }, + + empty: function(element) { + return $(element).innerHTML.blank(); + }, + + descendantOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + var originalAncestor = ancestor; + + if (element.compareDocumentPosition) + return (element.compareDocumentPosition(ancestor) & 8) === 8; + + if (element.sourceIndex && !Prototype.Browser.Opera) { + var e = element.sourceIndex, a = ancestor.sourceIndex, + nextAncestor = ancestor.nextSibling; + if (!nextAncestor) { + do { ancestor = ancestor.parentNode; } + while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); + } + if (nextAncestor && nextAncestor.sourceIndex) + return (e > a && e < nextAncestor.sourceIndex); + } + + while (element = element.parentNode) + if (element == originalAncestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = element.cumulativeOffset(); + window.scrollTo(pos[0], pos[1]); + return element; + }, + + getStyle: function(element, style) { + element = $(element); + style = style == 'float' ? 'cssFloat' : style.camelize(); + var value = element.style[style]; + if (!value) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + if (style == 'opacity') return value ? parseFloat(value) : 1.0; + return value == 'auto' ? null : value; + }, + + getOpacity: function(element) { + return $(element).getStyle('opacity'); + }, + + setStyle: function(element, styles) { + element = $(element); + var elementStyle = element.style, match; + if (Object.isString(styles)) { + element.style.cssText += ';' + styles; + return styles.include('opacity') ? + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; + } + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]); + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : + property] = styles[property]; + + return element; + }, + + setOpacity: function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + return element; + }, + + getDimensions: function(element) { + element = $(element); + var display = $(element).getStyle('display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = 'block'; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = originalDisplay; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + return element; + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + return element; + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return element; + element._overflow = Element.getStyle(element, 'overflow') || 'auto'; + if (element._overflow !== 'hidden') + element.style.overflow = 'hidden'; + return element; + }, + + undoClipping: function(element) { + element = $(element); + if (!element._overflow) return element; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + element._overflow = null; + return element; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (element.tagName == 'BODY') break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + absolutize: function(element) { + element = $(element); + if (element.getStyle('position') == 'absolute') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + var offsets = element.positionedOffset(); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.width = width + 'px'; + element.style.height = height + 'px'; + return element; + }, + + relativize: function(element) { + element = $(element); + if (element.getStyle('position') == 'relative') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + return element; + }, + + cumulativeScrollOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + getOffsetParent: function(element) { + if (element.offsetParent) return $(element.offsetParent); + if (element == document.body) return $(element); + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return $(element); + + return $(document.body); + }, + + viewportOffset: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent == document.body && + Element.getStyle(element, 'position') == 'absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!Prototype.Browser.Opera || element.tagName == 'BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return Element._returnOffset(valueL, valueT); + }, + + clonePosition: function(element, source) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || { }); + + // find page position of source + source = $(source); + var p = source.viewportOffset(); + + // find coordinate system to use + element = $(element); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(element, 'position') == 'absolute') { + parent = element.getOffsetParent(); + delta = parent.viewportOffset(); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if (options.setWidth) element.style.width = source.offsetWidth + 'px'; + if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + return element; + } +}; + +Element.Methods.identify.counter = 1; + +Object.extend(Element.Methods, { + getElementsBySelector: Element.Methods.select, + childElements: Element.Methods.immediateDescendants +}); + +Element._attributeTranslations = { + write: { + names: { + className: 'class', + htmlFor: 'for' + }, + values: { } + } +}; + +if (Prototype.Browser.Opera) { + Element.Methods.getStyle = Element.Methods.getStyle.wrap( + function(proceed, element, style) { + switch (style) { + case 'left': case 'top': case 'right': case 'bottom': + if (proceed(element, 'position') === 'static') return null; + case 'height': case 'width': + // returns '0px' for hidden elements; we want it to return null + if (!Element.visible(element)) return null; + + // returns the border-box dimensions rather than the content-box + // dimensions, so we subtract padding and borders from the value + var dim = parseInt(proceed(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + var properties; + if (style === 'height') { + properties = ['border-top-width', 'padding-top', + 'padding-bottom', 'border-bottom-width']; + } + else { + properties = ['border-left-width', 'padding-left', + 'padding-right', 'border-right-width']; + } + return properties.inject(dim, function(memo, property) { + var val = proceed(element, property); + return val === null ? memo : memo - parseInt(val, 10); + }) + 'px'; + default: return proceed(element, style); + } + } + ); + + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( + function(proceed, element, attribute) { + if (attribute === 'title') return element.title; + return proceed(element, attribute); + } + ); +} + +else if (Prototype.Browser.IE) { + // IE doesn't report offsets correctly for static elements, so we change them + // to "relative" to get the values, then change them back. + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { + Element.Methods[method] = Element.Methods[method].wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + // Trigger hasLayout on the offset parent so that IE6 reports + // accurate offsetTop and offsetLeft values for position: fixed. + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + }); + + Element.Methods.getStyle = function(element, style) { + element = $(element); + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); + var value = element.style[style]; + if (!value && element.currentStyle) value = element.currentStyle[style]; + + if (style == 'opacity') { + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) + if (value[1]) return parseFloat(value[1]) / 100; + return 1.0; + } + + if (value == 'auto') { + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) + return element['offset' + style.capitalize()] + 'px'; + return null; + } + return value; + }; + + Element.Methods.setOpacity = function(element, value) { + function stripAlpha(filter){ + return filter.replace(/alpha\([^\)]*\)/gi,''); + } + element = $(element); + var currentStyle = element.currentStyle; + if ((currentStyle && !currentStyle.hasLayout) || + (!currentStyle && element.style.zoom == 'normal')) + element.style.zoom = 1; + + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + (filter = stripAlpha(filter)) ? + style.filter = filter : style.removeAttribute('filter'); + return element; + } else if (value < 0.00001) value = 0; + style.filter = stripAlpha(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + Element._attributeTranslations = { + read: { + names: { + 'class': 'className', + 'for': 'htmlFor' + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: function(element, attribute) { + attribute = element.getAttribute(attribute); + return attribute ? attribute.toString().slice(23, -2) : null; + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + } + }; + + Element._attributeTranslations.write = { + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + Element._attributeTranslations.has = {}; + + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + + 'encType maxLength readOnly longDesc').each(function(attr) { + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; + Element._attributeTranslations.has[attr.toLowerCase()] = attr; + }); + + (function(v) { + Object.extend(v, { + href: v._getAttr, + src: v._getAttr, + type: v._getAttr, + action: v._getAttrNode, + disabled: v._flag, + checked: v._flag, + readonly: v._flag, + multiple: v._flag, + onload: v._getEv, + onunload: v._getEv, + onclick: v._getEv, + ondblclick: v._getEv, + onmousedown: v._getEv, + onmouseup: v._getEv, + onmouseover: v._getEv, + onmousemove: v._getEv, + onmouseout: v._getEv, + onfocus: v._getEv, + onblur: v._getEv, + onkeypress: v._getEv, + onkeydown: v._getEv, + onkeyup: v._getEv, + onsubmit: v._getEv, + onreset: v._getEv, + onselect: v._getEv, + onchange: v._getEv + }); + })(Element._attributeTranslations.read.values); +} + +else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +else if (Prototype.Browser.WebKit) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + + if (value == 1) + if(element.tagName == 'IMG' && element.width) { + element.width++; element.width--; + } else try { + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch (e) { } + + return element; + }; + + // Safari returns margins on body which is incorrect if the child is absolutely + // positioned. For performance reasons, redefine Element#cumulativeOffset for + // KHTML/WebKit only. + Element.Methods.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return Element._returnOffset(valueL, valueT); + }; +} + +if (Prototype.Browser.IE || Prototype.Browser.Opera) { + // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements + Element.Methods.update = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName in Element._insertionTranslations.tags) { + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { element.appendChild(node) }); + } + else element.innerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +if ('outerHTML' in document.createElement('div')) { + Element.Methods.replace = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (Element._insertionTranslations.tags[tagName]) { + var nextSibling = element.next(); + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + parent.removeChild(element); + if (nextSibling) + fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); + else + fragments.each(function(node) { parent.appendChild(node) }); + } + else element.outerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +Element._returnOffset = function(l, t) { + var result = [l, t]; + result.left = l; + result.top = t; + return result; +}; + +Element._getContentFromAnonymousElement = function(tagName, html) { + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; + return $A(div.childNodes); +}; + +Element._insertionTranslations = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + tags: { + TABLE: ['', '
', 1], + TBODY: ['', '
', 2], + TR: ['', '
', 3], + TD: ['
', '
', 4], + SELECT: ['', 1] + } +}; + +(function() { + Object.extend(this.tags, { + THEAD: this.tags.TBODY, + TFOOT: this.tags.TBODY, + TH: this.tags.TD + }); +}).call(Element._insertionTranslations); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + attribute = Element._attributeTranslations.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return node && node.specified; + } +}; + +Element.Methods.ByTag = { }; + +Object.extend(Element, Element.Methods); + +if (!Prototype.BrowserFeatures.ElementExtensions && + document.createElement('div').__proto__) { + window.HTMLElement = { }; + window.HTMLElement.prototype = document.createElement('div').__proto__; + Prototype.BrowserFeatures.ElementExtensions = true; +} + +Element.extend = (function() { + if (Prototype.BrowserFeatures.SpecificElementExtensions) + return Prototype.K; + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || element._extendedByPrototype || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName, property, value; + + // extend methods for specific tags + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + for (property in methods) { + value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + // extend methods for all tags (Safari doesn't need this) + if (!Prototype.BrowserFeatures.ElementExtensions) { + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + } + } + }); + + extend.refresh(); + return extend; +})(); + +Element.hasAttribute = function(element, attribute) { + if (element.hasAttribute) return element.hasAttribute(attribute); + return Element.Methods.Simulated.hasAttribute(element, attribute); +}; + +Element.addMethods = function(methods) { + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; + + if (!methods) { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods) + }); + } + + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || { }); + else { + if (Object.isArray(tagName)) tagName.each(extend); + else extend(tagName); + } + + function extend(tagName) { + tagName = tagName.toUpperCase(); + if (!Element.Methods.ByTag[tagName]) + Element.Methods.ByTag[tagName] = { }; + Object.extend(Element.Methods.ByTag[tagName], methods); + } + + function copy(methods, destination, onlyIfAbsent) { + onlyIfAbsent = onlyIfAbsent || false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + window[klass] = { }; + window[klass].prototype = document.createElement(tagName).__proto__; + return window[klass]; + } + + if (F.ElementExtensions) { + copy(Element.Methods, HTMLElement.prototype); + copy(Element.Methods.Simulated, HTMLElement.prototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; + + if (Element.extend.refresh) Element.extend.refresh(); + Element.cache = { }; +}; + +document.viewport = { + getDimensions: function() { + var dimensions = { }; + var B = Prototype.Browser; + $w('width height').each(function(d) { + var D = d.capitalize(); + dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : + (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; + }); + return dimensions; + }, + + getWidth: function() { + return this.getDimensions().width; + }, + + getHeight: function() { + return this.getDimensions().height; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style + * license. Please see http://www.yui-ext.com/ for more information. */ + +var Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + shouldUseXPath: function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + // Safari 3 chokes on :*-of-type and :empty + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + // XPath can't do namespaced attributes, nor can it read + // the "checked" property from DOM nodes + if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) + return false; + + return true; + }, + + compileMatcher: function() { + if (this.shouldUseXPath()) + return this.compileXPathMatcher(); + + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m; + + if (Selector._cache[e]) { + this.matcher = Selector._cache[e]; + return; + } + + this.matcher = ["this.matcher = function(root) {", + "var r = root, h = Selector.handlers, c = false, n;"]; + + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : + new Template(c[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.matcher.push("return h.unique(n);\n}"); + eval(this.matcher.join('\n')); + Selector._cache[this.expression] = this.matcher; + }, + + compileXPathMatcher: function() { + var e = this.expression, ps = Selector.patterns, + x = Selector.xpath, le, m; + + if (Selector._cache[e]) { + this.xpath = Selector._cache[e]; return; + } + + this.matcher = ['.//*']; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + if (m = e.match(ps[i])) { + this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : + new Template(x[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.xpath = this.matcher.join(''); + Selector._cache[this.expression] = this.xpath; + }, + + findElements: function(root) { + root = root || document; + if (this.xpath) return document._getElementsByXPath(this.xpath, root); + return this.matcher(root); + }, + + match: function(element) { + this.tokens = []; + + var e = this.expression, ps = Selector.patterns, as = Selector.assertions; + var le, p, m; + + while (e && le !== e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + // use the Selector.assertions methods unless the selector + // is too complex. + if (as[i]) { + this.tokens.push([i, Object.clone(m)]); + e = e.replace(m[0], ''); + } else { + // reluctantly do a document-wide search + // and look for a match in the array + return this.findElements(document).include(element); + } + } + } + } + + var match = true, name, matches; + for (var i = 0, token; token = this.tokens[i]; i++) { + name = token[0], matches = token[1]; + if (!Selector.assertions[name](element, matches)) { + match = false; break; + } + } + + return match; + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } +}); + +Object.extend(Selector, { + _cache: { }, + + xpath: { + descendant: "//*", + child: "/*", + adjacent: "/following-sibling::*[1]", + laterSibling: '/following-sibling::*', + tagName: function(m) { + if (m[1] == '*') return ''; + return "[local-name()='" + m[1].toLowerCase() + + "' or local-name()='" + m[1].toUpperCase() + "']"; + }, + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", + id: "[@id='#{1}']", + attrPresence: function(m) { + m[1] = m[1].toLowerCase(); + return new Template("[@#{1}]").evaluate(m); + }, + attr: function(m) { + m[1] = m[1].toLowerCase(); + m[3] = m[5] || m[6]; + return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + }, + pseudo: function(m) { + var h = Selector.xpath.pseudos[m[1]]; + if (!h) return ''; + if (Object.isFunction(h)) return h(m); + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + }, + operators: { + '=': "[@#{1}='#{3}']", + '!=': "[@#{1}!='#{3}']", + '^=': "[starts-with(@#{1}, '#{3}')]", + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", + '*=': "[contains(@#{1}, '#{3}')]", + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + }, + pseudos: { + 'first-child': '[not(preceding-sibling::*)]', + 'last-child': '[not(following-sibling::*)]', + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'checked': "[@checked]", + 'disabled': "[@disabled]", + 'enabled': "[not(@disabled)]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); + exclusion.push("(" + v.substring(1, v.length - 1) + ")"); + e = e.replace(m[0], ''); + break; + } + } + } + return "[not(" + exclusion.join(" and ") + ")]"; + }, + 'nth-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + }, + 'nth-last-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + }, + 'nth-of-type': function(m) { + return Selector.xpath.pseudos.nth("position() ", m); + }, + 'nth-last-of-type': function(m) { + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + }, + 'first-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + }, + 'last-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + }, + 'only-of-type': function(m) { + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + }, + nth: function(fragment, m) { + var mm, formula = m[6], predicate; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + if (mm = formula.match(/^(\d+)$/)) // digit only + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; + var a = mm[1] ? Number(mm[1]) : 1; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); + } + } + } + }, + + criteria: { + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); + }, + pseudo: function(m) { + if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + }, + descendant: 'c = "descendant";', + child: 'c = "child";', + adjacent: 'c = "adjacent";', + laterSibling: 'c = "laterSibling";' + }, + + patterns: { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: +/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, + attrPresence: /^\[([\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + // for Selector.match and Element#match + assertions: { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }, + + handlers: { + // UTILITY FUNCTIONS + // joins two collections + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + // marks an array of nodes for counting + mark: function(nodes) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = undefined; + return nodes; + }, + + // mark each child node with its position (for nth calls) + // "ofType" flag indicates whether we're indexing for nth-of-type + // rather than nth-child + index: function(parentNode, reverse, ofType) { + parentNode._countedByPrototype = Prototype.emptyFunction; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + var node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + }, + + // filters out duplicates and extends all nodes + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (!(n = nodes[i])._countedByPrototype) { + n._countedByPrototype = Prototype.emptyFunction; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + // COMBINATOR FUNCTIONS + descendant: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName('*')); + return results; + }, + + child: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } + return results; + }, + + adjacent: function(nodes) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + var next = this.nextElementSibling(node); + if (next) results.push(next); + } + return results; + }, + + laterSibling: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, Element.nextSiblings(node)); + return results; + }, + + nextElementSibling: function(node) { + while (node = node.nextSibling) + if (node.nodeType == 1) return node; + return null; + }, + + previousElementSibling: function(node) { + while (node = node.previousSibling) + if (node.nodeType == 1) return node; + return null; + }, + + // TOKEN FUNCTIONS + tagName: function(nodes, root, tagName, combinator) { + var uTagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + // fastlane for ordinary descendant combinators + if (combinator == "descendant") { + for (var i = 0, node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName(tagName)); + return results; + } else nodes = this[combinator](nodes); + if (tagName == "*") return nodes; + } + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName.toUpperCase() === uTagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!targetNode) return []; + if (!nodes && root == document) return [targetNode]; + if (nodes) { + if (combinator) { + if (combinator == 'child') { + for (var i = 0, node; node = nodes[i]; i++) + if (targetNode.parentNode == node) return [targetNode]; + } else if (combinator == 'descendant') { + for (var i = 0, node; node = nodes[i]; i++) + if (Element.descendantOf(targetNode, node)) return [targetNode]; + } else if (combinator == 'adjacent') { + for (var i = 0, node; node = nodes[i]; i++) + if (Selector.handlers.previousElementSibling(targetNode) == node) + return [targetNode]; + } else nodes = h[combinator](nodes); + } + for (var i = 0, node; node = nodes[i]; i++) + if (node == targetNode) return [targetNode]; + return []; + } + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; + }, + + className: function(nodes, root, className, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + return Selector.handlers.byClassName(nodes, root, className); + }, + + byClassName: function(nodes, root, className) { + if (!nodes) nodes = Selector.handlers.descendant([root]); + var needle = ' ' + className + ' '; + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { + nodeClassName = node.className; + if (nodeClassName.length == 0) continue; + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) + results.push(node); + } + return results; + }, + + attrPresence: function(nodes, root, attr, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + var results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (Element.hasAttribute(node, attr)) results.push(node); + return results; + }, + + attr: function(nodes, root, attr, value, operator, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + var handler = Selector.operators[operator], results = []; + for (var i = 0, node; node = nodes[i]; i++) { + var nodeValue = Element.readAttribute(node, attr); + if (nodeValue === null) continue; + if (handler(nodeValue, value)) results.push(node); + } + return results; + }, + + pseudo: function(nodes, name, value, root, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + if (!nodes) nodes = root.getElementsByTagName("*"); + return Selector.pseudos[name](nodes, value, root); + } + }, + + pseudos: { + 'first-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.previousElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'last-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.nextElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'only-child': function(nodes, value, root) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) + results.push(node); + return results; + }, + 'nth-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root); + }, + 'nth-last-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true); + }, + 'nth-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, false, true); + }, + 'nth-last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true, true); + }, + 'first-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, false, true); + }, + 'last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, true, true); + }, + 'only-of-type': function(nodes, formula, root) { + var p = Selector.pseudos; + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); + }, + + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type + nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + var h = Selector.handlers, results = [], indexed = [], m; + h.mark(nodes); + for (var i = 0, node; node = nodes[i]; i++) { + if (!node.parentNode._countedByPrototype) { + h.index(node.parentNode, reverse, ofType); + indexed.push(node.parentNode); + } + } + if (formula.match(/^\d+$/)) { // just a number + formula = Number(formula); + for (var i = 0, node; node = nodes[i]; i++) + if (node.nodeIndex == formula) results.push(node); + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; + var a = m[1] ? Number(m[1]) : 1; + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } + } + h.unmark(nodes); + h.unmark(indexed); + return results; + }, + + 'empty': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + // IE treats comments as element nodes + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + results.push(node); + } + return results; + }, + + 'not': function(nodes, selector, root) { + var h = Selector.handlers, selectorType, m; + var exclusions = new Selector(selector).findElements(root); + h.mark(exclusions); + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node._countedByPrototype) results.push(node); + h.unmark(exclusions); + return results; + }, + + 'enabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node.disabled) results.push(node); + return results; + }, + + 'disabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.disabled) results.push(node); + return results; + }, + + 'checked': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.checked) results.push(node); + return results; + } + }, + + operators: { + '=': function(nv, v) { return nv == v; }, + '!=': function(nv, v) { return nv != v; }, + '^=': function(nv, v) { return nv.startsWith(v); }, + '$=': function(nv, v) { return nv.endsWith(v); }, + '*=': function(nv, v) { return nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + }, + + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + + matchElements: function(elements, expression) { + var matches = $$(expression), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._countedByPrototype) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (Object.isNumber(expression)) { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + expressions = Selector.split(expressions.join(',')); + var results = [], h = Selector.handlers; + for (var i = 0, l = expressions.length, selector; i < l; i++) { + selector = new Selector(expressions[i].strip()); + h.concat(results, selector.findElements(element)); + } + return (l > 1) ? h.unique(results) : results; + } +}); + +if (Prototype.Browser.IE) { + Object.extend(Selector.handlers, { + // IE returns comment nodes on getElementsByTagName("*"). + // Filter them out. + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + }, + + // IE improperly serializes _countedByPrototype in (inner|outer)HTML. + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit; + + var data = elements.inject({ }, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + if (key in result) { + // a key is already present; construct an array of values + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return options.hash ? data : Object.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + getElements: function(form) { + return $A($(form).getElementsByTagName('*')).inject([], + function(elements, child) { + if (Form.Element.Serializers[child.tagName.toLowerCase()]) + elements.push(Element.extend(child)); + return elements; + } + ); + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !['button', 'reset', 'submit'].include(element.type))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.blur(); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element, value); + default: + return Form.Element.Serializers.textarea(element, value); + } + }, + + inputSelector: function(element, value) { + if (Object.isUndefined(value)) return element.checked ? element.value : null; + else element.checked = !!value; + }, + + textarea: function(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + }, + + select: function(element, index) { + if (Object.isUndefined(index)) + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + else { + var opt, value, single = !Object.isArray(index); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + value = this.optionValue(opt); + if (single) { + if (value == index) { + opt.selected = true; + return; + } + } + else opt.selected = index.include(value); + } + } + }, + + selectOne: function(element) { + var index = element.selectedIndex; + return index >= 0 ? this.optionValue(element.options[index]) : null; + }, + + selectMany: function(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(this.optionValue(opt)); + } + return values; + }, + + optionValue: function(opt) { + // extend element because hasAttribute may not be native + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +}; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) var Event = { }; + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45, + + cache: { }, + + relatedTarget: function(event) { + var element; + switch(event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } +}); + +Event.Methods = (function() { + var isButton; + + if (Prototype.Browser.IE) { + var buttonMap = { 0: 1, 1: 4, 2: 2 }; + isButton = function(event, code) { + return event.button == buttonMap[code]; + }; + + } else if (Prototype.Browser.WebKit) { + isButton = function(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; + default: return false; + } + }; + + } else { + isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } + + return { + isLeftClick: function(event) { return isButton(event, 0) }, + isMiddleClick: function(event) { return isButton(event, 1) }, + isRightClick: function(event) { return isButton(event, 2) }, + + element: function(event) { + var node = Event.extend(event).target; + return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); + }, + + findElement: function(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + }, + + pointer: function(event) { + return { + x: event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)), + y: event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)) + }; + }, + + pointerX: function(event) { return Event.pointer(event).x }, + pointerY: function(event) { return Event.pointer(event).y }, + + stop: function(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + event.stopped = true; + } + }; +})(); + +Event.extend = (function() { + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return "[object Event]" } + }); + + return function(event) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + Object.extend(event, { + target: event.srcElement, + relatedTarget: Event.relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + return Object.extend(event, methods); + }; + + } else { + Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; + Object.extend(Event.prototype, methods); + return Prototype.K; + } +})(); + +Object.extend(Event, (function() { + var cache = Event.cache; + + function getEventID(element) { + if (element._prototypeEventID) return element._prototypeEventID[0]; + arguments.callee.id = arguments.callee.id || 1; + return element._prototypeEventID = [++arguments.callee.id]; + } + + function getDOMEventName(eventName) { + if (eventName && eventName.include(':')) return "dataavailable"; + return eventName; + } + + function getCacheForID(id) { + return cache[id] = cache[id] || { }; + } + + function getWrappersForEventName(id, eventName) { + var c = getCacheForID(id); + return c[eventName] = c[eventName] || []; + } + + function createWrapper(element, eventName, handler) { + var id = getEventID(element); + var c = getWrappersForEventName(id, eventName); + if (c.pluck("handler").include(handler)) return false; + + var wrapper = function(event) { + if (!Event || !Event.extend || + (event.eventName && event.eventName != eventName)) + return false; + + Event.extend(event); + handler.call(element, event); + }; + + wrapper.handler = handler; + c.push(wrapper); + return wrapper; + } + + function findWrapper(id, eventName, handler) { + var c = getWrappersForEventName(id, eventName); + return c.find(function(wrapper) { return wrapper.handler == handler }); + } + + function destroyWrapper(id, eventName, handler) { + var c = getCacheForID(id); + if (!c[eventName]) return false; + c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); + } + + function destroyCache() { + for (var id in cache) + for (var eventName in cache[id]) + cache[id][eventName] = null; + } + + if (window.attachEvent) { + window.attachEvent("onunload", destroyCache); + } + + return { + observe: function(element, eventName, handler) { + element = $(element); + var name = getDOMEventName(eventName); + + var wrapper = createWrapper(element, eventName, handler); + if (!wrapper) return element; + + if (element.addEventListener) { + element.addEventListener(name, wrapper, false); + } else { + element.attachEvent("on" + name, wrapper); + } + + return element; + }, + + stopObserving: function(element, eventName, handler) { + element = $(element); + var id = getEventID(element), name = getDOMEventName(eventName); + + if (!handler && eventName) { + getWrappersForEventName(id, eventName).each(function(wrapper) { + element.stopObserving(eventName, wrapper.handler); + }); + return element; + + } else if (!eventName) { + Object.keys(getCacheForID(id)).each(function(eventName) { + element.stopObserving(eventName); + }); + return element; + } + + var wrapper = findWrapper(id, eventName, handler); + if (!wrapper) return element; + + if (element.removeEventListener) { + element.removeEventListener(name, wrapper, false); + } else { + element.detachEvent("on" + name, wrapper); + } + + destroyWrapper(id, eventName, handler); + + return element; + }, + + fire: function(element, eventName, memo) { + element = $(element); + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + + var event; + if (document.createEvent) { + event = document.createEvent("HTMLEvents"); + event.initEvent("dataavailable", true, true); + } else { + event = document.createEventObject(); + event.eventType = "ondataavailable"; + } + + event.eventName = eventName; + event.memo = memo || { }; + + if (document.createEvent) { + element.dispatchEvent(event); + } else { + element.fireEvent(event.eventType, event); + } + + return Event.extend(event); + } + }; +})()); + +Object.extend(Event, Event.Methods); + +Element.addMethods({ + fire: Event.fire, + observe: Event.observe, + stopObserving: Event.stopObserving +}); + +Object.extend(document, { + fire: Element.Methods.fire.methodize(), + observe: Element.Methods.observe.methodize(), + stopObserving: Element.Methods.stopObserving.methodize(), + loaded: false +}); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards and John Resig. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearInterval(timer); + document.fire("dom:loaded"); + document.loaded = true; + } + + if (document.addEventListener) { + if (Prototype.Browser.WebKit) { + timer = window.setInterval(function() { + if (/loaded|complete/.test(document.readyState)) + fireContentLoadedEvent(); + }, 0); + + Event.observe(window, "load", fireContentLoadedEvent); + + } else { + document.addEventListener("DOMContentLoaded", + fireContentLoadedEvent, false); + } + + } else { + document.write(" + Install a Plugin + + +
+
+
+ +
+

Plugin Search

+
+ + +
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/admin/base_controller.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/admin/base_controller.rb new file mode 100644 index 000000000..1737b31d2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/admin/base_controller.rb @@ -0,0 +1,9 @@ +class Admin::BaseController < ApplicationController + def edit + @user = User.new(params[:user]) + end + + def update + @user = User.new(params[:user]) + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/posts_controller.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/posts_controller.rb new file mode 100644 index 000000000..0ad983a82 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/posts_controller.rb @@ -0,0 +1,24 @@ +class PostsController < ApplicationController + def new + @post = Post.new + end + + def create + @post = Post.new(params[:post]) + end + + def index + respond_to do |wants| + wants.html { } + wants.xml { } + wants.js { } + wants.wacky { } + end + respond_to do |format| + format.html { } + end + end + + def edit + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/user_controller.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/user_controller.rb new file mode 100644 index 000000000..62e416e19 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/user_controller.rb @@ -0,0 +1,9 @@ +class UserController < ApplicationController + def new + @user = User.new + end + + def create + @user = User.new(params[:user]) + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/users_controller.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/users_controller.rb new file mode 100644 index 000000000..e382ffd1d --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/controllers/users_controller.rb @@ -0,0 +1,31 @@ +class UsersController < ApplicationController + def new + @user = User.new + end + + def create + @user = User.new(params[:user]) + end + + def no_existing_views + respond_to do |wants| + wants.html { } # format with an inline block + wants.js do + # format with a multi-line block + end + wants.xml # format without a block + wants.wacky # non-standard format + end + end + + def existing_views + respond_to do |wants| + wants.html { } # format with an inline block + wants.js do + # format with a multi-line block + end + wants.xml # format without a block + wants.wacky # non-standard format + end + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/helpers/user_helper.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/helpers/user_helper.rb new file mode 100644 index 000000000..e3ac4a23e --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/helpers/user_helper.rb @@ -0,0 +1,2 @@ +module UserHelper +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/notifier.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/notifier.rb new file mode 100644 index 000000000..35c3c2061 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/notifier.rb @@ -0,0 +1,6 @@ +class Notifier < ActionMailer::Base + def forgot_password(user, url) + # Email header info + @subject += "Forgotten password notification" + end +end diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/person.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/person.rb new file mode 100644 index 000000000..e64a82621 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/person.rb @@ -0,0 +1,2 @@ +class Person < ActiveRecord::Base +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/user.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/user.rb new file mode 100644 index 000000000..431cf7baf --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/models/user.rb @@ -0,0 +1,2 @@ +class User < ActiveRecord::Base +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/admin/base/action.html.erb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/admin/base/action.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/notifier/forgot_password.html.erb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/notifier/forgot_password.html.erb new file mode 100644 index 000000000..477e4f334 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/notifier/forgot_password.html.erb @@ -0,0 +1,15 @@ +Dear <%= @name %>, + +

So you forgot your password to the family Website? No problem! Here's what we need to do in that case:

+ +
    +
  1. Request this email (DONE!)
  2. +
  3. Click on this link to reset your password
  4. +
  5. And you'll get a new password!
  6. +
+ +

NOTE: You'll also need your sign-in name, which is '<%= @login %>'.

+ +

Remember that you can always change your password after you sign in by visiting your account information.

+ +<%= @url %> \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/user/new.rhtml b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/user/new.rhtml new file mode 100644 index 000000000..e69de29bb diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/create.html.erb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/create.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.html.erb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.html.erb new file mode 100644 index 000000000..1c538fb9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.html.erb @@ -0,0 +1 @@ +existing_views \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.js.rjs b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.js.rjs new file mode 100644 index 000000000..1c538fb9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.js.rjs @@ -0,0 +1 @@ +existing_views \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.wacky.erb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.wacky.erb new file mode 100644 index 000000000..1c538fb9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.wacky.erb @@ -0,0 +1 @@ +existing_views \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.xml.builder b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.xml.builder new file mode 100644 index 000000000..1c538fb9f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/existing_views.xml.builder @@ -0,0 +1 @@ +existing_views \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/new.html.erb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/app/views/users/new.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/config/boot.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/config/boot.rb new file mode 100644 index 000000000..5697cc1bc --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/config/boot.rb @@ -0,0 +1,109 @@ +# Don't change this file! +# Configure your app in config/environment.rb and config/environments/*.rb + +RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) + +module Rails + class << self + def boot! + unless booted? + preinitialize + pick_boot.run + end + end + + def booted? + defined? Rails::Initializer + end + + def pick_boot + (vendor_rails? ? VendorBoot : GemBoot).new + end + + def vendor_rails? + File.exist?("#{RAILS_ROOT}/vendor/rails") + end + + # FIXME : Ruby 1.9 + def preinitialize + load(preinitializer_path) if File.exists?(preinitializer_path) + end + + def preinitializer_path + "#{RAILS_ROOT}/config/preinitializer.rb" + end + end + + class Boot + def run + load_initializer + Rails::Initializer.run(:set_load_path) + end + end + + class VendorBoot < Boot + def load_initializer + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" + end + end + + class GemBoot < Boot + def load_initializer + self.class.load_rubygems + load_rails_gem + require 'initializer' + end + + def load_rails_gem + if version = self.class.gem_version + gem 'rails', version + else + gem 'rails' + end + rescue Gem::LoadError => load_error + $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) + exit 1 + end + + class << self + def rubygems_version + Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion + end + + def gem_version + if defined? RAILS_GEM_VERSION + RAILS_GEM_VERSION + elsif ENV.include?('RAILS_GEM_VERSION') + ENV['RAILS_GEM_VERSION'] + else + parse_gem_version(read_environment_rb) + end + end + + def load_rubygems + require 'rubygems' + + unless rubygems_version >= '0.9.4' + $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.) + exit 1 + end + + rescue LoadError + $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org) + exit 1 + end + + def parse_gem_version(text) + $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/ + end + + private + def read_environment_rb + File.read("#{RAILS_ROOT}/config/environment.rb") + end + end + end +end + +# All that for this: +Rails.boot! diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/config/environment.rb b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/config/environment.rb new file mode 100644 index 000000000..5697cc1bc --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/config/environment.rb @@ -0,0 +1,109 @@ +# Don't change this file! +# Configure your app in config/environment.rb and config/environments/*.rb + +RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) + +module Rails + class << self + def boot! + unless booted? + preinitialize + pick_boot.run + end + end + + def booted? + defined? Rails::Initializer + end + + def pick_boot + (vendor_rails? ? VendorBoot : GemBoot).new + end + + def vendor_rails? + File.exist?("#{RAILS_ROOT}/vendor/rails") + end + + # FIXME : Ruby 1.9 + def preinitialize + load(preinitializer_path) if File.exists?(preinitializer_path) + end + + def preinitializer_path + "#{RAILS_ROOT}/config/preinitializer.rb" + end + end + + class Boot + def run + load_initializer + Rails::Initializer.run(:set_load_path) + end + end + + class VendorBoot < Boot + def load_initializer + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" + end + end + + class GemBoot < Boot + def load_initializer + self.class.load_rubygems + load_rails_gem + require 'initializer' + end + + def load_rails_gem + if version = self.class.gem_version + gem 'rails', version + else + gem 'rails' + end + rescue Gem::LoadError => load_error + $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) + exit 1 + end + + class << self + def rubygems_version + Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion + end + + def gem_version + if defined? RAILS_GEM_VERSION + RAILS_GEM_VERSION + elsif ENV.include?('RAILS_GEM_VERSION') + ENV['RAILS_GEM_VERSION'] + else + parse_gem_version(read_environment_rb) + end + end + + def load_rubygems + require 'rubygems' + + unless rubygems_version >= '0.9.4' + $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.) + exit 1 + end + + rescue LoadError + $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org) + exit 1 + end + + def parse_gem_version(text) + $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/ + end + + private + def read_environment_rb + File.read("#{RAILS_ROOT}/config/environment.rb") + end + end + end +end + +# All that for this: +Rails.boot! diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/script/generate b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/script/generate new file mode 100755 index 000000000..173a9f147 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/app_fixtures/script/generate @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.dirname(__FILE__) + '/../config/boot' +require 'commands/generate' diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/fixtures/app/controllers/posts_controller.rb b/bundles/ruby-on-rails.tmbundle/Support/test/fixtures/app/controllers/posts_controller.rb new file mode 100644 index 000000000..0ad983a82 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/fixtures/app/controllers/posts_controller.rb @@ -0,0 +1,24 @@ +class PostsController < ApplicationController + def new + @post = Post.new + end + + def create + @post = Post.new(params[:post]) + end + + def index + respond_to do |wants| + wants.html { } + wants.xml { } + wants.js { } + wants.wacky { } + end + respond_to do |format| + format.html { } + end + end + + def edit + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/fixtures/vendor/plugins/haml/init.rb b/bundles/ruby-on-rails.tmbundle/Support/test/fixtures/vendor/plugins/haml/init.rb new file mode 100644 index 000000000..e69de29bb diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/test_buffer.rb b/bundles/ruby-on-rails.tmbundle/Support/test/test_buffer.rb new file mode 100644 index 000000000..99737f1e6 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/test_buffer.rb @@ -0,0 +1,118 @@ +require File.dirname(__FILE__) + '/test_helper' + +require 'text_mate_mock' +require 'rails/buffer' + +TextMate.line_number = '1' +TextMate.column_number = '1' +TextMate.selected_text = <<-END +def my_method + puts 'hi' + # some comment, 'hi' +end + +def my_other_method + x = y + z + # another comment +end + +def index + respond_to do |wants| + wants.html { } + wants.js { } + wants.css { } + end + respond_to { | wacky | + wacky.wackier { } + } +end + +def edit +end +END + +class BufferTest < Test::Unit::TestCase + def test_find + b = Buffer.new(TextMate.selected_text) + match = b.find { /'(.+)'/ } + assert_equal [1, "hi"], match + + match = b.find(:from => 2, :to => 1, :direction => :backward) { /'(.+)'/ } + assert_equal [2, "hi"], match + + match = b.find(:from => 2, :to => 1, :direction => :backward) { /my_method/ } + assert_nil match + end + + def test_find_method + b = Buffer.new(TextMate.selected_text) + assert_equal [0, 'my_method'], b.find_method + + b.line_number = 4 + assert_equal [0, 'my_method'], b.find_method + + b.line_number = 5 + assert_equal [5, 'my_other_method'], b.find_method + end + + def test_find_respond_to_format + b = Buffer.new(TextMate.selected_text) + assert_equal nil, b.find_respond_to_format + b.line_number = 10 + assert_equal nil, b.find_respond_to_format + b.line_number = 11 + assert_equal [12, 'html'], b.find_respond_to_format + b.line_number = 12 + assert_equal [12, 'html'], b.find_respond_to_format + b.line_number = 13 + assert_equal [13, 'js'], b.find_respond_to_format + b.line_number = 14 + assert_equal [14, 'css'], b.find_respond_to_format + b.line_number = 15 + assert_equal [14, 'css'], b.find_respond_to_format + b.line_number = 16 + assert_equal [17, 'wackier'], b.find_respond_to_format + b.line_number = 17 + assert_equal [17, 'wackier'], b.find_respond_to_format + b.line_number = 18 + assert_equal [17, 'wackier'], b.find_respond_to_format + b.line_number = 19 + assert_equal [17, 'wackier'], b.find_respond_to_format + b.line_number = 20 + assert_equal [17, 'wackier'], b.find_respond_to_format + b.line_number = 21 + assert_equal nil, b.find_respond_to_format + end + + def test_find_multiple_matches + b = Buffer.new(TextMate.selected_text) + match = b.find { /^\s*x = (\w) \+ (\w)\s*$/ } + assert_equal [6, 'y', 'z'], match + + b = Buffer.new(TextMate.selected_text) + match = b.find { /^\s*x = (\w) \+ (\w)(\w?)\s*$/ } + assert_equal [6, 'y', 'z', ''], match + end + + def test_find_nearest_string_or_symbol + b = Buffer.new "String :with => 'strings', :and, :symbols" + match = b.find_nearest_string_or_symbol + assert_equal ["with", 8], match + + b.column_number = 8 + match = b.find_nearest_string_or_symbol + assert_equal ["with", 8], match + + b.column_number = 25 + match = b.find_nearest_string_or_symbol + assert_equal ["strings", 17], match + + b.column_number = 37 + match = b.find_nearest_string_or_symbol + assert_equal ["symbols", 34], match + + b = Buffer.new "String without symbols or strings" + match = b.find_nearest_string_or_symbol + assert_nil match + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/test_generator.rb b/bundles/ruby-on-rails.tmbundle/Support/test/test_generator.rb new file mode 100644 index 000000000..1c3e07cf4 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/test_generator.rb @@ -0,0 +1,29 @@ +require File.dirname(__FILE__) + '/test_helper' +require "rails/generate" + +class TestBinGenerate < Test::Unit::TestCase + def test_known_generators + expected = %w[scaffold controller model mailer migration plugin] + actual = Generator.known_generators.map { |gen| gen.name } + assert_equal(expected, actual) + end + + def test_find_generator_names + list = Generator.find_generator_names + assert_equal(Array, list.class) + list.each do |name| + assert_equal(String, name.class) + assert_no_match(/[ \t\n]/, name, "generator names should not contain spaces") + end + end + + def test_generators + generators = Generator.setup_generators + assert(generators.length > 6, "Failure message.") + assert_equal(Array, generators.class) + generators.each do |gen| + assert_equal(Generator, gen.class) + assert_no_match(/[ \t\n]/, gen.name, "generator names should not contain spaces") + end + end +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/test_helper.rb b/bundles/ruby-on-rails.tmbundle/Support/test/test_helper.rb new file mode 100644 index 000000000..121b37853 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/test_helper.rb @@ -0,0 +1,9 @@ +require 'test/unit' +require "fileutils" +$:.push File.dirname(__FILE__) +$:.push File.dirname(__FILE__) + '/../lib' +FIXTURE_PATH = File.expand_path(File.dirname(__FILE__) + '/app_fixtures') + +def ruby(command) + `/usr/bin/env ruby #{command}` +end diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/test_rails_path.rb b/bundles/ruby-on-rails.tmbundle/Support/test/test_rails_path.rb new file mode 100644 index 000000000..a62b31082 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/test_rails_path.rb @@ -0,0 +1,289 @@ +require File.dirname(__FILE__) + '/test_helper' + +require 'text_mate_mock' +require 'rails/rails_path' + +class RailsPathTest < Test::Unit::TestCase + def setup + TextMate.line_number = '1' + TextMate.column_number = '1' + TextMate.project_directory = File.expand_path(File.dirname(__FILE__) + '/app_fixtures') + @rp_controller = RailsPath.new(FIXTURE_PATH + '/app/controllers/user_controller.rb') + @rp_controller_with_module = RailsPath.new(FIXTURE_PATH + '/app/controllers/admin/base_controller.rb') + @rp_view = RailsPath.new(FIXTURE_PATH + '/app/views/user/new.rhtml') + @rp_view_with_module = RailsPath.new(FIXTURE_PATH + '/app/views/admin/base/action.rhtml') + @rp_fixture = RailsPath.new(FIXTURE_PATH + '/test/fixtures/users.yml') + @rp_fixture_spec = RailsPath.new(FIXTURE_PATH + '/spec/fixtures/users.yml') + @rp_wacky = RailsPath.new(FIXTURE_PATH + '/wacky/users.yml') + end + + def test_rails_root + assert_equal File.expand_path(File.dirname(__FILE__) + '/app_fixtures'), RailsPath.new.rails_root + end + + def test_extension + assert_equal "rb", @rp_controller.extension + assert_equal "rhtml", @rp_view.extension + end + + def test_file_type + assert_equal :controller, @rp_controller.file_type + assert_equal :view, @rp_view.file_type + assert_equal :fixture, @rp_fixture.file_type + assert_equal :fixture, @rp_fixture_spec.file_type + assert_equal nil, @rp_wacky.file_type + end + + def test_modules + assert_equal [], @rp_controller.modules + assert_equal ['admin'], @rp_controller_with_module.modules + assert_equal [], @rp_view.modules + assert_equal ['admin'], @rp_view_with_module.modules + assert_equal [], @rp_fixture.modules + assert_equal [], @rp_fixture_spec.modules + assert_equal nil, @rp_wacky.modules + end + + def test_controller_name + rp = RailsPath.new(FIXTURE_PATH + '/app/models/person.rb') + assert_equal "people", rp.controller_name + rp = RailsPath.new(FIXTURE_PATH + '/app/models/user.rb') + assert_equal "user", rp.controller_name + rp = RailsPath.new(FIXTURE_PATH + '/app/models/users.rb') + assert_equal "users", rp.controller_name + end + + def test_controller_name_and_action_name_for_controller + rp = RailsPath.new(FIXTURE_PATH + '/app/controllers/user_controller.rb') + assert_equal "user", rp.controller_name + assert_equal nil, rp.action_name + + TextMate.line_number = '3' + rp = RailsPath.new(FIXTURE_PATH + '/app/controllers/user_controller.rb') + assert_equal "user", rp.controller_name + assert_equal "new", rp.action_name + + TextMate.line_number = '6' + rp = RailsPath.new(FIXTURE_PATH + '/app/controllers/user_controller.rb') + assert_equal "user", rp.controller_name + assert_equal "create", rp.action_name + end + + def test_controller_name_and_action_name_for_view + rp = RailsPath.new(FIXTURE_PATH + '/app/views/user/new.rhtml') + assert_equal "user", rp.controller_name # this was pre-2.0 behavior. s/b "users" + assert_equal "new", rp.action_name + end + + # Rails 2.x convention is for pluralized controllers + def test_controller_name_and_action_name_for_2_dot_ooh_views + rp = RailsPath.new(FIXTURE_PATH + '/app/views/users/new.html.erb') + assert_equal "users", rp.controller_name + assert_equal "new", rp.action_name + end + + def test_controller_name_pluralization + rp = RailsPath.new(FIXTURE_PATH + '/app/views/people/new.html.erb') + assert_equal "people", rp.controller_name + end + + def test_controller_name_suggestion_when_controller_absent + rp = RailsPath.new(FIXTURE_PATH + '/app/views/people/new.html.erb') + assert_equal "people", rp.controller_name + end + + def test_respond_to_format + current_file = RailsPath.new(FIXTURE_PATH + '/app/controllers/posts_controller.rb') + TextMate.line_number = '14' + assert_equal [13, 'js'], current_file.respond_to_format + end + + def test_rails_path_for + partners = [ + # Basic tests + [FIXTURE_PATH + '/app/controllers/user_controller.rb', :helper, FIXTURE_PATH + '/app/helpers/user_helper.rb'], + [FIXTURE_PATH + '/app/controllers/user_controller.rb', :javascript, FIXTURE_PATH + '/public/javascripts/user.js'], + [FIXTURE_PATH + '/app/controllers/user_controller.rb', :functional_test, FIXTURE_PATH + '/test/functional/user_controller_test.rb'], + [FIXTURE_PATH + '/app/helpers/user_helper.rb', :controller, FIXTURE_PATH + '/app/controllers/users_controller.rb'], + [FIXTURE_PATH + '/app/models/user.rb', :controller, FIXTURE_PATH + '/app/controllers/users_controller.rb'], + [FIXTURE_PATH + '/app/models/post.rb', :controller, FIXTURE_PATH + '/app/controllers/posts_controller.rb'], + [FIXTURE_PATH + '/test/fixtures/users.yml', :model, FIXTURE_PATH + '/app/models/user.rb'], + [FIXTURE_PATH + '/spec/fixtures/users.yml', :model, FIXTURE_PATH + '/app/models/user.rb'], + [FIXTURE_PATH + '/app/controllers/user_controller.rb', :model, FIXTURE_PATH + '/app/models/user.rb'], + [FIXTURE_PATH + '/test/fixtures/users.yml', :unit_test, FIXTURE_PATH + '/test/unit/user_test.rb'], + [FIXTURE_PATH + '/app/models/user.rb', :fixture, FIXTURE_PATH + '/test/fixtures/users.yml'], + # With modules + [FIXTURE_PATH + '/app/controllers/admin/base_controller.rb', :helper, FIXTURE_PATH + '/app/helpers/admin/base_helper.rb'], + [FIXTURE_PATH + '/app/controllers/admin/inside/outside_controller.rb', :javascript, FIXTURE_PATH + '/public/javascripts/admin/inside/outside.js'], + [FIXTURE_PATH + '/app/controllers/admin/base_controller.rb', :functional_test, FIXTURE_PATH + '/test/functional/admin/base_controller_test.rb'], + [FIXTURE_PATH + '/app/helpers/admin/base_helper.rb', :controller, FIXTURE_PATH + '/app/controllers/admin/base_controller.rb'], + ] + # TODO Add [posts.yml, :model, post.rb] + for pair in partners + assert_equal RailsPath.new(pair[2]), RailsPath.new(pair[0]).rails_path_for(pair[1]) + end + + # Test controller to view + ENV['RAILS_VIEW_EXT'] = nil + TextMate.line_number = '6' + current_file = RailsPath.new(FIXTURE_PATH + '/app/controllers/user_controller.rb') + assert_equal RailsPath.new(FIXTURE_PATH + '/app/views/user/create.html.erb'), current_file.rails_path_for(:view) + + # 2.0 plural controllers + current_file = RailsPath.new(FIXTURE_PATH + '/app/controllers/users_controller.rb') + assert_equal RailsPath.new(FIXTURE_PATH + '/app/views/users/create.html.erb'), current_file.rails_path_for(:view) + + TextMate.line_number = '3' + current_file = RailsPath.new(FIXTURE_PATH + '/app/controllers/user_controller.rb') + assert_equal RailsPath.new(FIXTURE_PATH + '/app/views/user/new.rhtml'), current_file.rails_path_for(:view) + + # 2.0 plural controllers + current_file = RailsPath.new(FIXTURE_PATH + '/app/controllers/users_controller.rb') + assert_equal RailsPath.new(FIXTURE_PATH + '/app/views/users/new.html.erb'), current_file.rails_path_for(:view) + + # Test view to controller + current_file = RailsPath.new(FIXTURE_PATH + '/app/views/user/new.html.erb') + assert_equal RailsPath.new(FIXTURE_PATH + '/app/controllers/users_controller.rb'), current_file.rails_path_for(:controller) + + # 2.0 plural controllers + current_file = RailsPath.new(FIXTURE_PATH + '/app/views/users/new.html.erb') + assert_equal RailsPath.new(FIXTURE_PATH + '/app/controllers/users_controller.rb'), current_file.rails_path_for(:controller) + + ENV['RAILS_VIEW_EXT'] = nil + # view defaults from respond_to block + challenges = [ + [11, 'no_existing_views.html.erb'], + [12, 'no_existing_views.html.erb'], + [13, 'no_existing_views.js.rjs'], + [14, 'no_existing_views.js.rjs'], + [15, 'no_existing_views.js.rjs'], + [16, 'no_existing_views.xml.builder'], + [17, 'no_existing_views.wacky.erb'], + [18, 'no_existing_views.wacky.erb'], + + [22, 'existing_views.html.erb'], + [23, 'existing_views.html.erb'], + [24, 'existing_views.js.rjs'], + [25, 'existing_views.js.rjs'], + [26, 'existing_views.js.rjs'], + [27, 'existing_views.xml.builder'], + [28, 'existing_views.wacky.erb'], + [28, 'existing_views.wacky.erb'], + ] + challenges.each do |line, expected| + TextMate.line_number = line.to_s + current_file = RailsPath.new(FIXTURE_PATH + '/app/controllers/users_controller.rb') + assert_equal( + RailsPath.new(FIXTURE_PATH + '/app/views/users/' + expected), + current_file.rails_path_for(:view), + "Mismatch for line #{line}, should be #{expected}" + ) + end + + # test wacky + assert_equal(nil, + @rp_wacky.rails_path_for(:controller), + "wacky/wackier.rb has no associations") + end + + def test_file_parts + current_file = RailsPath.new(FIXTURE_PATH + '/app/views/users/new.html.erb') + assert_equal(FIXTURE_PATH + '/app/views/users/new.html.erb', current_file.filepath) + pathname, basename, content_type, extension = current_file.parse_file_parts + assert_equal(FIXTURE_PATH + '/app/views/users', pathname) + assert_equal('new', basename) + assert_equal('html', content_type) + assert_equal('erb', extension) + + current_file = RailsPath.new(FIXTURE_PATH + '/app/views/user/new.rhtml') + pathname, basename, content_type, extension = current_file.parse_file_parts + assert_equal(FIXTURE_PATH + '/app/views/user', pathname) + assert_equal('new', basename) + assert_equal(nil, content_type) + assert_equal('rhtml', extension) + end + + def test_new_rails_path_has_parts + current_file = RailsPath.new(FIXTURE_PATH + '/app/views/users/new.html.erb') + assert_equal(FIXTURE_PATH + '/app/views/users/new.html.erb', current_file.filepath) + assert_equal(FIXTURE_PATH + '/app/views/users', current_file.path_name) + assert_equal('new', current_file.file_name) + assert_equal('html', current_file.content_type) + assert_equal('erb', current_file.extension) + end + + def test_best_match + assert_equal(nil, RailsPath.new(FIXTURE_PATH + '/config/boot.rb').best_match) + assert_equal(:functional_test, RailsPath.new(FIXTURE_PATH + '/app/controllers/posts_controller.rb').best_match) + assert_equal(:model, RailsPath.new(FIXTURE_PATH + '/app/controllers/users_controller.rb').best_match) + assert_equal(:functional_test, RailsPath.new(FIXTURE_PATH + '/app/controllers/admin/base_controller.rb').best_match) + + TextMate.line_number = '3' # edit action + assert_equal(:view, RailsPath.new(FIXTURE_PATH + '/app/controllers/admin/base_controller.rb').best_match) + TextMate.line_number = '0' + + assert_equal(:controller, RailsPath.new(FIXTURE_PATH + '/app/views/users/new.html.erb').best_match) + assert_equal(:controller, RailsPath.new(FIXTURE_PATH + '/app/views/user/new.rhtml').best_match) + assert_equal(:controller, RailsPath.new(FIXTURE_PATH + '/app/views/admin/base/action.html.erb').best_match) + assert_equal(:model, RailsPath.new(FIXTURE_PATH + '/app/views/notifier/forgot_password.html.erb').best_match) + assert_equal(:controller, RailsPath.new(FIXTURE_PATH + '/app/views/books/new.haml').best_match) + end + + def test_wants_haml + begin + assert_equal false, @rp_view.wants_haml + haml_fixture_path = File.expand_path(File.dirname(__FILE__) + '/fixtures') + TextMate.project_directory = haml_fixture_path + assert_equal true, RailsPath.new(haml_fixture_path + '/app/views/posts/index.html.haml').wants_haml + ensure + TextMate.project_directory = File.expand_path(File.dirname(__FILE__) + '/app_fixtures') + end + end + + def test_haml + begin + haml_fixture_path = File.expand_path(File.dirname(__FILE__) + '/fixtures') + TextMate.project_directory = haml_fixture_path + + assert_equal [], RailsPath.new(haml_fixture_path + '/public/stylesheets/sass/posts.sass').modules + assert_equal ["admin"], RailsPath.new(haml_fixture_path + '/public/stylesheets/sass/admin/posts.sass').modules + + # Going from controller to view + current_file = RailsPath.new(haml_fixture_path + '/app/controllers/posts_controller.rb') + TextMate.line_number = '2' + assert_equal RailsPath.new(haml_fixture_path + '/app/views/posts/new.html.haml'), current_file.rails_path_for(:view) + + current_file = RailsPath.new(haml_fixture_path + '/app/controllers/posts_controller.rb') + TextMate.line_number = '12' + assert_equal RailsPath.new(haml_fixture_path + '/app/views/posts/index.html.haml'), current_file.rails_path_for(:view) + + current_file = RailsPath.new(haml_fixture_path + '/app/controllers/posts_controller.rb') + TextMate.line_number = '13' + assert_equal RailsPath.new(haml_fixture_path + '/app/views/posts/index.xml.builder'), current_file.rails_path_for(:view) + + current_file = RailsPath.new(haml_fixture_path + '/app/controllers/posts_controller.rb') + TextMate.line_number = '14' + assert_equal RailsPath.new(haml_fixture_path + '/app/views/posts/index.js.rjs'), current_file.rails_path_for(:view) + + current_file = RailsPath.new(haml_fixture_path + '/app/controllers/posts_controller.rb') + TextMate.line_number = '15' + assert_equal RailsPath.new(haml_fixture_path + '/app/views/posts/index.wacky.haml'), current_file.rails_path_for(:view) + + # Going from view to controller + current_file = RailsPath.new(haml_fixture_path + '/app/views/posts/index.html.haml') + assert_equal RailsPath.new(haml_fixture_path + '/app/controllers/posts_controller.rb'), current_file.rails_path_for(:controller) + + # Going from view to stylesheet + current_file = RailsPath.new(haml_fixture_path + '/app/views/posts/index.html.haml') + assert_equal RailsPath.new(haml_fixture_path + '/public/stylesheets/sass/posts.sass'), current_file.rails_path_for(:stylesheet) + + # Going from stylesheet to helper + current_file = RailsPath.new(haml_fixture_path + '/public/stylesheets/sass/posts.sass') + assert_equal RailsPath.new(haml_fixture_path + '/app/helpers/posts_helper.rb'), current_file.rails_path_for(:helper) + + ensure + TextMate.project_directory = File.expand_path(File.dirname(__FILE__) + '/app_fixtures') + end + end + +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/test_text_mate.rb b/bundles/ruby-on-rails.tmbundle/Support/test/test_text_mate.rb new file mode 100644 index 000000000..a086bd16e --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/test_text_mate.rb @@ -0,0 +1 @@ +require File.dirname(__FILE__) + '/test_helper' diff --git a/bundles/ruby-on-rails.tmbundle/Support/test/text_mate_mock.rb b/bundles/ruby-on-rails.tmbundle/Support/test/text_mate_mock.rb new file mode 100644 index 000000000..e720fd705 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Support/test/text_mate_mock.rb @@ -0,0 +1,45 @@ +require 'rails/text_mate' + +TM_VARIABLE_DEFAULTS = { + :bundle_path => "~/Library/Application Support/TextMate/Bundles/TextMate.tmbundle", + :bundle_support => "~/Library/Application Support/TextMate/Bundles/TextMate.tmbundle/Support", + :columns => "100", + :column_number => "1", + :comment_end => "", + :comment_mode => "line", + :comment_start => "# ", + :current_line => "", + :directory => "~/Library/Application Support/TextMate/Bundles/TextMate.tmbundle/Support/test", + :filename => "text_mate_mock.rb", + :filepath => "~/Library/Application Support/TextMate/Bundles/Rails.tmbundle/Support/test/text_mate_mock.rb", + :line_index => "0", + :line_number => "4", + :mode => "Ruby on Rails", + :organization_name => "syncPEOPLE", + :project_directory => "~/Library/Application Support/TextMate/Bundles/Rails.tmbundle", + :project_filepath => "~/Library/Application Support/TextMate/Bundles/Rails.tmbundle/Rails.tmproj", + :scope => "source.ruby.rails", + :selected_file => "~/Library/Application Support/TextMate/Bundles/Rails.tmbundle/Support/test/text_mate_mock.rb", + :selected_files => "'~/Library/Application Support/TextMate/Bundles/Rails.tmbundle/Support/test/text_mate_mock.rb'", + :selected_text => "", + :soft_tabs => "YES", + :support_path => "~/Library/Application Support/TextMate/Support", + :tab_size => "2" } + +module TextMate + class < + + + + fileTypes + + rhtml + erb + html.erb + + foldingStartMarker + (?x) + (<(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|form|dl)\b.*?> + |<!--(?!.*-->) + |\{\s*($|\?>\s*$|//|/\*(.*\*/\s*$|(?!.*?\*/))) + ) + foldingStopMarker + (?x) + (</(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|form|dl)> + |^\s*--> + |(^|\s)\} + ) + keyEquivalent + ^~R + name + HTML (Rails) + patterns + + + begin + <%+# + captures + + 0 + + name + punctuation.definition.comment.erb + + + end + %> + name + comment.block.erb + + + begin + <%+(?!>)[-=]? + captures + + 0 + + name + punctuation.section.embedded.ruby + + + end + -?%> + name + source.ruby.rails.embedded.html + patterns + + + captures + + 1 + + name + punctuation.definition.comment.ruby + + + match + (#).*?(?=-?%>) + name + comment.line.number-sign.ruby + + + include + source.ruby.rails + + + + + include + text.html.basic + + + scopeName + text.html.ruby + uuid + 45D7E1FC-7D0B-4105-A1A2-3D10BB555A5C + + diff --git a/bundles/ruby-on-rails.tmbundle/Syntaxes/JavaScript (Rails).tmLanguage b/bundles/ruby-on-rails.tmbundle/Syntaxes/JavaScript (Rails).tmLanguage new file mode 100644 index 000000000..fb9a9bc01 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Syntaxes/JavaScript (Rails).tmLanguage @@ -0,0 +1,82 @@ + + + + + fileTypes + + js.erb + + foldingStartMarker + /\*\*|\{\s*$ + foldingStopMarker + \*\*/|^\s*\} + keyEquivalent + ^~J + name + JavaScript (Rails) + patterns + + + begin + <%+# + captures + + 0 + + name + punctuation.definition.comment.erb + + + end + %> + name + comment.block.erb + + + begin + <%+(?!>)[-=]? + captures + + 0 + + name + punctuation.section.embedded.ruby + + + end + -?%> + name + source.ruby.rails.erb + patterns + + + captures + + 1 + + name + punctuation.definition.comment.ruby + + + match + (#).*?(?=-?%>) + name + comment.line.number-sign.ruby + + + include + source.ruby.rails + + + + + include + source.js + + + scopeName + source.js.rails + uuid + 4A3E6DA7-67A3-45B1-9EE0-ECFF9C7FA6C0 + + diff --git a/bundles/ruby-on-rails.tmbundle/Syntaxes/RJS.tmLanguage b/bundles/ruby-on-rails.tmbundle/Syntaxes/RJS.tmLanguage new file mode 100644 index 000000000..15017e7ad --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Syntaxes/RJS.tmLanguage @@ -0,0 +1,25 @@ + + + + + fileTypes + + rjs + + keyEquivalent + ^~R + name + RJS + patterns + + + include + source.ruby + + + scopeName + source.ruby.rails.rjs + uuid + A6AE60CD-18CA-4474-BE7A-5255BC3EF083 + + diff --git a/bundles/ruby-on-rails.tmbundle/Syntaxes/Ruby on Rails.plist b/bundles/ruby-on-rails.tmbundle/Syntaxes/Ruby on Rails.plist new file mode 100644 index 000000000..6f7b4527b --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Syntaxes/Ruby on Rails.plist @@ -0,0 +1,288 @@ + + + + + fileTypes + + rb + rxml + builder + + foldingStartMarker + (?x)^ + (\s*+ + (module|class|def + |unless|if + |case + |begin + |for|while|until + |( "(\\.|[^"])*+" # eat a double quoted string + | '(\\.|[^'])*+' # eat a single quoted string + | [^#"'] # eat all but comments and strings + )* + ( \s (do|begin|case) + | [-+=&|*/~%^<>~] \s*+ (if|unless) + ) + )\b + (?! [^;]*+ ; .*? \bend\b ) + |( "(\\.|[^"])*+" # eat a double quoted string + | '(\\.|[^'])*+' # eat a single quoted string + | [^#"'] # eat all but comments and strings + )* + ( \{ (?! [^}]*+ \} ) + | \[ (?! [^\]]*+ \] ) + ) + ).*$ + | [#] .*? \(fold\) \s*+ $ # Sune’s special marker + + foldingStopMarker + (?x) + ( (^|;) \s*+ end \s*+ ([#].*)? $ + | ^ \s*+ [}\]] \s*+ ([#].*)? $ + | [#] .*? \(end\) \s*+ $ # Sune’s special marker + ) + keyEquivalent + ^~R + name + Ruby on Rails + patterns + + + begin + (^\s*)(?=class\s+(([.a-zA-Z0-9_:]+ControllerTest(\s*<\s*[.a-zA-Z0-9_:]+)?))) + comment + Uses lookahead to match classes with the ControllerTest suffix; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.functional_test + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=class\s+(([.a-zA-Z0-9_:]+Controller\b(\s*<\s*[.a-zA-Z0-9_:]+)?)|(<<\s*[.a-zA-Z0-9_:]+)))(?!.+\bend\b) + comment + Uses lookahead to match classes with the Controller suffix; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.controller + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=module\s+((([A-Z]\w*::)*)[A-Z]\w*)Helper) + comment + Uses lookahead to match modules with the Helper suffix; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.helper + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=class\s+(([.a-zA-Z0-9_:]+(\s*<\s*ActionMailer::Base)))) + comment + Uses lookahead to match classes that inherit from ActionMailer::Base; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.mailer + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=class\s+.+ActiveRecord::Base) + comment + Uses lookahead to match classes that (may) inherit from ActiveRecord::Base; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.model + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=class\s+.+ActiveRecord::Migration) + comment + Uses lookahead to match classes that (may) inherit from ActiveRecord::Migration; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.migration + patterns + + + begin + (^\s*)(?=change_table)\b + comment + Uses lookahead to match methods change_table; includes 'source.ruby' to avoid infinite recursion + contentName + meta.rails.migration.change_table + end + ^\1(?=end)\b + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=create_table)\b + comment + Uses lookahead to match methods create_table; includes 'source.ruby' to avoid infinite recursion + contentName + meta.rails.migration.create_table + end + ^\1(?=end)\b + patterns + + + include + source.ruby + + + include + $self + + + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)(?=class\s+(?![.a-zA-Z0-9_:]+ControllerTest)(([.a-zA-Z0-9_:]+Test(\s*<\s*[.a-zA-Z0-9_:]+)?)|(<<\s*[.a-zA-Z0-9_:]+))) + comment + Uses lookahead to match classes with the Test suffix; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.unit_test + patterns + + + include + source.ruby + + + include + $self + + + + + begin + (^\s*)ActionController::Routing::Routes + comment + Uses ActionController::Routing::Routes to determine it is a routes file; includes 'source.ruby' to avoid infinite recursion + end + ^\1(?=end)\b + name + meta.rails.routes + patterns + + + include + source.ruby + + + include + $self + + + + + match + \b(before_filter|skip_before_filter|skip_after_filter|after_filter|around_filter|filter|filter_parameter_logging|layout|require_dependency|render|render_action|render_text|render_file|render_template|render_nothing|render_component|render_without_layout|rescue_from|url_for|redirect_to|redirect_to_path|redirect_to_url|respond_to|helper|helper_method|model|service|observer|serialize|scaffold|verify|hide_action)\b + name + support.function.actionpack.rails + + + match + \b(named_scope|after_create|after_destroy|after_save|after_update|after_validation|after_validation_on_create|after_validation_on_update|before_create|before_destroy|before_save|before_update|before_validation|before_validation_on_create|before_validation_on_update|composed_of|belongs_to|has_one|has_many|has_and_belongs_to_many|validate|validate_on_create|validates_numericality_of|validate_on_update|validates_acceptance_of|validates_associated|validates_confirmation_of|validates_each|validates_format_of|validates_inclusion_of|validates_exclusion_of|validates_length_of|validates_presence_of|validates_size_of|validates_uniqueness_of|attr_protected|attr_accessible|attr_readonly)\b + name + support.function.activerecord.rails + + + match + \b(alias_method_chain|alias_attribute|delegate|cattr_accessor|mattr_accessor|returning)\b + name + support.function.activesupport.rails + + + include + source.ruby + + + scopeName + source.ruby.rails + uuid + 54D6E91E-8F31-11D9-90C5-0011242E4184 + + diff --git a/bundles/ruby-on-rails.tmbundle/Syntaxes/SQL (Rails).plist b/bundles/ruby-on-rails.tmbundle/Syntaxes/SQL (Rails).plist new file mode 100644 index 000000000..cfb581e68 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/Syntaxes/SQL (Rails).plist @@ -0,0 +1,51 @@ + + + + + fileTypes + + erbsql + sql.erb + + foldingStartMarker + \s*\(\s*$ + foldingStopMarker + ^\s*\) + keyEquivalent + ^~R + name + SQL (Rails) + patterns + + + begin + <%+(?!>)=? + end + %> + name + source.ruby.rails.embedded.sql + patterns + + + match + #.*?(?=%>) + name + comment.line.number-sign.ruby + + + include + source.ruby.rails + + + + + include + source.sql + + + scopeName + source.sql.ruby + uuid + D54FBDED-5481-4CC7-B75F-66465A499882 + + diff --git a/bundles/ruby-on-rails.tmbundle/TODO b/bundles/ruby-on-rails.tmbundle/TODO new file mode 100644 index 000000000..cd78a43f0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/TODO @@ -0,0 +1,24 @@ +== TODO - From Dr Nic == + +- Call Generate Script - dynamically load generator names (bin/generate.rb) + +== TODO - From Duane == +- Let RailsPath recognize application.rb as a controller +- Remove extraneous dirname from setting each RUBYLIB -- remove from plugins too? +- License for footnotes? +- Chop rails_root from inline-partial-edit-mode's partial names +- Use
instead of for inline-partial-edit-mode? May allow for folding. +- Remove indentation from partials during create-partial-from-selection +- Add indentation to partials when entering inline-partial-edit-mode +- Add indentation to erb fragment that replaces the partials after create-partial-from-selection +- Intelligent Go To File needs to look for rxml and rjs files + - pop up window asking for filename on create (if view doesn't exist, for example) +- Fix inline-partial mode for rjs files ( " + new_name +end + +# Sanitize the transit dir +bad_dirs = [] +Find.find(DIR_TO_CLEAN) do |f| + f = File.expand_path(f).sub(DIR_TO_CLEAN, '') + next if f =~ /\/?\.(git|svn)/ + orig_name = File.basename(f) + next if orig_name === ( clean_name = sanitize(File.basename(f)) ) + if File.directory? f + bad_dirs.push(f) + next + end + dirname = File.dirname(f) + "/" + verbose_rename(dirname + orig_name, dirname + clean_name) unless (orig_name === clean_name) +end + +puts "Renaming #{bad_dirs.size} dirs..." +bad_dirs.each do |d| + orig_name = File.basename(d) + clean_name = sanitize(File.basename(d)) + dirname = File.dirname(d) + "/" + verbose_rename(dirname + orig_name, dirname + clean_name) +end diff --git a/bundles/ruby-on-rails.tmbundle/script/destroy b/bundles/ruby-on-rails.tmbundle/script/destroy new file mode 100755 index 000000000..2216b5e10 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/script/destroy @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:newjs, :newjs_theme, :test_unit] +RubiGen::Scripts::Destroy.new.run(ARGV) diff --git a/bundles/ruby-on-rails.tmbundle/script/generate b/bundles/ruby-on-rails.tmbundle/script/generate new file mode 100755 index 000000000..bf33992a8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/script/generate @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:newjs, :newjs_theme, :test_unit] +RubiGen::Scripts::Generate.new.run(ARGV) diff --git a/bundles/ruby-on-rails.tmbundle/script/txt2html b/bundles/ruby-on-rails.tmbundle/script/txt2html new file mode 100755 index 000000000..2d2b9282f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/script/txt2html @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby + +require 'erb' +require 'rubygems' +begin + require 'newgem' +rescue LoadError + puts "\n\nGenerating the website requires the newgem RubyGem" + puts "Install: gem install newgem\n\n" + exit(1) +end +require 'redcloth' +require 'syntax/convertors/html' +require 'erb' +ENV['rakefile_just_config'] = "true" +load File.dirname(__FILE__) + '/../Rakefile' + +version = APP_VERSION +download = 'http://railsbundle.com/dist/JavaScript Unit Testing.tar.gz' + +class Fixnum + def ordinal + # teens + return 'th' if (10..19).include?(self % 100) + # others + case self % 10 + when 1: return 'st' + when 2: return 'nd' + when 3: return 'rd' + else return 'th' + end + end +end + +class Time + def pretty + return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}" + end +end + +def convert_syntax(syntax, source) + return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^
|
$!,'') +end + +if ARGV.length >= 1 + src, template = ARGV + template ||= File.join(File.dirname(__FILE__), '/../website/template.html.erb') + +else + puts("Usage: #{File.split($0).last} source.txt [template.html.erb] > output.html") + exit! +end + +template = ERB.new(File.open(template).read) + +title = nil +body = nil +File.open(src) do |fsrc| + title_text = fsrc.readline + body_text = fsrc.read + syntax_items = [] + body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)!m){ + ident = syntax_items.length + element, syntax, source = $1, $2, $3 + syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}" + "syntax-temp-#{ident}" + } + + # a few extra supported markup commands specifically for documenting tm bundles + [ + ['COMMAND', '⌘'], + ['TAB', '⇥'], + ['SHIFT', '⇧'], + ['OPTION', '⌥'], + ['CONTROL', '⌃'], + ['BACKSPACE', '⌫'], + ['DELETE', '⌦'], + ['RETURN', '↩'], + ['ENTER', '⌅'], + ['ESCAPE', '⎋'], + ['DOWN', '↓'], + ['UP', '↑'], + ['LEFT', '←'], + ['RIGHT', '→'] + ].map do |regexp, replacement| + body_text.gsub!(Regexp.new(regexp), replacement) + end + + title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip + body = RedCloth.new(body_text).to_html + body.gsub!(%r!(?:
)?syntax-temp-(\d+)(?:
)?!){ syntax_items[$1.to_i] } +end +stat = File.stat(src) +created = stat.ctime +modified = stat.mtime + +$stdout << template.result(binding) diff --git a/bundles/ruby-on-rails.tmbundle/tasks/dist.rake b/bundles/ruby-on-rails.tmbundle/tasks/dist.rake new file mode 100644 index 000000000..1dd5ce1ca --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/tasks/dist.rake @@ -0,0 +1,9 @@ +desc "Build bundle archive for distribution" +task :dist do + bundle = 'Ruby\ on\ Rails.tmbundle' + bundle_src = APP_ROOT + bundle_dist = "website/dist" + FileUtils.mkdir_p bundle_dist + bundle_file = "#{bundle_dist}/#{bundle}.tar.gz" + sh %{tar zcvf #{bundle_file} --exclude .git --exclude #{bundle_file} .} +end \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/tasks/website.rake b/bundles/ruby-on-rails.tmbundle/tasks/website.rake new file mode 100644 index 000000000..4312e9da0 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/tasks/website.rake @@ -0,0 +1,35 @@ +require 'yaml' + +def website_config + unless @website_config + begin + @website_config = YAML.load(File.read(File.expand_path(File.dirname(__FILE__) + "/../config/website.yml"))) + rescue + puts <<-EOS +To upload your website to a host, you need to configure +config/website.yml. See config/website.yml.sample for +an example. +EOS + exit + end + end + @website_config +end + +desc 'Generate website files' +task :website_generate => :dist do + (Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt| + sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} } + end +end + +desc 'Upload website files to rubyforge' +task :website_upload do + host = website_config["host"] # "#{rubyforge_username}@rubyforge.org" + remote_dir = website_config["remote_dir"] # "/var/www/gforge-projects/#{PATH}/" + local_dir = 'website' + sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}} +end + +desc 'Generate and upload website files' +task :website => [:website_generate, :website_upload] diff --git a/bundles/ruby-on-rails.tmbundle/website/demo.html b/bundles/ruby-on-rails.tmbundle/website/demo.html new file mode 100644 index 000000000..ddd7df1be --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/demo.html @@ -0,0 +1,1016 @@ + + + + + + + Step-by-step demonstration + + + + + + +
+ +

Step-by-step demonstration

+
+

Get Version

+ 2.0.0 + Logo Bundle +
+

In this demo we’ll create a blog; because that’s what blogs are for: being +demonstrations of web frameworks.

+ + +

The demonstration uses new features of Rails 2.0 and the snippets in this bundle.

+ + +

A New App

+ + +
rails blog
+cd blog
+mate .
+ +

Add some models

+ + +
ruby script/generate model Post subject:string body:text
+ +

This creates a 001_create_posts migration with a create_table:

+ + +

create_table :posts do |t|
+  t.string :subject
+  t.text :body
+
+  t.timestamps
+end

+ + +

Sexy Migration support

+ + +

If you put the cursor on the line after t.text :body, type t. and press ⇥. Select “Create boolean column” (by pressing 0), and type “published” into the template field. If nothing happened when you pressed ⇥, check that when you opened the migrations file you’ve selected the bundle “Ruby on Rails”.

+ + +

Note that another t. was created on the next line! Press ⇥ and the cursor will be placed after it. You can now press ⇥ again to create another column, or delete this line.

+ + +

Here, delete the extraneous t. line (⌃ ⇧ K). And save the file (⌘ S).

+ + +

Run the migrations, either from the prompt:

+ + +
rake db:migrate
+ + +

or directly from the editor with ⌃ | (Ctrl-Pipeline), and choosing option “Migrate to Current”.

+ + +

Post fixtures

+ + +

Update the test/fixtures/posts.yml file as:

+ + +

published:
+  subject: Some article
+  body: A test article
+  published: true
+
+nonpublished:
+  body: Still writing this one

+ + +

Note, in Rails 2.0 fixtures no longer have explicit ids. Later on we’ll look at snippets for using Foxy Fixtures with auto-completion for associations.

+ + +

Public blog controller

+ + +

Create a controller for our blog, either via the command prompt:

+ + +
ruby script/generate controller blog
+ + +

or directly from the editor with ⌃ |, and choosing option “Call Generate Script”, choose “Controller”, give it the name “blog”, and empty the list of actions.

+ + +

Now open blog_controller_test.rb. To find this file quickly press ⌘ T, enter bct, and select the file.

+ + +

Note how much cleaner functional tests are now via ActionController::TestCase.

+ + +

Let’s do some TDD. First, delete the test_truth dummy method.

+ + +

To create a test to show a list of blog articles:

+ + +
deftg
+ + +

and ⇥ gives:

+ + +

def test_should_get_action
+  @model = models(:fixture_name)
+  get :action, :id => @model.to_param
+  assert_response :success
+
+end

+ + +

Type index to replace action. Press ⇥, and then ⌫ to remove the first line, then press ⇥ three times and then ⌫ to remove the :id => @model.to_param part. The press ⇥ again to go to the end of the method. Now we have:

+ + +

def test_should_get_index
+  get :index
+  assert_response :success
+
+end

+ + +

Now type asg, press ⇥, and type posts, and press ⇥ again. This creates an instance variable lookup within an assertion:

+ + +

assert(posts = assigns(:posts), "Cannot find @posts")

+ + +

Now, let’s assert the HTML format.

+ + +

Type ass and press ⇥. Type div#posts, press ⇥ and ⌫, then ⇥ twice to place the cursor within the assert_select block:

+ + +

assert_select 'div#posts' do
+
+end

+ + +

Now we’ll check that the @posts objects are represented in the div#posts element.

+ + +

With the cursor inside the assert_select:

+ + +

Type ass, press ⇥, type div.post, press ⇥ twice, and type count (to replace the text). Now press ⇥ again, and type posts.size. Press ⇥ a final time (it will highlight the do...end block), and press ⌫.

+ + +

Our test method is now finished:

+ + +

def test_should_get_index
+  get :index
+  assert_response :success
+  assert(posts = assigns(:posts), "Cannot find @posts")
+  assert_select 'div#posts' do
+    assert_select 'div.post', :count => posts.size
+  end
+end

+ + +

NOTE: there is also a deftp snippet for functional tests to create a POST test stub. +To memorize: deftg stands for define test get and deftp stands for define test post

+ + +

Controller actions

+ + +

To navigate to blog_controller.rb there are three options:

+ + +
    +
  • press ⇧ ⌥ ⌘ ↓, and select “Controller” from the drop-down list
  • +
  • press ⌥ ⌘ ↓ and you’ll go directly to the controller (toggles between the two files)
  • +
  • press ⌘ T, type bc, choose the file, press ↩.
  • +
+ + +

Add the index action method:

+ + +

def index
+  @posts = Post.find_all_by_published(true)
+end

+ + +

Action views

+ + +

To create/navigate to the view, press ⇧ ⌥ ⌘ ↓ and select “View” (like above). Or press ⌥ ⌘ ↓ to toggle between a controller method and it’s view.

+ + +

As there are no app/views/blog/index* files, it will prompt you to create a blank view file. By default it guesses index.html.erb (because the method was named index), but of course you can change that in the dialog box.

+ + +

If instead you got the message “blog_controller.rb does not have a view”, note that you first need to save the controller file before hitting ⇧ ⌥ ⌘ ↓ or ⌥ ⌘ ↓. Also note that the cursor must be within the scope of a method for ⇧ ⌥ ⌘ ↓ or ⌥ ⌘ ↓ to work.

+ + +

Press enter to accept index.html.erb. You are taken to the new file.

+ + +

Let’s create HTML to match the earlier tests.

+ + +

Type div and press ⇥ twice, then type posts and press ⇥:

+ + +

<div id="posts">
+
+</div>

+ + +

Inside the div element, type for and press ⇥. This expands into a large ERb-enabled for-loop. Type @posts, press ⇥, type post and press ⇥. The cursor is now inside the for-loop.

+ + +

Inside the for-loop, type: div and press ⇥. Press ⌫, and type class='post' and press ⇥ to enter the div element.

+ + +

Create a <%= %> element (⌃ >). If you press ⌃ > again, it toggles to <% %>, and then again and it becomes <%- -%>, and again and it becomes <%# %> (a Ruby comment). Pressing ⌃ > again starts at <%= %> again.

+ + +

Enter post.body within the ERb template field.

+ + +

Actually, we’ll need to show the subject too, so above the <%= post.body %> line (press ↑ followed by ⌘ ↩) +type ‘h3’, and press ⌃ < (LessThan), then ⌃ > (GreatherThan), and post.subject.

+ + +

The resulting line is: <h3><%= post.subject %></h3>

+ + +

Move the cursor down between <% else %> and <% end %>.

+ + +

Create a simple element <p></p> (⌃ ⇧ W or ⌃ <). You can change the element type here. Just press ⇥ to go inside the element. Type There are no posts available to read. All y'all come back soon, yer hear. because its funny.

+ + +

Our index.html.erb template is now:

+ + +

<div id="posts">
+  <% if !@posts.blank? %>
+    <% for post in @posts %>
+      <div class="post">
+        <h3><%= post.subject %></h3>
+        <%= post.body %>
+      </div>
+    <% end %>
+  <% else %>
+    <p>There are no posts available to read. All y'all come back soon, yer hear.</p>
+  <% end %>
+
+</div>

+ + +

If we run our functional tests they now pass: run either from the command prompt with rake test:functionals or directly from the editor by pressing ⌃ \ and press 2 for “Test Functionals”

+ + +

As yet, we have no way for users to leave comments.

+ + +

Foxy Fixtures

+ + +

Create a comment model:

+ + +
ruby script/generate model Comment body:text name:string post:references
+ + +

Note: here post:references is effectively the same as post_id:integer. Within the generated migration it creates t.reference :post. There is also a t. and tcr snippet for references, as for other standard datatypes, which helps setup polymorphic associations.

+ + +

The generated create_table in 002_create_comments.rb is:

+ + +

create_table :comments do |t|
+  t.text :body
+  t.string :name
+  t.references :post
+
+  t.timestamps
+end

+ + +

Run rake db:migrate, or directly from the editor with ⌃ | and choose option “Migrate to Current”.

+ + +

Now create some comment fixtures so we can look at Foxy Fixtures. Open text/fixtures/comments.yml (⌘ T, type cy, press ↩).

+ + +

By default, the generated comments.yml starts like:

+ + +
one:
+  body: MyText
+  name: MyString
+  post:
+
+two:
+  body: MyText
+  name: MyString
+  post:
+ +

The post fields replace the rails1.2 post_id fields. Now, we can specify the post.yml labels for a post. From above we have published and unpublished. It can be hard to remember what fixtures we have, so there is a key-combo helper.

+ + +

Put the cursor after post: and press ⌥ ⎋. A drop-down box appears with the names of the posts.yml fixtures. Select published and press ↩. Repeat for the 2nd fixture. This gives us:

+ + +
one:
+  body: MyText
+  name: MyString
+  post: published
+
+two:
+  body: MyText
+  name: MyString
+  post: published
+ +

Associations

+ + +

To enable the Foxy Fixtures, we need to add associations to the model classes.

+ + +

You can now quickly go from a fixtures file (we’re in comments.yml) to the model file (⇧ ⌥ ⌘ ↓).

+ + +

Within comment.rb model, create a new line within the class, and type bt and press ⇥. Type post. This creates a snippet:

+ + +

belongs_to :post, :class_name => "Post", :foreign_key => "post_id"

+ + +

The class name and foreign key are now generated from the association name. You can change them by tabbing across. But, we only need the default, so we can delete these options.

+ + +

Press ⇥ and ⌫ to remove the :class_name and :foreign_key options. The Comment class is now:

+ + +

class Comment < ActiveRecord::Base
+  belongs_to :post
+end

+ + +

Now go to the Post class. Press ⌘ T and type post and select the model file, and press ↩.

+ + +

Create a new line within the Post class (⌘ ↩). Type hm and press ⇥ to generate a has_many association. Type comment, and the resulting snippet is:

+ + +

has_many :comments, :class_name => "comment", :foreign_key => "class_name_id"

+ + +

We don’t need the options. So press ⇥ once and then ⌫.

+ + +

class Post < ActiveRecord::Base
+  has_many :comments
+end

+ + +

Note: there is also a has_many :through snippet. Type hmt and ⇥ to activate it.

+ + +

Finally, we can run our tests since adding the Comment model + fixtures (⌃ \).

+ + +
rake test
+ +

Routes

+ + +

Open the routes file (⌘ T, type routes and press ↩).

+ + +

Change the routes file to:

+ + +

ActionController::Routing::Routes.draw do |map|
+  map.resources :posts
+  map.connect ':controller/:action/:id'
+  map.connect ':controller/:action/:id.:format'
+end

+ + +

Creating Posts

+ + +

From the Post model class (post.rb) you can now quickly navigate to a controller +of the same name. It supports either singular or plural controller names, but +will default to the plural name, which is the REST/resources preferred name.

+ + +

To create a PostsController, use the ‘Go To’ hot key (as above) ⇧ ⌥ ⌘ ↓ and select ‘Controller’. As there is no post_controller.rb nor posts_controller.rb it will create a posts_controller.rb controller file; which is what we want here.

+ + +

Note; at this stage you could use the Rails 2.0 scaffold generator to create the posts_controller.rb (and tests and routes).

+ + +

In the blank file, we need to create a controller class.

+ + +Type cla and ⇥, and select “Create controller class”. Type Posts and ⇥, +post and ⇥, and finally, Post and ⇥. This leaves the cursor in the middle +of the generated class: + +

class PostsController < ApplicationController
+  before_filter :find_post
+
+
+
+  private
+  def find_post
+    @post = Post.find(params[:id]) if params[:id]
+  end
+end

+ + +

TDD for Posts controller

+ + +

Currently there is not a functional test for our posts_controller.rb. To create it, use the ‘Go To’ hot key (⇧ ⌥ ⌘ ↓) and select ‘Functional Test’. This will create a blank file.

+ + +

Type cla and ⇥, and select “Create functional test class”. +Type Posts and ⇥. (The functional test class name +should match the controller class, with Test suffixed to it).

+ + +

The functional test class snippet gives you a deft stub. If you +press ⇥ now, it creates a generic test method snippet:

+ + +

def test_case_name
+
+end

+ + +

Instead, we will use the deftg (GET request) and deftp (POST request) snippets.

+ + +

Create a test for the index, new and edit actions. For index and new, we can delete the @model = models(:fixture_name), +etc parts.

+ + +

To test for the create action, type deftp and ⇥. Type create and ⇥, type post and ⇥, type ⌫ and ⇥, and again ⌫ and ⇥. Now enter in a hash of the values to pass in for the test, say :subject => 'Test', :body => 'Some body', :published => '1'. The result should look like:

+ + +

def test_should_post_create
+  post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
+  assert_response :redirect
+
+end

+ + +

On the line after the assert_response expression, we’ll test +for where we want to be redirected to.

+ + +

If you type art you create an old-style assert_redirected_to :action => "index" +snippet.

+ + +

In addition there are now various assert_redirected_to snippets that +use resourceful routes:

+ + +
    +
  • artp – assert_redirected_to model_path(@model)
  • +
  • artpp – assert_redirected_to models_path
  • +
  • artnp – assert_redirected_to parent_child_path(@parent, @child)
  • +
  • artnpp – assert_redirected_to parent_child_path(@parent)
  • +
+ + +

As we’ll see later, this naming scheme is used for other snippets that +use resourceful routes, like link_to and redirect_to.

+ + +

Type artpp and ⇥, and type post, to assert that the create +action must redirect to the index page.

+ + +

The final test_should_post_create method is:

+ + +

def test_should_post_create
+  post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
+  assert_response :redirect
+  assert_redirected_to posts_path
+end

+ + +

Running our tests (rake test:functionals or ⌃ \) shows all these new tests failing.

+ + +

Views

+ + +

Go back to the posts_controller.rb file (⌥ ⌘ ↓).

+ + +

Now add three actions – index, new and edit. New methods can be created +with the def snippet:

+ + +

class PostsController < ApplicationController
+  before_filter :find_post
+
+  def index
+    @posts = Post.find(:all)
+  end
+
+  def new
+    @post = Post.new
+  end
+
+  def edit
+  end
+
+  private
+  def find_post
+    @post = Post.find(params[:id]) if params[:id]
+  end
+end

+ + +

Note: the index method could be created by typing def, ⇥, index, ⇥, @posts = Post.fina, ⇥, ⌫.

+ + +

Now we need templates for the index, new and edit actions.

+ + +

Place the cursor inside the index method, +and use the ‘Go To’ hot key (⇧ ⌥ ⌘ ↓) +and select ‘View’. A dialog box will pop up asking for the name of the new +template (as there are no app/views/posts/index* files). By default, the +suffix is now .html.erb rather than the old .rhtml. Press ↩, +to accept index.html.erb as your template name.

+ + +

Let’s just create a simple table showing the Posts.

+ + +

Type table and ⌃ < to generate <table></table>, and +press ↩ to put the tags on separate lines.

+ + +

Do the same to create a <tbody></tbody> element.

+ + +

Inside the <tbody></tbody> we want to iterate over the @posts, +one per <tr></tr> row.

+ + +

Press ⌃ >, three times, to create a <%- -%> tag. Inside it +type @posts.each do |post|.

+ + +

On the next line (⌘ ↩), type end and ⇥, to create <% end -%>. +We now have a Ruby block within this ERb template.

+ + +

Inside the block, create a <tr></tr> element, and within it +create a <td></td> element. We’ll skip over anything fancy +here, and just put the post’s subject here.

+ + +

Type post.subject and select it. Now press ⌃ > to wrap +the selected text inside <%= post.subject %>.

+ + +

The resulting index.html.erb is:

+ + +

<table>
+  <tbody>
+    <%- @posts.each do |post| -%>
+      <tr>
+        <td><%= post.subject %></td>
+      </tr>
+    <% end -%>
+  </tbody>
+</table>

+ + +

Forms

+ + +

Place the cursor inside the new method, +and use the ‘Go To’ hot key (⇧ ⌥ ⌘ ↓) +and select ‘View’. Press ↩ to accept new.html.erb.

+ + +

Inside the blank new.html.erb file, type ffe and press ⇥, and type post +and press ⇥ twice:

+ + +

<%= error_messages_for :post %>
+<% form_for @post do |f| -%>
+
+<% end -%>

+ + +form_for is the Rails 2.0 preferred helper for managing forms, and +there are now snippets for common form_for helpers. There are ff and ffe +snippets; the former does not have the error messages section. + +

To create a label and text field for the subject attribute:

+ + +

Create a <p></p> block (Press ⌃ <, then ⇥, then ↩). +Type f. and ⇥, and select “Label”. Type subject, press ⇥ and press ⌫. +Create a <br /> (⌃ ↩). +Type f. and ⇥, and select “Text Field”. Type subject.

+ + +

This gives us:

+ + +

<%= error_messages_for :post %>
+<% form_for @post do |f| -%>
+  <p>
+    <%= f.label :subject %><br />
+    <%= f.text_field :subject %>
+  </p>
+<% end -%>

+ + +

Now repeat for body and published fields.

+ + +

Note, for published, you might change the label to Published yet? by tabbing +into the default string file.

+ + +

Finally, add a “Submit” button using the f. snippet tab completion.

+ + +

Start script/server from the prompt and you can now view this form at http://localhost:3000/posts/new

+ + +

The final form is:

+ + +

<%= error_messages_for :post %>
+<% form_for @post do |f| -%>
+  <p>
+    <%= f.label :subject %><br />
+    <%= f.text_field :subject %>
+  </p>
+  <p>
+    <%= f.label :body %><br />
+    <%= f.text_area :body %>
+  </p>
+  <p>
+    <%= f.label :published, "Published yet?" %><br />
+    <%= f.check_box :published %>
+  </p>
+  <p>
+    <%= f.submit "Submit" %>
+  </p>
+<% end -%>

+ + +

Note: if you got <br> when hitting ⌃ ↩ instead of <br /> then you might want to go to the preferences of TextMate (⌘ ,), choose tab “Advanced”, choose “Shell Variables”, click the + sign to add a new shell variable, and give it the name TM_XHTML and a value of /

+ + +

Partials

+ + +

The form we just created is exactly the same as the form required for the edit.html.erb template.

+ + +

Instead of copy+pasting it into the edit.html.erb file, we’ll create a partial +template.

+ + +

Select the entire form (⌘ A), and press ⌃ ⇧ H and a dialog box appears. +Type in form and press ↩.

+ + +

You’ll notice a new file _form.html.erb has appeared which contains the code you had selected, +while the code in the file new.html.erb has been replaced by:

+ + +
<%= render :partial => 'form' %>
+ + +

Now copy and paste this into the edit.html.erb file. To create this file, +return to the controller (from the new.html.erb file, press ⌥ ⌘ ↓), go to the edit action, +and use ⌥ ⌘ ↓ again to create the edit.html.erb template file.

+ + +

Link helpers

+ + +At the bottom of the new.html.erb we want a link back to the list of all posts +(within the posts controller, not the public blog controller). This +will be the index action, and will be accessible via the resources route +posts_path. + +

There are several link_to snippets that support the resources routes:

+ + +
    +
  • lip – <%= link_to "link text...", model_path(@model) %>
  • +
  • lipp – <%= link_to "link text...", models_path %>
  • +
  • linp – <%= link_to "link text...", parent_child_path(@parent, @child) %>
  • +
  • linpp – <%= link_to "link text...", parent_child_path(@parent) %>
  • +
  • lim – <%= link_to model.name, model_path(model) %>
  • +
+ + +

The tab stop points are in useful places.

+ + +So, to create our link to the posts page, type lipp and ⇥, type +Show all posts, press ⇥ twice and type post. + +

Controllers: respond_to and redirect_to

+ + +

Now we’ll add a create action to the posts_controller.rb. Let’s go there (⌥ ⌘ ↓).

+ + +Below the edit method type def and ⇥, and type +create and ⇥. Now fill out the create action like: + +

def create
+  @post = Post.new(params[:post])
+  if @post.save
+
+  else
+
+  end
+end

+ + +

Place the cursor in the true section of the if statement. +Type repp and ⇥ to create a redirect_to expression. Press ⇥ +again and replace the selected text with post.

+ + +

Like the various link_to snippets, there are matching redirect_to +snippets.

+ + +
    +
  • rep – redirect_to(model_path(@model))
  • +
  • repp – redirect_to(models_path)
  • +
  • renp – redirect_to(parent_child_path(@parent, @child))
  • +
  • renpp – redirect_to(parent_child_path(@parent))
  • +
+ + +

There are tab stops in useful places.

+ + +In the false section of the if expression, we’ll demonstrate the +respond_to block. There are two ways to generate a respond_to block. + +

Type rest and ⇥, and you get a standard empty block you can work with:

+ + +

respond_to do |wants|
+  wants.html {  }
+end

+ + +

Press ⇥ twice to get inside the wants.html block, type ra, press ⇥, then type new. The final block is:

+ + +

respond_to do |wants|
+  wants.html { render :action => "new" }
+end

+ + +

Alternately, there is the “upgrade” hot key (⇧ ⌘ H), where you can convert some +existing selected code, into a respond_to block.

+ + +Select the whole line containing the redirect_to expression from the +true section of the if statement (⇧ ⌘ L). + +

Press ⇧ ⌘ H and the line is replaced with:

+ + +

respond_to do |wants|
+  wants.html do
+    redirect_to(posts_path)
+  end
+  wants.js {  }
+end

+ + +

The js is the first tab stop. The point of this hot key is to instantly +refactor your existing html respond code, and support a second response +format.

+ + +

For now remove the line with wants.js (⌃ ⇧ K).

+ + +

The completed create action is:

+ + +

def create
+  @post = Post.new(params[:post])
+  if @post.save
+    respond_to do |wants|
+      wants.html do
+        redirect_to(posts_path)
+      end
+    end
+  else
+    respond_to do |wants|
+      wants.html { render :action => "new" }
+    end
+  end
+end

+ + +

Yes you’d probably only have one respond_to block, but this is a +demo so I am taking the scenic route.

+ + +

Our application so far

+ + +

In the browser, we can create posts via http://localhost:3000/posts/new +and then view them as a blog visitor at http://localhost:3000/blog.

+ + +

Some more migrations

+ + +

We’re looking for the following additions:

+ + +
    +
  • rename the column name of table comments to author
  • +
  • add a new column author_url to table comments
  • +
  • add an index to the column post_id of the comments table
  • +
+ + +

Let’s try to do this all in one migrations file. Start Quick Migration (⌃ ⇧ M). Let’s name it ModifyComments. A new migrations file 003_modify_comments.rb is created and opened, and the cursor is placed behind the mtab trigger. For now delete mtab and instead enter mcol and press ⇥. Choose Rename / Rename Column (3). Type commentsnameauthor ⇥ ↩.

+ + +

Again type mcol and ⇥. This time choose Add / Remove Column (1). Type commentsauthor_url, then ⇥ twice, and press ↩.

+ + +

Now type mind and ⇥. Choose Add / Remove Index (1). Type commentspost_id.

+ + +

The end result looks like this:

+ + +

class ModifyComments < ActiveRecord::Migration
+  def self.up
+    rename_column :comments, :name, :author
+    add_column :comments, :author_url, :string
+    add_index :comments, :post_id
+  end
+
+  def self.down
+    remove_index :comments, :post_id
+    remove_column :comments, :author_url
+    rename_column :comments, :author, :name
+  end
+end

+ + +

Notice how the down method calls are in reversed order of the up method calls.

+ + +

Save the file (⌘ S) and migrate to current (⌃ |).

+ + +

Be sure to modify the comments fixture file. Go there (⌘ T, press cy, choose comments.yml). Rename name to author and add a row for author_url for each comment. Check your tests again (⌃ \, choose option 1). All tests should pass.

+ + +

Futhermore we’d like to know when a post was published. To do this we’ll want the following modifications:

+ + +
    +
  • keep track of the datetime when a post was published.
  • +
  • remove the column published from the posts table because it can be determined if a post is published by looking at whether or not a value is present for the published date.
  • +
+ + +

Start Quick Migration (⌃ ⇧ M). Let’s name it AddPublishedAtForPosts. A new migrations file 004_add_published_at_for_posts.rb is created and opened, and the cursor is placed behind the mtab trigger. Again delete mtab and instead enter mcol and press ⇥. Choose Add / Remove Column (1). Type postspublished_atdatetime ⇥ and ↩.

+ + +

Again type mcol and ⇥. Choose Remove / Add Column (5). Type postspublished and press ⇥ twice.

+ + +

The end result looks like this:

+ + +

class AddPublishedAtForPosts < ActiveRecord::Migration
+  def self.up
+    add_column :posts, :published_at, :datetime
+    remove_column :posts, :published
+  end
+
+  def self.down
+    add_column :posts, :published, :boolean
+    remove_column :posts, :published_at
+  end
+end

+ + +

Notice how the Remove / Add Column command automagically determined in the down method the column type of column published to be a boolean. It determines this by looking at the current state of your db/schema.rb file.

+ + +

Save the file (⌘ S) and migrate to current (⌃ |).

+ + +

Now we need to modify the posts fixtures file. Go there (⌘ T, type pyml, choose posts.yml). Replace the line published: true by published_at: 2008-1-1.

+ + +

Modify the posts functional test, first go there (⇧ ⌥ ⌘ ↓, choose “Go to Functional Test”). Replace :published => '1' by :published_at => Date.new(2008, 1, 1).

+ + +

Modify the post model, first go there (⇧ ⌥ ⌘ ↓, choose “Go to Model”). Have the code look like:

+ + +

class Post < ActiveRecord::Base
+  has_many :comments
+
+  def published
+    !self.published_at.nil?
+  end
+
+  def published=(publish)
+    if publish
+      self.published_at = DateTime.now if self.published_at.nil?
+    else
+      self.published_at = nil
+    end
+  end
+end

+ + +

Modify the blog_controller.rb file. Replace Post.find_all_by_published(true) by Post.find(:all, :conditions => "published_at IS NOT NULL").

+ + +

Finally, check your tests again (⌃ \). All tests should pass.

+ + +

TODO

+ + +
    +
  • Model snippets (validates_…)
  • +
  • link_to(model) (ltm)
  • +
  • RJS demo
  • +
+

+ Dr Nic Williams, 28th February 2008
+ Theme extended from Paul Battley +

+ + +
+ + + + + diff --git a/bundles/ruby-on-rails.tmbundle/website/demo.txt b/bundles/ruby-on-rails.tmbundle/website/demo.txt new file mode 100644 index 000000000..37d3c383a --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/demo.txt @@ -0,0 +1,738 @@ +h1. Step-by-step demonstration + +In this demo we'll create a blog; because that's what blogs are for: being +demonstrations of web frameworks. + +The demonstration uses new features of Rails 2.0 and the snippets in this bundle. + +h2. A New App + +
rails blog
+cd blog
+mate .
+ +h2. Add some models + +
ruby script/generate model Post subject:string body:text
+ +This creates a 001_create_posts migration with a create_table: + +
create_table :posts do |t|
+  t.string :subject
+  t.text :body
+
+  t.timestamps
+end
+ +h2. Sexy Migration support + +If you put the cursor on the line after t.text :body, type t. and press TAB. Select "Create boolean column" (by pressing 0), and type "published" into the template field. If nothing happened when you pressed TAB, check that when you opened the migrations file you've selected the bundle "Ruby on Rails". + +Note that another t. was created on the next line! Press TAB and the cursor will be placed after it. You can now press TAB again to create another column, or delete this line. + +Here, delete the extraneous t. line (CONTROL SHIFT K). And save the file (COMMAND S). + +Run the migrations, either from the prompt: + + rake db:migrate + +or directly from the editor with CONTROL | (Ctrl-Pipeline), and choosing option "Migrate to Current". + + +h2. Post fixtures + +Update the test/fixtures/posts.yml file as: + +
published:
+  subject: Some article
+  body: A test article
+  published: true
+
+nonpublished:
+  body: Still writing this one
+ +Note, in Rails 2.0 fixtures no longer have explicit ids. Later on we'll look at snippets for using Foxy Fixtures with auto-completion for associations. + +h2. Public blog controller + +Create a controller for our blog, either via the command prompt: + + ruby script/generate controller blog + +or directly from the editor with CONTROL |, and choosing option "Call Generate Script", choose "Controller", give it the name "blog", and empty the list of actions. + +Now open blog_controller_test.rb. To find this file quickly press COMMAND T, type bct, and select the file. + +Note how much cleaner functional tests are now via ActionController::TestCase. + +Let's do some TDD. First, delete the test_truth dummy method. + +To create a test to show a list of blog articles: + + deftg + +and TAB gives: + +
def test_should_get_action
+  @model = models(:fixture_name)
+  get :action, :id => @model.to_param
+  assert_response :success
+
+end
+ +Type index to replace action. Press TAB, and then BACKSPACE to remove the first line, then press TAB three times and then BACKSPACE to remove the :id => @model.to_param part. The press TAB again to go to the end of the method. Now we have: + +
def test_should_get_index
+  get :index
+  assert_response :success
+
+end
+ +Now type asg, press TAB, and type posts, and press TAB again. This creates an instance variable lookup within an assertion: + +
assert(posts = assigns(:posts), "Cannot find @posts")
+ +Now, let's assert the HTML format. + +Type ass and press TAB. Type div#posts, press TAB and BACKSPACE, then TAB twice to place the cursor within the assert_select block: + +
assert_select 'div#posts' do
+
+end
+ +Now we'll check that the @posts objects are represented in the div#posts element. + +With the cursor inside the assert_select: + +Type ass, press TAB, type div.post, press TAB twice, and type count (to replace the text). Now press TAB again, and type posts.size. Press TAB a final time (it will highlight the do...end block), and press BACKSPACE. + +Our test method is now finished: + +
def test_should_get_index
+  get :index
+  assert_response :success
+  assert(posts = assigns(:posts), "Cannot find @posts")
+  assert_select 'div#posts' do
+    assert_select 'div.post', :count => posts.size
+  end
+end
+ +NOTE: there is also a deftp snippet for functional tests to create a POST test stub. +To memorize: deftg stands for define test get and deftp stands for define test post + +h2. Controller actions + +To navigate to blog_controller.rb there are three options: + +* press SHIFT OPTION COMMAND DOWN, and select "Controller" from the drop-down list +* press OPTION COMMAND DOWN and you'll go directly to the controller (toggles between the two files) +* press COMMAND T, type bc, choose the file, press RETURN. + +Add the index action method: + +
def index
+  @posts = Post.find_all_by_published(true)
+end
+ +h2. Action views + +To create/navigate to the view, press SHIFT OPTION COMMAND DOWN and select "View" (like above). Or press OPTION COMMAND DOWN to toggle between a controller method and it's view. + +As there are no app/views/blog/index* files, it will prompt you to create a blank view file. By default it guesses index.html.erb (because the method was named index), but of course you can change that in the dialog box. + +If instead you got the message "blog_controller.rb does not have a view", note that you first need to save the controller file before hitting SHIFT OPTION COMMAND DOWN or OPTION COMMAND DOWN. Also note that the cursor must be within the scope of a method for SHIFT OPTION COMMAND DOWN or OPTION COMMAND DOWN to work. + +Press enter to accept index.html.erb. You are taken to the new file. + +Let's create HTML to match the earlier tests. + +Type div and press TAB twice, then type posts and press TAB: + +
+ +
+ +Inside the div element, type for and press TAB. This expands into a large ERb-enabled for-loop. Type @posts, press TAB, type post and press TAB. The cursor is now inside the for-loop. + +Inside the for-loop, type: div and press TAB. Press BACKSPACE, and type class='post' and press TAB to enter the div element. + +Create a <%= %> element (CONTROL >). If you press CONTROL > again, it toggles to <% %>, and then again and it becomes <%- -%>, and again and it becomes <%# %> (a Ruby comment). Pressing CONTROL > again starts at <%= %> again. + +Enter post.body within the ERb template field. + +Actually, we'll need to show the subject too, so above the <%= post.body %> line (press UP followed by COMMAND RETURN) +type 'h3', and press CONTROL < (LessThan), then CONTROL > (GreatherThan), and post.subject. + +The resulting line is:

<%= post.subject %>

+ +Move the cursor down between <% else %> and <% end %>. + +Create a simple element

(CONTROL SHIFT W or CONTROL <). You can change the element type here. Just press TAB to go inside the element. Type There are no posts available to read. All y'all come back soon, yer hear. because its funny. + +Our index.html.erb template is now: + +
+ <% if !@posts.blank? %> + <% for post in @posts %> +
+

<%= post.subject %>

+ <%= post.body %> +
+ <% end %> + <% else %> +

There are no posts available to read. All y'all come back soon, yer hear.

+ <% end %> + +
+ +If we run our functional tests they now pass: run either from the command prompt with rake test:functionals or directly from the editor by pressing CONTROL \ and press 2 for "Test Functionals" + +As yet, we have no way for users to leave comments. + +h2. Foxy Fixtures + +Create a comment model: + + ruby script/generate model Comment body:text name:string post:references + +Note: here post:references is effectively the same as post_id:integer. Within the generated migration it creates t.reference :post. There is also a t. and tcr snippet for references, as for other standard datatypes, which helps setup polymorphic associations. + +The generated create_table in 002_create_comments.rb is: + +
create_table :comments do |t|
+  t.text :body
+  t.string :name
+  t.references :post
+
+  t.timestamps
+end
+ +Run rake db:migrate, or directly from the editor with CONTROL | and choose option "Migrate to Current". + +Now create some comment fixtures so we can look at Foxy Fixtures. Open text/fixtures/comments.yml (COMMAND T, type cy, press RETURN). + +By default, the generated comments.yml starts like: + +
one:
+  body: MyText
+  name: MyString
+  post:
+
+two:
+  body: MyText
+  name: MyString
+  post:
+ +The post fields replace the rails1.2 post_id fields. Now, we can specify the post.yml labels for a post. From above we have published and unpublished. It can be hard to remember what fixtures we have, so there is a key-combo helper. + +Put the cursor after post: and press OPTION ESCAPE. A drop-down box appears with the names of the posts.yml fixtures. Select published and press RETURN. Repeat for the 2nd fixture. This gives us: + +
one:
+  body: MyText
+  name: MyString
+  post: published
+
+two:
+  body: MyText
+  name: MyString
+  post: published
+ +h2. Associations + +To enable the Foxy Fixtures, we need to add associations to the model classes. + +You can now quickly go from a fixtures file (we're in comments.yml) to the model file (SHIFT OPTION COMMAND DOWN). + +Within comment.rb model, create a new line within the class, and type bt and press TAB. Type post. This creates a snippet: + +
belongs_to :post, :class_name => "Post", :foreign_key => "post_id"
+ +The class name and foreign key are now generated from the association name. You can change them by tabbing across. But, we only need the default, so we can delete these options. + +Press TAB and BACKSPACE to remove the :class_name and :foreign_key options. The Comment class is now: + +
class Comment < ActiveRecord::Base
+  belongs_to :post
+end
+ +Now go to the Post class. Press COMMAND T and type post and select the model file, and press RETURN. + +Create a new line within the Post class (COMMAND RETURN). Type hm and press TAB to generate a has_many association. Type comment, and the resulting snippet is: + +
has_many :comments, :class_name => "comment", :foreign_key => "class_name_id"
+ +We don't need the options. So press TAB once and then BACKSPACE. + +
class Post < ActiveRecord::Base
+  has_many :comments
+end
+ +Note: there is also a has_many :through snippet. Type hmt and TAB to activate it. + +Finally, we can run our tests since adding the Comment model + fixtures (CONTROL \). + +
rake test
+ +h2. Routes + +Open the routes file (COMMAND T, type routes and press RETURN). + +Change the routes file to: + +
ActionController::Routing::Routes.draw do |map|
+  map.resources :posts
+  map.connect ':controller/:action/:id'
+  map.connect ':controller/:action/:id.:format'
+end
+ +h2. Creating Posts + +From the Post model class (post.rb) you can now quickly navigate to a controller +of the same name. It supports either singular or plural controller names, but +will default to the plural name, which is the REST/resources preferred name. + +To create a PostsController, use the 'Go To' hot key (as above) SHIFT OPTION COMMAND DOWN and select 'Controller'. As there is no post_controller.rb nor posts_controller.rb it will create a posts_controller.rb controller file; which is what we want here. + +Note; at this stage you could use the Rails 2.0 scaffold generator to create the posts_controller.rb (and tests and routes). + +In the blank file, we need to create a controller class. + +Type cla and TAB, and select "Create controller class". Type Posts and TAB, +post and TAB, and finally, Post and TAB. This leaves the cursor in the middle +of the generated class: + +
class PostsController < ApplicationController
+  before_filter :find_post
+
+
+
+  private
+  def find_post
+    @post = Post.find(params[:id]) if params[:id]
+  end
+end
+ +h2. TDD for Posts controller + +Currently there is not a functional test for our posts_controller.rb. To create it, use the 'Go To' hot key (SHIFT OPTION COMMAND DOWN) and select 'Functional Test'. This will create a blank file. + +Type cla and TAB, and select "Create functional test class". +Type Posts and TAB. (The functional test class name +should match the controller class, with Test suffixed to it). + +The functional test class snippet gives you a deft stub. If you +press TAB now, it creates a generic test method snippet: + +
def test_case_name
+
+end
+ +Instead, we will use the deftg (GET request) and deftp (POST request) snippets. + +Create a test for the index, new and edit actions. For index and new, we can delete the @model = models(:fixture_name), +etc parts. + +To test for the create action, type deftp and TAB. Type create and TAB, type post and TAB, type BACKSPACE and TAB, and again BACKSPACE and TAB. Now enter in a hash of the values to pass in for the test, say :subject => 'Test', :body => 'Some body', :published => '1'. The result should look like: + +
def test_should_post_create
+  post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
+  assert_response :redirect
+
+end
+ +On the line after the assert_response expression, we'll test +for where we want to be redirected to. + +If you type art you create an old-style assert_redirected_to :action => "index" +snippet. + +In addition there are now various assert_redirected_to snippets that +use resourceful routes: + +* artp - assert_redirected_to model_path(@model) +* artpp - assert_redirected_to models_path +* artnp - assert_redirected_to parent_child_path(@parent, @child) +* artnpp - assert_redirected_to parent_child_path(@parent) + +As we'll see later, this naming scheme is used for other snippets that +use resourceful routes, like link_to and redirect_to. + +Type artpp and TAB, and type post, to assert that the create +action must redirect to the index page. + +The final test_should_post_create method is: + +
def test_should_post_create
+  post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
+  assert_response :redirect
+  assert_redirected_to posts_path
+end
+ +Running our tests (rake test:functionals or CONTROL \) shows all these new tests failing. + +h2. Views + +Go back to the posts_controller.rb file (OPTION COMMAND DOWN). + +Now add three actions - index, new and edit. New methods can be created +with the def snippet: + +
class PostsController < ApplicationController
+  before_filter :find_post
+
+  def index
+    @posts = Post.find(:all)
+  end
+
+  def new
+    @post = Post.new
+  end
+
+  def edit
+  end
+
+  private
+  def find_post
+    @post = Post.find(params[:id]) if params[:id]
+  end
+end
+ +Note: the index method could be created by typing def, TAB, index, TAB, @posts = Post.fina, TAB, BACKSPACE. + +Now we need templates for the index, new and edit actions. + +Place the cursor inside the index method, +and use the 'Go To' hot key (SHIFT OPTION COMMAND DOWN) +and select 'View'. A dialog box will pop up asking for the name of the new +template (as there are no app/views/posts/index* files). By default, the +suffix is now .html.erb rather than the old .rhtml. Press RETURN, +to accept index.html.erb as your template name. + +Let's just create a simple table showing the Posts. + +Type table and CONTROL < to generate
, and +press RETURN to put the tags on separate lines. + +Do the same to create a element. + +Inside the we want to iterate over the @posts, +one per row. + +Press CONTROL >, three times, to create a <%- -%> tag. Inside it +type @posts.each do |post|. + +On the next line (COMMAND RETURN), type end and TAB, to create <% end -%>. +We now have a Ruby block within this ERb template. + +Inside the block, create a element, and within it +create a element. We'll skip over anything fancy +here, and just put the post's subject here. + +Type post.subject and select it. Now press CONTROL > to wrap +the selected text inside <%= post.subject %>. + +The resulting index.html.erb is: + +

+  
+    <%- @posts.each do |post| -%>
+      
+        
+      
+    <% end -%>
+  
+
<%= post.subject %>
+ +h2. Forms + +Place the cursor inside the new method, +and use the 'Go To' hot key (SHIFT OPTION COMMAND DOWN) +and select 'View'. Press RETURN to accept new.html.erb. + +Inside the blank new.html.erb file, type ffe and press TAB, and type post +and press TAB twice: + +
<%= error_messages_for :post %>
+<% form_for @post do |f| -%>
+
+<% end -%>
+ +form_for is the Rails 2.0 preferred helper for managing forms, and +there are now snippets for common form_for helpers. There are ff and ffe +snippets; the former does not have the error messages section. + +To create a label and text field for the subject attribute: + +Create a

block (Press CONTROL <, then TAB, then RETURN). +Type f. and TAB, and select "Label". Type subject, press TAB and press BACKSPACE. +Create a
(CONTROL RETURN). +Type f. and TAB, and select "Text Field". Type subject. + +This gives us: + +
<%= error_messages_for :post %>
+<% form_for @post do |f| -%>
+  

+ <%= f.label :subject %>
+ <%= f.text_field :subject %> +

+<% end -%>
+ +Now repeat for body and published fields. + +Note, for published, you might change the label to Published yet? by tabbing +into the default string file. + +Finally, add a "Submit" button using the f. snippet tab completion. + +Start script/server from the prompt and you can now view this form at [http://localhost:3000/posts/new](http://localhost:3000/posts/new) + +The final form is: + +
<%= error_messages_for :post %>
+<% form_for @post do |f| -%>
+  

+ <%= f.label :subject %>
+ <%= f.text_field :subject %> +

+

+ <%= f.label :body %>
+ <%= f.text_area :body %> +

+

+ <%= f.label :published, "Published yet?" %>
+ <%= f.check_box :published %> +

+

+ <%= f.submit "Submit" %> +

+<% end -%>
+ +Note: if you got
when hitting CONTROL RETURN instead of
then you might want to go to the preferences of TextMate (COMMAND ,), choose tab "Advanced", choose "Shell Variables", click the + sign to add a new shell variable, and give it the name TM_XHTML and a value of / + +h2. Partials + +The form we just created is exactly the same as the form required for the edit.html.erb template. + +Instead of copy+pasting it into the edit.html.erb file, we'll create a partial +template. + +Select the entire form (COMMAND A), and press CONTROL SHIFT H and a dialog box appears. +Type in form and press RETURN. + +You'll notice a new file _form.html.erb has appeared which contains the code you had selected, +while the code in the file new.html.erb has been replaced by: + + <%= render :partial => 'form' %> + +Now copy and paste this into the edit.html.erb file. To create this file, +return to the controller (from the new.html.erb file, press OPTION COMMAND DOWN), go to the edit action, +and use OPTION COMMAND DOWN again to create the edit.html.erb template file. + +h2. Link helpers + +At the bottom of the new.html.erb we want a link back to the list of all posts +(within the posts controller, not the public blog controller). This +will be the index action, and will be accessible via the resources route +posts_path. + +There are several link_to snippets that support the resources routes: + +* lip - <%= link_to "link text...", model_path(@model) %> +* lipp - <%= link_to "link text...", models_path %> +* linp - <%= link_to "link text...", parent_child_path(@parent, @child) %> +* linpp - <%= link_to "link text...", parent_child_path(@parent) %> +* lim - <%= link_to model.name, model_path(model) %> + +The tab stop points are in useful places. + +So, to create our link to the posts page, type lipp and TAB, type +Show all posts, press TAB twice and type post. + +h2. Controllers: respond_to and redirect_to + +Now we'll add a create action to the posts_controller.rb. Let's go there (OPTION COMMAND DOWN). + +Below the edit method type def and TAB, and type +create and TAB. Now fill out the create action like: + +
def create
+  @post = Post.new(params[:post])
+  if @post.save
+
+  else
+
+  end
+end
+ +Place the cursor in the true section of the if statement. +Type repp and TAB to create a redirect_to expression. Press TAB +again and replace the selected text with post. + +Like the various link_to snippets, there are matching redirect_to +snippets. + +* rep - redirect_to(model_path(@model)) +* repp - redirect_to(models_path) +* renp - redirect_to(parent_child_path(@parent, @child)) +* renpp - redirect_to(parent_child_path(@parent)) + +There are tab stops in useful places. + +In the false section of the if expression, we'll demonstrate the +respond_to block. There are two ways to generate a respond_to block. + +Type rest and TAB, and you get a standard empty block you can work with: + +
respond_to do |wants|
+  wants.html {  }
+end
+ +Press TAB twice to get inside the wants.html block, type ra, press TAB, then type new. The final block is: + +
respond_to do |wants|
+  wants.html { render :action => "new" }
+end
+ +Alternately, there is the "upgrade" hot key (SHIFT COMMAND H), where you can convert some +existing selected code, into a respond_to block. + +Select the whole line containing the redirect_to expression from the +true section of the if statement (SHIFT COMMAND L). + +Press SHIFT COMMAND H and the line is replaced with: + +
respond_to do |wants|
+  wants.html do
+    redirect_to(posts_path)
+  end
+  wants.js {  }
+end
+ +The js is the first tab stop. The point of this hot key is to instantly +refactor your existing html respond code, and support a second response +format. + +For now remove the line with wants.js (CONTROL SHIFT K). + +The completed create action is: + +
def create
+  @post = Post.new(params[:post])
+  if @post.save
+    respond_to do |wants|
+      wants.html do
+        redirect_to(posts_path)
+      end
+    end
+  else
+    respond_to do |wants|
+      wants.html { render :action => "new" }
+    end
+  end
+end
+ +Yes you'd probably only have one respond_to block, but this is a +demo so I am taking the scenic route. + +h2. Our application so far + +In the browser, we can create posts via [http://localhost:3000/posts/new](http://localhost:3000/posts/new) +and then view them as a blog visitor at [http://localhost:3000/blog](http://localhost:3000/blog). + +h2. Some more migrations + +We're looking for the following additions: + +* rename the column name of table comments to author +* add a new column author_url to table comments +* add an index to the column post_id of the comments table + +Let's try to do this all in one migrations file. Start Quick Migration (CONTROL SHIFT M). Let's name it ModifyComments. A new migrations file 003_modify_comments.rb is created and opened, and the cursor is placed behind the mtab trigger. For now delete mtab and instead enter mcol and press TAB. Choose Rename / Rename Column (3). Type comments TAB name TAB author TAB RETURN. + +Again type mcol and TAB. This time choose Add / Remove Column (1). Type comments TAB author_url, then TAB twice, and press RETURN. + +Now type mind and TAB. Choose Add / Remove Index (1). Type comments TAB post_id. + +The end result looks like this: + +
class ModifyComments < ActiveRecord::Migration
+  def self.up
+    rename_column :comments, :name, :author
+    add_column :comments, :author_url, :string
+    add_index :comments, :post_id
+  end
+
+  def self.down
+    remove_index :comments, :post_id
+    remove_column :comments, :author_url
+    rename_column :comments, :author, :name
+  end
+end
+ +Notice how the down method calls are in reversed order of the up method calls. + +Save the file (COMMAND S) and migrate to current (CONTROL |). + +Be sure to modify the comments fixture file. Go there (COMMAND T, press cy, choose comments.yml). Rename name to author and add a row for author_url for each comment. Check your tests again (CONTROL \, choose option 1). All tests should pass. + +Futhermore we'd like to know when a post was published. To do this we'll want the following modifications: + +* keep track of the datetime when a post was published. +* remove the column published from the posts table because it can be determined if a post is published by looking at whether or not a value is present for the published date. + +Start Quick Migration (CONTROL SHIFT M). Let's name it AddPublishedAtForPosts. A new migrations file 004_add_published_at_for_posts.rb is created and opened, and the cursor is placed behind the mtab trigger. Again delete mtab and instead enter mcol and press TAB. Choose Add / Remove Column (1). Type posts TAB published_at TAB datetime TAB and RETURN. + +Again type mcol and TAB. Choose Remove / Add Column (5). Type posts TAB published and press TAB twice. + +The end result looks like this: + +
class AddPublishedAtForPosts < ActiveRecord::Migration
+  def self.up
+    add_column :posts, :published_at, :datetime
+    remove_column :posts, :published
+  end
+
+  def self.down
+    add_column :posts, :published, :boolean
+    remove_column :posts, :published_at
+  end
+end
+ +Notice how the Remove / Add Column command automagically determined in the down method the column type of column published to be a boolean. It determines this by looking at the current state of your db/schema.rb file. + +Save the file (COMMAND S) and migrate to current (CONTROL |). + +Now we need to modify the posts fixtures file. Go there (COMMAND T, type pyml, choose posts.yml). Replace the line published: true by published_at: 2008-1-1. + +Modify the posts functional test, first go there (SHIFT OPTION COMMAND DOWN, choose "Go to Functional Test"). Replace :published => '1' by :published_at => Date.new(2008, 1, 1). + +Modify the post model, first go there (SHIFT OPTION COMMAND DOWN, choose "Go to Model"). Have the code look like: + +
class Post < ActiveRecord::Base
+  has_many :comments
+
+  def published
+    !self.published_at.nil?
+  end
+
+  def published=(publish)
+    if publish
+      self.published_at = DateTime.now if self.published_at.nil?
+    else
+      self.published_at = nil
+    end
+  end
+end
+ +Modify the blog_controller.rb file. Replace Post.find_all_by_published(true) by Post.find(:all, :conditions => "published_at IS NOT NULL"). + +Finally, check your tests again (CONTROL \). All tests should pass. + + +h1. TODO + +* Model snippets (validates_...) +* link_to(model) (ltm) +* RJS demo + diff --git a/bundles/ruby-on-rails.tmbundle/website/images/logo_bundle.png b/bundles/ruby-on-rails.tmbundle/website/images/logo_bundle.png new file mode 100644 index 000000000..63babefad Binary files /dev/null and b/bundles/ruby-on-rails.tmbundle/website/images/logo_bundle.png differ diff --git a/bundles/ruby-on-rails.tmbundle/website/index.html b/bundles/ruby-on-rails.tmbundle/website/index.html new file mode 100644 index 000000000..31c3982c1 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/index.html @@ -0,0 +1,99 @@ + + + + + + + Ruby on Rails.tmbundle + + + + + + +
+ +

Ruby on Rails.tmbundle

+
+

Get Version

+ 2.0.0 + Logo Bundle +
+

What

+ + +

Using Ruby on Rails? Using TextMate? Here is Rails TextMate bundle, now ready for Rails 2.0.

+ + +

Download

+ + +

Logo Bundle

+ + +

Ruby on Rails.tmbundle

+ + +

Forum

+ + +

http://groups.google.com/group/rubyonrails-textmate

+ + +

How to submit patches

+ + +

Read the 8 steps for fixing other people’s code and for section 8b: Submit patch to Google Groups, use the Google Group above.

+ + +

The source project is a Git repository. See Dr Nic’s master branch for clone/checkout details.

+ + +

License

+ + +

This code is free to use under the terms of the MIT license.

+ + +

Contact

+ + +

Comments are welcome. Send an email to Dr Nic Williams via the forum

+

+ Dr Nic Williams, 27th February 2008
+ Theme extended from Paul Battley +

+ + +
+ + + + + diff --git a/bundles/ruby-on-rails.tmbundle/website/index.txt b/bundles/ruby-on-rails.tmbundle/website/index.txt new file mode 100644 index 000000000..3398c24e8 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/index.txt @@ -0,0 +1,31 @@ +h1. Ruby on Rails.tmbundle + +h2. What + +Using "Ruby on Rails":http://rubyonrails.org? Using "TextMate":http://macromates.com? Here is Rails TextMate bundle, now ready for Rails 2.0. + +h2. Download + +Logo Bundle + +Ruby on Rails.tmbundle + + +h2. Forum + +"http://groups.google.com/group/rubyonrails-textmate":http://groups.google.com/group/rubyonrails-textmate + +h2. How to submit patches + +Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/ and for section "8b: Submit patch to Google Groups":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups, use the Google Group above. + +The source project is a "Git":http://git.or.cz/ repository. See Dr Nic's "master branch":http://github.com/drnic/ruby-on-rails-tmbundle/tree/master for clone/checkout details. + +h2. License + +This code is free to use under the terms of the MIT license. + +h2. Contact + +Comments are welcome. Send an email to "Dr Nic Williams":mailto:drnicwilliams@gmail.com via the "forum":http://groups.google.com/group/rubyonrails-textmate + diff --git a/bundles/ruby-on-rails.tmbundle/website/javascripts/rounded_corners_lite.inc.js b/bundles/ruby-on-rails.tmbundle/website/javascripts/rounded_corners_lite.inc.js new file mode 100644 index 000000000..afc3ea327 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/javascripts/rounded_corners_lite.inc.js @@ -0,0 +1,285 @@ + + /**************************************************************** + * * + * curvyCorners * + * ------------ * + * * + * This script generates rounded corners for your divs. * + * * + * Version 1.2.9 * + * Copyright (c) 2006 Cameron Cooke * + * By: Cameron Cooke and Tim Hutchison. * + * * + * * + * Website: http://www.curvycorners.net * + * Email: info@totalinfinity.com * + * Forum: http://www.curvycorners.net/forum/ * + * * + * * + * This library is free software; you can redistribute * + * it and/or modify it under the terms of the GNU * + * Lesser General Public License as published by the * + * Free Software Foundation; either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will * + * be useful, but WITHOUT ANY WARRANTY; without even the * + * implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU Lesser General Public * + * License for more details. * + * * + * You should have received a copy of the GNU Lesser * + * General Public License along with this library; * + * Inc., 59 Temple Place, Suite 330, Boston, * + * MA 02111-1307 USA * + * * + ****************************************************************/ + +var isIE = navigator.userAgent.toLowerCase().indexOf("msie") > -1; var isMoz = document.implementation && document.implementation.createDocument; var isSafari = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false; function curvyCorners() +{ if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object."); if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name."); if(typeof(arguments[1]) == "string") +{ var startIndex = 0; var boxCol = getElementsByClass(arguments[1]);} +else +{ var startIndex = 1; var boxCol = arguments;} +var curvyCornersCol = new Array(); if(arguments[0].validTags) +var validElements = arguments[0].validTags; else +var validElements = ["div"]; for(var i = startIndex, j = boxCol.length; i < j; i++) +{ var currentTag = boxCol[i].tagName.toLowerCase(); if(inArray(validElements, currentTag) !== false) +{ curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);} +} +this.objects = curvyCornersCol; this.applyCornersToAll = function() +{ for(var x = 0, k = this.objects.length; x < k; x++) +{ this.objects[x].applyCorners();} +} +} +function curvyObject() +{ this.box = arguments[1]; this.settings = arguments[0]; this.topContainer = null; this.bottomContainer = null; this.masterCorners = new Array(); this.contentDIV = null; var boxHeight = get_style(this.box, "height", "height"); var boxWidth = get_style(this.box, "width", "width"); var borderWidth = get_style(this.box, "borderTopWidth", "border-top-width"); var borderColour = get_style(this.box, "borderTopColor", "border-top-color"); var boxColour = get_style(this.box, "backgroundColor", "background-color"); var backgroundImage = get_style(this.box, "backgroundImage", "background-image"); var boxPosition = get_style(this.box, "position", "position"); var boxPadding = get_style(this.box, "paddingTop", "padding-top"); this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); this.boxColour = format_colour(boxColour); this.boxPadding = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0)); this.borderColour = format_colour(borderColour); this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); this.boxContent = this.box.innerHTML; if(boxPosition != "absolute") this.box.style.position = "relative"; this.box.style.padding = "0px"; if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%"; if(this.settings.autoPad == true && this.boxPadding > 0) +this.box.innerHTML = ""; this.applyCorners = function() +{ for(var t = 0; t < 2; t++) +{ switch(t) +{ case 0: +if(this.settings.tl || this.settings.tr) +{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); newMainContainer.style.height = topMaxRadius + "px"; newMainContainer.style.top = 0 - topMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.topContainer = this.box.appendChild(newMainContainer);} +break; case 1: +if(this.settings.bl || this.settings.br) +{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); newMainContainer.style.height = botMaxRadius + "px"; newMainContainer.style.bottom = 0 - botMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.bottomContainer = this.box.appendChild(newMainContainer);} +break;} +} +if(this.topContainer) this.box.style.borderTopWidth = "0px"; if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; var corners = ["tr", "tl", "br", "bl"]; for(var i in corners) +{ if(i > -1 < 4) +{ var cc = corners[i]; if(!this.settings[cc]) +{ if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null)) +{ var newCorner = document.createElement("DIV"); newCorner.style.position = "relative"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; if(this.backgroundImage == "") +newCorner.style.backgroundColor = this.boxColour; else +newCorner.style.backgroundImage = this.backgroundImage; switch(cc) +{ case "tl": +newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.left = -this.borderWidth + "px"; break; case "tr": +newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; newCorner.style.left = this.borderWidth + "px"; break; case "bl": +newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = -this.borderWidth + "px"; newCorner.style.backgroundPosition = "-" + (this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break; case "br": +newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = this.borderWidth + "px" +newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break;} +} +} +else +{ if(this.masterCorners[this.settings[cc].radius]) +{ var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);} +else +{ var newCorner = document.createElement("DIV"); newCorner.style.height = this.settings[cc].radius + "px"; newCorner.style.width = this.settings[cc].radius + "px"; newCorner.style.position = "absolute"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++) +{ if((intx +1) >= borderRadius) +var y1 = -1; else +var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); if(borderRadius != j) +{ if((intx) >= borderRadius) +var y2 = -1; else +var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); if((intx+1) >= j) +var y3 = -1; else +var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);} +if((intx) >= j) +var y4 = -1; else +var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); if(borderRadius != j) +{ for(var inty = (y1 + 1); inty < y2; inty++) +{ if(this.settings.antiAlias) +{ if(this.backgroundImage != "") +{ var borderFract = (pixelFraction(intx, inty, borderRadius) * 100); if(borderFract < 30) +{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);} +else +{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);} +} +else +{ var pixelcolour = BlendColour(this.boxColour, this.borderColour, pixelFraction(intx, inty, borderRadius)); this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);} +} +} +if(this.settings.antiAlias) +{ if(y3 >= y2) +{ if (y2 == -1) y2 = 0; this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, 0);} +} +else +{ if(y3 >= y1) +{ this.drawPixel(intx, (y1 + 1), this.borderColour, 100, (y3 - y1), newCorner, 0, 0);} +} +var outsideColour = this.borderColour;} +else +{ var outsideColour = this.boxColour; var y3 = y1;} +if(this.settings.antiAlias) +{ for(var inty = (y3 + 1); inty < y4; inty++) +{ this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);} +} +} +this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);} +if(cc != "br") +{ for(var t = 0, k = newCorner.childNodes.length; t < k; t++) +{ var pixelBar = newCorner.childNodes[t]; var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); if(cc == "tl" || cc == "bl"){ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px";} +if(cc == "tr" || cc == "tl"){ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px";} +switch(cc) +{ case "tr": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "tl": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "bl": +pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px"; break;} +} +} +} +if(newCorner) +{ switch(cc) +{ case "tl": +if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "tr": +if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "bl": +if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break; case "br": +if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break;} +} +} +} +var radiusDiff = new Array(); radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius) +radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius); for(z in radiusDiff) +{ if(z == "t" || z == "b") +{ if(radiusDiff[z]) +{ var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); var newFiller = document.createElement("DIV"); newFiller.style.height = radiusDiff[z] + "px"; newFiller.style.width = this.settings[smallerCornerType].radius+ "px" +newFiller.style.position = "absolute"; newFiller.style.fontSize = "1px"; newFiller.style.overflow = "hidden"; newFiller.style.backgroundColor = this.boxColour; switch(smallerCornerType) +{ case "tl": +newFiller.style.bottom = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.topContainer.appendChild(newFiller); break; case "tr": +newFiller.style.bottom = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.topContainer.appendChild(newFiller); break; case "bl": +newFiller.style.top = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.bottomContainer.appendChild(newFiller); break; case "br": +newFiller.style.top = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.bottomContainer.appendChild(newFiller); break;} +} +var newFillerBar = document.createElement("DIV"); newFillerBar.style.position = "relative"; newFillerBar.style.fontSize = "1px"; newFillerBar.style.overflow = "hidden"; newFillerBar.style.backgroundColor = this.boxColour; newFillerBar.style.backgroundImage = this.backgroundImage; switch(z) +{ case "t": +if(this.topContainer) +{ if(this.settings.tl.radius && this.settings.tr.radius) +{ newFillerBar.style.height = topMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.tl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px"; newFillerBar.style.borderTop = this.borderString; if(this.backgroundImage != "") +newFillerBar.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; this.topContainer.appendChild(newFillerBar);} +this.box.style.backgroundPosition = "0px -" + (topMaxRadius - this.borderWidth) + "px";} +break; case "b": +if(this.bottomContainer) +{ if(this.settings.bl.radius && this.settings.br.radius) +{ newFillerBar.style.height = botMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.bl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.br.radius - this.borderWidth + "px"; newFillerBar.style.borderBottom = this.borderString; if(this.backgroundImage != "") +newFillerBar.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius + this.borderWidth)) + "px"; this.bottomContainer.appendChild(newFillerBar);} +} +break;} +} +} +if(this.settings.autoPad == true && this.boxPadding > 0) +{ var contentContainer = document.createElement("DIV"); contentContainer.style.position = "relative"; contentContainer.innerHTML = this.boxContent; contentContainer.className = "autoPadDiv"; var topPadding = Math.abs(topMaxRadius - this.boxPadding); var botPadding = Math.abs(botMaxRadius - this.boxPadding); if(topMaxRadius < this.boxPadding) +contentContainer.style.paddingTop = topPadding + "px"; if(botMaxRadius < this.boxPadding) +contentContainer.style.paddingBottom = botMaxRadius + "px"; contentContainer.style.paddingLeft = this.boxPadding + "px"; contentContainer.style.paddingRight = this.boxPadding + "px"; this.contentDIV = this.box.appendChild(contentContainer);} +} +this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius) +{ var pixel = document.createElement("DIV"); pixel.style.height = height + "px"; pixel.style.width = "1px"; pixel.style.position = "absolute"; pixel.style.fontSize = "1px"; pixel.style.overflow = "hidden"; var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius); if(image == -1 && this.backgroundImage != "") +{ pixel.style.backgroundImage = this.backgroundImage; pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";} +else +{ pixel.style.backgroundColor = colour;} +if (transAmount != 100) +setOpacity(pixel, transAmount); pixel.style.top = inty + "px"; pixel.style.left = intx + "px"; newCorner.appendChild(pixel);} +} +function insertAfter(parent, node, referenceNode) +{ parent.insertBefore(node, referenceNode.nextSibling);} +function BlendColour(Col1, Col2, Col1Fraction) +{ var red1 = parseInt(Col1.substr(1,2),16); var green1 = parseInt(Col1.substr(3,2),16); var blue1 = parseInt(Col1.substr(5,2),16); var red2 = parseInt(Col2.substr(1,2),16); var green2 = parseInt(Col2.substr(3,2),16); var blue2 = parseInt(Col2.substr(5,2),16); if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1; var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction))); if(endRed > 255) endRed = 255; if(endRed < 0) endRed = 0; var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction))); if(endGreen > 255) endGreen = 255; if(endGreen < 0) endGreen = 0; var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction))); if(endBlue > 255) endBlue = 255; if(endBlue < 0) endBlue = 0; return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);} +function IntToHex(strNum) +{ base = strNum / 16; rem = strNum % 16; base = base - (rem / 16); baseS = MakeHex(base); remS = MakeHex(rem); return baseS + '' + remS;} +function MakeHex(x) +{ if((x >= 0) && (x <= 9)) +{ return x;} +else +{ switch(x) +{ case 10: return "A"; case 11: return "B"; case 12: return "C"; case 13: return "D"; case 14: return "E"; case 15: return "F";} +} +} +function pixelFraction(x, y, r) +{ var pixelfraction = 0; var xvalues = new Array(1); var yvalues = new Array(1); var point = 0; var whatsides = ""; var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); if ((intersect >= y) && (intersect < (y+1))) +{ whatsides = "Left"; xvalues[point] = 0; yvalues[point] = intersect - y; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); if ((intersect >= x) && (intersect < (x+1))) +{ whatsides = whatsides + "Top"; xvalues[point] = intersect - x; yvalues[point] = 1; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); if ((intersect >= y) && (intersect < (y+1))) +{ whatsides = whatsides + "Right"; xvalues[point] = 1; yvalues[point] = intersect - y; point = point + 1;} +var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); if ((intersect >= x) && (intersect < (x+1))) +{ whatsides = whatsides + "Bottom"; xvalues[point] = intersect - x; yvalues[point] = 0;} +switch (whatsides) +{ case "LeftRight": +pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); break; case "TopRight": +pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); break; case "TopBottom": +pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); break; case "LeftBottom": +pixelfraction = (yvalues[0]*xvalues[1])/2; break; default: +pixelfraction = 1;} +return pixelfraction;} +function rgb2Hex(rgbColour) +{ try{ var rgbArray = rgb2Array(rgbColour); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);} +catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");} +return hexColour;} +function rgb2Array(rgbColour) +{ var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); var rgbArray = rgbValues.split(", "); return rgbArray;} +function setOpacity(obj, opacity) +{ opacity = (opacity == 100)?99.999:opacity; if(isSafari && obj.tagName != "IFRAME") +{ var rgbArray = rgb2Array(obj.style.backgroundColor); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";} +else if(typeof(obj.style.opacity) != "undefined") +{ obj.style.opacity = opacity/100;} +else if(typeof(obj.style.MozOpacity) != "undefined") +{ obj.style.MozOpacity = opacity/100;} +else if(typeof(obj.style.filter) != "undefined") +{ obj.style.filter = "alpha(opacity:" + opacity + ")";} +else if(typeof(obj.style.KHTMLOpacity) != "undefined") +{ obj.style.KHTMLOpacity = opacity/100;} +} +function inArray(array, value) +{ for(var i = 0; i < array.length; i++){ if (array[i] === value) return i;} +return false;} +function inArrayKey(array, value) +{ for(key in array){ if(key === value) return true;} +return false;} +function addEvent(elm, evType, fn, useCapture) { if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture); return true;} +else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn); return r;} +else { elm['on' + evType] = fn;} +} +function removeEvent(obj, evType, fn, useCapture){ if (obj.removeEventListener){ obj.removeEventListener(evType, fn, useCapture); return true;} else if (obj.detachEvent){ var r = obj.detachEvent("on"+evType, fn); return r;} else { alert("Handler could not be removed");} +} +function format_colour(colour) +{ var returnColour = "#ffffff"; if(colour != "" && colour != "transparent") +{ if(colour.substr(0, 3) == "rgb") +{ returnColour = rgb2Hex(colour);} +else if(colour.length == 4) +{ returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);} +else +{ returnColour = colour;} +} +return returnColour;} +function get_style(obj, property, propertyNS) +{ try +{ if(obj.currentStyle) +{ var returnVal = eval("obj.currentStyle." + property);} +else +{ if(isSafari && obj.style.display == "none") +{ obj.style.display = ""; var wasHidden = true;} +var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS); if(isSafari && wasHidden) +{ obj.style.display = "none";} +} +} +catch(e) +{ } +return returnVal;} +function getElementsByClass(searchClass, node, tag) +{ var classElements = new Array(); if(node == null) +node = document; if(tag == null) +tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)"); for (i = 0, j = 0; i < elsLen; i++) +{ if(pattern.test(els[i].className)) +{ classElements[j] = els[i]; j++;} +} +return classElements;} +function newCurvyError(errorMessage) +{ return new Error("curvyCorners Error:\n" + errorMessage) +} diff --git a/bundles/ruby-on-rails.tmbundle/website/peepcode.html b/bundles/ruby-on-rails.tmbundle/website/peepcode.html new file mode 100644 index 000000000..96a35ec65 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/peepcode.html @@ -0,0 +1,520 @@ + + + + + + + Peepcode Script + + + + + + +
+ +

Peepcode Script

+
+

Get Version

+ 2.0.0 + Logo Bundle +
+

Introduction

+ + +

Many many people’s first introduction to Ruby on Rails was the 15 minute video from DHH showing how you create a blog application with Rails. Nearly everyone finished that video asking one question: what was that editor? It is awesome.

+ + +

Let’s take a stroll down memory lane. We pick up the action at the X minute mark:

+ + +

[transition into a portion of the video that shows off TMs features, plus DHH saying “Whooops!”]

+ + +

What is interesting, is that as fast as David is able to write his blog application, even with his detour demonstrating the now deleted scaffold command, he never uses any special TextMate extensions for Rails. He uses some HTML snippets and commands and some Ruby snippets and commands. The original Ruby on Rails extensions were in their infancy.

+ + +

[Open Bundle Editor] A collection of TextMate extensions is called a “bundle”.

+ + +

When working with Rails with TextMate you’ll use several bundles> + + +

    +
  • HTML
  • +
  • Ruby
  • +
  • Ruby on Rails
  • +
  • JavaScript
  • +
+ + +

You might also use the ProtoType, jQuery or other JavaScript bundle if you use those libraries.

+ + +Ruby on Rails 2.0 was recently released and includes many improvements to +
    +
  • the syntax that you type [redirect_to :action => 'show', :id => @person became redirect_to @person]
  • +
  • changes to some file extensions [rhtml -> html.erb] and
  • +
  • changes to some file name conventions [user_controller.rb -> users_controller.rb, more commonly].
  • +
+ + +

The Ruby on Rails bundle has also been given a major upgrade to match all the changes for developing Rails 2.0 applications.

+ + +

The snippets and commands we’re going to look at are actually described across all three bundles. Often it is the Rails bundle that reuses the HTML and Ruby bundles. For example, in an HTML file you have a variety of ways to create new elements. [examples] Within an html.erb file you can reuse all the same snippets and commands.

+ + +

Know thy Editor

+ + +

Overview key icons (see PeepCode TM video)

+ + +

Preferences > Advanced > Folder References > Folder Patterns: add ‘vendor/rails’ and it will ignore that folder in any project – both from the Dock and from Search.

+ + +

Access your bundles via Ctrl+Alt+Cmd+B.

+ + +

Quick menu: Ctrl+Esc

+ + +

Change language for a file, e.g. Ruby <—> Ruby on Rails. Either click the “L” section of the status bar and select the Language; or learn the Language shortcut. These always start with Shift+Alt+Cmd with a letter. Typically the letter is the first letter of the Language. So to choose either Ruby or Ruby on Rails, use Shift+Alt+Cmd+R and choose the language from the drop-down.

+ + +

Models and Migrations

+ + +
    +
  • Create new app
  • +
  • Go to app/controllers/application.rb (need a Ruby on Rails file)
  • +
  • | + 1 – Call Generate Script – create new model “Post” TODO – not working @ moment + OR CLI:script/generate model Post title:string
  • +
  • Go to migration file: Cmd+T + 001
  • +
  • t. – list of available sexy migration columns; when selecting one, another t. is automatically created for you. (create body:text + published:boolean)
  • +
  • Shft++K – remove a line
  • +
  • CLI:script/generate model Comment
  • +
  • Cmd+T + 002 – comment migration + add t.references :post, body:text, name:string
  • +
  • Ctrl+| + 3 – run migrations + * See error? + * In terminal, go to ”/Applications/TextMate.app/Contents/SharedSupport/Support/lib” and rename/remove Builder.rb + * Run migrations again
  • +
+ + +
    +
  • Go to post.rb + * hm + comments; notice you can tab across to change fields if required + * Also, hmt -> for has_many :through*
  • +
  • Go to comments.rb + * bt + post; notice the autocompletions are correct here, but still optional for rails so can be deleted + * vp + name, body + * [Ctrl-Esc -> Models] All the validations are available as snippets based on the first letter of each word.
  • +
  • Place the cursor on ‘Comment’ class name, Sft+Ctrl+Cmd+S -> shows schema for this class. Alternately, place it on ‘post’ to get the schema for Post class.
  • +
+ + +

Testing

+ + +

I’ll talk more about navigating between files in a Rails application later, but for the moment, know that there is a special relationship between a model file and a unit test file. You can toggle between them with Alt+Cmd+DownArrow.

+ + +

The Rails generator for models created this unit test file and a fixtures file.

+ + +

First, let’s set up some fixtures for posts and comments. Go to posts.yml and create a “published” and a “unpublished” fixture. Now go to comments.yml – using Ctrl+Cmd+R to show posts.yml in the dock.

+ + +

TODO – how to move focus from editor to dock?

+ + +

Within comments, create one comment:

+ + +
valid:
+  name: Dr Nic
+  body: Cool post
+  post:
+ +

With Rails 2.0, your fixture ids are no longer required, and you can use Foxy Fixtures to select an associated fixture. Here we’d type ‘published’ to reference that posts.yml fixture. But, since the column name is ‘post’ we can get a drop-down list using Alt+Esc. We’ll use this same fixture autocompletion feature again within the test files.

+ + +

Back to the comments unit test, and that dummy test needs removing.

+ + +

Create a new test method with deft + should_require_title.

+ + +

Type posts(:) and Alt+Esc to access the fixtures autocompletion. You can now select a posts.yml fixture. Note, the current implementation replaces the whole line, so if you need multiple or want a different instance variable you’ll need to make some manual changes.

+ + +

Finished:

+ + +

def test_should_require_title
+  @post = posts(:published)
+  @comment = @post.comments.create
+  @comment.valid?
+  assert(@comment.errors[:name], "Should be errors for name field")
+end

+ + +

To run your unit tests, Ctrl-\ + ‘Test Units’ or ‘Test Recent’.

+ + +

In order to write some functional tests for our blog, let’s generate a controller as it generates the functional test at the same time. [Ctrl-|] We won’t specify any actions for the moment, and we’ll clean away the open windows for the controller and the helper.

+ + +

For now, let’s write some functional tests. [remove the test_truth method]

+ + +

Diagram

+ + +
    +
  • deftg -> test GET action
  • +
  • deftp -> test POST action
  • +
+ + +

Since many functional tests start with the same setup, there are two functional test snippets to write tests faster. The last letter maps the the http method to be used for the test: g for GET and p for POST.

+ + +

To create a test for the index action, use deftg + 'index'. Then delete the two optional @model sections.

+ + +

Next, to load an instance variable into the test, type asg + 'posts'

+ + +

Now, let’s assert the HTML format.

+ + +

Type ass and press ⇥. Type div#posts, press ⇥ and ⌦, then ⇥ twice to place the cursor within the assert_select block:

+ + +

assert_select 'div#posts' do
+
+end

+ + +

Now we’ll check that the @posts objects are represented in the div#posts element.

+ + +

With the cursor inside the assert_select:

+ + +

Type ass, press ⇥, type div.post, press ⇥, press ⇥ again, and type count (to replace the text). Now press ⇥ again, and type @posts.size. Press ⇥ a final time (it will highlight the do...end block), and press ⌦.

+ + +

Our test method is now finished:

+ + +

def test_should_get_index
+  get :index
+  assert_response :success
+  assert(posts = assigns(:posts), "Cannot find @posts")
+  assert_select 'div#posts' do
+    assert_select 'div.post', :count => posts.size
+  end
+end

+ + +

Now we create a test for the ‘show’ action. Type deftg and ‘show’, and then ‘post’. Then tab into ‘fixture_name’ and delete it. Now we’ll autocomplete on fixtures again. Alt+Esc and select ‘published’.

+ + +

Now copy the ‘div.post’ assert_select line for this test, but change the :count to 1.

+ + +

To run our functional tests, use Ctrl+\.

+ + +

Controllers and Routing

+ + +

Similarly to navigating between model and unit test files, you can toggle between functional tests and controllers via Alt+Cmd+DownArrow.

+ + +

In the controller, type index and use Shift+Enter to convert it to a method. Press BackSpace to delete the arguments.

+ + +

To load all Posts:

+ + +

@posts = Post.fina

+ + +

Pressing ⇥ creates find(:all, :conditions => [...]). Tab and change ‘field’ to ‘published’.

+ + +

Diagram: fina – find(:all); finf – find(:first); fini – find(id); ^-| – params[:id]

+ + +

Now create a ‘show’ action, with @post = Post.fini + tab. Use ^-| to insert params[:id].

+ + +

We’ll come back and look at views for our index action later. Instead, let’s create a controller for creating + updating posts for the admin of the site.

+ + +

We could use the scaffold generator to create a posts controller, with functional tests and default views. Instead, we’ll build this controller from the ground up.

+ + +

Diagram: Navigation between file types

+ + +

Shift+Alt+Cmd+DownArrow:

+ + +

TODO – finish this

+ + +
    +
  • post.rb -> post_test.rb, posts_controller.rb, etc
  • +
  • post_test.rb -> post.rb, posts_controller.rb, etc
  • +
+ + +

Diagram: views/users/show.rhtml -> user_controller.rb; views/users/show.html.erb -> users_controller.rb

+ + +

One thing to note for developers of Rails apps using older versions of Rails. If you are in a template file with the extension .rhtml, then it will navigate to the singular controller name ‘post’, rather than the plural default for controllers. This is for backwards compatibility with older naming conventions.

+ + +

So we can create a new posts_controller.rb by returning to the post.rb or post_test.rb files, and navigating to the matching controller. It doesn’t exist so a blank file is created.

+ + +

We have a simple way to create new controller classes; the cla snippet has a ‘Create controller class’ option. Select that, and type ‘Posts’ and then ‘post’ to create our posts_controller.rb class.

+ + +

For simpler actions, use the standard techniques for new methods, def + ‘new’ to create the ‘new’ method.

+ + +

For some of the common, more complex controller actions there are also snippets. For the create action, type ‘defcreate’ and fill out the template with ‘post’.

+ + +

What I want the ‘create’ action to do is redirect back to the main blog controller for a page. Originally, I could use ‘recai’ for redirect_to :controller => "blog", :action => "show", :id => @post but its uncool to use anonymous paths.

+ + +

So instead let’s just enter a named route that sounds nice, replace @post with blog_post_path(@post).

+ + +

[Change to the ‘routes.rb’ file.] So finally let’s setup our routes. I’m going to delete all the default comments and default routes, and reconstruct it with named routes.

+ + +

Diagram: Routes: map – named routes; maprs – map.resources; mapr – map.resource; mapwo – map.with_options

+ + +

Within a routes file, you have the three ‘map’ snippets. As you’ll see in moment, the resources snippets include a block for nested resources.

+ + +

So we’ll use ‘map’ to create named routes. Leave the name as ‘connect’ for anonymous routes. It is handy to know a Ruby snippet here: tabbing on colon (:) creates a hash key/value pair.

+ + +
map.public_page 'blog/show/:id', :controller => "blog", :action => "show"
+ +

We could place this route within a with_options block, using ‘mapwo’. Then add map.root within it.

+ + +

For our posts controller, we’ll use the resources snippets. We use ‘maprs’ and change ‘resource’ to ‘posts’ and then we can tab into the block. Say we wanted comments to be a nested resource of posts, then we’d use ‘maprs’ again, and change ‘map’ to ‘post’ and remove the block. [then delete the comment resource + post’s block].

+ + +

There is also a shortcut for the respond_to block – ‘rest’, which gives a default html response. To add additional response formats, tab on ‘wants’. [remove these examples from new method]

+ + +

Now let’s have a final look at functional tests for our create actions, which will require a POST request. [try to change to functional test] We don’t have a posts_controller_test.rb, so it creates a blank file for us.

+ + +

To create a functional test class, use ‘cla’ again and select ‘Create functional test class’, and type ‘Posts’ for the class name prefix. Save the file, close the window and reopen within the project, otherwise some commands might not work if the file is initially not saved.

+ + +

Remember that test methods are created with ‘deft’, and the GET + POST methods are created with deftg and deftp. Let’s test our create action with ‘deftp’.

+ + +

Enter ‘post’, but remove the two optional sections as we’re creating a new object, not updating an existing object. Within the :post => { }, type : and tab to create the hash key values.

+ + +

Fetch the resulting object with asg + ‘post’.

+ + +

Add a redirection assertion with artp.

+ + +

def test_should_post_create
+  post :create, :post => { :title => "hi there", :body => "oh yeah" }
+  assert_response :redirect
+  assert(post = assigns(:post), "Cannot find @post")
+
+  assert_redirected_to blog_post_path(post)
+end

+ + +

If you just want to run the tests in the current file, or in fact run any Ruby file, use Cmd+R. As we can see, this test passes.

+ + +

Views

+ + +

[back to posts_controller.rb]

+ + +

In the posts controller, we have a new action but we haven’t created a template yet. As before, we can use the Navigation cmd to change from the controller to a view. In this case, the view is based on the current method. So we place the cursor in the new method, and it will ask us to create a new.html.erb file. Here is your opportunity to rename the template with alternate format or template extensions, say .xml.builder. But we’ll use .html.erb for now.

+ + +

Diagram: form_for – ff, form_for with error output – ffe

+ + +

We’ll use ‘ffe’ and enter ‘post’. In Rails 2.0, the form_for helper is very powerful and knows from the object it is passed whether it is creating or updating an object.

+ + +

Diagram: f. – list of available helpers; or use first initial of helpers’ names (fftf – text_field)

+ + +

Inside the form_for block we have two ways to access the common helpers.

+ + +

By using ‘f.’ it reminds us what helpers are available and tells us what their tab completions are.

+ + +

So, let’s use the tab completion versions. ffl for label, fftf for text field.

+ + +
<%= error_messages_for :post %>
+
+<% form_for @post do |f| -%>
+  <%= f.label :title, "Title" %>
+  <%= f.text_field :title %>
+  <%= f.label :body, "Body" %>
+  <%= f.text_area :body, :rows => 10 %>
+  <%= f.submit "Post", :disable_with => 'Posting...' %>
+<% end -%>
+ +

Now let’s put each label or form field in a separate paragraph. Select those lines and use ‘Wrap Each Selected Line in Open/Close’ – Shift+Ctrl+Cmd+W, and type ‘p’.

+ + +

Diagram: Partials – Shift-Ctrl-H

+ + +

Since the form_for helper works with saved and unsaved model objects its easily reusable within new and edit templates. So let’s extract it as a partial. [do it] And now the render can be pasted into an edit.html.erb file as required.

+ + +

[go to browser http://localhost:3000/posts/new] +Now we can fire up the server and have a look at our form and use it. [submit form]

+ + +

As requested, the browser is redirected to the public post page; which we haven’t implemented yet. So let’s do that.

+ + +

We head back to the blog_controller, and create the show.html.erb via navigating away from the show action.

+ + +

Create a div and set its class to post, then layout the title and body:

+ + +
+

<%= @post.title %>

+ <%= @post.body %> +
+ +

If we run our tests, the ‘show’ action test is now passing.

+ + +

[Shft+Ctrl+H] So we can reuse it in the index.html.erb file, let’s move it to a partial called ‘post’ and remove the ’@’ signs to reference a local variable instead of an instance variable.

+ + +

The returned ‘render’ expression isn’t quite right as we need to pass the @post object into the partial. Let’s remove it and use the ‘render partial’ snippets.

+ + +

Diagram: render :partial – rp; render :partial, :object – rpo; render :partial, :collection – rpc; render :partial, :locals – rpl

+ + +

Create <%= %> and inside use ‘rpo’ and set the :object => @post.

+ + +

Now we create index.html.erb and use <%= %> with ‘rpc’ and ‘post’. The tests require that its wrapped in a div with id=”posts”. So select all, and Shift+Ctrl+W + div id=”posts”.

+ + +

Diagram: Link helpers; lip – link_to “label”, model_path(@model), etc.

+ + +
    +
  • lip – <%= link_to "link text...", model_path(@model) %>
  • +
  • lipp – <%= link_to "link text...", models_path %>
  • +
  • linp – <%= link_to "link text...", parent_child_path(@parent, @child) %>
  • +
  • linpp – <%= link_to "link text...", parent_child_path(@parent) %>
  • +
  • lim – <%= link_to model.name, model %>
  • +
+ + +

Alternate templates

+ + +

Diagram: html.erb, js.erb, js.rjs, xml.builder, xml.erb, html.builder

+ + +

In addition to html.erb templates, Rails supports any combination of output format and template engine. As appropriate you can combine html or javascript or xml with erb or rjs or builder, or any other 3rd party templating system such as haml.

+ + +

The Rails TextMate bundle provides varying support for different combinations.

+ + +

Diagram: wants.js -> .js.rjs; wants.xml -> .xml.builder; wants.different -> .different.erb

+ + +

Navigation (e.g. tasty tidbit)

+ + +

Go to posts_controller, add wants.js to create action. Navigate and create posts/create.js.rjs.

+ + +

Diagram: RJS snippets – hide, insert_html (ins), replace (rep), replace_html (reph), show, toggle (tog), visual_effect (vis)

+ + +

All these snippets include the page prefix and are nice ways to start each RJS expression.

+ + +

[remove all lines; rename with .erb] Another way to generate JavaScript within Rails is with the .js.erb extension. Instead of RJS expressions, you write pure JavaScript, but you can embed ruby using erb.

+ + +

A new syntax supported in the bundle is JavaScript (Rails), which maps to .js.erb files. That is, JavaScript files that permit embedded Ruby. This means, within the <%= %> tags you have access to your Ruby snippets and syntax highlighting.

+ + +

Extending

+

+ Dr Nic Williams, 27th February 2008
+ Theme extended from Paul Battley +

+ + +
+ + + + + diff --git a/bundles/ruby-on-rails.tmbundle/website/peepcode.txt b/bundles/ruby-on-rails.tmbundle/website/peepcode.txt new file mode 100644 index 000000000..7f04499b2 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/peepcode.txt @@ -0,0 +1,327 @@ +h1. Peepcode Script + +h2. Introduction + +Many many people's first introduction to Ruby on Rails was the 15 minute video from DHH showing how you create a blog application with Rails. Nearly everyone finished that video asking one question: what was that editor? It is awesome. + +Let's take a stroll down memory lane. We pick up the action at the X minute mark: + +[transition into a portion of the video that shows off TMs features, plus DHH saying "Whooops!"] + +What is interesting, is that as fast as David is able to write his blog application, even with his detour demonstrating the now deleted scaffold command, he never uses any special TextMate extensions for Rails. He uses some HTML snippets and commands and some Ruby snippets and commands. The original Ruby on Rails extensions were in their infancy. + +[Open Bundle Editor] A collection of TextMate extensions is called a "bundle". + +When working with Rails with TextMate you'll use several "bundles": + +* HTML +* Ruby +* Ruby on Rails +* JavaScript + +You might also use the ProtoType, jQuery or other JavaScript bundle if you use those libraries. + +Ruby on Rails 2.0 was recently released and includes many improvements to +* the syntax that you type [redirect_to :action => 'show', :id => @person became redirect_to @person] +* changes to some file extensions [rhtml -> html.erb] and +* changes to some file name conventions [user_controller.rb -> users_controller.rb, more commonly]. + +The Ruby on Rails bundle has also been given a major upgrade to match all the changes for developing Rails 2.0 applications. + +The snippets and commands we're going to look at are actually described across all three bundles. Often it is the Rails bundle that reuses the HTML and Ruby bundles. For example, in an HTML file you have a variety of ways to create new elements. [examples] Within an html.erb file you can reuse all the same snippets and commands. + + +h2. Know thy Editor + +Overview key icons (see PeepCode TM video) + +Preferences > Advanced > Folder References > Folder Patterns: add 'vendor/rails' and it will ignore that folder in any project - both from the Dock and from Search. + +Access your bundles via Ctrl+Alt+Cmd+B. + +Quick menu: Ctrl+Esc + +Change language for a file, e.g. Ruby <--> Ruby on Rails. Either click the "L" section of the status bar and select the Language; or learn the Language shortcut. These always start with Shift+Alt+Cmd with a letter. Typically the letter is the first letter of the Language. So to choose either Ruby or Ruby on Rails, use Shift+Alt+Cmd+R and choose the language from the drop-down. + + +h2. Models and Migrations + +* Create new app +* Go to app/controllers/application.rb (need a Ruby on Rails file) +* ^| + 1 - Call Generate Script - create new model "Post" TODO - not working @ moment + OR CLI:script/generate model Post title:string +* Go to migration file: Cmd+T + 001 +* t. - list of available sexy migration columns; when selecting one, another t. is automatically created for you. (create body:text + published:boolean) +* Shft+^+K - remove a line +* CLI:script/generate model Comment +* Cmd+T + 002 - comment migration + add t.references :post, body:text, name:string +* Ctrl+| + 3 - run migrations + * See error? + * In terminal, go to "/Applications/TextMate.app/Contents/SharedSupport/Support/lib" and rename/remove Builder.rb + * Run migrations again + +* Go to post.rb + * hm + comments; notice you can tab across to change fields if required + * Also, hmt -> for has_many :through* +* Go to comments.rb + * bt + post; notice the autocompletions are correct here, but still optional for rails so can be deleted + * vp + name, body + * [Ctrl-Esc -> Models] All the validations are available as snippets based on the first letter of each word. +* Place the cursor on 'Comment' class name, Sft+Ctrl+Cmd+S -> shows schema for this class. Alternately, place it on 'post' to get the schema for Post class. + +h2. Testing + +I'll talk more about navigating between files in a Rails application later, but for the moment, know that there is a special relationship between a model file and a unit test file. You can toggle between them with Alt+Cmd+DownArrow. + +The Rails generator for models created this unit test file and a fixtures file. + +First, let's set up some fixtures for posts and comments. Go to posts.yml and create a "published" and a "unpublished" fixture. Now go to comments.yml - using Ctrl+Cmd+R to show posts.yml in the dock. + +TODO - how to move focus from editor to dock? + +Within comments, create one comment: + +
valid:
+  name: Dr Nic
+  body: Cool post
+  post:
+ +With Rails 2.0, your fixture ids are no longer required, and you can use Foxy Fixtures to select an associated fixture. Here we'd type 'published' to reference that posts.yml fixture. But, since the column name is 'post' we can get a drop-down list using Alt+Esc. We'll use this same fixture autocompletion feature again within the test files. + +Back to the comments unit test, and that dummy test needs removing. + +Create a new test method with deft + should_require_title. + +Type posts(:) and Alt+Esc to access the fixtures autocompletion. You can now select a posts.yml fixture. Note, the current implementation replaces the whole line, so if you need multiple or want a different instance variable you'll need to make some manual changes. + +Finished: + +
def test_should_require_title
+  @post = posts(:published)
+  @comment = @post.comments.create
+  @comment.valid?
+  assert(@comment.errors[:name], "Should be errors for name field")
+end
+ +To run your unit tests, Ctrl-\ + 'Test Units' or 'Test Recent'. + +In order to write some functional tests for our blog, let's generate a controller as it generates the functional test at the same time. [Ctrl-|] We won't specify any actions for the moment, and we'll clean away the open windows for the controller and the helper. + +For now, let's write some functional tests. [remove the test_truth method] + +h3. Diagram + +* deftg -> test GET action +* deftp -> test POST action + +Since many functional tests start with the same setup, there are two functional test snippets to write tests faster. The last letter maps the the http method to be used for the test: g for GET and p for POST. + +To create a test for the index action, use deftg + 'index'. Then delete the two optional @model sections. + +Next, to load an instance variable into the test, type asg + 'posts' + +Now, let's assert the HTML format. + +Type ass and press TAB. Type div#posts, press TAB and DELETE, then TAB twice to place the cursor within the assert_select block: + +
assert_select 'div#posts' do
+
+end
+ +Now we'll check that the @posts objects are represented in the div#posts element. + +With the cursor inside the assert_select: + +Type ass, press TAB, type div.post, press TAB, press TAB again, and type count (to replace the text). Now press TAB again, and type @posts.size. Press TAB a final time (it will highlight the do...end block), and press DELETE. + +Our test method is now finished: + +
def test_should_get_index
+  get :index
+  assert_response :success
+  assert(posts = assigns(:posts), "Cannot find @posts")
+  assert_select 'div#posts' do
+    assert_select 'div.post', :count => posts.size
+  end
+end
+ +Now we create a test for the 'show' action. Type deftg and 'show', and then 'post'. Then tab into 'fixture_name' and delete it. Now we'll autocomplete on fixtures again. Alt+Esc and select 'published'. + +Now copy the 'div.post' assert_select line for this test, but change the :count to 1. + +To run our functional tests, use Ctrl+\. + +h2. Controllers and Routing + +Similarly to navigating between model and unit test files, you can toggle between functional tests and controllers via Alt+Cmd+DownArrow. + +In the controller, type index and use Shift+Enter to convert it to a method. Press BackSpace to delete the arguments. + +To load all Posts: + +
@posts = Post.fina
+ +Pressing TAB creates find(:all, :conditions => [...]). Tab and change 'field' to 'published'. + +h3. Diagram: fina - find(:all); finf - find(:first); fini - find(id); ^-| - params[:id] + +Now create a 'show' action, with @post = Post.fini + tab. Use ^-| to insert params[:id]. + +We'll come back and look at views for our index action later. Instead, let's create a controller for creating + updating posts for the admin of the site. + +We could use the scaffold generator to create a posts controller, with functional tests and default views. Instead, we'll build this controller from the ground up. + +h3. Diagram: Navigation between file types + +Shift+Alt+Cmd+DownArrow: + +TODO - finish this + +* post.rb -> post_test.rb, posts_controller.rb, etc +* post_test.rb -> post.rb, posts_controller.rb, etc + +h3. Diagram: views/users/show.rhtml -> user_controller.rb; views/users/show.html.erb -> users_controller.rb + +One thing to note for developers of Rails apps using older versions of Rails. If you are in a template file with the extension .rhtml, then it will navigate to the singular controller name 'post', rather than the plural default for controllers. This is for backwards compatibility with older naming conventions. + +So we can create a new posts_controller.rb by returning to the post.rb or post_test.rb files, and navigating to the matching controller. It doesn't exist so a blank file is created. + +We have a simple way to create new controller classes; the cla snippet has a 'Create controller class' option. Select that, and type 'Posts' and then 'post' to create our posts_controller.rb class. + +For simpler actions, use the standard techniques for new methods, def + 'new' to create the 'new' method. + +For some of the common, more complex controller actions there are also snippets. For the create action, type 'defcreate' and fill out the template with 'post'. + +What I want the 'create' action to do is redirect back to the main blog controller for a page. Originally, I could use 'recai' for redirect_to :controller => "blog", :action => "show", :id => @post but its uncool to use anonymous paths. + +So instead let's just enter a named route that sounds nice, replace @post with blog_post_path(@post). + +[Change to the 'routes.rb' file.] So finally let's setup our routes. I'm going to delete all the default comments and default routes, and reconstruct it with named routes. + +h3. Diagram: Routes: map - named routes; maprs - map.resources; mapr - map.resource; mapwo - map.with_options + +Within a routes file, you have the three 'map' snippets. As you'll see in moment, the resources snippets include a block for nested resources. + +So we'll use 'map' to create named routes. Leave the name as 'connect' for anonymous routes. It is handy to know a Ruby snippet here: tabbing on colon (:) creates a hash key/value pair. + +
map.public_page 'blog/show/:id', :controller => "blog", :action => "show"
+ +We could place this route within a with_options block, using 'mapwo'. Then add map.root within it. + +For our posts controller, we'll use the resources snippets. We use 'maprs' and change 'resource' to 'posts' and then we can tab into the block. Say we wanted comments to be a nested resource of posts, then we'd use 'maprs' again, and change 'map' to 'post' and remove the block. [then delete the comment resource + post's block]. + +There is also a shortcut for the respond_to block - 'rest', which gives a default html response. To add additional response formats, tab on 'wants'. [remove these examples from new method] + +Now let's have a final look at functional tests for our create actions, which will require a POST request. [try to change to functional test] We don't have a posts_controller_test.rb, so it creates a blank file for us. + +To create a functional test class, use 'cla' again and select 'Create functional test class', and type 'Posts' for the class name prefix. Save the file, close the window and reopen within the project, otherwise some commands might not work if the file is initially not saved. + +Remember that test methods are created with 'deft', and the GET + POST methods are created with deftg and deftp. Let's test our create action with 'deftp'. + +Enter 'post', but remove the two optional sections as we're creating a new object, not updating an existing object. Within the :post => { }, type : and tab to create the hash key values. + +Fetch the resulting object with asg + 'post'. + +Add a redirection assertion with artp. + +
def test_should_post_create
+  post :create, :post => { :title => "hi there", :body => "oh yeah" }
+  assert_response :redirect
+  assert(post = assigns(:post), "Cannot find @post")
+
+  assert_redirected_to blog_post_path(post)
+end
+ +If you just want to run the tests in the current file, or in fact run any Ruby file, use Cmd+R. As we can see, this test passes. + +h2. Views + +[back to posts_controller.rb] + +In the posts controller, we have a new action but we haven't created a template yet. As before, we can use the Navigation cmd to change from the controller to a view. In this case, the view is based on the current method. So we place the cursor in the new method, and it will ask us to create a new.html.erb file. Here is your opportunity to rename the template with alternate format or template extensions, say .xml.builder. But we'll use .html.erb for now. + +h3. Diagram: form_for - ff, form_for with error output - ffe + +We'll use 'ffe' and enter 'post'. In Rails 2.0, the form_for helper is very powerful and knows from the object it is passed whether it is creating or updating an object. + +h3. Diagram: f. - list of available helpers; or use first initial of helpers' names (fftf - text_field) + +Inside the form_for block we have two ways to access the common helpers. + +By using 'f.' it reminds us what helpers are available and tells us what their tab completions are. + +So, let's use the tab completion versions. ffl for label, fftf for text field. + +
<%= error_messages_for :post %>
+
+<% form_for @post do |f| -%>
+  <%= f.label :title, "Title" %>
+  <%= f.text_field :title %>
+  <%= f.label :body, "Body" %>
+  <%= f.text_area :body, :rows => 10 %>
+  <%= f.submit "Post", :disable_with => 'Posting...' %>
+<% end -%>
+ +Now let's put each label or form field in a separate paragraph. Select those lines and use 'Wrap Each Selected Line in Open/Close' - Shift+Ctrl+Cmd+W, and type 'p'. + +h3. Diagram: Partials - Shift-Ctrl-H + +Since the form_for helper works with saved and unsaved model objects its easily reusable within new and edit templates. So let's extract it as a partial. [do it] And now the render can be pasted into an edit.html.erb file as required. + +[go to browser http://localhost:3000/posts/new] +Now we can fire up the server and have a look at our form and use it. [submit form] + +As requested, the browser is redirected to the public post page; which we haven't implemented yet. So let's do that. + +We head back to the blog_controller, and create the show.html.erb via navigating away from the show action. + +Create a div and set its class to post, then layout the title and body: + +
+

<%= @post.title %>

+ <%= @post.body %> +
+ +If we run our tests, the 'show' action test is now passing. + +[Shft+Ctrl+H] So we can reuse it in the index.html.erb file, let's move it to a partial called 'post' and remove the '@' signs to reference a local variable instead of an instance variable. + +The returned 'render' expression isn't quite right as we need to pass the @post object into the partial. Let's remove it and use the 'render partial' snippets. + +h3. Diagram: render :partial - rp; render :partial, :object - rpo; render :partial, :collection - rpc; render :partial, :locals - rpl + +Create <%= %> and inside use 'rpo' and set the :object => @post. + +Now we create index.html.erb and use <%= %> with 'rpc' and 'post'. The tests require that its wrapped in a div with id="posts". So select all, and Shift+Ctrl+W + div id="posts". + +h3. Diagram: Link helpers; lip - link_to "label", model_path(@model), etc. + +* lip - <%= link_to "link text...", model_path(@model) %> +* lipp - <%= link_to "link text...", models_path %> +* linp - <%= link_to "link text...", parent_child_path(@parent, @child) %> +* linpp - <%= link_to "link text...", parent_child_path(@parent) %> +* lim - <%= link_to model.name, model %> + +h2. Alternate templates + +h3. Diagram: html.erb, js.erb, js.rjs, xml.builder, xml.erb, html.builder + +In addition to html.erb templates, Rails supports any combination of output format and template engine. As appropriate you can combine html or javascript or xml with erb or rjs or builder, or any other 3rd party templating system such as haml. + +The Rails TextMate bundle provides varying support for different combinations. + +h3. Diagram: wants.js -> .js.rjs; wants.xml -> .xml.builder; wants.different -> .different.erb + +Navigation (e.g. tasty tidbit) + +Go to posts_controller, add wants.js to create action. Navigate and create posts/create.js.rjs. + +h3. Diagram: RJS snippets - hide, insert_html (ins), replace (rep), replace_html (reph), show, toggle (tog), visual_effect (vis) + +All these snippets include the page prefix and are nice ways to start each RJS expression. + +[remove all lines; rename with .erb] Another way to generate JavaScript within Rails is with the .js.erb extension. Instead of RJS expressions, you write pure JavaScript, but you can embed ruby using erb. + +A new syntax supported in the bundle is JavaScript (Rails), which maps to .js.erb files. That is, JavaScript files that permit embedded Ruby. This means, within the <%= %> tags you have access to your Ruby snippets and syntax highlighting. + +h2. Extending \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/website/stylesheets/screen.css b/bundles/ruby-on-rails.tmbundle/website/stylesheets/screen.css new file mode 100644 index 000000000..70f1cde3f --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/stylesheets/screen.css @@ -0,0 +1,177 @@ +body { + background-color: #fff; + font-family: "Georgia", sans-serif; + font-size: 16px; + line-height: 1.6em; + padding: 1.6em 0 0 0; + color: #333; +} + +h1, h2, h3, h4, h5, h6 { + color: #444; +} + +h1 { + font-family: sans-serif; + font-weight: normal; + font-size: 4em; + line-height: 0.8em; + letter-spacing: -0.1ex; + margin: 5px; +} + +li { + padding: 0; + margin: 0; + list-style-type: square; +} + +a { + color: #5E5AFF; + font-weight: normal; + text-decoration: underline; +} + +blockquote { + font-size: 90%; + font-style: italic; + border-left: 1px solid #111; + padding-left: 1em; +} + +.caps { + font-size: 80%; +} + +#main { + width: 45em; + padding: 0; + margin: 0 auto; +} + +.coda { + text-align: right; + color: #77f; + font-size: smaller; +} + +table { + font-size: 90%; + line-height: 1.4em; + color: #ff8; + background-color: #111; + padding: 2px 10px 2px 10px; + border-style: dashed; +} + +th { + color: #fff; +} + +td { + padding: 2px 10px 2px 10px; +} + +.success { + color: #0CC52B; +} + +.failed { + color: #E90A1B; +} + +.unknown { + color: #995000; +} + +pre, code { + font-family: monospace; + font-size: 90%; + line-height: 1.4em; + color: #ff8; + background-color: #111; + padding: 2px 10px 2px 10px; +} + +.comment { + color: #aaa; + font-style: italic; +} + +.keyword { + color: #eff; + font-weight: bold; +} + +.punct { + color: #eee; + font-weight: bold; +} + +.symbol { + color: #0bb; +} + +.string { + color: #6b4; +} + +.ident { + color: #ff8; +} + +.constant { + color: #66f; +} + +.regex { + color: #ec6; +} + +.number { + color: #F99; +} + +.expr { + color: #227; +} + +#version { + float: right; + text-align: right; + font-family: sans-serif; + font-weight: normal; + background-color: #9A5535; + color: #141331; + padding: 15px 20px 10px 20px; + margin: 0 auto; + margin-top: 15px; + border: 3px solid #7E393E; +} + +#version .numbers { + display: block; + font-size: 4em; + line-height: 0.8em; + letter-spacing: -0.1ex; + margin-bottom: 5px; +} + +#version p { + text-decoration: none; + color: #F1F4FF; + background-color: #9A5535; + margin: 0; + padding: 0; +} + +#version a { + text-decoration: none; + color: #F1F4FF; + background-color: #9A5535; +} + +.clickable { + cursor: pointer; + cursor: hand; +} \ No newline at end of file diff --git a/bundles/ruby-on-rails.tmbundle/website/template.html.erb b/bundles/ruby-on-rails.tmbundle/website/template.html.erb new file mode 100644 index 000000000..5efa86a24 --- /dev/null +++ b/bundles/ruby-on-rails.tmbundle/website/template.html.erb @@ -0,0 +1,60 @@ + + + + + + + <%= title %> + + + + + + +
+ +

<%= title %>

+
+

Get Version

+ <%= version %> + Logo Bundle +
+ <%= body %> +

+ Dr Nic Williams, <%= modified.pretty %>
+ Theme extended from Paul Battley +

+ + +
+ + + + +