Compare commits

...

283 Commits

Author SHA1 Message Date
Michael Koziarski
a43ef2436c Prepare for 2.3.4 release 2009-09-04 09:56:09 +12:00
Michael Koziarski
9a68c72b4b Clean tag attributes before passing through the escape_once logic.
Addresses CVE-2009-3009
2009-09-04 09:26:13 +12:00
Manfred Stienstra
07c69380cf Add verify and clean methods to ActiveSupport::Multibyte.
When accepting character input from outside of your application you can't
blindly trust that all strings are properly encoded. With these methods
you can check incoming strings and clean them up if necessary.

Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-09-04 09:26:13 +12:00
Coda Hale
1f07a89c59 Fix timing attack vulnerability in ActiveSupport::MessageVerifier.
Use a constant-time comparison algorithm to compare the candidate HMAC with the calculated HMAC to prevent leaking information about the calculated HMAC.

Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-09-04 09:26:13 +12:00
Jeremy Kemper
2b82708b0e Revert "Assert primary key does not exist in habtm when the association is defined, instead of doing that everytime a record is inserted."
Test failures on PostgreSQL.

[#3128]

This reverts commit 594a281d66.
2009-09-02 13:57:33 -07:00
José Valim
594a281d66 Assert primary key does not exist in habtm when the association is defined, instead of doing that everytime a record is inserted.
[#3128 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-09-01 10:18:47 -07:00
Geoff Buesing
6bf17770af Rails::Info doesn't require version for unwanted frameworks
[#3124 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-09-01 02:29:46 -07:00
Joshua Peek
6fdfe4cb5f Deprecated "best fit" detection is to difficult. Just provide a switch to toggle the new behavor on.
# new_rails_defaults.rb
  ActionController::Routing.generate_best_match = false
2009-08-31 16:09:47 -05:00
Jay Pignata
49c4a79e59 Duplicating the options hash in Date#advance to prevent modification of the original [#1133 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-08-31 13:01:34 -05:00
Antonio Tapiador del Dujo
49342d1745 I18n support for plugins
Rails will now automatically add locale files found in any engine's locale
directory to the I18n.load_path (i.e. files that match the glob pattern
"config/locales/**/*.{rb,yml}" relative to engine directories).

[#2325 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-30 13:38:10 -07:00
Jay Pignata
ff8cb50f25 Ensuring that a singular model name is set for use in controllers when scaffold is passed a plural model name
[#3062 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-30 13:31:16 -07:00
Chad Woolley
70ed47f5b4 CI config updates: do not send CI emails unless explicitly enabled, use 'gem update --system', and send emails from an address which can post to the core list
[#3116 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-30 00:06:39 -07:00
Sven Fuchs + Mateo Murphy
13fb26b714 Fix ActiveRecord Error message I18n:
* allow messages and full_messages to be lazily translated at any time
* allow locales to be swapped and still obtain correctly localized messages
* allow localized global and error-type specific full_message formats
* extract an Error class

[#1687 state:open]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-29 09:48:16 -07:00
Jeremy Kemper
05d7409ae5 Prefer utf8_unicode_ci (better) over utf8_general_ci (faster) 2009-08-27 23:09:21 -07:00
Jeffrey Hardy
058459dc22 When running multiple test tasks, don't abort early if one produces failures
[#3107 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-27 11:17:25 -07:00
Jeffrey Hardy
8a49183563 Don't use AR::Base.connection for fixture column quoting. Use the connection given to Fixtures.new
[#3104 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-26 14:53:32 -07:00
Jeffrey Hardy
4240890b28 UrlRewriter#rewrite_url should call #to_param on the value given in :anchor option, just as #url_for does
[#2746 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-26 14:11:39 -07:00
Akira Matsuda
e46e67c71f I18n: use I18n for select helpers' prompt text
[#2252 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-26 13:56:15 -07:00
Jeremy Kemper
3cd245b7fa Revert "I18n: use I18n for select helpers' prompt text"
Broke CI.

[#2252 state:open]

This reverts commit d725ad39da.
2009-08-26 12:12:04 -07:00
Sven Fuchs
a4838ee466 allow ActiveRecord#RecordInvalid exception message to be localized
[#2754 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-26 11:23:30 -07:00
Akira Matsuda
d725ad39da I18n: use I18n for select helpers' prompt text
[#2252 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-26 09:39:01 -07:00
Jeremy Kemper
e213f0caaa Fix typo 2009-08-25 13:43:20 -07:00
Emilio Tagua
38d6e65c5a timestamps gives a created_at field not created_on.
[#3093 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-25 13:43:14 -07:00
Jeremy Kemper
f1355e6a4d Benchmark script via miloops' arel fork via DataMapper's AR comparison script 2009-08-25 13:43:03 -07:00
Jeremy Kemper
d6a944f778 Add active_support/all for forward compatibility. 2009-08-23 17:18:23 -07:00
Mike Gunderloy
9127c5b7f5 Fix trivial typo in template runner example [#3082 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-20 13:35:21 +01:00
David Heinemeier Hansson
f3c7bbeedd Added db/seeds.rb as a default file for storing seed data for the database. Can be loaded with rake db:seed (or created alongside the db with db:setup). (This is also known as the "Stop Putting Gawd Damn Seed Data In Your Migrations" feature) [DHH]
Conflicts:

	railties/CHANGELOG
2009-08-18 16:08:50 +01:00
Pratik Naik
b9f668ea94 Deprecate SQLite2Adapter and DeprecatedSQLiteAdapter 2009-08-17 14:49:31 +01:00
Jeremy Kemper
d8ae3d5a8b 2-3-stable CI notifies rails core list 2009-08-15 20:31:02 -07:00
Jeremy Kemper
1cb433ce78 Bump pg gem requirement to 0.8.0. Build psql db with UTF8 encoding. 2009-08-15 19:04:16 -07:00
Hongli Lai (Phusion)
14b6ab0f01 Fix reloading of metal pieces.
- Do not hold references to old metal objects after metal classes have been reloaded.
- Obtain the reloader lock before building the middleware stack, so that reloading of metal pieces works in the face of multithreading.

[#2873 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-15 18:00:49 -07:00
Jay Pignata
1cf32ad35a Adding a call to logger from params_parser to give detailed debug information when invalid xml or json is posted
[#2481 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-15 16:46:43 -07:00
Jeremy Kemper
75a483e18e Normalize route generation order: associations, yield block, then own routes. 2009-08-15 15:58:20 -07:00
Jeremy Kemper
061b0ba6cb Refine the deprecated route check to explicitly check whether the future route pick comes before the deprecated route that was found. 2009-08-15 15:53:45 -07:00
Jatinder Singh
dbc62ad225 Fix ActiveResource load test for 64bit machines [#3051 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-15 19:57:22 +01:00
Jay Pignata
a249cad5ef Fix calculation tests on sqlite2 [#3053 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-15 19:52:08 +01:00
Jay Pignata
dad0f62dc9 Fix test_has_many_through_polymorphic_has_one on sqlite2 [#3054 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-15 19:27:35 +01:00
Jeremy Kemper
6f5d1f3190 'would of' => 'will be' 2009-08-11 01:25:15 -05:00
Bryan Helmkamp
e82b43599e Allow delegating to nil, because the method might actually exist on it 2009-08-10 18:49:37 -05:00
Hongli Lai (Phusion)
a91969803e Correctly unlock the reloader lock if the underlying app raises an exception.
[#2873 state:incomplete]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-10 16:18:35 -05:00
Kamal Fariz Mahyuddin
9284bcc35a find_cmd should return the full path of the db command
[#1488 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-10 00:04:51 -07:00
codeape
9a42096e95 Introduce grouped_collection_select helper.
[#1249 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-10 00:00:02 -07:00
Erik Ostrom
4e014379a3 Correctly handle offsets in Multibyte::Chars#index and #rindex.
The offset in codepoints was being passed directly to the wrapped string's index/rindex method. Now we translate the offset into bytes first.

[#3028 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 23:32:08 -07:00
Cristi Balan
25fe43bc14 Add tests for scoping schema_migrations index by global table prefix and suffix
[#1543 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:58:35 -07:00
Tim Peters
9e96f37edd Use table prefix and suffix for schema_migrations index.
[#1543 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:57:37 -07:00
Fabien Jakimowicz
c3da22c042 Add support for errors in JSON format.
[#1956 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:41:56 -07:00
Daniel Sheppard
9341655fa3 Fix that JSON parser fails to read escaped backslashes.
[#973 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:32:13 -07:00
Leonardo Borges
5c74cffae6 PostgreSQL: XML datatype support
[#1874 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:26:26 -07:00
Jaime Bellmyer
9d51f62866 raises an exception on habtm join table inserts if join table contains a primary key. Caches this check to save time on subsequent inserts.
[#2086 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:20:02 -07:00
Jaime Bellmyer
9a3a7983c3 raises exception (ActiveRecord::ConfigurationError with message) on habtm association creation if join table contains a primary key
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 22:19:56 -07:00
José Valim
e972acc0d7 Allow radio buttons to work with booleans.
[#2937 state:committed]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 22:15:35 -07:00
Morgan Schweers
d0bdff0799 Fix that creating a table whose primary key prefix type is :table_name generates an incorrectly pluralized primary key.
[#872 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 21:59:08 -07:00
Jeremy Kemper
011baa0f65 Fix test dependency on taggings 2009-08-09 21:31:22 -07:00
Gabe da Silveira
9bc80f4dd1 Fix that counter_cache breaks with has_many :dependent => :nullify.
[#1196 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 21:31:13 -07:00
Jatinder Singh
8a49af3158 AR should respect default values for MySQL BINARY and VARBINARY columns.
[#1273 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 21:05:29 -07:00
Joshua Peek
ff643ce967 Deprecate router generation "best match" sorting 2009-08-09 22:52:14 -05:00
Joshua Nichols
ebb6606a4d Only load db/schema.rb if it exists; otherwise, display a message to run db:migrate or remove active_record in environment.rb.
[#3012 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 20:19:31 -07:00
Joshua Nichols
15fd67e9d8 Backported XML serialization behavior from master for dealing with root nodes that have modules.
ie,
- the root node is dasherized, such that MyApplication::Business::Project becomes <my-application-project-business-project>
- association children nodes have type attributes, such that MyApplication::Business::Developer becomes <developer type="MyApplication::Project::Developer">

[#2723 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 20:13:42 -07:00
Dan Croak
33c054d7e0 has_many :through create should not raise validation errors
[#2934 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 18:47:25 -07:00
jeem
cc3183d4be make private_and_public_methods unmemoizable [#2372 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-08-09 20:04:15 -05:00
Max Lapshin
9e29c084eb Make sure link_to generates the form with the specified :href if any [#2254 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-10 01:00:07 +01:00
Visnu Pitiyanuvath
ecc9b705d7 Allow ho:through#build when the owner is a new record [#1749 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-10 00:49:51 +01:00
Tristan Dunn
491f1b5f36 Prevent overwriting of table name in merging SQL conditions [#2949 state:resolved] 2009-08-10 00:41:53 +01:00
Gabe da Silveira
b763858ed5 Enable has_many :through for going through a has_one association on the join model [#2719 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-10 00:34:32 +01:00
Arthur Zapparoli
9bcacf4962 Removed duplicated tests [#3026 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-10 00:08:50 +01:00
Arthur Zapparoli
18b4ac6992 Model#human_attribute_name now accept symbols [#3025 status:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2009-08-09 23:56:18 +01:00
Matt Duncan
ba961250bd Fixed to_label_tag to accept id attribute without changing for attribute [#2660 status:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2009-08-09 23:56:06 +01:00
Grzegorz Forysinski
59c3b0d0de Ensure ActiveResource#load works with numeric arrays [Grzegorz Forysinski, Elad Meidar]
[#2305 state:resolved]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 23:40:06 +01:00
Jordan Brough
be017fd7d5 Active Resource recognizes 410 as Resource Gone now [#2316 state:resolved] [Jordan Brough, Jatinder Singh]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>

Conflicts:

	activeresource/lib/active_resource/exceptions.rb

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 23:33:11 +01:00
Vladimir Meremyanin
93f5d9d5f0 Make sure association conditions work with :include and :joins [#358 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 23:10:34 +01:00
Tristan Dunn
7908cfabf7 No longer require database name for MySQL to allow cross database selects.
[#1122 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 14:36:51 -07:00
Hugo Peixoto
39de15f136 Added both the documentation and a test case for the collection path name customization feature.
[#1218 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 14:27:04 -07:00
Bence Nagy
250e718355 path_names could be used to customize collection actions too
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 14:27:04 -07:00
Jatinder Singh
323f58f19f Make ActiveResource#exists? work [#3020 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 22:25:20 +01:00
Joshua Nichols
29a5549b34 Added back support for destroying an association's object by id. [#2306 status:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2009-08-09 21:47:06 +01:00
David Burger
c9d4bcf163 Fix that Hash#to_xml and Array#to_xml shouldn't modify their options hashes [#672 state:resolved] [David Burger, Dana Jones]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 21:47:06 +01:00
Hugo Peixoto
2c4f4a8734 With multiparameter date attributes, the behaviour when empty fields are present is now coherent with the one described in the date_select documentation.
[#1715 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 13:39:31 -07:00
Roy Nicholson
de0b073f3e Add ability to set SSL options on ARes connections.
[#2370 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 13:23:48 -07:00
Elise Huard
f6f04f1549 validate uniqueness with limit in utf8
[#2653 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 12:53:09 -07:00
Hugo Peixoto
a8286af3c3 Added a uniqueness validation test that uses diacritics.
[#2883 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 11:56:16 -07:00
Jeremy Kemper
d44b5c6219 Fix that RedCloth shouldn't be required to run tests 2009-08-09 11:02:45 -07:00
Simon Jefford
c41fb5865f Add test for routes_for_controller_and_action deprecation [#3023]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 18:26:22 +01:00
Dan Croak
8058a1d7d7 Deprecation warning for routes_for_controller_and_action. Use routes_for instead. [#3023]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 18:01:54 +01:00
Matt Conway
d3a802cee0 Allow connect_timeout, read_timeout and write_timeout settings for MySQL [#2544 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 17:15:40 +01:00
railsbob
80d8608102 Ensure hm:t#find does not assign nil to :include [#1845 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 17:03:38 +01:00
rizwanreza
d1202cfeb2 Support passing Redcloth options via textilize helper [#2973 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 16:42:50 +01:00
Mike Breen
8056c57a94 Serialized attributes should only be saved with partial_updates when the serialized attribute is present [#2397 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 16:36:54 +01:00
Hugo Peixoto
5e4b946927 Fixed the end_of_* to work with Time.usec (and Time.nsec in ruby1.9) [#1225 status:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2009-08-09 16:11:55 +01:00
Hugo Peixoto
5f6623b1b4 added tests for namespaced models generation and fixed a bug related to it. Also fixed a pluralization=false issue.
Signed-off-by: José Valim <jose.valim@gmail.com>
2009-08-09 16:11:42 +01:00
Michael Siebert
4c96030d05 Fix deprecating =-methods by using send [#2431 status:resolved]
Signed-off-by: José Valim <jose.valim@gmail.com>
2009-08-09 16:11:36 +01:00
José Valim
2d2216fadb Make http digest work with different server/browser combinations. [#3006 status:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 16:11:27 +01:00
Dmitry Ratnikov
32c23552f5 Changed to use klass instead of constantizing in assign_ids generated method
[#260 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-09 02:51:39 -07:00
Jeremy Kemper
83cc7de2a4 Setting connection timeout also affects Net::HTTP open_timeout.
[#2947 state:resolved]
2009-08-09 02:28:27 -07:00
Yehuda Katz
ee8fe3ae4e Update bench harness in 2.3 to match master output 2009-08-09 05:45:44 -03:00
Yehuda Katz
3cad1df22e Benchmarks 2009-08-09 05:45:44 -03:00
Yehuda Katz
7d40ba1cbf Added benchmark to 2-3 2009-08-09 05:45:44 -03:00
Rich Bradley
0b95a2afab Fix for nested :include with namespaced models.
[#260 state:committed]
2009-08-09 00:31:05 -07:00
Tristan Dunn
9aa9bad024 Don't define a default primary key in the schema dumper.
[#1908 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 19:32:03 -07:00
Rob
a25296ab05 Fix binary fixture test on Windows
[#2597 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 19:18:54 -07:00
Hugo Peixoto
ac9f9a9c3e MySQL: fix diacritic uniqueness test by setting the default character set and collation to utf8/utf8_unicode_ci
[#2883 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 19:04:05 -07:00
Michael Koziarski
796b7c6ce6 Don't call additional methods on builders passed to the atom_feed helper.
Additionally, actually test that the atom_feed helper works with :xml as an option.

[#1836 state:committed]
2009-08-09 13:09:24 +12:00
Marc-Andre Lafortune
819c347f43 Enumerable#sum now works will all enumerables, even if they don't respond to :size
[#2489 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 17:51:34 -07:00
Matt Duncan
407fbb5090 Adding :from scoping to ActiveRecord calculations
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-08-09 12:42:47 +12:00
Jan Schwenzien
389449d9ae Fix HTTP basic authentication for long credentials [#2572 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-09 01:28:07 +01:00
Nick Quaranto and Josh Nichols
d39c45690e Adding a deprecation warning for output.flush when rendering a proc or lambda
[#2893 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 17:23:40 -07:00
Steve St. Martin
679a0bf17f Update truncate documentation / examples to more clearly demonstrate its actual behavior
[#3016 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 17:13:15 -07:00
Michael Koziarski
cd5e784389 Depend on rubygems 1.3.2
Also move the min_version definition up a line so it's present in the rescue block down below.
2009-08-09 11:13:12 +12:00
Marshall Huss
791c388039 HTTP proxy support
[#2133 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 15:35:59 -07:00
Jeremy Kemper
ab6d295ce4 Fix caller in assert_redirected_to deprecation warning.
[#2932 state:committed]
2009-08-08 14:45:45 -07:00
Emilio Tagua
6843fb9265 Fix models load order to be able to run unit tests.
[#2550 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-08 22:29:35 +01:00
Josh Sharpe
9aaeb18781 Tidy up the AR tests, removing duplicates and making tests clearer / more focussed.
Signed-off-by: Michael Koziarski <michael@koziarski.com>

[#2774 state:committed]
2009-08-08 14:52:16 +12:00
Mike Breen
1c6c216d91 Add option to routes task to target a specific controller with CONTROLLER=x.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#2928 state:committed]
2009-08-08 14:39:00 +12:00
Matt Duncan
f73d34c131 Default sent_on time to now in ActionMailer
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#2607 state:committed]
2009-08-08 14:10:46 +12:00
Brendan Schwartz
e1d27eedce Ruby 1.9 compat: fix for SSL in Active Resource
[#1272 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-07 12:25:13 -07:00
Jeremy Kemper
4b33155428 Merge branch '2-3-stable' of git@github.com:rails/rails into 2-3-stable 2009-08-05 17:09:43 -07:00
Akira Matsuda
8dab61d146 Ruby 1.9.2 compat: Array#* uses to_str instead of to_s to join values since Ruby 1.9.2
[#2959 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-05 17:09:20 -07:00
rick
55501b9f6a move the serialized AR record logic to #as_json. Leave ActiveRecord::Base#to_json to use the same Object#to_json implementation, but keep it around for documentation purposes. 2009-08-05 16:27:02 -07:00
Sven Fuchs
5a0e295911 Make app template git adapter sync back output immediately by using system() instead of backticks [#2047 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-08-05 19:40:11 +01:00
Michael Koziarski
95db8aaa5f Run the view tests too 2009-08-05 18:42:56 +12:00
Sven Fuchs
bf00de03de Stop messing with supposedly-deprecated interpolation placeholders when no interpolation values have been passed.
[#2885 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-01 20:51:15 -07:00
Jeremy Kemper
6cbcfffeb1 Ruby 1.9: fix reloader tests using lambdas with no env arg 2009-08-01 20:18:22 -07:00
Jeremy Kemper
f09ceb55c0 Ruby 1.9: fix encoding for test_file_stream 2009-08-01 20:17:30 -07:00
Sava Chankov
dc559f274f Ruby 1.9: fix Content-Length for multibyte send_data streaming
[#2661 state:resolved]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-01 20:07:12 -07:00
Jeremy Kemper
7244425b93 Extract String#bytesize shim 2009-08-01 19:52:00 -07:00
Jeremy Kemper
21029451d7 Fix errant require 2009-08-01 19:50:54 -07:00
Jeremy Kemper
32cfd4c2f8 SQLite: deprecate the 'dbfile' option in favor of 'database.' 2009-08-01 18:22:32 -07:00
Hongli Lai (Phusion)
d37ac7958f Make the new code reloading behavior work with multithreaded environments such as Mongrel.
[#2948 state:incomplete]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-08-01 13:25:38 -07:00
Jeremy Kemper
17f336e2f0 Revert "Methods invoked within named scope Procs should respect the scope stack. [#1267 state:resolved]"
This reverts commit 6a13376525.

Conflicts:

	activerecord/test/cases/named_scope_test.rb
2009-07-29 16:53:49 -07:00
Ross Kaffenburger and Bryan Helmkamp
523f3ba8da Don't check authenticity tokens for any AJAX requests 2009-07-27 23:15:35 +01:00
Luke Melia
60122e81a3 Avoid loading the ActiveRecord::SessionStore class on initialization if it is not in use [#2737 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-07-27 23:13:56 +01:00
Sebastian Delmont
ead5d88bf1 Fix filter_parameter_logging of non-hash values within array params
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#2927 state:committed]
2009-07-23 09:33:29 +12:00
Michael Koziarski
143c55d325 Memoize cookies so that updates to cookies are available in the current request. [#2733 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>

Conflicts:

	actionpack/test/controller/cookie_test.rb
2009-07-22 09:46:53 +12:00
Akira Matsuda
be4d743645 Ruby 1.9.2 compat: name method was renamed to __name__ since MiniTest 1.4.x [#2922 state:resolved]
Signed-off-by: Yehuda Katz <wycats@gmail.com>
2009-07-21 01:17:53 -07:00
Akira Matsuda
7a427a83ca Ruby 1.9.2 compat: Use File#expand_path for require path because "." will not be included in LOAD_PATH since Ruby 1.9.2 [#2921 state:resolved]
Signed-off-by: Yehuda Katz <wycats@gmail.com>
2009-07-21 01:17:20 -07:00
Michael Koziarski
c7bcbb983f Forgot to bump the railties versions 2009-07-19 17:27:45 +12:00
Michael Koziarski
a147becfb8 Move from referencing the BlueCloth constant directly, to referencing Markdown.
This supports alternative implementations of markdown such as rpeg-markdown or rdiscount, and later releases of bluecloth.  There are some performance issues with earlier releases of bluecloth, and you should probably upgrade.  In the event that you can't you can place the following code into an initializer:

  Markdown = BlueCloth
2009-07-16 13:30:47 +12:00
Szymon Nowak
b3ec7b2d03 Add primary_key option to belongs_to association
[#765 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-07-15 18:14:00 -07:00
Joshua Peek
ae85927ea8 Correctly setup the rack gem dependency. 2009-07-12 19:31:41 -05:00
Joshua Peek
f36d9a6758 Go back to depending on Rack 1.0.x gem 2009-07-12 19:28:04 -05:00
Michael Koziarski
18e68d9524 Prepare version numbers, changelogs and gem dependencies for 2.3.3. 2009-07-12 15:43:39 +12:00
Michael Koziarski
6a50d6c8e3 Match tests with new reloading behaviour 2009-07-08 17:30:33 +12:00
Hongli Lai (Phusion)
bc2c4a4595 Cleanup application after #close has been called on the Rack response body, not when AC::Reload#call is done.
The Rack body might lazily evaluate its output, which is for example the case
if one calls 'render :text => lambda { ... }'. The code which lazily evaluates
the output might use other application classes. So we will want to defer
cleanup until the Rack request is completely finished.

Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-07-08 13:46:10 +12:00
Lawrence Pit
29c5985849 Use the i18n gem if present instead of vendor code. [#2871 state:resolved]
Signed-off-by: Yehuda Katz <wycats@gmail.com>
2009-07-07 18:38:02 -07:00
Hongli Lai (Phusion)
d8f8066cd1 Add support for dumping non-standard primary keys when using the SQLite3 adapter. Fix unit tests so that this feature is tested for all adapters.
Signed-off-by: Yehuda Katz <wycats@yehuda-katzs-macbookpro41.local>
2009-07-07 16:17:49 -07:00
Hongli Lai (Phusion)
31254bedae Mocha >= 0.9.7 is required, otherwise mocking doesn't work. [#2874 state:resolved]
Signed-off-by: Yehuda Katz <wycats@yehuda-katzs-macbookpro41.local>
2009-07-07 16:14:22 -07:00
Jesús García Sáez
6673001a5e Allow symbols on routes declaration (:controller and :action values) [#2828 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-02 12:19:38 -07:00
Brian Abreu
944f4fc7d2 Fixed ActiveSupport::OrderedHash::[] work identically to ::Hash::[] in ruby 1.8.7 [#2832 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-02 12:01:46 -07:00
Jarl Friis
1c855ad4e7 My suggestion to fix ticket 2401 [#2401 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-02 11:56:56 -07:00
Levin Alexander
d8fff7d9d5 make #inspect if zero length duration return '0 seconds' instead of empty string [#2838 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-02 11:23:04 -07:00
Yehuda Katz + Carl Lerche
281c1a82de Fixes a number of tests that inexplicably didn't fail when we committed the original patch 2009-07-02 10:50:39 -07:00
J.D. Hollis
f6f24b71a4 Only check for built extensions on gem dependencies that are in vendor/gems. [#2825 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-02 10:28:25 -07:00
Elliot Winkler
0d3c5f0a82 Patch FormTagHelper so that when a form tag is created, the div which holds the form authenticity token is set to display:inline [#2846 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-01 17:12:45 -07:00
Yehuda Katz + Carl Lerche
4d8fd8d335 Fixes bug where Memcached connections get corrupted when an invalid expire is passed in [#2854 state:resolved] 2009-07-01 17:00:08 -07:00
Jeremy Kemper
5217c16b09 JSON.escape returns UTF-8 strings
[#2849 state:resolved]
2009-07-01 16:27:13 -07:00
Yehuda Katz + Carl Lerche
97ad936148 Updates CI to use latest mocha 2009-07-01 13:49:35 -07:00
Jeremy Kemper
dfdf8e5dab Merge branch '2-3-stable' of git@github.com:rails/rails into 2-3-stable 2009-07-01 12:55:47 -07:00
Yehuda Katz + Carl Lerche
a8bd3c8a10 Move mocha down below initial T::U require and bump version to 0.9.7 [#2858 state:resolved] 2009-07-01 12:09:32 -07:00
Yehuda Katz + Carl Lerche
e10305f0f4 Accept Symbol for contoller name [#2855 state:resolved]
Signed-off-by: Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>
2009-07-01 11:51:09 -07:00
Chris Mear
eb52dc3db7 Make text_area_tag escape contents by default.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-27 13:15:53 +12:00
Michael Koziarski
9407f6e9a4 Make filter_parameters work correctly with array parameters.
Conflicts:

	actionpack/lib/action_controller/base/filter_parameter_logging.rb
2009-06-27 13:11:01 +12:00
Joshua Peek
6720b25b2d send_data should set Content-Length as a string 2009-06-25 14:44:09 -05:00
Jeremy Kemper
22554745b7 Turn on autolist for debugging also 2009-06-18 21:54:56 -04:00
Jeremy Kemper
8b9b954f40 Friendlier runtime exception if delegatee is nil 2009-06-18 20:11:02 -04:00
Jeremy Kemper
b75bc05bc5 Delegated methods report the expected file/line in backtraces 2009-06-18 18:06:42 -04:00
Joshua Peek
a491b19502 Add :concat option to asset tag helpers to force concatenation.
This is useful for working around IE's stylesheet limit.

  stylesheet_link_tag :all, :concat => true
2009-06-15 10:23:55 -05:00
Luca Guidi
447d60e9ed Bytes calculation speed up
[#2800 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-06-13 14:06:01 -07:00
Jeremy Kemper
cb9429a259 Update memcache-client to 1.7.4 for cheaper timeouts 2009-06-11 22:34:23 -07:00
Joshua Peek
25fde77674 Vendor rack 1.0.x stable 2009-06-11 19:39:21 -05:00
Andrew Kaspick
d3d4822262 allow absolute paths for the asset caches
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-11 16:33:01 +12:00
Jeremy Kemper
7e1bcef985 Remove dead AbstractRequest autoload 2009-06-09 23:51:04 -07:00
Michael Koziarski
19c38a9b70 Whitelist the methods which are called by multiparameter attribute assignment.
This prevents users from causing NoMethodErrors and the like by editing the parameter names, and closes a potential exploit of CVE-2009-1904.
2009-06-10 12:11:18 +12:00
Matt Jones
f43404d42b Fix incorrect specification path in GemDependency#from_directory_name
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-10 09:54:49 +12:00
Stephen Anderson
cd14a4a00e Sanitized the id generated by text_area_tag helper method. text_area_tag('item[description]') should return: <textarea id="item_description" name="item[description]"></textarea> instead of: <textarea id="item[description]" name="item[description]"></textarea> The old id was causing HTML validation failures.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 20:36:37 +12:00
David Stevenson
898a8801ff Made label target radio button tags with values. Radio button now respects inherited :index options when generating id.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 20:29:02 +12:00
Friedrich Göpel
72d111a21c 1.9 compatibility - don't pass an array as the from address as this ends up generating invalid SMTP commands. 2009-06-09 20:24:19 +12:00
Steven Luscher
d63fab344f Fixes #2439. ActionController::Integration::Session no longer mangles multiparameter attribute params when processing multipart requests.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 20:14:01 +12:00
Eugene Pimenov
c5c022c705 PostgreSQL adapter should call thread safe quote_string function
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 20:11:51 +12:00
Tom Ward
d97073337c Change autoload declaration in ActionView::Helpers from JavascriptHelper to JavascriptHelper, matching the actual helper name. Also removed require from UrlHelper which was inadvertently preventing the autoload typo from causing a failure.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 20:08:23 +12:00
Michael Koziarski
b1a044b629 Revert "Ensure HasManyThroughAssociation#destroy delete orphan records"
This reverts commit 7a85927da2.

There's still some debate about the intended behaviour in the ticket, leaving in master but removing prior to shipping 2.3.3
2009-06-09 20:03:36 +12:00
Matt Jones
2c3d2906b2 Fix several issues with the 2.3.2 gem loader.
Incorporates the following:

- migrates back small change to gems:build:force from bfc1609a50 to finish closing #2266.

- unrolls to_proc calls in gems.rake, to match the change in master.

- fixes #2722 by passing the options hash to dependencies during build. (includes a test)

- fixes #2721 by loading the specification directly in from_directory_name. Adds an option to opt-out of specification loading when needed (in gems:refresh_specs, for instance). Includes tests.

- fixes #2679 by refreshing specs for all frozen gems rather than just gems loaded from the environment.

- fixes #2678 by passing the options hash to dependencies during unpack.

Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 19:57:23 +12:00
nate
056ddbdcfb A test to show that http_authentication needs to fail authentication if the password procedure returns nil. Also includes a fix to validate_digest_response to fail validation if the password procedure returns nil.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-09 19:47:16 +12:00
Pratik Naik
5fb66a3abb Vendor Rack edge ( commit : 815342a8e15db564b766f209ffb1e340233f064f ) 2009-06-08 23:47:36 -07:00
Jeremy Kemper
e70272e2a4 Clearer String#first and #last edge cases. Fix that 'foo'.first(0) == 'foo' instead of '' 2009-06-08 20:42:39 -07:00
Jeremy Kemper
63d0c33787 Fix AR json encoding 2009-06-08 20:42:39 -07:00
Jeremy Kemper
f9b2227649 Qualify constant references in BasicObjects 2009-06-08 20:42:39 -07:00
Jeremy Kemper
756e82d1b6 Prefer JSON.encode(value) to value.to_json 2009-06-08 20:42:39 -07:00
Jeremy Kemper
74f16a56e7 Simplify json decoder backend lazy load 2009-06-08 20:42:39 -07:00
Jeremy Kemper
f1e75e4378 Add #element and #collection to ModelName 2009-06-08 20:42:39 -07:00
Jeremy Kemper
cc5d313a48 Lazier Rakefile requires to avoid needing full rake gem on 1.9 2009-06-08 20:42:39 -07:00
Jeremy Kemper
91727ae5e4 Ruby 1.9: sqlite escape encoding 2009-06-08 20:42:31 -07:00
Jeremy Kemper
91fbdfd5b3 Failsafe doesn't return bare String body 2009-06-08 20:35:52 -07:00
Jeremy Kemper
05abd7c196 Check for to_str instead of String 2009-06-08 20:35:52 -07:00
Jeremy Kemper
aebd1ba5b4 Integration tests use Rack::Lint on 1.9 also 2009-06-08 20:35:51 -07:00
Jeremy Kemper
ec10f13939 Ruby 1.9: fix json encoding 2009-06-08 15:18:11 -07:00
Jeremy Kemper
01f820c3b2 Use to_json instead of rails_to_json 2009-06-08 14:09:16 -07:00
Jeremy Kemper
a69ee11968 JSON: split encoding and coercion 2009-06-08 01:37:28 -07:00
Jeremy Kemper
4a78dae2ab Revert rails_to_json -> to_json so we don't break compatibility
[#2753 state:resolved]
2009-06-08 00:11:12 -07:00
Jeremy Kemper
4b4164e8a8 Don't rely on Rails.logger 2009-06-08 00:09:50 -07:00
Tim Connor
84a755b27e Work around a gem dependency edge case that prevents Rails from booting..
If you have a frozen gem with unfrozen dependencies (for instance if the
dependency has native extensions so can't be frozen) you can have a
nightmare upgrade problem, where you cannot rake gems:install, because
rake is broken by a gem loading problem.

If you bump up your frozen gem to a newer version that requires a newer
dependency, everybody else on the team will have rake broken by that
dependency mismatch, since you will have had to specify the dependency
in your config.gems, otherwise nobody will have installed it, since the
parent is frozen. And now the config.gems loading code will kill rake.

[#2609 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-06-05 23:06:19 -07:00
Michael Koziarski
b600bf2cd7 name is case sensitive, update tests to reflect that 2009-06-01 14:21:08 +12:00
Han Kessels
4d7c597e84 fix for IE incompatibility of :disable_with in submit_tag
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-01 14:03:35 +12:00
Ian Terrell
a92790ab86 added a failing test case for counting has_many :through associations with scopes
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-06-01 13:54:56 +12:00
Michael Koziarski
34c3162c5c Revert "Ensure calculations respect scoped :select". Broke .count on a has_many :through association.
This reverts commit 6543426c73.
2009-06-01 13:54:20 +12:00
Joshua Peek
c73cf7d2c0 Revert "Only save the session if we're actually writing to it [#2703 state:resolved]"
This reverts commit 14edaa104d.
2009-05-30 09:36:32 -05:00
Johan Sörensen
14edaa104d Only save the session if we're actually writing to it [#2703 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-05-28 09:32:16 -05:00
Johan Sörensen
dc94c09503 The FlashHash and friends causes a lot of needless session storing, when we know for a fact that there's no content in the flash. By not storing the empty hash in the session we save a lot of communication with the various session backends, while still keeping the same interface to the flash. [#2703 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-05-28 09:22:35 -05:00
Hongli Lai (Phusion)
34a1ed0df8 Make the Failsafe middleware attempt to render 500.html during failsafe response rendering. Also make the default static failsafe response more friendly, in case 500.html rendering fails. [#2715 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-05-27 14:59:11 -05:00
calavera
4196616778 ensure initialize_database_middleware doesn't use ActionController if action_controller framework is not enabled [#2680 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-05-27 14:56:26 -05:00
Joshua Peek
b4c7b3e893 Ensure Memcache local cache returns duplicated values [#2302 state:resolved] 2009-05-27 14:55:13 -05:00
Hongli Lai (Phusion)
9b2a665aff activesupport/json/encoders fix that to_json should call rails_to_json, not just be an alias to the rails_to_json method defined in Object. Fixes #2690
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-25 15:28:14 +02:00
Eloy Duran
a70c78177a Ensure the parent record is always saved when the child is invalid. [#2249 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-20 21:19:30 +02:00
Bryan Helmkamp
542d6a0abd Use duck typing to also allow MemCache-like object when initializing a MemCacheStore
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-19 16:58:20 -07:00
Jeremy Kemper
2a657725f1 Mark pending release in changelog instead of edge 2009-05-19 10:59:24 -07:00
Bryan Helmkamp
6339e5d360 Allow MemCacheStore to be initialized with a MemCache object instead of addresses and options 2009-05-19 10:58:30 -07:00
Jeremy Kemper
9fcadcbd68 Fix imprecise float comparison 2009-05-18 14:34:32 -07:00
Joe Van Dyk
ad85771221 Add ability to get multiple memcached keys at the same time (via MemCacheStore#read_multi).
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-18 13:44:45 -07:00
Ken Collins
50608ecccd Reimplement Fixtures.identify so that it consistently generates identities across ruby versions.
[#2633 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-18 13:42:04 -07:00
Luca Guidi
7a85927da2 Ensure HasManyThroughAssociation#destroy delete orphan records [#2251 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-18 22:31:14 +02:00
Pratik Naik
97b75c9f16 Make sure default_scope#create checks for options[:conditions] [#2181 state:resolved] [James Le Cuirot] 2009-05-18 21:27:55 +02:00
Pratik Naik
dbb0258279 Ensure HTTP Digest auth uses appropriate HTTP method [#2490 state:resolved] [Steve Madsen] 2009-05-18 17:00:29 +02:00
rick
2b5e4f38f5 load the JSON Backend lazily. 2009-05-17 19:16:11 -07:00
Jeremy Kemper
5b80ead2a3 Extract json string escaping 2009-05-17 18:42:56 -07:00
Jeremy Kemper
cc47d3ff0c Only Object to_json alias is needed. Prefer nil options. 2009-05-17 18:42:44 -07:00
rick
d052e9fb58 Add pluggable JSON backends with support for the JSON gem.
Example: ActiveSupport::JSON.backend = "JSONGem"

All internal Rails JSON encoding is now handled by
ActiveSupport::JSON.encode().  Use of #to_json is not recommended, as it
may clash with other libraries that overwrite it.  However, you can
recover Rails specific functionality
if you really want to use #to_json.

    gem 'json'
    ActiveSupport::JSON.backend = "JSONGem"

    class ActiveRecord::Base
      alias to_json rails_to_json
    end
2009-05-17 18:40:38 -07:00
Joshua Peek
43e537b9e8 Missed a file from the previous commit 2009-05-17 14:45:06 -05:00
Joshua Peek
e30016c29e Fix reset_session with ActiveRecord store [#2200 state:resolved] 2009-05-17 14:44:19 -05:00
Mike Breen
f383a4aa33 Allow assert_template to take a symbol [#2011 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-17 19:40:18 +02:00
Paulo Schneider
14b769899c Fix typo in the generated routes.rb [#2433 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-17 15:00:41 +02:00
Jacob Kjeldahl
d5f018eb10 Supply valid ruby-prof parameters [#1804 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-17 14:53:50 +02:00
Daniel Guettler
4cd40726eb has_one :through should not create a new association when assigned nil [#698 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-05-17 14:50:09 +02:00
Pratik Naik
ba92e83bcc Include guides directory in the rails gem 2009-05-16 17:08:34 +02:00
José Valim
66ead4f148 Allow strings to be sent as collection to select.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#2391 state:committed]
2009-05-15 15:30:42 +12:00
Joshua Peek
f7cb7fce4c Sweeper does not belong in Sweeping module 2009-05-14 16:47:24 -05:00
Peter Marklund
0380e9ca5f Changed ActiveRecord::Base#exists? to invoke find_initial so that it is compatible with, and doesn't lose, :include scopes (references to eager loaded tables)
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#2543 state:committed]
2009-05-14 20:42:13 +12:00
Jeremy Kemper
35e1785081 Revert "Fixed bug with polymorphic has_one :as pointing to an STI record"
[#2594 state:open]

This reverts commit 93c557828e.
2009-05-11 12:21:59 -07:00
Douglas F Shearer
2bcb2443a9 ActiveSupport::OrderedHash[1,2,3,4] creates an OrderedHash instead of a Hash.
[#2615 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-11 11:46:43 -07:00
Joshua Peek
4051dd3412 Fix syntax error from 5ac05f15 2009-05-09 22:22:14 -05:00
Anthony Crumley
88d5e3341d Fixed eager load error on find with include => [:table_name] and hash conditions like {:table_name => {:column => 'value'}}
Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-05-10 13:33:22 +12:00
John Small
7bf9bf3dd6 Add configuration options for :dasherize and :camelize calls to Hash#to_xml
People using ActiveResource & REST to integrate with other systems need to be able to control the default dasherize behavior of Hash.to_xml.
Currently there is no test for a default value, but existing code asssumes it's true. This patch adds tests for the default value and adds
mattr_accessor to ActiveSupport for :dasherize_xml and :camelize_xml. These module attributes set the defaults for :dasherize and :camelize in
rename_keys inside Hash#to_xml. The tests have been changed to separate out the testing of the parameter options for :camelize
and :dasherize so that we only test one thing at a time. We also test default values for :camelize_xml and :dasherize_xml.

The module attribute dasherize_xml is set to true in this patch to maintain existing code. But at some point in the future it should be set to
false because Hash#to_xml probably should not set underscores to dashes by default.

Changed documentation on ActiveResource#to_xml to correctly describe the behaviour of Hash#to_xml. The previous documentation said that
the default for :dasherize was false, in fact it was and still is true, but we now have a way to change the default. I've also added
documentation for the :camelize option.

Signed-off-by: Michael Koziarski <michael@koziarski.com>
2009-05-10 13:09:40 +12:00
Ken Collins
6dec3c45fc ActiveSupport::OrderedHash#to_a method returns an ordered set of arrays. Matches ruby1.9's Hash#to_a.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
[#2629 state:committed]
2009-05-10 13:05:14 +12:00
Joshua Peek
e61cceb37f Don't stream each line of the body, just send the whole thing 2009-05-08 17:00:16 -05:00
Joshua Peek
7f1f16c01f Deprecate assert_redirect_to's partial hash matching. This will be fully removed in 3.0. 2009-05-04 20:24:49 -05:00
Wincent Colaiuta
5ac05f15c6 Extract ActionController::Caching::Sweeper into separate file [#1977 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-05-04 20:17:27 -05:00
Tim Connor
49169f7a6a fix problems with requires in metal choking under development reloading [#2579 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-05-04 20:12:16 -05:00
codebrulee
ebe8dd6108 Remove stray underscore from the hash conversion methods which broke backwards compatibility with Hash.from_xml
Also add an all-caps test to prevent future regressions
2009-05-04 09:51:35 -07:00
Ruy Asan
17e712d3a3 Added routing test for irregular ID requirements and custom member action.
[#2595 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-01 15:02:51 -07:00
Ruy Asan
93c557828e Fixed bug with polymorphic has_one :as pointing to an STI record
[#2594 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-01 14:49:38 -07:00
Alexander Podgorbunsky
628b4ad679 Default scope :order should be overridden by named scopes.
[#2346 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-05-01 14:45:35 -07:00
Jeremy Kemper
a5ed7eede6 Missed commit for 7c4b325e0a 2009-04-30 16:49:34 -07:00
John F. Douthat
d1d1894c2f Fix action-cached exception responses.
Methods raising ActiveRecord::RecordNotFound were returning 404 on first request and 200 OK with blank body on subsequent requests.

[#2533 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-04-30 16:48:07 -07:00
Jeremy Kemper
7c4b325e0a Fix render :json => nil [#2589 state:resolved] 2009-04-30 16:47:42 -07:00
David Dollar
00eee49e1e Additional tests for the gem subsystem
* test_gem_ignores_development_dependencies
  * test_gem_guards_against_duplicate_unpacks
  * test_gem_does_not_unpack_framework_gems

[#2236 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-04-29 08:08:48 -07:00
David Dollar
6e3bede928 Attempt to deal with more cases of gems with native components.
This commit adds a rudimentary check for 'unbuilt' gems, so that we can abort
the application load if there are any gems that have native components that
have not yet been built.

The rake task gems:build has now only builds 'unbuilt' gems as a result.

The rake task gems:build:force has been added to deal with cases of incomplete
builds, or any case where you need to force the build of all of your gems.

Changes the gems:build task to get its gem list by parsing directory entries
in vendor/gems, which sidesteps the chicken/egg issues involved with having a
gem unpacked into vendor/gems without before its native bits are compiled.

[#2266 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-04-28 23:19:18 -07:00
Stephen Bannasch
4b68debb1c add JRuby-JDOM backend for XmlMini
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
2009-04-26 16:23:22 -07:00
Hongli Lai (Phusion)
2633108e1f Fix environment variable testing code in failsafe.rb.
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-04-26 11:22:44 -05:00
Hongli Lai (Phusion)
5e57e2fa58 Remove reference to Rack::RewindableInput, which has been removed a while ago.
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-04-26 11:21:15 -05:00
Yehuda Katz
cb9a1f17f0 Updated 2-3-stable to Rack 1.0 2009-04-25 12:47:51 -07:00
Joshua Peek
61a1456937 Remove RewindableInput middleware since all input MUST be rewindable according to a recent change in the Rack 1.0 SPEC 2009-04-25 14:05:58 -05:00
Joshua Peek
16f36b6171 Remove vendored version of Rack 2009-04-25 13:59:26 -05:00
Joshua Peek
2d9b45722c Remove pending rack specifications until they are official 2009-04-25 13:44:34 -05:00
Jeremy Kemper
617d7eb57b Merge branch '2-3-stable' of git@github.com:rails/rails into 2-3-stable 2009-04-22 01:08:53 -07:00
Pratik Naik
09a976ac58 Change table to prevent copying indexes on sqlite2 2009-04-21 13:29:00 +01:00
Pratik Naik
5bbd097ce9 Specify :group with the table name for it to work on sqlite3 2009-04-21 13:12:15 +01:00
Pratik Naik
3267097393 Fix tests for sqlite3 3.6.xx 2009-04-21 13:08:26 +01:00
Max Lapshin
5a4603fafb Fixed dumping from postgresql columns in index in wrong order. [#2515 state:resolved]
Signed-off-by: Tarmo Tänav <tarmo@itech.ee>
2009-04-21 11:45:02 +01:00
Scott Woods
70ba90b072 Quote table names when casting to regclass so that capitalized tables are supported. [#2418 state:resolved]
Signed-off-by: Tarmo Tänav <tarmo@itech.ee>
2009-04-21 11:44:54 +01:00
Max Lapshin
de4cc53f74 Fixed wrong quoting of index names in postgres [#2402 state:resolved]
Signed-off-by: Tarmo Tänav <tarmo@itech.ee>
2009-04-21 11:44:47 +01:00
Max Lapshin
6060123470 Support multiple schemas in table names for postgresql [#390 state:resolved]
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2009-04-21 11:40:12 +01:00
Pratik Naik
fc2421b784 Ensure :dependent => :delete_all works for association with hash conditions 2009-04-21 11:33:27 +01:00
Pratik Naik
375e8976e3 Ensure JoinAssociation uses aliased table name when multiple associations have hash conditions on the same table 2009-04-20 13:56:03 +01:00
Mislav Marohnić
35c5727ace Always buffer rack.input if it is not rewindable
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-04-17 21:54:09 -05:00
Mislav Marohnić
878aec9d95 Improve rewindable input test coverage so tests fail when you remove the middleware
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-04-17 19:35:25 -05:00
Jeremy Kemper
c9a3d99164 Clearer String#first and #last edge cases. Fix that foo.first(0) == instead of foo. 2009-04-17 18:06:47 -05:00
David Heinemeier Hansson
fa750e08a8 Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH] 2009-04-16 17:26:10 -05:00
David Heinemeier Hansson
50e867480a Added ActiveRecord::Base#touch to update the updated_at/on attributes with the current time [DHH] 2009-04-16 16:48:07 -05:00
Doug McInnes
dc69d9308a Fix for TestResponse.cookies returning cookies unescaped [#1867 state:resolved]
Signed-off-by: David Heinemeier Hansson <david@loudthinking.com>
2009-04-07 13:22:21 -05:00
Kenny Ortmann
ace154d067 added tests for session options being defaulted correctly to rack defaults [#2403 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-04-07 11:08:04 -05:00
Ryan Angilly
651611999d adding session_options initialization and test [#2303 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-03-24 10:53:29 -05:00
thedarkone
6a1267a0b1 Fix template extension parsing. [#2315 state:resolved] [#2284 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-03-24 10:53:24 -05:00
thedarkone
d2e6a0fbc3 Simplify handling of absolute path templates. [#2276 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-03-24 10:53:20 -05:00
David Dollar
dace54b2e9 Updates tests to cause the tests for the Request class not to proxy through a fake TestRequest object [#2278 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-03-24 10:53:14 -05:00
Peter Marklund
daffa5cbdd Reset request_parameters in TestRequest#recycle! to avoid multiple posts clobbering each other [#2271 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
2009-03-24 10:53:07 -05:00
Joshua Peek
c91912700d just kill brittle test 2009-03-24 10:52:49 -05:00
380 changed files with 7535 additions and 7549 deletions

View File

@@ -1,6 +1,5 @@
require 'rake'
require 'rake/rdoctask'
require 'rake/contrib/sshpublisher'
env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
@@ -74,6 +73,7 @@ end
desc "Publish API docs for Rails as a whole and for each component"
task :pdoc => :rdoc do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc/rdoc").upload
PROJECTS.each do |project|
system %(cd #{project} && #{env} #{$0} pdoc)

View File

@@ -1,3 +1,11 @@
*2.3.4 (September 4, 2009)*
* Minor bug fixes.
*2.3.3 (July 12, 2009)*
* No changes, just a version bump.
*2.3.2 [Final] (March 15, 2009)*
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]

View File

@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@@ -55,7 +54,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 2.3.2' + PKG_BUILD)
s.add_dependency('actionpack', '= 2.3.4' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'
@@ -76,12 +75,14 @@ end
desc "Publish the API documentation"
task :pgem => [:package] do
require 'rake/contrib/sshpublisher'
Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload
end

View File

@@ -543,6 +543,7 @@ module ActionMailer #:nodoc:
@headers ||= {}
@body ||= {}
@mime_version = @@default_mime_version.dup if @@default_mime_version
@sent_on ||= Time.now
end
def render_message(method_name, body)
@@ -674,7 +675,7 @@ module ActionMailer #:nodoc:
def perform_delivery_smtp(mail)
destinations = mail.destinations
mail.ready_to_send
sender = (mail['return-path'] && mail['return-path'].spec) || mail.from
sender = (mail['return-path'] && mail['return-path'].spec) || mail['from']
smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)

View File

@@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 2
TINY = 4
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -1,9 +1,6 @@
require 'rubygems'
require 'test/unit'
gem 'mocha', '>= 0.9.5'
require 'mocha'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib"

View File

@@ -18,7 +18,6 @@ class TestMailer < ActionMailer::Base
@recipients = recipient
@subject = "[Signed up] Welcome #{recipient}"
@from = "system@loudthinking.com"
@sent_on = Time.local(2004, 12, 12)
@body["recipient"] = recipient
end
@@ -356,12 +355,14 @@ class ActionMailerTest < Test::Unit::TestCase
end
def test_signed_up
Time.stubs(:now => Time.now)
expected = new_mail
expected.to = @recipient
expected.subject = "[Signed up] Welcome #{@recipient}"
expected.body = "Hello there, \n\nMr. #{@recipient}"
expected.from = "system@loudthinking.com"
expected.date = Time.local(2004, 12, 12)
expected.date = Time.now
created = nil
assert_nothing_raised { created = TestMailer.create_signed_up(@recipient) }

View File

@@ -1,7 +1,23 @@
*2.3.4 (September 4, 2009)*
* Sanitize multibyte strings before escaping them with escape_once. CVE-2009-3009
* Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom]
* Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov]
*2.3.3 (July 12, 2009)*
* Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes]
*2.3.2 [Final] (March 15, 2009)*
* Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") [DHH]
* Don't check authenticity tokens for any AJAX requests [Ross Kaffenberger/Bryan Helmkamp]
* Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 [Gregg Pollack]
* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]

View File

@@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@@ -30,7 +29,7 @@ Rake::TestTask.new(:test_action_pack) do |t|
# make sure we include the tests in alphabetical order as on some systems
# this will not happen automatically and the tests (as a whole) will error
t.test_files = Dir.glob( "test/[cft]*/**/*_test.rb" ).sort
t.test_files = Dir.glob( "test/[cftv]*/**/*_test.rb" ).sort
t.verbose = true
#t.warning = true
@@ -80,7 +79,8 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD)
s.add_dependency('activesupport', '= 2.3.4' + PKG_BUILD)
s.add_dependency('rack', '~> 1.0.0')
s.require_path = 'lib'
s.autorequire = 'action_controller'
@@ -136,12 +136,14 @@ task :update_js => [ :update_scriptaculous ]
desc "Publish the API documentation"
task :pgem => [:package] do
require 'rake/contrib/sshpublisher'
Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
end

View File

@@ -0,0 +1,87 @@
$:.push File.join(File.dirname(__FILE__), "..", "lib")
$:.push File.join(File.dirname(__FILE__), "..", "..", "activesupport", "lib")
require "action_controller"
class Runner
def initialize(app, output)
@app, @output = app, output
end
def puts(*)
super if @output
end
def call(env)
env['n'].to_i.times { @app.call(env) }
@app.call(env).tap { |response| report(env, response) }
end
def report(env, response)
if ENV["DEBUG"]
out = env['rack.errors']
p response.headers
out.puts response.status, response.headers.to_yaml, '---'
response.body.each { |part| out.puts part }
out.puts '---'
end
end
def self.puts(*)
super if @output
end
def self.run(app, n, label = nil, uri = "/", output = true)
@output = output
puts label, '=' * label.size if label
env = Rack::MockRequest.env_for(uri).merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout)
t = Benchmark.realtime { new(app, output).call(env) }
puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n]
puts
end
end
N = (ENV['N'] || 1000).to_i
class BasePostController < ActionController::Base
append_view_path "#{File.dirname(__FILE__)}/views"
def index
render :text => 'Hello'
end
def partial
render :partial => "/partial"
end
def many_partials
render :partial => "/many_partials"
end
def partial_collection
render :partial => "/collection", :collection => [1,2,3,4,5,6,7,8,9,10]
end
def show_template
render :template => "template"
end
end
# p BasePostController.call(Rack::MockRequest.env_for("/?action=index").merge("REQUEST_URI" => "/")).body
Runner.run(BasePostController, N, 'index', "/?action=index", false)
Runner.run(BasePostController, N, 'partial', "/?action=partial", false)
Runner.run(BasePostController, N, 'many partials', "/?action=many_partials", false)
Runner.run(BasePostController, N, 'collection', "/?action=partial_collection", false)
Runner.run(BasePostController, N, 'template', "/?action=show_template", false)
(ENV["M"] || 1).to_i.times do
Runner.run(BasePostController, N, 'index', "/?action=index")
Runner.run(BasePostController, N, 'partial', "/?action=partial")
Runner.run(BasePostController, N, 'many partials', "/?action=many_partials")
Runner.run(BasePostController, N, 'collection', "/?action=partial_collection")
Runner.run(BasePostController, N, 'template', "/?action=show_template")
end
# Runner.run(BasePostController.action(:many_partials), N, 'index')
# Runner.run(BasePostController.action(:many_partials), N, 'many_partials')
# Runner.run(BasePostController.action(:partial_collection), N, 'collection')
# Runner.run(BasePostController.action(:show_template), N, 'template')

View File

@@ -0,0 +1 @@
<%= collection %>

View File

@@ -0,0 +1 @@
Hello

View File

@@ -0,0 +1,10 @@
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>
<%= render :partial => '/hello' %>

View File

@@ -0,0 +1,10 @@
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>
<%= "Hello" %>

View File

@@ -0,0 +1 @@
+ <%= yield %> +

View File

@@ -0,0 +1 @@
Hello <%= yield %> Goodbye

View File

@@ -0,0 +1 @@
Hello

View File

@@ -31,12 +31,8 @@ rescue LoadError
end
end
begin
gem 'rack', '~> 1.0.0'
require 'rack'
rescue Gem::LoadError
require 'action_controller/vendor/rack-1.0/rack'
end
gem 'rack', '~> 1.0.0'
require 'rack'
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
@@ -45,7 +41,6 @@ module ActionController
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :AbstractRequest, 'action_controller/request'
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'

View File

@@ -63,7 +63,12 @@ module ActionController
# Support partial arguments for hash redirections
if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
return true if options.all? {|(key, value)| @response.redirected_to[key] == value}
if options.all? {|(key, value)| @response.redirected_to[key] == value}
callstack = caller.dup
callstack.slice!(0, 2)
::ActiveSupport::Deprecation.warn("Using assert_redirected_to with partial hash arguments is deprecated. Specify the full set arguments instead", callstack)
return true
end
end
redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to)
@@ -82,6 +87,9 @@ module ActionController
# # assert that the "new" view template was rendered
# assert_template "new"
#
# # assert that the "new" view template was rendered with Symbol
# assert_template :new
#
# # assert that the "_customer" partial was rendered twice
# assert_template :partial => '_customer', :count => 2
#
@@ -91,7 +99,7 @@ module ActionController
def assert_template(options = {}, message = nil)
clean_backtrace do
case options
when NilClass, String
when NilClass, String, Symbol
rendered = @response.rendered[:template].to_s
msg = build_message(message,
"expecting <?> but rendering with <?>",
@@ -100,7 +108,7 @@ module ActionController
if options.nil?
@response.rendered[:template].blank?
else
rendered.to_s.match(options)
rendered.to_s.match(options.to_s)
end
end
when Hash
@@ -123,6 +131,8 @@ module ActionController
assert @response.rendered[:partials].empty?,
"Expected no partials to be rendered"
end
else
raise ArgumentError
end
end
end

View File

@@ -491,6 +491,15 @@ module ActionController #:nodoc:
filtered_parameters[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif value.is_a?(Array)
filtered_parameters[key] = value.collect do |item|
case item
when Hash, Array
filter_parameters(item)
else
item
end
end
elsif block_given?
key = key.dup
value = value.dup if value
@@ -810,7 +819,6 @@ module ActionController #:nodoc:
# render :text => proc { |response, output|
# 10_000_000.times do |i|
# output.write("This is line #{i}\n")
# output.flush
# end
# }
#
@@ -950,8 +958,9 @@ module ActionController #:nodoc:
response.content_type ||= Mime::JS
render_for_text(js, options[:status])
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
elsif options.include?(:json)
json = options[:json]
json = ActiveSupport::JSON.encode(json) unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])

View File

@@ -27,7 +27,7 @@ module ActionController #:nodoc:
autoload :Actions, 'action_controller/caching/actions'
autoload :Fragments, 'action_controller/caching/fragments'
autoload :Pages, 'action_controller/caching/pages'
autoload :Sweeper, 'action_controller/caching/sweeping'
autoload :Sweeper, 'action_controller/caching/sweeper'
autoload :Sweeping, 'action_controller/caching/sweeping'
def self.included(base) #:nodoc:

View File

@@ -61,7 +61,9 @@ module ActionController #:nodoc:
filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) }
cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
around_filter(cache_filter, filter_options)
around_filter(filter_options) do |controller, action|
cache_filter.filter(controller, action)
end
end
end
@@ -83,6 +85,12 @@ module ActionController #:nodoc:
@options = options
end
def filter(controller, action)
should_continue = before(controller)
action.call if should_continue
after(controller)
end
def before(controller)
cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path)))
if cache = controller.read_fragment(cache_path.path, @options[:store_options])

View File

@@ -0,0 +1,45 @@
require 'active_record'
module ActionController #:nodoc:
module Caching
class Sweeper < ActiveRecord::Observer #:nodoc:
attr_accessor :controller
def before(controller)
self.controller = controller
callback(:before) if controller.perform_caching
end
def after(controller)
callback(:after) if controller.perform_caching
# Clean up, so that the controller can be collected after this request
self.controller = nil
end
protected
# gets the action cache path for the given options.
def action_path_for(options)
ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
end
# Retrieve instance variables set in the controller.
def assigns(key)
controller.instance_variable_get("@#{key}")
end
private
def callback(timing)
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
__send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
__send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
end
def method_missing(method, *arguments, &block)
return if @controller.nil?
@controller.__send__(method, *arguments, &block)
end
end
end
end

View File

@@ -51,47 +51,5 @@ module ActionController #:nodoc:
end
end
end
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
class Sweeper < ActiveRecord::Observer #:nodoc:
attr_accessor :controller
def before(controller)
self.controller = controller
callback(:before) if controller.perform_caching
end
def after(controller)
callback(:after) if controller.perform_caching
# Clean up, so that the controller can be collected after this request
self.controller = nil
end
protected
# gets the action cache path for the given options.
def action_path_for(options)
ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
end
# Retrieve instance variables set in the controller.
def assigns(key)
controller.instance_variable_get("@#{key}")
end
private
def callback(timing)
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
__send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
__send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
end
def method_missing(method, *arguments, &block)
return if @controller.nil?
@controller.__send__(method, *arguments, &block)
end
end
end
end
end

View File

@@ -51,7 +51,7 @@ module ActionController #:nodoc:
protected
# Returns the cookie container, which operates as described above.
def cookies
CookieJar.new(self)
@cookies ||= CookieJar.new(self)
end
end

View File

@@ -2,13 +2,12 @@ module ActionController
# Dispatches requests to the appropriate controller and takes care of
# reloading the app after each request when Dependencies.load? is true.
class Dispatcher
@@cache_classes = true
class << self
def define_dispatcher_callbacks(cache_classes)
@@cache_classes = cache_classes
unless cache_classes
unless self.middleware.include?(Reloader)
self.middleware.insert_after(Failsafe, Reloader)
end
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
@@ -79,7 +78,7 @@ module ActionController
# DEPRECATE: Remove arguments, since they are only used by CGI
def initialize(output = $stdout, request = nil, response = nil)
@output = output
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
build_middleware_stack if @@cache_classes
end
def dispatch
@@ -103,7 +102,18 @@ module ActionController
end
def call(env)
@app.call(env)
if @@cache_classes
@app.call(env)
else
Reloader.run do
# When class reloading is turned on, we will want to rebuild the
# middleware stack every time we process a request. If we don't
# rebuild the middleware stack, then the stack may contain references
# to old classes metal classes, which will b0rk class reloading.
build_middleware_stack
@app.call(env)
end
end
end
def _call(env)
@@ -114,5 +124,10 @@ module ActionController
def flush_logger
Base.logger.flush
end
private
def build_middleware_stack
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
end
end
end

View File

@@ -1,4 +1,19 @@
require 'erb'
module ActionController
# The Failsafe middleware is usually the top-most middleware in the Rack
# middleware chain. It returns the underlying middleware's response, but if
# the underlying middle raises an exception then Failsafe will log the
# exception into the Rails log file, and will attempt to return an error
# message response.
#
# Failsafe is a last resort for logging errors and for telling the HTTP
# client that something went wrong. Do not confuse this with the
# ActionController::Rescue module, which is responsible for catching
# exceptions at deeper levels. Unlike Failsafe, which is as simple as
# possible, Rescue provides features that allow developers to hook into
# the error handling logic, and can customize the error message response
# based on the HTTP client's IP.
class Failsafe
cattr_accessor :error_file_path
self.error_file_path = Rails.public_path if defined?(Rails.public_path)
@@ -11,7 +26,7 @@ module ActionController
@app.call(env)
rescue Exception => exception
# Reraise exception in test environment
if env["rack.test"]
if defined?(Rails) && Rails.env.test?
raise exception
else
failsafe_response(exception)
@@ -21,18 +36,37 @@ module ActionController
private
def failsafe_response(exception)
log_failsafe_exception(exception)
[500, {'Content-Type' => 'text/html'}, failsafe_response_body]
[500, {'Content-Type' => 'text/html'}, [failsafe_response_body]]
rescue Exception => failsafe_error # Logger or IO errors
$stderr.puts "Error during failsafe response: #{failsafe_error}"
end
def failsafe_response_body
error_path = "#{self.class.error_file_path}/500.html"
if File.exist?(error_path)
File.read(error_path)
error_template_path = "#{self.class.error_file_path}/500.html"
if File.exist?(error_template_path)
begin
result = render_template(error_template_path)
rescue Exception
result = nil
end
else
"<html><body><h1>500 Internal Server Error</h1></body></html>"
result = nil
end
if result.nil?
result = "<html><body><h1>500 Internal Server Error</h1>" <<
"If you are the administrator of this website, then please read this web " <<
"application's log file to find out what went wrong.</body></html>"
end
result
end
# The default 500.html uses the h() method.
def h(text) # :nodoc:
ERB::Util.h(text)
end
def render_template(filename)
ERB.new(File.read(filename)).result(binding)
end
def log_failsafe_exception(exception)

View File

@@ -120,6 +120,11 @@ module ActionController #:nodoc:
(@used.keys - keys).each{ |k| @used.delete(k) }
end
def store(session, key = "flash")
return if self.empty?
session[key] = self
end
private
# Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
# use() # marks the entire flash as used
@@ -139,7 +144,10 @@ module ActionController #:nodoc:
protected
def perform_action_with_flash
perform_action_without_flash
remove_instance_variable(:@_flash) if defined? @_flash
if defined? @_flash
@_flash.store(session)
remove_instance_variable(:@_flash)
end
end
def reset_session_with_flash
@@ -151,8 +159,8 @@ module ActionController #:nodoc:
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash #:doc:
unless defined? @_flash
@_flash = session["flash"] ||= FlashHash.new
if !defined?(@_flash)
@_flash = session["flash"] || FlashHash.new
@_flash.sweep
end

View File

@@ -139,7 +139,7 @@ module ActionController
end
def decode_credentials(request)
ActiveSupport::Base64.decode64(authorization(request).split.last || '')
ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '')
end
def encode_credentials(user_name, password)
@@ -183,7 +183,7 @@ module ActionController
request.env['REDIRECT_X_HTTP_AUTHORIZATION']
end
# Raises error unless the request credentials response value matches the expected value.
# Returns false unless the request credentials response value matches the expected value.
# First try the password as a ha1 digest password. If this fails, then try it as a plain
# text password.
def validate_digest_response(request, realm, &password_procedure)
@@ -192,9 +192,13 @@ module ActionController
if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
password = password_procedure.call(credentials[:username])
return false unless password
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url
[true, false].any? do |password_is_ha1|
expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1)
expected = expected_response(method, uri, credentials, password, password_is_ha1)
expected == credentials[:response]
end
end

View File

@@ -292,9 +292,7 @@ module ActionController
"rack.errors" => StringIO.new,
"rack.multithread" => true,
"rack.multiprocess" => true,
"rack.run_once" => false,
"rack.test" => true
"rack.run_once" => false
)
(headers || {}).each do |key, value|
@@ -311,12 +309,7 @@ module ActionController
ActionController::Base.clear_last_instantiation!
app = @application
# Rack::Lint doesn't accept String headers or bodies in Ruby 1.9
unless RUBY_VERSION >= '1.9.0' && Rack.release <= '0.9.0'
app = Rack::Lint.new(app)
end
app = Rack::Lint.new(@application)
status, headers, body = app.call(env)
@request_count += 1
@@ -333,7 +326,7 @@ module ActionController
end
@body = ""
if body.is_a?(String)
if body.respond_to?(:to_str)
@body << body
else
body.each { |part| @body << part }
@@ -416,7 +409,7 @@ module ActionController
def multipart_requestify(params, first=true)
returning Hash.new do |p|
params.each do |key, value|
k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]"
k = first ? key.to_s : "[#{key.to_s}]"
if Hash === value
multipart_requestify(value, false).each do |subkey, subvalue|
p[k + subkey] = subvalue

View File

@@ -7,7 +7,6 @@ use "ActionController::Failsafe"
use lambda { ActionController::Base.session_store },
lambda { ActionController::Base.session_options }
use "ActionController::RewindableInput"
use "ActionController::ParamsParser"
use "Rack::MethodOverride"
use "Rack::Head"

View File

@@ -47,6 +47,8 @@ module ActionController
false
end
rescue Exception => e # YAML, XML or Ruby code block errors
logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
raise
{ "body" => request.raw_post,
"content_type" => request.content_type,
@@ -67,5 +69,9 @@ module ActionController
nil
end
def logger
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
end
end
end

View File

@@ -1,14 +1,54 @@
require 'thread'
module ActionController
class Reloader
def initialize(app)
@app = app
@@default_lock = Mutex.new
cattr_accessor :default_lock
class BodyWrapper
def initialize(body, lock)
@body = body
@lock = lock
end
def close
@body.close if @body.respond_to?(:close)
ensure
Dispatcher.cleanup_application
@lock.unlock
end
def method_missing(*args, &block)
@body.send(*args, &block)
end
def respond_to?(symbol, include_private = false)
symbol == :close || @body.respond_to?(symbol, include_private)
end
end
def call(env)
Dispatcher.reload_application
@app.call(env)
ensure
Dispatcher.cleanup_application
def self.run(lock = @@default_lock)
lock.lock
begin
Dispatcher.reload_application
status, headers, body = yield
# We do not want to call 'cleanup_application' in an ensure block
# because the returned Rack response body may lazily generate its data. This
# is for example the case if one calls
#
# render :text => lambda { ... code here which refers to application models ... }
#
# in an ActionController.
#
# Instead, we will want to cleanup the application code after the request is
# completely finished. So we wrap the body in a BodyWrapper class so that
# when the Rack handler calls #close during the end of the request, we get to
# run our cleanup code.
[status, headers, BodyWrapper.new(body, lock)]
rescue Exception
lock.unlock
raise
end
end
end
end

View File

@@ -95,6 +95,10 @@ module ActionController
end
end
def media_type
content_type.to_s
end
# Returns the accepted MIME type for the request.
def accepts
@accepts ||= begin
@@ -383,7 +387,7 @@ EOM
alias_method :params, :parameters
def path_parameters=(parameters) #:nodoc:
@env["rack.routing_args"] = parameters
@env["action_controller.request.path_parameters"] = parameters
@symbolized_path_parameters = @parameters = nil
end
@@ -399,7 +403,7 @@ EOM
#
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
def path_parameters
@env["rack.routing_args"] ||= {}
@env["action_controller.request.path_parameters"] ||= {}
end
# The request body is an IO input stream. If the RAW_POST_DATA environment

View File

@@ -81,12 +81,13 @@ module ActionController #:nodoc:
# Returns true or false if a request is verified. Checks:
#
# * is the format restricted? By default, only HTML and AJAX requests are checked.
# * is the format restricted? By default, only HTML requests are checked.
# * is it a GET request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
def verified_request?
!protect_against_forgery? ||
request.method == :get ||
request.xhr? ||
!verifiable_request_format? ||
form_authenticity_token == params[request_forgery_protection_token]
end

View File

@@ -317,9 +317,10 @@ module ActionController
# notes.resources :attachments
# end
#
# * <tt>:path_names</tt> - Specify different names for the 'new' and 'edit' actions. For example:
# * <tt>:path_names</tt> - Specify different path names for the actions. For example:
# # new_products_path == '/productos/nuevo'
# map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' }
# # bids_product_path(1) == '/productos/1/licitacoes'
# map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
#
# You can also set default action names from an environment, like this:
# config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
@@ -525,16 +526,16 @@ module ActionController
resource = Resource.new(entities, options)
with_options :controller => resource.controller do |map|
map_collection_actions(map, resource)
map_default_collection_actions(map, resource)
map_new_actions(map, resource)
map_member_actions(map, resource)
map_associations(resource, options)
if block_given?
with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
end
map_collection_actions(map, resource)
map_default_collection_actions(map, resource)
map_new_actions(map, resource)
map_member_actions(map, resource)
end
end
@@ -542,16 +543,16 @@ module ActionController
resource = SingletonResource.new(entities, options)
with_options :controller => resource.controller do |map|
map_collection_actions(map, resource)
map_new_actions(map, resource)
map_member_actions(map, resource)
map_default_singleton_actions(map, resource)
map_associations(resource, options)
if block_given?
with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
end
map_collection_actions(map, resource)
map_new_actions(map, resource)
map_member_actions(map, resource)
map_default_singleton_actions(map, resource)
end
end
@@ -586,7 +587,10 @@ module ActionController
resource.collection_methods.each do |method, actions|
actions.each do |action|
[method].flatten.each do |m|
map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
action_path ||= action
map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
end
end
end

View File

@@ -151,8 +151,8 @@ module ActionController # :nodoc:
if @body.respond_to?(:call)
@writer = lambda { |x| callback.call(x) }
@body.call(self, self)
elsif @body.is_a?(String)
@body.each_line(&callback)
elsif @body.respond_to?(:to_str)
yield @body
else
@body.each(&callback)
end
@@ -166,6 +166,12 @@ module ActionController # :nodoc:
str
end
def flush #:nodoc:
ActiveSupport::Deprecation.warn(
'Calling output.flush is no longer needed for streaming output ' +
'because ActionController::Response automatically handles it', caller)
end
def set_cookie(key, value)
if value.has_key?(:http_only)
ActiveSupport::Deprecation.warn(

View File

@@ -1,28 +0,0 @@
module ActionController
class RewindableInput
class RewindableIO < ActiveSupport::BasicObject
def initialize(io)
@io = io
@rewindable = io.is_a?(::StringIO)
end
def method_missing(method, *args, &block)
unless @rewindable
@io = ::StringIO.new(@io.read)
@rewindable = true
end
@io.__send__(method, *args, &block)
end
end
def initialize(app)
@app = app
end
def call(env)
env['rack.input'] = RewindableIO.new(env['rack.input'])
@app.call(env)
end
end
end

View File

@@ -271,6 +271,9 @@ module ActionController
ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
mattr_accessor :generate_best_match
self.generate_best_match = true
# The root paths which may contain controller files
mattr_accessor :controller_paths
self.controller_paths = []

View File

@@ -305,6 +305,7 @@ module ActionController
end
def add_route(path, options = {})
options.each { |k, v| options[k] = v.to_s if [:controller, :action].include?(k) && v.is_a?(Symbol) }
route = builder.build(path, options)
routes << route
route
@@ -404,11 +405,14 @@ module ActionController
end
# don't use the recalled keys when determining which routes to check
routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }]
future_routes, deprecated_routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }]
routes = Routing.generate_best_match ? deprecated_routes : future_routes
routes.each do |route|
routes.each_with_index do |route, index|
results = route.__send__(method, options, merged, expire_on)
return results if results && (!results.is_a?(Array) || results.first)
if results && (!results.is_a?(Array) || results.first)
return results
end
end
end
@@ -436,7 +440,7 @@ module ActionController
def recognize(request)
params = recognize_path(request.path, extract_request_environment(request))
request.path_parameters = params.with_indifferent_access
"#{params[:controller].camelize}Controller".constantize
"#{params[:controller].to_s.camelize}Controller".constantize
end
def recognize_path(path, environment={})
@@ -447,7 +451,10 @@ module ActionController
@routes_by_controller ||= Hash.new do |controller_hash, controller|
controller_hash[controller] = Hash.new do |action_hash, action|
action_hash[action] = Hash.new do |key_hash, keys|
key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys)
key_hash[keys] = [
routes_for_controller_and_action_and_keys(controller, action, keys),
deprecated_routes_for_controller_and_action_and_keys(controller, action, keys)
]
end
end
end
@@ -459,10 +466,11 @@ module ActionController
merged = options if expire_on[:controller]
action = merged[:action] || 'index'
routes_by_controller[controller][action][merged.keys]
routes_by_controller[controller][action][merged.keys][1]
end
def routes_for_controller_and_action(controller, action)
ActiveSupport::Deprecation.warn "routes_for_controller_and_action() has been deprecated. Please use routes_for()"
selected = routes.select do |route|
route.matches_controller_and_action? controller, action
end
@@ -470,6 +478,12 @@ module ActionController
end
def routes_for_controller_and_action_and_keys(controller, action, keys)
routes.select do |route|
route.matches_controller_and_action? controller, action
end
end
def deprecated_routes_for_controller_and_action_and_keys(controller, action, keys)
selected = routes.select do |route|
route.matches_controller_and_action? controller, action
end

View File

@@ -1,3 +1,5 @@
require 'active_support/core_ext/string/bytesize'
module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
# instead of rendering.
@@ -137,7 +139,7 @@ module ActionController #:nodoc:
# instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
logger.info "Sending data #{options[:filename]}" if logger
send_file_headers! options.merge(:length => data.size)
send_file_headers! options.merge(:length => data.bytesize)
@performed_render = false
render :status => options[:status], :text => data
end
@@ -161,7 +163,7 @@ module ActionController #:nodoc:
content_type = content_type.to_s.strip # fixes a problem with extra '\r' with some browsers
headers.merge!(
'Content-Length' => options[:length],
'Content-Length' => options[:length].to_s,
'Content-Type' => content_type,
'Content-Disposition' => disposition,
'Content-Transfer-Encoding' => 'binary'

View File

@@ -1,3 +1,4 @@
require 'rack/session/abstract/id'
module ActionController #:nodoc:
class TestRequest < Request #:nodoc:
attr_accessor :cookies, :session_options
@@ -13,6 +14,8 @@ module ActionController #:nodoc:
@query_parameters = {}
@session = TestSession.new
default_rack_options = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
@session_options ||= {:id => generate_sid(default_rack_options[:sidbits])}.merge(default_rack_options)
initialize_default_values
initialize_containers
@@ -110,6 +113,7 @@ module ActionController #:nodoc:
end
def recycle!
@env["action_controller.request.request_parameters"] = {}
self.query_parameters = {}
self.path_parameters = {}
@headers, @request_method, @accepts, @content_type = nil, nil, nil, nil
@@ -120,6 +124,10 @@ module ActionController #:nodoc:
end
private
def generate_sid(sidbits)
"%0#{sidbits / 4}x" % rand(2**sidbits - 1)
end
def initialize_containers
@cookies = {}
end
@@ -250,7 +258,7 @@ module ActionController #:nodoc:
def cookies
cookies = {}
Array(headers['Set-Cookie']).each do |cookie|
key, value = cookie.split(";").first.split("=")
key, value = cookie.split(";").first.split("=").map {|val| Rack::Utils.unescape(val)}
cookies[key] = value
end
cookies

View File

@@ -184,7 +184,7 @@ module ActionController
path = rewrite_path(options)
rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
rewritten_url << "##{options[:anchor]}" if options[:anchor]
rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
rewritten_url
end

View File

@@ -1,89 +0,0 @@
# Copyright (C) 2007, 2008, 2009 Christian Neukirchen <purl.org/net/chneukirchen>
#
# Rack is freely distributable under the terms of an MIT-style license.
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
$:.unshift(File.expand_path(File.dirname(__FILE__)))
# The Rack main module, serving as a namespace for all core Rack
# modules and classes.
#
# All modules meant for use in your application are <tt>autoload</tt>ed here,
# so it should be enough just to <tt>require rack.rb</tt> in your code.
module Rack
# The Rack protocol version number implemented.
VERSION = [0,1]
# Return the Rack protocol version as a dotted string.
def self.version
VERSION.join(".")
end
# Return the Rack release as a dotted string.
def self.release
"1.0 bundled"
end
autoload :Builder, "rack/builder"
autoload :Cascade, "rack/cascade"
autoload :Chunked, "rack/chunked"
autoload :CommonLogger, "rack/commonlogger"
autoload :ConditionalGet, "rack/conditionalget"
autoload :ContentLength, "rack/content_length"
autoload :ContentType, "rack/content_type"
autoload :File, "rack/file"
autoload :Deflater, "rack/deflater"
autoload :Directory, "rack/directory"
autoload :ForwardRequest, "rack/recursive"
autoload :Handler, "rack/handler"
autoload :Head, "rack/head"
autoload :Lint, "rack/lint"
autoload :Lock, "rack/lock"
autoload :MethodOverride, "rack/methodoverride"
autoload :Mime, "rack/mime"
autoload :Recursive, "rack/recursive"
autoload :Reloader, "rack/reloader"
autoload :ShowExceptions, "rack/showexceptions"
autoload :ShowStatus, "rack/showstatus"
autoload :Static, "rack/static"
autoload :URLMap, "rack/urlmap"
autoload :Utils, "rack/utils"
autoload :MockRequest, "rack/mock"
autoload :MockResponse, "rack/mock"
autoload :Request, "rack/request"
autoload :Response, "rack/response"
module Auth
autoload :Basic, "rack/auth/basic"
autoload :AbstractRequest, "rack/auth/abstract/request"
autoload :AbstractHandler, "rack/auth/abstract/handler"
autoload :OpenID, "rack/auth/openid"
module Digest
autoload :MD5, "rack/auth/digest/md5"
autoload :Nonce, "rack/auth/digest/nonce"
autoload :Params, "rack/auth/digest/params"
autoload :Request, "rack/auth/digest/request"
end
end
module Session
autoload :Cookie, "rack/session/cookie"
autoload :Pool, "rack/session/pool"
autoload :Memcache, "rack/session/memcache"
end
# *Adapters* connect Rack with third party web frameworks.
#
# Rack includes an adapter for Camping, see README for other
# frameworks supporting Rack in their code bases.
#
# Refer to the submodules for framework-specific calling details.
module Adapter
autoload :Camping, "rack/adapter/camping"
end
end

View File

@@ -1,22 +0,0 @@
module Rack
module Adapter
class Camping
def initialize(app)
@app = app
end
def call(env)
env["PATH_INFO"] ||= ""
env["SCRIPT_NAME"] ||= ""
controller = @app.run(env['rack.input'], env)
h = controller.headers
h.each_pair do |k,v|
if v.kind_of? URI
h[k] = v.to_s
end
end
[controller.status, controller.headers, [controller.body.to_s]]
end
end
end
end

View File

@@ -1,37 +0,0 @@
module Rack
module Auth
# Rack::Auth::AbstractHandler implements common authentication functionality.
#
# +realm+ should be set for all handlers.
class AbstractHandler
attr_accessor :realm
def initialize(app, realm=nil, &authenticator)
@app, @realm, @authenticator = app, realm, authenticator
end
private
def unauthorized(www_authenticate = challenge)
return [ 401,
{ 'Content-Type' => 'text/plain',
'Content-Length' => '0',
'WWW-Authenticate' => www_authenticate.to_s },
[]
]
end
def bad_request
return [ 400,
{ 'Content-Type' => 'text/plain',
'Content-Length' => '0' },
[]
]
end
end
end
end

View File

@@ -1,37 +0,0 @@
module Rack
module Auth
class AbstractRequest
def initialize(env)
@env = env
end
def provided?
!authorization_key.nil?
end
def parts
@parts ||= @env[authorization_key].split(' ', 2)
end
def scheme
@scheme ||= parts.first.downcase.to_sym
end
def params
@params ||= parts.last
end
private
AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
def authorization_key
@authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) }
end
end
end
end

View File

@@ -1,58 +0,0 @@
require 'rack/auth/abstract/handler'
require 'rack/auth/abstract/request'
module Rack
module Auth
# Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617.
#
# Initialize with the Rack application that you want protecting,
# and a block that checks if a username and password pair are valid.
#
# See also: <tt>example/protectedlobster.rb</tt>
class Basic < AbstractHandler
def call(env)
auth = Basic::Request.new(env)
return unauthorized unless auth.provided?
return bad_request unless auth.basic?
if valid?(auth)
env['REMOTE_USER'] = auth.username
return @app.call(env)
end
unauthorized
end
private
def challenge
'Basic realm="%s"' % realm
end
def valid?(auth)
@authenticator.call(*auth.credentials)
end
class Request < Auth::AbstractRequest
def basic?
:basic == scheme
end
def credentials
@credentials ||= params.unpack("m*").first.split(/:/, 2)
end
def username
credentials.first
end
end
end
end
end

View File

@@ -1,124 +0,0 @@
require 'rack/auth/abstract/handler'
require 'rack/auth/digest/request'
require 'rack/auth/digest/params'
require 'rack/auth/digest/nonce'
require 'digest/md5'
module Rack
module Auth
module Digest
# Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
# HTTP Digest Authentication, as per RFC 2617.
#
# Initialize with the [Rack] application that you want protecting,
# and a block that looks up a plaintext password for a given username.
#
# +opaque+ needs to be set to a constant base64/hexadecimal string.
#
class MD5 < AbstractHandler
attr_accessor :opaque
attr_writer :passwords_hashed
def initialize(*args)
super
@passwords_hashed = nil
end
def passwords_hashed?
!!@passwords_hashed
end
def call(env)
auth = Request.new(env)
unless auth.provided?
return unauthorized
end
if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
return bad_request
end
if valid?(auth)
if auth.nonce.stale?
return unauthorized(challenge(:stale => true))
else
env['REMOTE_USER'] = auth.username
return @app.call(env)
end
end
unauthorized
end
private
QOP = 'auth'.freeze
def params(hash = {})
Params.new do |params|
params['realm'] = realm
params['nonce'] = Nonce.new.to_s
params['opaque'] = H(opaque)
params['qop'] = QOP
hash.each { |k, v| params[k] = v }
end
end
def challenge(hash = {})
"Digest #{params(hash)}"
end
def valid?(auth)
valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
end
def valid_qop?(auth)
QOP == auth.qop
end
def valid_opaque?(auth)
H(opaque) == auth.opaque
end
def valid_nonce?(auth)
auth.nonce.valid?
end
def valid_digest?(auth)
digest(auth, @authenticator.call(auth.username)) == auth.response
end
def md5(data)
::Digest::MD5.hexdigest(data)
end
alias :H :md5
def KD(secret, data)
H([secret, data] * ':')
end
def A1(auth, password)
[ auth.username, auth.realm, password ] * ':'
end
def A2(auth)
[ auth.method, auth.uri ] * ':'
end
def digest(auth, password)
password_hash = passwords_hashed? ? password : H(A1(auth, password))
KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':')
end
end
end
end
end

View File

@@ -1,51 +0,0 @@
require 'digest/md5'
module Rack
module Auth
module Digest
# Rack::Auth::Digest::Nonce is the default nonce generator for the
# Rack::Auth::Digest::MD5 authentication handler.
#
# +private_key+ needs to set to a constant string.
#
# +time_limit+ can be optionally set to an integer (number of seconds),
# to limit the validity of the generated nonces.
class Nonce
class << self
attr_accessor :private_key, :time_limit
end
def self.parse(string)
new(*string.unpack("m*").first.split(' ', 2))
end
def initialize(timestamp = Time.now, given_digest = nil)
@timestamp, @given_digest = timestamp.to_i, given_digest
end
def to_s
[([ @timestamp, digest ] * ' ')].pack("m*").strip
end
def digest
::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':')
end
def valid?
digest == @given_digest
end
def stale?
!self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit
end
def fresh?
!stale?
end
end
end
end
end

View File

@@ -1,55 +0,0 @@
module Rack
module Auth
module Digest
class Params < Hash
def self.parse(str)
split_header_value(str).inject(new) do |header, param|
k, v = param.split('=', 2)
header[k] = dequote(v)
header
end
end
def self.dequote(str) # From WEBrick::HTTPUtils
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
ret.gsub!(/\\(.)/, "\\1")
ret
end
def self.split_header_value(str)
str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] }
end
def initialize
super
yield self if block_given?
end
def [](k)
super k.to_s
end
def []=(k, v)
super k.to_s, v.to_s
end
UNQUOTED = ['qop', 'nc', 'stale']
def to_s
inject([]) do |parts, (k, v)|
parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
parts
end.join(', ')
end
def quote(str) # From WEBrick::HTTPUtils
'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
end
end
end
end
end

View File

@@ -1,40 +0,0 @@
require 'rack/auth/abstract/request'
require 'rack/auth/digest/params'
require 'rack/auth/digest/nonce'
module Rack
module Auth
module Digest
class Request < Auth::AbstractRequest
def method
@env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
end
def digest?
:digest == scheme
end
def correct_uri?
(@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri
end
def nonce
@nonce ||= Nonce.parse(params['nonce'])
end
def params
@params ||= Params.parse(parts.last)
end
def method_missing(sym)
if params.has_key? key = sym.to_s
return params[key]
end
super
end
end
end
end
end

View File

@@ -1,480 +0,0 @@
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
gem 'ruby-openid', '~> 2' if defined? Gem
require 'rack/request'
require 'rack/utils'
require 'rack/auth/abstract/handler'
require 'uri'
require 'openid' #gem
require 'openid/extension' #gem
require 'openid/store/memory' #gem
module Rack
class Request
def openid_request
@env['rack.auth.openid.request']
end
def openid_response
@env['rack.auth.openid.response']
end
end
module Auth
# Rack::Auth::OpenID provides a simple method for setting up an OpenID
# Consumer. It requires the ruby-openid library from janrain to operate,
# as well as a rack method of session management.
#
# The ruby-openid home page is at http://openidenabled.com/ruby-openid/.
#
# The OpenID specifications can be found at
# http://openid.net/specs/openid-authentication-1_1.html
# and
# http://openid.net/specs/openid-authentication-2_0.html. Documentation
# for published OpenID extensions and related topics can be found at
# http://openid.net/developers/specs/.
#
# It is recommended to read through the OpenID spec, as well as
# ruby-openid's documentation, to understand what exactly goes on. However
# a setup as simple as the presented examples is enough to provide
# Consumer functionality.
#
# This library strongly intends to utilize the OpenID 2.0 features of the
# ruby-openid library, which provides OpenID 1.0 compatiblity.
#
# NOTE: Due to the amount of data that this library stores in the
# session, Rack::Session::Cookie may fault.
class OpenID
class NoSession < RuntimeError; end
class BadExtension < RuntimeError; end
# Required for ruby-openid
ValidStatus = [:success, :setup_needed, :cancel, :failure]
# = Arguments
#
# The first argument is the realm, identifying the site they are trusting
# with their identity. This is required, also treated as the trust_root
# in OpenID 1.x exchanges.
#
# The optional second argument is a hash of options.
#
# == Options
#
# <tt>:return_to</tt> defines the url to return to after the client
# authenticates with the openid service provider. This url should point
# to where Rack::Auth::OpenID is mounted. If <tt>:return_to</tt> is not
# provided, return_to will be the current url which allows flexibility
# with caveats.
#
# <tt>:session_key</tt> defines the key to the session hash in the env.
# It defaults to 'rack.session'.
#
# <tt>:openid_param</tt> defines at what key in the request parameters to
# find the identifier to resolve. As per the 2.0 spec, the default is
# 'openid_identifier'.
#
# <tt>:store</tt> defined what OpenID Store to use for persistant
# information. By default a Store::Memory will be used.
#
# <tt>:immediate</tt> as true will make initial requests to be of an
# immediate type. This is false by default. See OpenID specification
# documentation.
#
# <tt>:extensions</tt> should be a hash of openid extension
# implementations. The key should be the extension main module, the value
# should be an array of arguments for extension::Request.new.
# The hash is iterated over and passed to #add_extension for processing.
# Please see #add_extension for further documentation.
#
# == Examples
#
# simple_oid = OpenID.new('http://mysite.com/')
#
# return_oid = OpenID.new('http://mysite.com/', {
# :return_to => 'http://mysite.com/openid'
# })
#
# complex_oid = OpenID.new('http://mysite.com/',
# :immediate => true,
# :extensions => {
# ::OpenID::SReg => [['email'],['nickname']]
# }
# )
#
# = Advanced
#
# Most of the functionality of this library is encapsulated such that
# expansion and overriding functions isn't difficult nor tricky.
# Alternately, to avoid opening up singleton objects or subclassing, a
# wrapper rack middleware can be composed to act upon Auth::OpenID's
# responses. See #check and #finish for locations of pertinent data.
#
# == Responses
#
# To change the responses that Auth::OpenID returns, override the methods
# #redirect, #bad_request, #unauthorized, #access_denied, and
# #foreign_server_failure.
#
# Additionally #confirm_post_params is used when the URI would exceed
# length limits on a GET request when doing the initial verification
# request.
#
# == Processing
#
# To change methods of processing completed transactions, override the
# methods #success, #setup_needed, #cancel, and #failure. Please ensure
# the returned object is a rack compatible response.
#
# The first argument is an OpenID::Response, the second is a
# Rack::Request of the current request, the last is the hash used in
# ruby-openid handling, which can be found manually at
# env['rack.session'][:openid].
#
# This is useful if you wanted to expand the processing done, such as
# setting up user accounts.
#
# oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to
# def oid_app.success oid, request, session
# user = Models::User[oid.identity_url]
# user ||= Models::User.create_from_openid oid
# request['rack.session'][:user] = user.id
# redirect MyApp.site_home
# end
#
# site_map['/openid'] = oid_app
# map = Rack::URLMap.new site_map
# ...
def initialize(realm, options={})
realm = URI(realm)
raise ArgumentError, "Invalid realm: #{realm}" \
unless realm.absolute? \
and realm.fragment.nil? \
and realm.scheme =~ /^https?$/ \
and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/
realm.path = '/' if realm.path.empty?
@realm = realm.to_s
if ruri = options[:return_to]
ruri = URI(ruri)
raise ArgumentError, "Invalid return_to: #{ruri}" \
unless ruri.absolute? \
and ruri.scheme =~ /^https?$/ \
and ruri.fragment.nil?
raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \
unless self.within_realm?(ruri)
@return_to = ruri.to_s
end
@session_key = options[:session_key] || 'rack.session'
@openid_param = options[:openid_param] || 'openid_identifier'
@store = options[:store] || ::OpenID::Store::Memory.new
@immediate = !!options[:immediate]
@extensions = {}
if extensions = options.delete(:extensions)
extensions.each do |ext, args|
add_extension ext, *args
end
end
# Undocumented, semi-experimental
@anonymous = !!options[:anonymous]
end
attr_reader :realm, :return_to, :session_key, :openid_param, :store,
:immediate, :extensions
# Sets up and uses session data at <tt>:openid</tt> within the session.
# Errors in this setup will raise a NoSession exception.
#
# If the parameter 'openid.mode' is set, which implies a followup from
# the openid server, processing is passed to #finish and the result is
# returned. However, if there is no appropriate openid information in the
# session, a 400 error is returned.
#
# If the parameter specified by <tt>options[:openid_param]</tt> is
# present, processing is passed to #check and the result is returned.
#
# If neither of these conditions are met, #unauthorized is called.
def call(env)
env['rack.auth.openid'] = self
env_session = env[@session_key]
unless env_session and env_session.is_a?(Hash)
raise NoSession, 'No compatible session'
end
# let us work in our own namespace...
session = (env_session[:openid] ||= {})
unless session and session.is_a?(Hash)
raise NoSession, 'Incompatible openid session'
end
request = Rack::Request.new(env)
consumer = ::OpenID::Consumer.new(session, @store)
if mode = request.GET['openid.mode']
if session.key?(:openid_param)
finish(consumer, session, request)
else
bad_request
end
elsif request.GET[@openid_param]
check(consumer, session, request)
else
unauthorized
end
end
# As the first part of OpenID consumer action, #check retrieves the data
# required for completion.
#
# If all parameters fit within the max length of a URI, a 303 redirect
# will be returned. Otherwise #confirm_post_params will be called.
#
# Any messages from OpenID's request are logged to env['rack.errors']
#
# <tt>env['rack.auth.openid.request']</tt> is the openid checkid request
# instance.
#
# <tt>session[:openid_param]</tt> is set to the openid identifier
# provided by the user.
#
# <tt>session[:return_to]</tt> is set to the return_to uri given to the
# identity provider.
def check(consumer, session, req)
oid = consumer.begin(req.GET[@openid_param], @anonymous)
req.env['rack.auth.openid.request'] = oid
req.env['rack.errors'].puts(oid.message)
p oid if $DEBUG
## Extension support
extensions.each do |ext,args|
oid.add_extension(ext::Request.new(*args))
end
session[:openid_param] = req.GET[openid_param]
return_to_uri = return_to ? return_to : req.url
session[:return_to] = return_to_uri
immediate = session.key?(:setup_needed) ? false : immediate
if oid.send_redirect?(realm, return_to_uri, immediate)
uri = oid.redirect_url(realm, return_to_uri, immediate)
redirect(uri)
else
confirm_post_params(oid, realm, return_to_uri, immediate)
end
rescue ::OpenID::DiscoveryFailure => e
# thrown from inside OpenID::Consumer#begin by yadis stuff
req.env['rack.errors'].puts([e.message, *e.backtrace]*"\n")
return foreign_server_failure
end
# This is the final portion of authentication.
# If successful, a redirect to the realm is be returned.
# Data gathered from extensions are stored in session[:openid] with the
# extension's namespace uri as the key.
#
# Any messages from OpenID's response are logged to env['rack.errors']
#
# <tt>env['rack.auth.openid.response']</tt> will contain the openid
# response.
def finish(consumer, session, req)
oid = consumer.complete(req.GET, req.url)
req.env['rack.auth.openid.response'] = oid
req.env['rack.errors'].puts(oid.message)
p oid if $DEBUG
raise unless ValidStatus.include?(oid.status)
__send__(oid.status, oid, req, session)
end
# The first argument should be the main extension module.
# The extension module should contain the constants:
# * class Request, should have OpenID::Extension as an ancestor
# * class Response, should have OpenID::Extension as an ancestor
# * string NS_URI, which defining the namespace of the extension
#
# All trailing arguments will be passed to extension::Request.new in
# #check.
# The openid response will be passed to
# extension::Response#from_success_response, #get_extension_args will be
# called on the result to attain the gathered data.
#
# This method returns the key at which the response data will be found in
# the session, which is the namespace uri by default.
def add_extension(ext, *args)
raise BadExtension unless valid_extension?(ext)
extensions[ext] = args
return ext::NS_URI
end
# Checks the validitity, in the context of usage, of a submitted
# extension.
def valid_extension?(ext)
if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) }
raise ArgumentError, 'Extension is missing constants.'
elsif not ext::Response.respond_to?(:from_success_response)
raise ArgumentError, 'Response is missing required method.'
end
return true
rescue
return false
end
# Checks the provided uri to ensure it'd be considered within the realm.
# is currently not compatible with wildcard realms.
def within_realm? uri
uri = URI.parse(uri.to_s)
realm = URI.parse(self.realm)
return false unless uri.absolute?
return false unless uri.path[0, realm.path.size] == realm.path
return false unless uri.host == realm.host or realm.host[/^\*\./]
# for wildcard support, is awkward with URI limitations
realm_match = Regexp.escape(realm.host).
sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$'
return false unless uri.host.match(realm_match)
return true
end
alias_method :include?, :within_realm?
protected
### These methods define some of the boilerplate responses.
# Returns an html form page for posting to an Identity Provider if the
# GET request would exceed the upper URI length limit.
def confirm_post_params(oid, realm, return_to, immediate)
Rack::Response.new.finish do |r|
r.write '<html><head><title>Confirm...</title></head><body>'
r.write oid.form_markup(realm, return_to, immediate)
r.write '</body></html>'
end
end
# Returns a 303 redirect with the destination of that provided by the
# argument.
def redirect(uri)
[ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain',
'Location' => uri},
[] ]
end
# Returns an empty 400 response.
def bad_request
[ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'},
[''] ]
end
# Returns a basic unauthorized 401 response.
def unauthorized
[ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'},
['Unauthorized.'] ]
end
# Returns a basic access denied 403 response.
def access_denied
[ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'},
['Access denied.'] ]
end
# Returns a 503 response to be used if communication with the remote
# OpenID server fails.
def foreign_server_failure
[ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'},
['Foreign server failure.'] ]
end
private
### These methods are called after a transaction is completed, depending
# on its outcome. These should all return a rack compatible response.
# You'd want to override these to provide additional functionality.
# Called to complete processing on a successful transaction.
# Within the openid session, :openid_identity and :openid_identifier are
# set to the user friendly and the standard representation of the
# validated identity. All other data in the openid session is cleared.
def success(oid, request, session)
session.clear
session[:openid_identity] = oid.display_identifier
session[:openid_identifier] = oid.identity_url
extensions.keys.each do |ext|
label = ext.name[/[^:]+$/].downcase
response = ext::Response.from_success_response(oid)
session[label] = response.data
end
redirect(realm)
end
# Called if the Identity Provider indicates further setup by the user is
# required.
# The identifier is retrived from the openid session at :openid_param.
# And :setup_needed is set to true to prevent looping.
def setup_needed(oid, request, session)
identifier = session[:openid_param]
session[:setup_needed] = true
redirect req.script_name + '?' + openid_param + '=' + identifier
end
# Called if the user indicates they wish to cancel identification.
# Data within openid session is cleared.
def cancel(oid, request, session)
session.clear
access_denied
end
# Called if the Identity Provider indicates the user is unable to confirm
# their identity. Data within the openid session is left alone, in case
# of swarm auth attacks.
def failure(oid, request, session)
unauthorized
end
end
# A class developed out of the request to use OpenID as an authentication
# middleware. The request will be sent to the OpenID instance unless the
# block evaluates to true. For example in rackup, you can use it as such:
#
# use Rack::Session::Pool
# use Rack::Auth::OpenIDAuth, realm, openid_options do |env|
# env['rack.session'][:authkey] == a_string
# end
# run RackApp
#
# Or simply:
#
# app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth
class OpenIDAuth < Rack::Auth::AbstractHandler
attr_reader :oid
def initialize(app, realm, options={}, &auth)
@oid = OpenID.new(realm, options)
super(app, &auth)
end
def call(env)
to = auth.call(env) ? @app : @oid
to.call env
end
end
end
end

View File

@@ -1,63 +0,0 @@
module Rack
# Rack::Builder implements a small DSL to iteratively construct Rack
# applications.
#
# Example:
#
# app = Rack::Builder.new {
# use Rack::CommonLogger
# use Rack::ShowExceptions
# map "/lobster" do
# use Rack::Lint
# run Rack::Lobster.new
# end
# }
#
# Or
#
# app = Rack::Builder.app do
# use Rack::CommonLogger
# lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] }
# end
#
# +use+ adds a middleware to the stack, +run+ dispatches to an application.
# You can use +map+ to construct a Rack::URLMap in a convenient way.
class Builder
def initialize(&block)
@ins = []
instance_eval(&block) if block_given?
end
def self.app(&block)
self.new(&block).to_app
end
def use(middleware, *args, &block)
@ins << lambda { |app| middleware.new(app, *args, &block) }
end
def run(app)
@ins << app #lambda { |nothing| app }
end
def map(path, &block)
if @ins.last.kind_of? Hash
@ins.last[path] = self.class.new(&block).to_app
else
@ins << {}
map(path, &block)
end
end
def to_app
@ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last
inner_app = @ins.last
@ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) }
end
def call(env)
to_app.call(env)
end
end
end

View File

@@ -1,36 +0,0 @@
module Rack
# Rack::Cascade tries an request on several apps, and returns the
# first response that is not 404 (or in a list of configurable
# status codes).
class Cascade
attr_reader :apps
def initialize(apps, catch=404)
@apps = apps
@catch = [*catch]
end
def call(env)
status = headers = body = nil
raise ArgumentError, "empty cascade" if @apps.empty?
@apps.each { |app|
begin
status, headers, body = app.call(env)
break unless @catch.include?(status.to_i)
end
}
[status, headers, body]
end
def add app
@apps << app
end
def include? app
@apps.include? app
end
alias_method :<<, :add
end
end

View File

@@ -1,49 +0,0 @@
require 'rack/utils'
module Rack
# Middleware that applies chunked transfer encoding to response bodies
# when the response does not include a Content-Length header.
class Chunked
include Rack::Utils
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers = HeaderHash.new(headers)
if env['HTTP_VERSION'] == 'HTTP/1.0' ||
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
headers['Content-Length'] ||
headers['Transfer-Encoding']
[status, headers.to_hash, body]
else
dup.chunk(status, headers, body)
end
end
def chunk(status, headers, body)
@body = body
headers.delete('Content-Length')
headers['Transfer-Encoding'] = 'chunked'
[status, headers.to_hash, self]
end
def each
term = "\r\n"
@body.each do |chunk|
size = bytesize(chunk)
next if size == 0
yield [size.to_s(16), term, chunk, term].join
end
yield ["0", term, "", term].join
end
def close
@body.close if @body.respond_to?(:close)
end
end
end

View File

@@ -1,61 +0,0 @@
module Rack
# Rack::CommonLogger forwards every request to an +app+ given, and
# logs a line in the Apache common log format to the +logger+, or
# rack.errors by default.
class CommonLogger
def initialize(app, logger=nil)
@app = app
@logger = logger
end
def call(env)
dup._call(env)
end
def _call(env)
@env = env
@logger ||= self
@time = Time.now
@status, @header, @body = @app.call(env)
[@status, @header, self]
end
def close
@body.close if @body.respond_to? :close
end
# By default, log to rack.errors.
def <<(str)
@env["rack.errors"].write(str)
@env["rack.errors"].flush
end
def each
length = 0
@body.each { |part|
length += part.size
yield part
}
@now = Time.now
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
# lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
@logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} %
[
@env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-",
@env["REMOTE_USER"] || "-",
@now.strftime("%d/%b/%Y %H:%M:%S"),
@env["REQUEST_METHOD"],
@env["PATH_INFO"],
@env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"],
@env["HTTP_VERSION"],
@status.to_s[0..3],
(length.zero? ? "-" : length.to_s),
@now - @time
]
end
end
end

View File

@@ -1,45 +0,0 @@
require 'rack/utils'
module Rack
# Middleware that enables conditional GET using If-None-Match and
# If-Modified-Since. The application should set either or both of the
# Last-Modified or Etag response headers according to RFC 2616. When
# either of the conditions is met, the response body is set to be zero
# length and the response status is set to 304 Not Modified.
#
# Applications that defer response body generation until the body's each
# message is received will avoid response body generation completely when
# a conditional GET matches.
#
# Adapted from Michael Klishin's Merb implementation:
# http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb
class ConditionalGet
def initialize(app)
@app = app
end
def call(env)
return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD'])
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
if etag_matches?(env, headers) || modified_since?(env, headers)
status = 304
body = []
end
[status, headers, body]
end
private
def etag_matches?(env, headers)
etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH']
end
def modified_since?(env, headers)
last_modified = headers['Last-Modified'] and
last_modified == env['HTTP_IF_MODIFIED_SINCE']
end
end
end

View File

@@ -1,29 +0,0 @@
require 'rack/utils'
module Rack
# Sets the Content-Length header on responses with fixed-length bodies.
class ContentLength
include Rack::Utils
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers = HeaderHash.new(headers)
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
!headers['Content-Length'] &&
!headers['Transfer-Encoding'] &&
(body.respond_to?(:to_ary) || body.respond_to?(:to_str))
body = [body] if body.respond_to?(:to_str) # rack 0.4 compat
length = body.to_ary.inject(0) { |len, part| len + bytesize(part) }
headers['Content-Length'] = length.to_s
end
[status, headers, body]
end
end
end

View File

@@ -1,23 +0,0 @@
require 'rack/utils'
module Rack
# Sets the Content-Type header on responses which don't have one.
#
# Builder Usage:
# use Rack::ContentType, "text/plain"
#
# When no content type argument is provided, "text/html" is assumed.
class ContentType
def initialize(app, content_type = "text/html")
@app, @content_type = app, content_type
end
def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
headers['Content-Type'] ||= @content_type
[status, headers.to_hash, body]
end
end
end

View File

@@ -1,85 +0,0 @@
require "zlib"
require "stringio"
require "time" # for Time.httpdate
require 'rack/utils'
module Rack
class Deflater
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
# Skip compressing empty entity body responses and responses with
# no-transform set.
if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
headers['Cache-Control'].to_s =~ /\bno-transform\b/
return [status, headers, body]
end
request = Request.new(env)
encoding = Utils.select_best_encoding(%w(gzip deflate identity),
request.accept_encoding)
# Set the Vary HTTP header.
vary = headers["Vary"].to_s.split(",").map { |v| v.strip }
unless vary.include?("*") || vary.include?("Accept-Encoding")
headers["Vary"] = vary.push("Accept-Encoding").join(",")
end
case encoding
when "gzip"
mtime = headers.key?("Last-Modified") ?
Time.httpdate(headers["Last-Modified"]) : Time.now
body = self.class.gzip(body, mtime)
size = Rack::Utils.bytesize(body)
headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s)
[status, headers, [body]]
when "deflate"
body = self.class.deflate(body)
size = Rack::Utils.bytesize(body)
headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s)
[status, headers, [body]]
when "identity"
[status, headers, body]
when nil
message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
[406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
end
end
def self.gzip(body, mtime)
io = StringIO.new
gzip = Zlib::GzipWriter.new(io)
gzip.mtime = mtime
# TODO: Add streaming
body.each { |part| gzip << part }
gzip.close
return io.string
end
DEFLATE_ARGS = [
Zlib::DEFAULT_COMPRESSION,
# drop the zlib header which causes both Safari and IE to choke
-Zlib::MAX_WBITS,
Zlib::DEF_MEM_LEVEL,
Zlib::DEFAULT_STRATEGY
]
# Loosely based on Mongrel's Deflate handler
def self.deflate(body)
deflater = Zlib::Deflate.new(*DEFLATE_ARGS)
# TODO: Add streaming
body.each { |part| deflater << part }
return deflater.finish
end
end
end

View File

@@ -1,153 +0,0 @@
require 'time'
require 'rack/utils'
require 'rack/mime'
module Rack
# Rack::Directory serves entries below the +root+ given, according to the
# path info of the Rack request. If a directory is found, the file's contents
# will be presented in an html based index. If a file is found, the env will
# be passed to the specified +app+.
#
# If +app+ is not specified, a Rack::File of the same +root+ will be used.
class Directory
DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"
DIR_PAGE = <<-PAGE
<html><head>
<title>%s</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style type='text/css'>
table { width:100%%; }
.name { text-align:left; }
.size, .mtime { text-align:right; }
.type { width:11em; }
.mtime { width:15em; }
</style>
</head><body>
<h1>%s</h1>
<hr />
<table>
<tr>
<th class='name'>Name</th>
<th class='size'>Size</th>
<th class='type'>Type</th>
<th class='mtime'>Last Modified</th>
</tr>
%s
</table>
<hr />
</body></html>
PAGE
attr_reader :files
attr_accessor :root, :path
def initialize(root, app=nil)
@root = F.expand_path(root)
@app = app || Rack::File.new(@root)
end
def call(env)
dup._call(env)
end
F = ::File
def _call(env)
@env = env
@script_name = env['SCRIPT_NAME']
@path_info = Utils.unescape(env['PATH_INFO'])
if forbidden = check_forbidden
forbidden
else
@path = F.join(@root, @path_info)
list_path
end
end
def check_forbidden
return unless @path_info.include? ".."
body = "Forbidden\n"
size = Rack::Utils.bytesize(body)
return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
end
def list_directory
@files = [['../','Parent Directory','','','']]
glob = F.join(@path, '*')
Dir[glob].sort.each do |node|
stat = stat(node)
next unless stat
basename = F.basename(node)
ext = F.extname(node)
url = F.join(@script_name, @path_info, basename)
size = stat.size
type = stat.directory? ? 'directory' : Mime.mime_type(ext)
size = stat.directory? ? '-' : filesize_format(size)
mtime = stat.mtime.httpdate
url << '/' if stat.directory?
basename << '/' if stat.directory?
@files << [ url, basename, size, type, mtime ]
end
return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ]
end
def stat(node, max = 10)
F.stat(node)
rescue Errno::ENOENT, Errno::ELOOP
return nil
end
# TODO: add correct response if not readable, not sure if 404 is the best
# option
def list_path
@stat = F.stat(@path)
if @stat.readable?
return @app.call(@env) if @stat.file?
return list_directory if @stat.directory?
else
raise Errno::ENOENT, 'No such file or directory'
end
rescue Errno::ENOENT, Errno::ELOOP
return entity_not_found
end
def entity_not_found
body = "Entity not found: #{@path_info}\n"
size = Rack::Utils.bytesize(body)
return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
end
def each
show_path = @path.sub(/^#{@root}/,'')
files = @files.map{|f| DIR_FILE % f }*"\n"
page = DIR_PAGE % [ show_path, show_path , files ]
page.each_line{|l| yield l }
end
# Stolen from Ramaze
FILESIZE_FORMAT = [
['%.1fT', 1 << 40],
['%.1fG', 1 << 30],
['%.1fM', 1 << 20],
['%.1fK', 1 << 10],
]
def filesize_format(int)
FILESIZE_FORMAT.each do |format, size|
return format % (int.to_f / size) if int >= size
end
int.to_s + 'B'
end
end
end

View File

@@ -1,88 +0,0 @@
require 'time'
require 'rack/utils'
require 'rack/mime'
module Rack
# Rack::File serves files below the +root+ given, according to the
# path info of the Rack request.
#
# Handlers can detect if bodies are a Rack::File, and use mechanisms
# like sendfile on the +path+.
class File
attr_accessor :root
attr_accessor :path
alias :to_path :path
def initialize(root)
@root = root
end
def call(env)
dup._call(env)
end
F = ::File
def _call(env)
@path_info = Utils.unescape(env["PATH_INFO"])
return forbidden if @path_info.include? ".."
@path = F.join(@root, @path_info)
begin
if F.file?(@path) && F.readable?(@path)
serving
else
raise Errno::EPERM
end
rescue SystemCallError
not_found
end
end
def forbidden
body = "Forbidden\n"
[403, {"Content-Type" => "text/plain",
"Content-Length" => body.size.to_s},
[body]]
end
# NOTE:
# We check via File::size? whether this file provides size info
# via stat (e.g. /proc files often don't), otherwise we have to
# figure it out by reading the whole file into memory. And while
# we're at it we also use this as body then.
def serving
if size = F.size?(@path)
body = self
else
body = [F.read(@path)]
size = Utils.bytesize(body.first)
end
[200, {
"Last-Modified" => F.mtime(@path).httpdate,
"Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'),
"Content-Length" => size.to_s
}, body]
end
def not_found
body = "File not found: #{@path_info}\n"
[404, {"Content-Type" => "text/plain",
"Content-Length" => body.size.to_s},
[body]]
end
def each
F.open(@path, "rb") { |file|
while part = file.read(8192)
yield part
end
}
end
end
end

View File

@@ -1,48 +0,0 @@
module Rack
# *Handlers* connect web servers with Rack.
#
# Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI
# and LiteSpeed.
#
# Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>.
# A second optional hash can be passed to include server-specific
# configuration.
module Handler
def self.get(server)
return unless server
if klass = @handlers[server]
obj = Object
klass.split("::").each { |x| obj = obj.const_get(x) }
obj
else
Rack::Handler.const_get(server.capitalize)
end
end
def self.register(server, klass)
@handlers ||= {}
@handlers[server] = klass
end
autoload :CGI, "rack/handler/cgi"
autoload :FastCGI, "rack/handler/fastcgi"
autoload :Mongrel, "rack/handler/mongrel"
autoload :EventedMongrel, "rack/handler/evented_mongrel"
autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel"
autoload :WEBrick, "rack/handler/webrick"
autoload :LSWS, "rack/handler/lsws"
autoload :SCGI, "rack/handler/scgi"
autoload :Thin, "rack/handler/thin"
register 'cgi', 'Rack::Handler::CGI'
register 'fastcgi', 'Rack::Handler::FastCGI'
register 'mongrel', 'Rack::Handler::Mongrel'
register 'emongrel', 'Rack::Handler::EventedMongrel'
register 'smongrel', 'Rack::Handler::SwiftipliedMongrel'
register 'webrick', 'Rack::Handler::WEBrick'
register 'lsws', 'Rack::Handler::LSWS'
register 'scgi', 'Rack::Handler::SCGI'
register 'thin', 'Rack::Handler::Thin'
end
end

View File

@@ -1,61 +0,0 @@
require 'rack/content_length'
module Rack
module Handler
class CGI
def self.run(app, options=nil)
serve app
end
def self.serve(app)
app = ContentLength.new(app)
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [0,1],
"rack.input" => $stdin,
"rack.errors" => $stderr,
"rack.multithread" => false,
"rack.multiprocess" => true,
"rack.run_once" => true,
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
})
env["QUERY_STRING"] ||= ""
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["REQUEST_PATH"] ||= "/"
status, headers, body = app.call(env)
begin
send_headers status, headers
send_body body
ensure
body.close if body.respond_to? :close
end
end
def self.send_headers(status, headers)
STDOUT.print "Status: #{status}\r\n"
headers.each { |k, vs|
vs.split("\n").each { |v|
STDOUT.print "#{k}: #{v}\r\n"
}
}
STDOUT.print "\r\n"
STDOUT.flush
end
def self.send_body(body)
body.each { |part|
STDOUT.print part
STDOUT.flush
}
end
end
end
end

View File

@@ -1,8 +0,0 @@
require 'swiftcore/evented_mongrel'
module Rack
module Handler
class EventedMongrel < Handler::Mongrel
end
end
end

View File

@@ -1,89 +0,0 @@
require 'fcgi'
require 'socket'
require 'rack/content_length'
module Rack
module Handler
class FastCGI
def self.run(app, options={})
file = options[:File] and STDIN.reopen(UNIXServer.new(file))
port = options[:Port] and STDIN.reopen(TCPServer.new(port))
FCGI.each { |request|
serve request, app
}
end
module ProperStream # :nodoc:
def each # This is missing by default.
while line = gets
yield line
end
end
def read(*args)
if args.empty?
super || "" # Empty string on EOF.
else
super
end
end
end
def self.serve(request, app)
app = Rack::ContentLength.new(app)
env = request.env
env.delete "HTTP_CONTENT_LENGTH"
request.in.extend ProperStream
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [0,1],
"rack.input" => request.in,
"rack.errors" => request.err,
"rack.multithread" => false,
"rack.multiprocess" => true,
"rack.run_once" => false,
"rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
})
env["QUERY_STRING"] ||= ""
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["REQUEST_PATH"] ||= "/"
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
status, headers, body = app.call(env)
begin
send_headers request.out, status, headers
send_body request.out, body
ensure
body.close if body.respond_to? :close
request.finish
end
end
def self.send_headers(out, status, headers)
out.print "Status: #{status}\r\n"
headers.each { |k, vs|
vs.split("\n").each { |v|
out.print "#{k}: #{v}\r\n"
}
}
out.print "\r\n"
out.flush
end
def self.send_body(out, body)
body.each { |part|
out.print part
out.flush
}
end
end
end
end

View File

@@ -1,55 +0,0 @@
require 'lsapi'
require 'rack/content_length'
module Rack
module Handler
class LSWS
def self.run(app, options=nil)
while LSAPI.accept != nil
serve app
end
end
def self.serve(app)
app = Rack::ContentLength.new(app)
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [0,1],
"rack.input" => StringIO.new($stdin.read.to_s),
"rack.errors" => $stderr,
"rack.multithread" => false,
"rack.multiprocess" => true,
"rack.run_once" => false,
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
})
env["QUERY_STRING"] ||= ""
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["REQUEST_PATH"] ||= "/"
status, headers, body = app.call(env)
begin
send_headers status, headers
send_body body
ensure
body.close if body.respond_to? :close
end
end
def self.send_headers(status, headers)
print "Status: #{status}\r\n"
headers.each { |k, vs|
vs.split("\n").each { |v|
print "#{k}: #{v}\r\n"
}
}
print "\r\n"
STDOUT.flush
end
def self.send_body(body)
body.each { |part|
print part
STDOUT.flush
}
end
end
end
end

View File

@@ -1,84 +0,0 @@
require 'mongrel'
require 'stringio'
require 'rack/content_length'
require 'rack/chunked'
module Rack
module Handler
class Mongrel < ::Mongrel::HttpHandler
def self.run(app, options={})
server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
options[:Port] || 8080)
# Acts like Rack::URLMap, utilizing Mongrel's own path finding methods.
# Use is similar to #run, replacing the app argument with a hash of
# { path=>app, ... } or an instance of Rack::URLMap.
if options[:map]
if app.is_a? Hash
app.each do |path, appl|
path = '/'+path unless path[0] == ?/
server.register(path, Rack::Handler::Mongrel.new(appl))
end
elsif app.is_a? URLMap
app.instance_variable_get(:@mapping).each do |(host, path, appl)|
next if !host.nil? && !options[:Host].nil? && options[:Host] != host
path = '/'+path unless path[0] == ?/
server.register(path, Rack::Handler::Mongrel.new(appl))
end
else
raise ArgumentError, "first argument should be a Hash or URLMap"
end
else
server.register('/', Rack::Handler::Mongrel.new(app))
end
yield server if block_given?
server.run.join
end
def initialize(app)
@app = Rack::Chunked.new(Rack::ContentLength.new(app))
end
def process(request, response)
env = {}.replace(request.params)
env.delete "HTTP_CONTENT_TYPE"
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [0,1],
"rack.input" => request.body || StringIO.new(""),
"rack.errors" => $stderr,
"rack.multithread" => true,
"rack.multiprocess" => false, # ???
"rack.run_once" => false,
"rack.url_scheme" => "http",
})
env["QUERY_STRING"] ||= ""
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
status, headers, body = @app.call(env)
begin
response.status = status.to_i
response.send_status(nil)
headers.each { |k, vs|
vs.split("\n").each { |v|
response.header[k] = v
}
}
response.send_header
body.each { |part|
response.write part
response.socket.flush
}
ensure
body.close if body.respond_to? :close
end
end
end
end
end

View File

@@ -1,59 +0,0 @@
require 'scgi'
require 'stringio'
require 'rack/content_length'
require 'rack/chunked'
module Rack
module Handler
class SCGI < ::SCGI::Processor
attr_accessor :app
def self.run(app, options=nil)
new(options.merge(:app=>app,
:host=>options[:Host],
:port=>options[:Port],
:socket=>options[:Socket])).listen
end
def initialize(settings = {})
@app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
@log = Object.new
def @log.info(*args); end
def @log.error(*args); end
super(settings)
end
def process_request(request, input_body, socket)
env = {}.replace(request)
env.delete "HTTP_CONTENT_TYPE"
env.delete "HTTP_CONTENT_LENGTH"
env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2)
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["PATH_INFO"] = env["REQUEST_PATH"]
env["QUERY_STRING"] ||= ""
env["SCRIPT_NAME"] = ""
env.update({"rack.version" => [0,1],
"rack.input" => StringIO.new(input_body),
"rack.errors" => $stderr,
"rack.multithread" => true,
"rack.multiprocess" => true,
"rack.run_once" => false,
"rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
})
status, headers, body = app.call(env)
begin
socket.write("Status: #{status}\r\n")
headers.each do |k, vs|
vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")}
end
socket.write("\r\n")
body.each {|s| socket.write(s)}
ensure
body.close if body.respond_to? :close
end
end
end
end
end

View File

@@ -1,8 +0,0 @@
require 'swiftcore/swiftiplied_mongrel'
module Rack
module Handler
class SwiftipliedMongrel < Handler::Mongrel
end
end
end

View File

@@ -1,18 +0,0 @@
require "thin"
require "rack/content_length"
require "rack/chunked"
module Rack
module Handler
class Thin
def self.run(app, options={})
app = Rack::Chunked.new(Rack::ContentLength.new(app))
server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
options[:Port] || 8080,
app)
yield server if block_given?
server.start
end
end
end
end

View File

@@ -1,67 +0,0 @@
require 'webrick'
require 'stringio'
require 'rack/content_length'
module Rack
module Handler
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
def self.run(app, options={})
server = ::WEBrick::HTTPServer.new(options)
server.mount "/", Rack::Handler::WEBrick, app
trap(:INT) { server.shutdown }
yield server if block_given?
server.start
end
def initialize(server, app)
super server
@app = Rack::ContentLength.new(app)
end
def service(req, res)
env = req.meta_vars
env.delete_if { |k, v| v.nil? }
env.update({"rack.version" => [0,1],
"rack.input" => StringIO.new(req.body.to_s),
"rack.errors" => $stderr,
"rack.multithread" => true,
"rack.multiprocess" => false,
"rack.run_once" => false,
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
})
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["QUERY_STRING"] ||= ""
env["REQUEST_PATH"] ||= "/"
if env["PATH_INFO"] == ""
env.delete "PATH_INFO"
else
path, n = req.request_uri.path, env["SCRIPT_NAME"].length
env["PATH_INFO"] = path[n, path.length-n]
end
status, headers, body = @app.call(env)
begin
res.status = status.to_i
headers.each { |k, vs|
if k.downcase == "set-cookie"
res.cookies.concat vs.split("\n")
else
vs.split("\n").each { |v|
res[k] = v
}
end
}
body.each { |part|
res.body << part
}
ensure
body.close if body.respond_to? :close
end
end
end
end
end

View File

@@ -1,19 +0,0 @@
module Rack
class Head
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
if env["REQUEST_METHOD"] == "HEAD"
[status, headers, []]
else
[status, headers, body]
end
end
end
end

View File

@@ -1,462 +0,0 @@
require 'rack/utils'
module Rack
# Rack::Lint validates your application and the requests and
# responses according to the Rack spec.
class Lint
def initialize(app)
@app = app
end
# :stopdoc:
class LintError < RuntimeError; end
module Assertion
def assert(message, &block)
unless block.call
raise LintError, message
end
end
end
include Assertion
## This specification aims to formalize the Rack protocol. You
## can (and should) use Rack::Lint to enforce it.
##
## When you develop middleware, be sure to add a Lint before and
## after to catch all mistakes.
## = Rack applications
## A Rack application is an Ruby object (not a class) that
## responds to +call+.
def call(env=nil)
dup._call(env)
end
def _call(env)
## It takes exactly one argument, the *environment*
assert("No env given") { env }
check_env env
env['rack.input'] = InputWrapper.new(env['rack.input'])
env['rack.errors'] = ErrorWrapper.new(env['rack.errors'])
## and returns an Array of exactly three values:
status, headers, @body = @app.call(env)
## The *status*,
check_status status
## the *headers*,
check_headers headers
## and the *body*.
check_content_type status, headers
check_content_length status, headers, env
[status, headers, self]
end
## == The Environment
def check_env(env)
## The environment must be an true instance of Hash (no
## subclassing allowed) that includes CGI-like headers.
## The application is free to modify the environment.
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
env.instance_of? Hash
}
##
## The environment is required to include these variables
## (adopted from PEP333), except when they'd be empty, but see
## below.
## <tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
## "GET" or "POST". This cannot ever
## be an empty string, and so is
## always required.
## <tt>SCRIPT_NAME</tt>:: The initial portion of the request
## URL's "path" that corresponds to the
## application object, so that the
## application knows its virtual
## "location". This may be an empty
## string, if the application corresponds
## to the "root" of the server.
## <tt>PATH_INFO</tt>:: The remainder of the request URL's
## "path", designating the virtual
## "location" of the request's target
## within the application. This may be an
## empty string, if the request URL targets
## the application root and does not have a
## trailing slash. This information should be
## decoded by the server if it comes from a
## URL.
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
## follows the <tt>?</tt>, if any. May be
## empty, but is always required!
## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
## <tt>HTTP_</tt> Variables:: Variables corresponding to the
## client-supplied HTTP request
## headers (i.e., variables whose
## names begin with <tt>HTTP_</tt>). The
## presence or absence of these
## variables should correspond with
## the presence or absence of the
## appropriate HTTP header in the
## request.
## In addition to this, the Rack environment must include these
## Rack-specific variables:
## <tt>rack.version</tt>:: The Array [0,1], representing this version of Rack.
## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
## <tt>rack.input</tt>:: See below, the input stream.
## <tt>rack.errors</tt>:: See below, the error stream.
## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
## The server or the application can store their own data in the
## environment, too. The keys must contain at least one dot,
## and should be prefixed uniquely. The prefix <tt>rack.</tt>
## is reserved for use with the Rack core distribution and must
## not be used otherwise.
##
%w[REQUEST_METHOD SERVER_NAME SERVER_PORT
QUERY_STRING
rack.version rack.input rack.errors
rack.multithread rack.multiprocess rack.run_once].each { |header|
assert("env missing required key #{header}") { env.include? header }
}
## The environment must not contain the keys
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
## (use the versions without <tt>HTTP_</tt>).
%w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
assert("env contains #{header}, must use #{header[5,-1]}") {
not env.include? header
}
}
## The CGI keys (named without a period) must have String values.
env.each { |key, value|
next if key.include? "." # Skip extensions
assert("env variable #{key} has non-string value #{value.inspect}") {
value.instance_of? String
}
}
##
## There are the following restrictions:
## * <tt>rack.version</tt> must be an array of Integers.
assert("rack.version must be an Array, was #{env["rack.version"].class}") {
env["rack.version"].instance_of? Array
}
## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") {
%w[http https].include? env["rack.url_scheme"]
}
## * There must be a valid input stream in <tt>rack.input</tt>.
check_input env["rack.input"]
## * There must be a valid error stream in <tt>rack.errors</tt>.
check_error env["rack.errors"]
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
}
## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
assert("SCRIPT_NAME must start with /") {
!env.include?("SCRIPT_NAME") ||
env["SCRIPT_NAME"] == "" ||
env["SCRIPT_NAME"] =~ /\A\//
}
## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
assert("PATH_INFO must start with /") {
!env.include?("PATH_INFO") ||
env["PATH_INFO"] == "" ||
env["PATH_INFO"] =~ /\A\//
}
## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") {
!env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/
}
## * One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
## <tt>SCRIPT_NAME</tt> is empty.
assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") {
env["SCRIPT_NAME"] || env["PATH_INFO"]
}
## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") {
env["SCRIPT_NAME"] != "/"
}
end
## === The Input Stream
def check_input(input)
## The input stream must respond to +gets+, +each+ and +read+.
[:gets, :each, :read].each { |method|
assert("rack.input #{input} does not respond to ##{method}") {
input.respond_to? method
}
}
end
class InputWrapper
include Assertion
def initialize(input)
@input = input
end
def size
@input.size
end
def rewind
@input.rewind
end
## * +gets+ must be called without arguments and return a string,
## or +nil+ on EOF.
def gets(*args)
assert("rack.input#gets called with arguments") { args.size == 0 }
v = @input.gets
assert("rack.input#gets didn't return a String") {
v.nil? or v.instance_of? String
}
v
end
## * +read+ must be called without or with one integer argument
## and return a string, or +nil+ on EOF.
def read(*args)
assert("rack.input#read called with too many arguments") {
args.size <= 1
}
if args.size == 1
assert("rack.input#read called with non-integer argument") {
args.first.kind_of? Integer
}
end
v = @input.read(*args)
assert("rack.input#read didn't return a String") {
v.nil? or v.instance_of? String
}
v
end
## * +each+ must be called without arguments and only yield Strings.
def each(*args)
assert("rack.input#each called with arguments") { args.size == 0 }
@input.each { |line|
assert("rack.input#each didn't yield a String") {
line.instance_of? String
}
yield line
}
end
## * +close+ must never be called on the input stream.
def close(*args)
assert("rack.input#close must not be called") { false }
end
end
## === The Error Stream
def check_error(error)
## The error stream must respond to +puts+, +write+ and +flush+.
[:puts, :write, :flush].each { |method|
assert("rack.error #{error} does not respond to ##{method}") {
error.respond_to? method
}
}
end
class ErrorWrapper
include Assertion
def initialize(error)
@error = error
end
## * +puts+ must be called with a single argument that responds to +to_s+.
def puts(str)
@error.puts str
end
## * +write+ must be called with a single argument that is a String.
def write(str)
assert("rack.errors#write not called with a String") { str.instance_of? String }
@error.write str
end
## * +flush+ must be called without arguments and must be called
## in order to make the error appear for sure.
def flush
@error.flush
end
## * +close+ must never be called on the error stream.
def close(*args)
assert("rack.errors#close must not be called") { false }
end
end
## == The Response
## === The Status
def check_status(status)
## The status, if parsed as integer (+to_i+), must be greater than or equal to 100.
assert("Status must be >=100 seen as integer") { status.to_i >= 100 }
end
## === The Headers
def check_headers(header)
## The header must respond to each, and yield values of key and value.
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
header.respond_to? :each
}
header.each { |key, value|
## The header keys must be Strings.
assert("header key must be a string, was #{key.class}") {
key.instance_of? String
}
## The header must not contain a +Status+ key,
assert("header must not contain Status") { key.downcase != "status" }
## contain keys with <tt>:</tt> or newlines in their name,
assert("header names must not contain : or \\n") { key !~ /[:\n]/ }
## contain keys names that end in <tt>-</tt> or <tt>_</tt>,
assert("header names must not end in - or _") { key !~ /[-_]\z/ }
## but only contain keys that consist of
## letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ }
## The values of the header must be Strings,
assert("a header value must be a String, but the value of " +
"'#{key}' is a #{value.class}") { value.kind_of? String }
## consisting of lines (for multiple header values) seperated by "\n".
value.split("\n").each { |item|
## The lines must not contain characters below 037.
assert("invalid header value #{key}: #{item.inspect}") {
item !~ /[\000-\037]/
}
}
}
end
## === The Content-Type
def check_content_type(status, headers)
headers.each { |key, value|
## There must be a <tt>Content-Type</tt>, except when the
## +Status+ is 1xx, 204 or 304, in which case there must be none
## given.
if key.downcase == "content-type"
assert("Content-Type header found in #{status} response, not allowed") {
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
return
end
}
assert("No Content-Type header found") {
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
end
## === The Content-Length
def check_content_length(status, headers, env)
headers.each { |key, value|
if key.downcase == 'content-length'
## There must not be a <tt>Content-Length</tt> header when the
## +Status+ is 1xx, 204 or 304.
assert("Content-Length header found in #{status} response, not allowed") {
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
bytes = 0
string_body = true
if @body.respond_to?(:to_ary)
@body.each { |part|
unless part.kind_of?(String)
string_body = false
break
end
bytes += Rack::Utils.bytesize(part)
}
if env["REQUEST_METHOD"] == "HEAD"
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
}
else
if string_body
assert("Content-Length header was #{value}, but should be #{bytes}") {
value == bytes.to_s
}
end
end
end
return
end
}
end
## === The Body
def each
@closed = false
## The Body must respond to #each
@body.each { |part|
## and must only yield String values.
assert("Body yielded non-string value #{part.inspect}") {
part.instance_of? String
}
yield part
}
##
## If the Body responds to #close, it will be called after iteration.
# XXX howto: assert("Body has not been closed") { @closed }
##
## If the Body responds to #to_path, it must return a String
## identifying the location of a file whose contents are identical
## to that produced by calling #each.
if @body.respond_to?(:to_path)
assert("The file identified by body.to_path does not exist") {
::File.exist? @body.to_path
}
end
##
## The Body commonly is an Array of Strings, the application
## instance itself, or a File-like object.
end
def close
@closed = true
@body.close if @body.respond_to?(:close)
end
# :startdoc:
end
end
## == Thanks
## Some parts of this specification are adopted from PEP333: Python
## Web Server Gateway Interface
## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
## everyone involved in that effort.

View File

@@ -1,65 +0,0 @@
require 'zlib'
require 'rack/request'
require 'rack/response'
module Rack
# Paste has a Pony, Rack has a Lobster!
class Lobster
LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2
P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0
t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ
I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0])
LambdaLobster = lambda { |env|
if env["QUERY_STRING"].include?("flip")
lobster = LobsterString.split("\n").
map { |line| line.ljust(42).reverse }.
join("\n")
href = "?"
else
lobster = LobsterString
href = "?flip"
end
content = ["<title>Lobstericious!</title>",
"<pre>", lobster, "</pre>",
"<a href='#{href}'>flip!</a>"]
length = content.inject(0) { |a,e| a+e.size }.to_s
[200, {"Content-Type" => "text/html", "Content-Length" => length}, content]
}
def call(env)
req = Request.new(env)
if req.GET["flip"] == "left"
lobster = LobsterString.split("\n").
map { |line| line.ljust(42).reverse }.
join("\n")
href = "?flip=right"
elsif req.GET["flip"] == "crash"
raise "Lobster crashed"
else
lobster = LobsterString
href = "?flip=left"
end
res = Response.new
res.write "<title>Lobstericious!</title>"
res.write "<pre>"
res.write lobster
res.write "</pre>"
res.write "<p><a href='#{href}'>flip!</a></p>"
res.write "<p><a href='?flip=crash'>crash!</a></p>"
res.finish
end
end
end
if $0 == __FILE__
require 'rack'
require 'rack/showexceptions'
Rack::Handler::WEBrick.run \
Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)),
:Port => 9292
end

View File

@@ -1,16 +0,0 @@
module Rack
class Lock
FLAG = 'rack.multithread'.freeze
def initialize(app, lock = Mutex.new)
@app, @lock = app, lock
end
def call(env)
old, env[FLAG] = env[FLAG], false
@lock.synchronize { @app.call(env) }
ensure
env[FLAG] = old
end
end
end

View File

@@ -1,27 +0,0 @@
module Rack
class MethodOverride
HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
def initialize(app)
@app = app
end
def call(env)
if env["REQUEST_METHOD"] == "POST"
req = Request.new(env)
method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
env[HTTP_METHOD_OVERRIDE_HEADER]
method = method.to_s.upcase
if HTTP_METHODS.include?(method)
env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
env["REQUEST_METHOD"] = method
end
end
@app.call(env)
end
end
end

View File

@@ -1,204 +0,0 @@
module Rack
module Mime
# Returns String with mime type if found, otherwise use +fallback+.
# +ext+ should be filename extension in the '.ext' format that
# File.extname(file) returns.
# +fallback+ may be any object
#
# Also see the documentation for MIME_TYPES
#
# Usage:
# Rack::Mime.mime_type('.foo')
#
# This is a shortcut for:
# Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
def mime_type(ext, fallback='application/octet-stream')
MIME_TYPES.fetch(ext, fallback)
end
module_function :mime_type
# List of most common mime-types, selected various sources
# according to their usefulness in a webserving scope for Ruby
# users.
#
# To amend this list with your local mime.types list you can use:
#
# require 'webrick/httputils'
# list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
# Rack::Mime::MIME_TYPES.merge!(list)
#
# To add the list mongrel provides, use:
#
# require 'mongrel/handlers'
# Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
MIME_TYPES = {
".3gp" => "video/3gpp",
".a" => "application/octet-stream",
".ai" => "application/postscript",
".aif" => "audio/x-aiff",
".aiff" => "audio/x-aiff",
".asc" => "application/pgp-signature",
".asf" => "video/x-ms-asf",
".asm" => "text/x-asm",
".asx" => "video/x-ms-asf",
".atom" => "application/atom+xml",
".au" => "audio/basic",
".avi" => "video/x-msvideo",
".bat" => "application/x-msdownload",
".bin" => "application/octet-stream",
".bmp" => "image/bmp",
".bz2" => "application/x-bzip2",
".c" => "text/x-c",
".cab" => "application/vnd.ms-cab-compressed",
".cc" => "text/x-c",
".chm" => "application/vnd.ms-htmlhelp",
".class" => "application/octet-stream",
".com" => "application/x-msdownload",
".conf" => "text/plain",
".cpp" => "text/x-c",
".crt" => "application/x-x509-ca-cert",
".css" => "text/css",
".csv" => "text/csv",
".cxx" => "text/x-c",
".deb" => "application/x-debian-package",
".der" => "application/x-x509-ca-cert",
".diff" => "text/x-diff",
".djv" => "image/vnd.djvu",
".djvu" => "image/vnd.djvu",
".dll" => "application/x-msdownload",
".dmg" => "application/octet-stream",
".doc" => "application/msword",
".dot" => "application/msword",
".dtd" => "application/xml-dtd",
".dvi" => "application/x-dvi",
".ear" => "application/java-archive",
".eml" => "message/rfc822",
".eps" => "application/postscript",
".exe" => "application/x-msdownload",
".f" => "text/x-fortran",
".f77" => "text/x-fortran",
".f90" => "text/x-fortran",
".flv" => "video/x-flv",
".for" => "text/x-fortran",
".gem" => "application/octet-stream",
".gemspec" => "text/x-script.ruby",
".gif" => "image/gif",
".gz" => "application/x-gzip",
".h" => "text/x-c",
".hh" => "text/x-c",
".htm" => "text/html",
".html" => "text/html",
".ico" => "image/vnd.microsoft.icon",
".ics" => "text/calendar",
".ifb" => "text/calendar",
".iso" => "application/octet-stream",
".jar" => "application/java-archive",
".java" => "text/x-java-source",
".jnlp" => "application/x-java-jnlp-file",
".jpeg" => "image/jpeg",
".jpg" => "image/jpeg",
".js" => "application/javascript",
".json" => "application/json",
".log" => "text/plain",
".m3u" => "audio/x-mpegurl",
".m4v" => "video/mp4",
".man" => "text/troff",
".mathml" => "application/mathml+xml",
".mbox" => "application/mbox",
".mdoc" => "text/troff",
".me" => "text/troff",
".mid" => "audio/midi",
".midi" => "audio/midi",
".mime" => "message/rfc822",
".mml" => "application/mathml+xml",
".mng" => "video/x-mng",
".mov" => "video/quicktime",
".mp3" => "audio/mpeg",
".mp4" => "video/mp4",
".mp4v" => "video/mp4",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".ms" => "text/troff",
".msi" => "application/x-msdownload",
".odp" => "application/vnd.oasis.opendocument.presentation",
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
".odt" => "application/vnd.oasis.opendocument.text",
".ogg" => "application/ogg",
".p" => "text/x-pascal",
".pas" => "text/x-pascal",
".pbm" => "image/x-portable-bitmap",
".pdf" => "application/pdf",
".pem" => "application/x-x509-ca-cert",
".pgm" => "image/x-portable-graymap",
".pgp" => "application/pgp-encrypted",
".pkg" => "application/octet-stream",
".pl" => "text/x-script.perl",
".pm" => "text/x-script.perl-module",
".png" => "image/png",
".pnm" => "image/x-portable-anymap",
".ppm" => "image/x-portable-pixmap",
".pps" => "application/vnd.ms-powerpoint",
".ppt" => "application/vnd.ms-powerpoint",
".ps" => "application/postscript",
".psd" => "image/vnd.adobe.photoshop",
".py" => "text/x-script.python",
".qt" => "video/quicktime",
".ra" => "audio/x-pn-realaudio",
".rake" => "text/x-script.ruby",
".ram" => "audio/x-pn-realaudio",
".rar" => "application/x-rar-compressed",
".rb" => "text/x-script.ruby",
".rdf" => "application/rdf+xml",
".roff" => "text/troff",
".rpm" => "application/x-redhat-package-manager",
".rss" => "application/rss+xml",
".rtf" => "application/rtf",
".ru" => "text/x-script.ruby",
".s" => "text/x-asm",
".sgm" => "text/sgml",
".sgml" => "text/sgml",
".sh" => "application/x-sh",
".sig" => "application/pgp-signature",
".snd" => "audio/basic",
".so" => "application/octet-stream",
".svg" => "image/svg+xml",
".svgz" => "image/svg+xml",
".swf" => "application/x-shockwave-flash",
".t" => "text/troff",
".tar" => "application/x-tar",
".tbz" => "application/x-bzip-compressed-tar",
".tcl" => "application/x-tcl",
".tex" => "application/x-tex",
".texi" => "application/x-texinfo",
".texinfo" => "application/x-texinfo",
".text" => "text/plain",
".tif" => "image/tiff",
".tiff" => "image/tiff",
".torrent" => "application/x-bittorrent",
".tr" => "text/troff",
".txt" => "text/plain",
".vcf" => "text/x-vcard",
".vcs" => "text/x-vcalendar",
".vrml" => "model/vrml",
".war" => "application/java-archive",
".wav" => "audio/x-wav",
".wma" => "audio/x-ms-wma",
".wmv" => "video/x-ms-wmv",
".wmx" => "video/x-ms-wmx",
".wrl" => "model/vrml",
".wsdl" => "application/wsdl+xml",
".xbm" => "image/x-xbitmap",
".xhtml" => "application/xhtml+xml",
".xls" => "application/vnd.ms-excel",
".xml" => "application/xml",
".xpm" => "image/x-xpixmap",
".xsl" => "application/xml",
".xslt" => "application/xslt+xml",
".yaml" => "text/yaml",
".yml" => "text/yaml",
".zip" => "application/zip",
}
end
end

View File

@@ -1,160 +0,0 @@
require 'uri'
require 'stringio'
require 'rack/lint'
require 'rack/utils'
require 'rack/response'
module Rack
# Rack::MockRequest helps testing your Rack application without
# actually using HTTP.
#
# After performing a request on a URL with get/post/put/delete, it
# returns a MockResponse with useful helper methods for effective
# testing.
#
# You can pass a hash with additional configuration to the
# get/post/put/delete.
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
class MockRequest
class FatalWarning < RuntimeError
end
class FatalWarner
def puts(warning)
raise FatalWarning, warning
end
def write(warning)
raise FatalWarning, warning
end
def flush
end
def string
""
end
end
DEFAULT_ENV = {
"rack.version" => [0,1],
"rack.input" => StringIO.new,
"rack.errors" => StringIO.new,
"rack.multithread" => true,
"rack.multiprocess" => true,
"rack.run_once" => false,
}
def initialize(app)
@app = app
end
def get(uri, opts={}) request("GET", uri, opts) end
def post(uri, opts={}) request("POST", uri, opts) end
def put(uri, opts={}) request("PUT", uri, opts) end
def delete(uri, opts={}) request("DELETE", uri, opts) end
def request(method="GET", uri="", opts={})
env = self.class.env_for(uri, opts.merge(:method => method))
if opts[:lint]
app = Rack::Lint.new(@app)
else
app = @app
end
errors = env["rack.errors"]
MockResponse.new(*(app.call(env) + [errors]))
end
# Return the Rack environment used for a request to +uri+.
def self.env_for(uri="", opts={})
uri = URI(uri)
env = DEFAULT_ENV.dup
env["REQUEST_METHOD"] = opts[:method] || "GET"
env["SERVER_NAME"] = uri.host || "example.org"
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
env["QUERY_STRING"] = uri.query.to_s
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
env["rack.url_scheme"] = uri.scheme || "http"
env["SCRIPT_NAME"] = opts[:script_name] || ""
if opts[:fatal]
env["rack.errors"] = FatalWarner.new
else
env["rack.errors"] = StringIO.new
end
opts[:input] ||= ""
if String === opts[:input]
env["rack.input"] = StringIO.new(opts[:input])
else
env["rack.input"] = opts[:input]
end
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
opts.each { |field, value|
env[field] = value if String === field
}
env
end
end
# Rack::MockResponse provides useful helpers for testing your apps.
# Usually, you don't create the MockResponse on your own, but use
# MockRequest.
class MockResponse
def initialize(status, headers, body, errors=StringIO.new(""))
@status = status.to_i
@original_headers = headers
@headers = Rack::Utils::HeaderHash.new
headers.each { |field, values|
@headers[field] = values
@headers[field] = "" if values.empty?
}
@body = ""
body.each { |part| @body << part }
@errors = errors.string
end
# Status
attr_reader :status
# Headers
attr_reader :headers, :original_headers
def [](field)
headers[field]
end
# Body
attr_reader :body
def =~(other)
@body =~ other
end
def match(other)
@body.match other
end
# Errors
attr_accessor :errors
include Response::Helpers
end
end

View File

@@ -1,57 +0,0 @@
require 'uri'
module Rack
# Rack::ForwardRequest gets caught by Rack::Recursive and redirects
# the current request to the app at +url+.
#
# raise ForwardRequest.new("/not-found")
#
class ForwardRequest < Exception
attr_reader :url, :env
def initialize(url, env={})
@url = URI(url)
@env = env
@env["PATH_INFO"] = @url.path
@env["QUERY_STRING"] = @url.query if @url.query
@env["HTTP_HOST"] = @url.host if @url.host
@env["HTTP_PORT"] = @url.port if @url.port
@env["rack.url_scheme"] = @url.scheme if @url.scheme
super "forwarding to #{url}"
end
end
# Rack::Recursive allows applications called down the chain to
# include data from other applications (by using
# <tt>rack['rack.recursive.include'][...]</tt> or raise a
# ForwardRequest to redirect internally.
class Recursive
def initialize(app)
@app = app
end
def call(env)
@script_name = env["SCRIPT_NAME"]
@app.call(env.merge('rack.recursive.include' => method(:include)))
rescue ForwardRequest => req
call(env.merge(req.env))
end
def include(env, path)
unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ ||
path[@script_name.size].nil?)
raise ArgumentError, "can only include below #{@script_name}, not #{path}"
end
env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name,
"REQUEST_METHOD" => "GET",
"CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
"rack.input" => StringIO.new(""))
@app.call(env)
end
end
end

View File

@@ -1,64 +0,0 @@
require 'thread'
module Rack
# Rack::Reloader checks on every request, but at most every +secs+
# seconds, if a file loaded changed, and reloads it, logging to
# rack.errors.
#
# It is recommended you use ShowExceptions to catch SyntaxErrors etc.
class Reloader
def initialize(app, secs=10)
@app = app
@secs = secs # reload every @secs seconds max
@last = Time.now
end
def call(env)
if Time.now > @last + @secs
Thread.exclusive {
reload!(env['rack.errors'])
@last = Time.now
}
end
@app.call(env)
end
def reload!(stderr=$stderr)
need_reload = $LOADED_FEATURES.find_all { |loaded|
begin
if loaded =~ /\A[.\/]/ # absolute filename or 1.9
abs = loaded
else
abs = $LOAD_PATH.map { |path| ::File.join(path, loaded) }.
find { |file| ::File.exist? file }
end
if abs
::File.mtime(abs) > @last - @secs rescue false
else
false
end
end
}
need_reload.each { |l|
$LOADED_FEATURES.delete l
}
need_reload.each { |to_load|
begin
if require to_load
stderr.puts "#{self.class}: reloaded `#{to_load}'"
end
rescue LoadError, SyntaxError => e
raise e # Possibly ShowExceptions
end
}
stderr.flush
need_reload
end
end
end

View File

@@ -1,241 +0,0 @@
require 'rack/utils'
module Rack
# Rack::Request provides a convenient interface to a Rack
# environment. It is stateless, the environment +env+ passed to the
# constructor will be directly modified.
#
# req = Rack::Request.new(env)
# req.post?
# req.params["data"]
#
# The environment hash passed will store a reference to the Request object
# instantiated so that it will only instantiate if an instance of the Request
# object doesn't already exist.
class Request
# The environment of the request.
attr_reader :env
def self.new(env)
if self == Rack::Request
env["rack.request"] ||= super
else
super
end
end
def initialize(env)
@env = env
end
def body; @env["rack.input"] end
def scheme; @env["rack.url_scheme"] end
def script_name; @env["SCRIPT_NAME"].to_s end
def path_info; @env["PATH_INFO"].to_s end
def port; @env["SERVER_PORT"].to_i end
def request_method; @env["REQUEST_METHOD"] end
def query_string; @env["QUERY_STRING"].to_s end
def content_length; @env['CONTENT_LENGTH'] end
def content_type; @env['CONTENT_TYPE'] end
# The media type (type/subtype) portion of the CONTENT_TYPE header
# without any media type parameters. e.g., when CONTENT_TYPE is
# "text/plain;charset=utf-8", the media-type is "text/plain".
#
# For more information on the use of media types in HTTP, see:
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
def media_type
content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase
end
# The media type parameters provided in CONTENT_TYPE as a Hash, or
# an empty Hash if no CONTENT_TYPE or media-type parameters were
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
# this method responds with the following Hash:
# { 'charset' => 'utf-8' }
def media_type_params
return {} if content_type.nil?
content_type.split(/\s*[;,]\s*/)[1..-1].
collect { |s| s.split('=', 2) }.
inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash }
end
# The character set of the request body if a "charset" media type
# parameter was given, or nil if no "charset" was specified. Note
# that, per RFC2616, text/* media types that specify no explicit
# charset are to be considered ISO-8859-1.
def content_charset
media_type_params['charset']
end
def host
# Remove port number.
(@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '')
end
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
def path_info=(s); @env["PATH_INFO"] = s.to_s end
def get?; request_method == "GET" end
def post?; request_method == "POST" end
def put?; request_method == "PUT" end
def delete?; request_method == "DELETE" end
def head?; request_method == "HEAD" end
# The set of form-data media-types. Requests that do not indicate
# one of the media types presents in this list will not be eligible
# for form-data / param parsing.
FORM_DATA_MEDIA_TYPES = [
nil,
'application/x-www-form-urlencoded',
'multipart/form-data'
]
# Determine whether the request body contains form-data by checking
# the request media_type against registered form-data media-types:
# "application/x-www-form-urlencoded" and "multipart/form-data". The
# list of form-data media types can be modified through the
# +FORM_DATA_MEDIA_TYPES+ array.
def form_data?
FORM_DATA_MEDIA_TYPES.include?(media_type)
end
# Returns the data recieved in the query string.
def GET
if @env["rack.request.query_string"] == query_string
@env["rack.request.query_hash"]
else
@env["rack.request.query_string"] = query_string
@env["rack.request.query_hash"] =
Utils.parse_nested_query(query_string)
end
end
# Returns the data recieved in the request body.
#
# This method support both application/x-www-form-urlencoded and
# multipart/form-data.
def POST
if @env["rack.request.form_input"].eql? @env["rack.input"]
@env["rack.request.form_hash"]
elsif form_data?
@env["rack.request.form_input"] = @env["rack.input"]
unless @env["rack.request.form_hash"] =
Utils::Multipart.parse_multipart(env)
form_vars = @env["rack.input"].read
# Fix for Safari Ajax postings that always append \0
form_vars.sub!(/\0\z/, '')
@env["rack.request.form_vars"] = form_vars
@env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars)
begin
@env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind)
rescue Errno::ESPIPE
# Handles exceptions raised by input streams that cannot be rewound
# such as when using plain CGI under Apache
end
end
@env["rack.request.form_hash"]
else
{}
end
end
# The union of GET and POST data.
def params
self.put? ? self.GET : self.GET.update(self.POST)
rescue EOFError => e
self.GET
end
# shortcut for request.params[key]
def [](key)
params[key.to_s]
end
# shortcut for request.params[key] = value
def []=(key, value)
params[key.to_s] = value
end
# like Hash#values_at
def values_at(*keys)
keys.map{|key| params[key] }
end
# the referer of the client or '/'
def referer
@env['HTTP_REFERER'] || '/'
end
alias referrer referer
def cookies
return {} unless @env["HTTP_COOKIE"]
if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
@env["rack.request.cookie_hash"]
else
@env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
# According to RFC 2109:
# If multiple cookies satisfy the criteria above, they are ordered in
# the Cookie header such that those with more specific Path attributes
# precede those with less specific. Ordering with respect to other
# attributes (e.g., Domain) is unspecified.
@env["rack.request.cookie_hash"] =
Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)|
h[k] = Array === v ? v.first : v
h
}
end
end
def xhr?
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
end
# Tries to return a remake of the original request URL as a string.
def url
url = scheme + "://"
url << host
if scheme == "https" && port != 443 ||
scheme == "http" && port != 80
url << ":#{port}"
end
url << fullpath
url
end
def fullpath
path = script_name + path_info
path << "?" << query_string unless query_string.empty?
path
end
def accept_encoding
@env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part|
m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
if m
[m[1], (m[2] || 1.0).to_f]
else
raise "Invalid value for Accept-Encoding: #{part.inspect}"
end
end
end
def ip
if addr = @env['HTTP_X_FORWARDED_FOR']
addr.split(',').last.strip
else
@env['REMOTE_ADDR']
end
end
end
end

View File

@@ -1,179 +0,0 @@
require 'rack/request'
require 'rack/utils'
module Rack
# Rack::Response provides a convenient interface to create a Rack
# response.
#
# It allows setting of headers and cookies, and provides useful
# defaults (a OK response containing HTML).
#
# You can use Response#write to iteratively generate your response,
# but note that this is buffered by Rack::Response until you call
# +finish+. +finish+ however can take a block inside which calls to
# +write+ are syncronous with the Rack response.
#
# Your application's +call+ should end returning Response#finish.
class Response
attr_accessor :length
def initialize(body=[], status=200, header={}, &block)
@status = status
@header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
merge(header))
@writer = lambda { |x| @body << x }
@block = nil
@length = 0
@body = []
if body.respond_to? :to_str
write body.to_str
elsif body.respond_to?(:each)
body.each { |part|
write part.to_s
}
else
raise TypeError, "stringable or iterable required"
end
yield self if block_given?
end
attr_reader :header
attr_accessor :status, :body
def [](key)
header[key]
end
def []=(key, value)
header[key] = value
end
def set_cookie(key, value)
case value
when Hash
domain = "; domain=" + value[:domain] if value[:domain]
path = "; path=" + value[:path] if value[:path]
# According to RFC 2109, we need dashes here.
# N.B.: cgi.rb uses spaces...
expires = "; expires=" + value[:expires].clone.gmtime.
strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
secure = "; secure" if value[:secure]
httponly = "; HttpOnly" if value[:httponly]
value = value[:value]
end
value = [value] unless Array === value
cookie = Utils.escape(key) + "=" +
value.map { |v| Utils.escape v }.join("&") +
"#{domain}#{path}#{expires}#{secure}#{httponly}"
case self["Set-Cookie"]
when Array
self["Set-Cookie"] << cookie
when String
self["Set-Cookie"] = [self["Set-Cookie"], cookie]
when nil
self["Set-Cookie"] = cookie
end
end
def delete_cookie(key, value={})
unless Array === self["Set-Cookie"]
self["Set-Cookie"] = [self["Set-Cookie"]].compact
end
self["Set-Cookie"].reject! { |cookie|
cookie =~ /\A#{Utils.escape(key)}=/
}
set_cookie(key,
{:value => '', :path => nil, :domain => nil,
:expires => Time.at(0) }.merge(value))
end
def finish(&block)
@block = block
if [204, 304].include?(status.to_i)
header.delete "Content-Type"
[status.to_i, header.to_hash, []]
else
[status.to_i, header.to_hash, self]
end
end
alias to_a finish # For *response
def each(&callback)
@body.each(&callback)
@writer = callback
@block.call(self) if @block
end
# Append to body and update Content-Length.
#
# NOTE: Do not mix #write and direct #body access!
#
def write(str)
s = str.to_s
@length += s.size
@writer.call s
header["Content-Length"] = @length.to_s
str
end
def close
body.close if body.respond_to?(:close)
end
def empty?
@block == nil && @body.empty?
end
alias headers header
module Helpers
def invalid?; @status < 100 || @status >= 600; end
def informational?; @status >= 100 && @status < 200; end
def successful?; @status >= 200 && @status < 300; end
def redirection?; @status >= 300 && @status < 400; end
def client_error?; @status >= 400 && @status < 500; end
def server_error?; @status >= 500 && @status < 600; end
def ok?; @status == 200; end
def forbidden?; @status == 403; end
def not_found?; @status == 404; end
def redirect?; [301, 302, 303, 307].include? @status; end
def empty?; [201, 204, 304].include? @status; end
# Headers
attr_reader :headers, :original_headers
def include?(header)
!!headers[header]
end
def content_type
headers["Content-Type"]
end
def content_length
cl = headers["Content-Length"]
cl ? cl.to_i : cl
end
def location
headers["Location"]
end
end
include Helpers
end
end

View File

@@ -1,142 +0,0 @@
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
# bugrep: Andreas Zehnder
require 'time'
require 'rack/request'
require 'rack/response'
module Rack
module Session
module Abstract
# ID sets up a basic framework for implementing an id based sessioning
# service. Cookies sent to the client for maintaining sessions will only
# contain an id reference. Only #get_session and #set_session are
# required to be overwritten.
#
# All parameters are optional.
# * :key determines the name of the cookie, by default it is
# 'rack.session'
# * :path, :domain, :expire_after, :secure, and :httponly set the related
# cookie options as by Rack::Response#add_cookie
# * :defer will not set a cookie in the response.
# * :renew (implementation dependent) will prompt the generation of a new
# session id, and migration of data to be referenced at the new id. If
# :defer is set, it will be overridden and the cookie will be set.
# * :sidbits sets the number of bits in length that a generated session
# id will be.
#
# These options can be set on a per request basis, at the location of
# env['rack.session.options']. Additionally the id of the session can be
# found within the options hash at the key :id. It is highly not
# recommended to change its value.
#
# Is Rack::Utils::Context compatible.
class ID
DEFAULT_OPTIONS = {
:path => '/',
:domain => nil,
:expire_after => nil,
:secure => false,
:httponly => true,
:defer => false,
:renew => false,
:sidbits => 128
}
attr_reader :key, :default_options
def initialize(app, options={})
@app = app
@key = options[:key] || "rack.session"
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
end
def call(env)
context(env)
end
def context(env, app=@app)
load_session(env)
status, headers, body = app.call(env)
commit_session(env, status, headers, body)
end
private
# Generate a new session id using Ruby #rand. The size of the
# session id is controlled by the :sidbits option.
# Monkey patch this to use custom methods for session id generation.
def generate_sid
"%0#{@default_options[:sidbits] / 4}x" %
rand(2**@default_options[:sidbits] - 1)
end
# Extracts the session id from provided cookies and passes it and the
# environment to #get_session. It then sets the resulting session into
# 'rack.session', and places options and session metadata into
# 'rack.session.options'.
def load_session(env)
request = Rack::Request.new(env)
session_id = request.cookies[@key]
begin
session_id, session = get_session(env, session_id)
env['rack.session'] = session
rescue
env['rack.session'] = Hash.new
end
env['rack.session.options'] = @default_options.
merge(:id => session_id)
end
# Acquires the session from the environment and the session id from
# the session options and passes them to #set_session. If successful
# and the :defer option is not true, a cookie will be added to the
# response with the session's id.
def commit_session(env, status, headers, body)
session = env['rack.session']
options = env['rack.session.options']
session_id = options[:id]
if not session_id = set_session(env, session_id, session, options)
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
[status, headers, body]
elsif options[:defer] and not options[:renew]
env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
[status, headers, body]
else
cookie = Hash.new
cookie[:value] = session_id
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
response = Rack::Response.new(body, status, headers)
response.set_cookie(@key, cookie.merge(options))
response.to_a
end
end
# All thread safety and session retrival proceedures should occur here.
# Should return [session_id, session].
# If nil is provided as the session id, generation of a new valid id
# should occur within.
def get_session(env, sid)
raise '#get_session not implemented.'
end
# All thread safety and session storage proceedures should occur here.
# Should return true or false dependant on whether or not the session
# was saved or not.
def set_session(env, sid, session, options)
raise '#set_session not implemented.'
end
end
end
end
end

View File

@@ -1,91 +0,0 @@
require 'openssl'
require 'rack/request'
require 'rack/response'
module Rack
module Session
# Rack::Session::Cookie provides simple cookie based session management.
# The session is a Ruby Hash stored as base64 encoded marshalled data
# set to :key (default: rack.session).
# When the secret key is set, cookie data is checked for data integrity.
#
# Example:
#
# use Rack::Session::Cookie, :key => 'rack.session',
# :domain => 'foo.com',
# :path => '/',
# :expire_after => 2592000,
# :secret => 'change_me'
#
# All parameters are optional.
class Cookie
def initialize(app, options={})
@app = app
@key = options[:key] || "rack.session"
@secret = options[:secret]
@default_options = {:domain => nil,
:path => "/",
:expire_after => nil}.merge(options)
end
def call(env)
load_session(env)
status, headers, body = @app.call(env)
commit_session(env, status, headers, body)
end
private
def load_session(env)
request = Rack::Request.new(env)
session_data = request.cookies[@key]
if @secret && session_data
session_data, digest = session_data.split("--")
session_data = nil unless digest == generate_hmac(session_data)
end
begin
session_data = session_data.unpack("m*").first
session_data = Marshal.load(session_data)
env["rack.session"] = session_data
rescue
env["rack.session"] = Hash.new
end
env["rack.session.options"] = @default_options.dup
end
def commit_session(env, status, headers, body)
session_data = Marshal.dump(env["rack.session"])
session_data = [session_data].pack("m*")
if @secret
session_data = "#{session_data}--#{generate_hmac(session_data)}"
end
if session_data.size > (4096 - @key.size)
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
[status, headers, body]
else
options = env["rack.session.options"]
cookie = Hash.new
cookie[:value] = session_data
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
response = Rack::Response.new(body, status, headers)
response.set_cookie(@key, cookie.merge(options))
response.to_a
end
end
def generate_hmac(data)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
end
end
end
end

View File

@@ -1,109 +0,0 @@
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
require 'rack/session/abstract/id'
require 'memcache'
module Rack
module Session
# Rack::Session::Memcache provides simple cookie based session management.
# Session data is stored in memcached. The corresponding session key is
# maintained in the cookie.
# You may treat Session::Memcache as you would Session::Pool with the
# following caveats.
#
# * Setting :expire_after to 0 would note to the Memcache server to hang
# onto the session data until it would drop it according to it's own
# specifications. However, the cookie sent to the client would expire
# immediately.
#
# Note that memcache does drop data before it may be listed to expire. For
# a full description of behaviour, please see memcache's documentation.
class Memcache < Abstract::ID
attr_reader :mutex, :pool
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
:namespace => 'rack:session',
:memcache_server => 'localhost:11211'
def initialize(app, options={})
super
@mutex = Mutex.new
@pool = MemCache.
new @default_options[:memcache_server], @default_options
raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?}
end
def generate_sid
loop do
sid = super
break sid unless @pool.get(sid, true)
end
end
def get_session(env, sid)
session = @pool.get(sid) if sid
@mutex.lock if env['rack.multithread']
unless sid and session
env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
session = {}
sid = generate_sid
ret = @pool.add sid, session
raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret
end
session.instance_variable_set('@old', {}.merge(session))
return [sid, session]
rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted
warn "#{self} is unable to find server."
warn $!.inspect
return [ nil, {} ]
ensure
@mutex.unlock if env['rack.multithread']
end
def set_session(env, session_id, new_session, options)
expiry = options[:expire_after]
expiry = expiry.nil? ? 0 : expiry + 1
@mutex.lock if env['rack.multithread']
session = @pool.get(session_id) || {}
if options[:renew] or options[:drop]
@pool.delete session_id
return false if options[:drop]
session_id = generate_sid
@pool.add session_id, 0 # so we don't worry about cache miss on #set
end
old_session = new_session.instance_variable_get('@old') || {}
session = merge_sessions session_id, old_session, new_session, session
@pool.set session_id, session, expiry
return session_id
rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted
warn "#{self} is unable to find server."
warn $!.inspect
return false
ensure
@mutex.unlock if env['rack.multithread']
end
private
def merge_sessions sid, old, new, cur=nil
cur ||= {}
unless Hash === old and Hash === new
warn 'Bad old or new sessions provided.'
return cur
end
delete = old.keys - new.keys
warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty?
delete.each{|k| cur.delete k }
update = new.keys.select{|k| new[k] != old[k] }
warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty?
update.each{|k| cur[k] = new[k] }
cur
end
end
end
end

View File

@@ -1,100 +0,0 @@
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
# THANKS:
# apeiros, for session id generation, expiry setup, and threadiness
# sergio, threadiness and bugreps
require 'rack/session/abstract/id'
require 'thread'
module Rack
module Session
# Rack::Session::Pool provides simple cookie based session management.
# Session data is stored in a hash held by @pool.
# In the context of a multithreaded environment, sessions being
# committed to the pool is done in a merging manner.
#
# The :drop option is available in rack.session.options if you with to
# explicitly remove the session from the session cache.
#
# Example:
# myapp = MyRackApp.new
# sessioned = Rack::Session::Pool.new(myapp,
# :domain => 'foo.com',
# :expire_after => 2592000
# )
# Rack::Handler::WEBrick.run sessioned
class Pool < Abstract::ID
attr_reader :mutex, :pool
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
def initialize(app, options={})
super
@pool = Hash.new
@mutex = Mutex.new
end
def generate_sid
loop do
sid = super
break sid unless @pool.key? sid
end
end
def get_session(env, sid)
session = @pool[sid] if sid
@mutex.lock if env['rack.multithread']
unless sid and session
env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
session = {}
sid = generate_sid
@pool.store sid, session
end
session.instance_variable_set('@old', {}.merge(session))
return [sid, session]
ensure
@mutex.unlock if env['rack.multithread']
end
def set_session(env, session_id, new_session, options)
@mutex.lock if env['rack.multithread']
session = @pool[session_id]
if options[:renew] or options[:drop]
@pool.delete session_id
return false if options[:drop]
session_id = generate_sid
@pool.store session_id, 0
end
old_session = new_session.instance_variable_get('@old') || {}
session = merge_sessions session_id, old_session, new_session, session
@pool.store session_id, session
return session_id
rescue
warn "#{new_session.inspect} has been lost."
warn $!.inspect
ensure
@mutex.unlock if env['rack.multithread']
end
private
def merge_sessions sid, old, new, cur=nil
cur ||= {}
unless Hash === old and Hash === new
warn 'Bad old or new sessions provided.'
return cur
end
delete = old.keys - new.keys
warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
delete.each{|k| cur.delete k }
update = new.keys.select{|k| new[k] != old[k] }
warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
update.each{|k| cur[k] = new[k] }
cur
end
end
end
end

View File

@@ -1,349 +0,0 @@
require 'ostruct'
require 'erb'
require 'rack/request'
require 'rack/utils'
module Rack
# Rack::ShowExceptions catches all exceptions raised from the app it
# wraps. It shows a useful backtrace with the sourcefile and
# clickable context, the whole Rack environment and the request
# data.
#
# Be careful when you use this on public-facing sites as it could
# reveal information helpful to attackers.
class ShowExceptions
CONTEXT = 7
def initialize(app)
@app = app
@template = ERB.new(TEMPLATE)
end
def call(env)
@app.call(env)
rescue StandardError, LoadError, SyntaxError => e
backtrace = pretty(env, e)
[500,
{"Content-Type" => "text/html",
"Content-Length" => backtrace.join.size.to_s},
backtrace]
end
def pretty(env, exception)
req = Rack::Request.new(env)
path = (req.script_name + req.path_info).squeeze("/")
frames = exception.backtrace.map { |line|
frame = OpenStruct.new
if line =~ /(.*?):(\d+)(:in `(.*)')?/
frame.filename = $1
frame.lineno = $2.to_i
frame.function = $4
begin
lineno = frame.lineno-1
lines = ::File.readlines(frame.filename)
frame.pre_context_lineno = [lineno-CONTEXT, 0].max
frame.pre_context = lines[frame.pre_context_lineno...lineno]
frame.context_line = lines[lineno].chomp
frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
frame.post_context = lines[lineno+1..frame.post_context_lineno]
rescue
end
frame
else
nil
end
}.compact
env["rack.errors"].puts "#{exception.class}: #{exception.message}"
env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l }
env["rack.errors"].flush
[@template.result(binding)]
end
def h(obj) # :nodoc:
case obj
when String
Utils.escape_html(obj)
else
Utils.escape_html(obj.inspect)
end
end
# :stopdoc:
# adapted from Django <djangoproject.com>
# Copyright (c) 2005, the Lawrence Journal-World
# Used under the modified BSD license:
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
TEMPLATE = <<'HTML'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="robots" content="NONE,NOARCHIVE" />
<title><%=h exception.class %> at <%=h path %></title>
<style type="text/css">
html * { padding:0; margin:0; }
body * { padding:10px 20px; }
body * * { padding:0; }
body { font:small sans-serif; }
body>div { border-bottom:1px solid #ddd; }
h1 { font-weight:normal; }
h2 { margin-bottom:.8em; }
h2 span { font-size:80%; color:#666; font-weight:normal; }
h3 { margin:1em 0 .5em 0; }
h4 { margin:0 0 .5em 0; font-weight: normal; }
table {
border:1px solid #ccc; border-collapse: collapse; background:white; }
tbody td, tbody th { vertical-align:top; padding:2px 3px; }
thead th {
padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
font-weight:normal; font-size:11px; border:1px solid #ddd; }
tbody th { text-align:right; color:#666; padding-right:.5em; }
table.vars { margin:5px 0 2px 40px; }
table.vars td, table.req td { font-family:monospace; }
table td.code { width:100%;}
table td.code div { overflow:hidden; }
table.source th { color:#666; }
table.source td {
font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
ul.traceback { list-style-type:none; }
ul.traceback li.frame { margin-bottom:1em; }
div.context { margin: 10px 0; }
div.context ol {
padding-left:30px; margin:0 10px; list-style-position: inside; }
div.context ol li {
font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
div.context ol.context-line li { color:black; background-color:#ccc; }
div.context ol.context-line li span { float: right; }
div.commands { margin-left: 40px; }
div.commands a { color:black; text-decoration:none; }
#summary { background: #ffc; }
#summary h2 { font-weight: normal; color: #666; }
#summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
#summary ul#quicklinks li { float: left; padding: 0 1em; }
#summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
#explanation { background:#eee; }
#template, #template-not-exist { background:#f6f6f6; }
#template-not-exist ul { margin: 0 0 0 20px; }
#traceback { background:#eee; }
#requestinfo { background:#f6f6f6; padding-left:120px; }
#summary table { border:none; background:transparent; }
#requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
#requestinfo h3 { margin-bottom:-1em; }
.error { background: #ffc; }
.specific { color:#cc3300; font-weight:bold; }
</style>
<script type="text/javascript">
//<!--
function getElementsByClassName(oElm, strTagName, strClassName){
// Written by Jonathan Snook, http://www.snook.ca/jon;
// Add-ons by Robert Nyman, http://www.robertnyman.com
var arrElements = (strTagName == "*" && document.all)? document.all :
oElm.getElementsByTagName(strTagName);
var arrReturnElements = new Array();
strClassName = strClassName.replace(/\-/g, "\\-");
var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
var oElement;
for(var i=0; i<arrElements.length; i++){
oElement = arrElements[i];
if(oRegExp.test(oElement.className)){
arrReturnElements.push(oElement);
}
}
return (arrReturnElements)
}
function hideAll(elems) {
for (var e = 0; e < elems.length; e++) {
elems[e].style.display = 'none';
}
}
window.onload = function() {
hideAll(getElementsByClassName(document, 'table', 'vars'));
hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
hideAll(getElementsByClassName(document, 'ol', 'post-context'));
}
function toggle() {
for (var i = 0; i < arguments.length; i++) {
var e = document.getElementById(arguments[i]);
if (e) {
e.style.display = e.style.display == 'none' ? 'block' : 'none';
}
}
return false;
}
function varToggle(link, id) {
toggle('v' + id);
var s = link.getElementsByTagName('span')[0];
var uarr = String.fromCharCode(0x25b6);
var darr = String.fromCharCode(0x25bc);
s.innerHTML = s.innerHTML == uarr ? darr : uarr;
return false;
}
//-->
</script>
</head>
<body>
<div id="summary">
<h1><%=h exception.class %> at <%=h path %></h1>
<h2><%=h exception.message %></h2>
<table><tr>
<th>Ruby</th>
<td><code><%=h frames.first.filename %></code>: in <code><%=h frames.first.function %></code>, line <%=h frames.first.lineno %></td>
</tr><tr>
<th>Web</th>
<td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td>
</tr></table>
<h3>Jump to:</h3>
<ul id="quicklinks">
<li><a href="#get-info">GET</a></li>
<li><a href="#post-info">POST</a></li>
<li><a href="#cookie-info">Cookies</a></li>
<li><a href="#env-info">ENV</a></li>
</ul>
</div>
<div id="traceback">
<h2>Traceback <span>(innermost first)</span></h2>
<ul class="traceback">
<% frames.each { |frame| %>
<li class="frame">
<code><%=h frame.filename %></code>: in <code><%=h frame.function %></code>
<% if frame.context_line %>
<div class="context" id="c<%=h frame.object_id %>">
<% if frame.pre_context %>
<ol start="<%=h frame.pre_context_lineno+1 %>" class="pre-context" id="pre<%=h frame.object_id %>">
<% frame.pre_context.each { |line| %>
<li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
<% } %>
</ol>
<% end %>
<ol start="<%=h frame.lineno %>" class="context-line">
<li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h frame.context_line %><span>...</span></li></ol>
<% if frame.post_context %>
<ol start='<%=h frame.lineno+1 %>' class="post-context" id="post<%=h frame.object_id %>">
<% frame.post_context.each { |line| %>
<li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
<% } %>
</ol>
<% end %>
</div>
<% end %>
</li>
<% } %>
</ul>
</div>
<div id="requestinfo">
<h2>Request information</h2>
<h3 id="get-info">GET</h3>
<% unless req.GET.empty? %>
<table class="req">
<thead>
<tr>
<th>Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
<tr>
<td><%=h key %></td>
<td class="code"><div><%=h val.inspect %></div></td>
</tr>
<% } %>
</tbody>
</table>
<% else %>
<p>No GET data.</p>
<% end %>
<h3 id="post-info">POST</h3>
<% unless req.POST.empty? %>
<table class="req">
<thead>
<tr>
<th>Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
<tr>
<td><%=h key %></td>
<td class="code"><div><%=h val.inspect %></div></td>
</tr>
<% } %>
</tbody>
</table>
<% else %>
<p>No POST data.</p>
<% end %>
<h3 id="cookie-info">COOKIES</h3>
<% unless req.cookies.empty? %>
<table class="req">
<thead>
<tr>
<th>Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<% req.cookies.each { |key, val| %>
<tr>
<td><%=h key %></td>
<td class="code"><div><%=h val.inspect %></div></td>
</tr>
<% } %>
</tbody>
</table>
<% else %>
<p>No cookie data.</p>
<% end %>
<h3 id="env-info">Rack ENV</h3>
<table class="req">
<thead>
<tr>
<th>Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
<tr>
<td><%=h key %></td>
<td class="code"><div><%=h val %></div></td>
</tr>
<% } %>
</tbody>
</table>
</div>
<div id="explanation">
<p>
You're seeing this error because you use <code>Rack::ShowExceptions</code>.
</p>
</div>
</body>
</html>
HTML
# :startdoc:
end
end

View File

@@ -1,106 +0,0 @@
require 'erb'
require 'rack/request'
require 'rack/utils'
module Rack
# Rack::ShowStatus catches all empty responses the app it wraps and
# replaces them with a site explaining the error.
#
# Additional details can be put into <tt>rack.showstatus.detail</tt>
# and will be shown as HTML. If such details exist, the error page
# is always rendered, even if the reply was not empty.
class ShowStatus
def initialize(app)
@app = app
@template = ERB.new(TEMPLATE)
end
def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
empty = headers['Content-Length'].to_i <= 0
# client or server error, or explicit message
if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
req = Rack::Request.new(env)
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
detail = env["rack.showstatus.detail"] || message
body = @template.result(binding)
size = Rack::Utils.bytesize(body)
[status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]]
else
[status, headers, body]
end
end
def h(obj) # :nodoc:
case obj
when String
Utils.escape_html(obj)
else
Utils.escape_html(obj.inspect)
end
end
# :stopdoc:
# adapted from Django <djangoproject.com>
# Copyright (c) 2005, the Lawrence Journal-World
# Used under the modified BSD license:
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
TEMPLATE = <<'HTML'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><%=h message %> at <%=h req.script_name + req.path_info %></title>
<meta name="robots" content="NONE,NOARCHIVE" />
<style type="text/css">
html * { padding:0; margin:0; }
body * { padding:10px 20px; }
body * * { padding:0; }
body { font:small sans-serif; background:#eee; }
body>div { border-bottom:1px solid #ddd; }
h1 { font-weight:normal; margin-bottom:.4em; }
h1 span { font-size:60%; color:#666; font-weight:normal; }
table { border:none; border-collapse: collapse; width:100%; }
td, th { vertical-align:top; padding:2px 3px; }
th { width:12em; text-align:right; color:#666; padding-right:.5em; }
#info { background:#f6f6f6; }
#info ol { margin: 0.5em 4em; }
#info ol li { font-family: monospace; }
#summary { background: #ffc; }
#explanation { background:#eee; border-bottom: 0px none; }
</style>
</head>
<body>
<div id="summary">
<h1><%=h message %> <span>(<%= status.to_i %>)</span></h1>
<table class="meta">
<tr>
<th>Request Method:</th>
<td><%=h req.request_method %></td>
</tr>
<tr>
<th>Request URL:</th>
<td><%=h req.url %></td>
</tr>
</table>
</div>
<div id="info">
<p><%= detail %></p>
</div>
<div id="explanation">
<p>
You're seeing this error because you use <code>Rack::ShowStatus</code>.
</p>
</div>
</body>
</html>
HTML
# :startdoc:
end
end

View File

@@ -1,38 +0,0 @@
module Rack
# The Rack::Static middleware intercepts requests for static files
# (javascript files, images, stylesheets, etc) based on the url prefixes
# passed in the options, and serves them using a Rack::File object. This
# allows a Rack stack to serve both static and dynamic content.
#
# Examples:
# use Rack::Static, :urls => ["/media"]
# will serve all requests beginning with /media from the "media" folder
# located in the current directory (ie media/*).
#
# use Rack::Static, :urls => ["/css", "/images"], :root => "public"
# will serve all requests beginning with /css or /images from the folder
# "public" in the current directory (ie public/css/* and public/images/*)
class Static
def initialize(app, options={})
@app = app
@urls = options[:urls] || ["/favicon.ico"]
root = options[:root] || Dir.pwd
@file_server = Rack::File.new(root)
end
def call(env)
path = env["PATH_INFO"]
can_serve = @urls.any? { |url| path.index(url) == 0 }
if can_serve
@file_server.call(env)
else
@app.call(env)
end
end
end
end

View File

@@ -1,55 +0,0 @@
module Rack
# Rack::URLMap takes a hash mapping urls or paths to apps, and
# dispatches accordingly. Support for HTTP/1.1 host names exists if
# the URLs start with <tt>http://</tt> or <tt>https://</tt>.
#
# URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part
# relevant for dispatch is in the SCRIPT_NAME, and the rest in the
# PATH_INFO. This should be taken care of when you need to
# reconstruct the URL in order to create links.
#
# URLMap dispatches in such a way that the longest paths are tried
# first, since they are most specific.
class URLMap
def initialize(map = {})
remap(map)
end
def remap(map)
@mapping = map.map { |location, app|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
host, location = $1, $2
else
host = nil
end
unless location[0] == ?/
raise ArgumentError, "paths need to start with /"
end
location = location.chomp('/')
[host, location, app]
}.sort_by { |(h, l, a)| [-l.size, h.to_s.size] } # Longest path first
end
def call(env)
path = env["PATH_INFO"].to_s.squeeze("/")
script_name = env['SCRIPT_NAME']
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
@mapping.each { |host, location, app|
next unless (hHost == host || sName == host \
|| (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
next unless location == path[0, location.size]
next unless path[location.size] == nil || path[location.size] == ?/
return app.call(
env.merge(
'SCRIPT_NAME' => (script_name + location),
'PATH_INFO' => path[location.size..-1]))
}
[404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]]
end
end
end

View File

@@ -1,392 +0,0 @@
require 'set'
require 'tempfile'
module Rack
# Rack::Utils contains a grab-bag of useful methods for writing web
# applications adopted from all kinds of Ruby libraries.
module Utils
# Performs URI escaping so that you can construct proper
# query strings faster. Use this rather than the cgi.rb
# version since it's faster. (Stolen from Camping).
def escape(s)
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
'%'+$1.unpack('H2'*$1.size).join('%').upcase
}.tr(' ', '+')
end
module_function :escape
# Unescapes a URI escaped string. (Stolen from Camping).
def unescape(s)
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
[$1.delete('%')].pack('H*')
}
end
module_function :unescape
# Stolen from Mongrel, with some small modifications:
# Parses a query string by breaking it up at the '&'
# and ';' characters. You can also use this to parse
# cookies by changing the characters used in the second
# parameter (which defaults to '&;').
def parse_query(qs, d = '&;')
params = {}
(qs || '').split(/[#{d}] */n).each do |p|
k, v = unescape(p).split('=', 2)
if cur = params[k]
if cur.class == Array
params[k] << v
else
params[k] = [cur, v]
end
else
params[k] = v
end
end
return params
end
module_function :parse_query
def parse_nested_query(qs, d = '&;')
params = {}
(qs || '').split(/[#{d}] */n).each do |p|
k, v = unescape(p).split('=', 2)
normalize_params(params, k, v)
end
return params
end
module_function :parse_nested_query
def normalize_params(params, name, v = nil)
name =~ %r([\[\]]*([^\[\]]+)\]*)
k = $1 || ''
after = $' || ''
return if k.empty?
if after == ""
params[k] = v
elsif after == "[]"
params[k] ||= []
raise TypeError unless params[k].is_a?(Array)
params[k] << v
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
child_key = $1
params[k] ||= []
raise TypeError unless params[k].is_a?(Array)
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
normalize_params(params[k].last, child_key, v)
else
params[k] << normalize_params({}, child_key, v)
end
else
params[k] ||= {}
params[k] = normalize_params(params[k], after, v)
end
return params
end
module_function :normalize_params
def build_query(params)
params.map { |k, v|
if v.class == Array
build_query(v.map { |x| [k, x] })
else
escape(k) + "=" + escape(v)
end
}.join("&")
end
module_function :build_query
# Escape ampersands, brackets and quotes to their HTML/XML entities.
def escape_html(string)
string.to_s.gsub("&", "&amp;").
gsub("<", "&lt;").
gsub(">", "&gt;").
gsub("'", "&#39;").
gsub('"', "&quot;")
end
module_function :escape_html
def select_best_encoding(available_encodings, accept_encoding)
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
expanded_accept_encoding =
accept_encoding.map { |m, q|
if m == "*"
(available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
else
[[m, q]]
end
}.inject([]) { |mem, list|
mem + list
}
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
unless encoding_candidates.include?("identity")
encoding_candidates.push("identity")
end
expanded_accept_encoding.find_all { |m, q|
q == 0.0
}.each { |m, _|
encoding_candidates.delete(m)
}
return (encoding_candidates & available_encodings)[0]
end
module_function :select_best_encoding
# Return the bytesize of String; uses String#length under Ruby 1.8 and
# String#bytesize under 1.9.
if ''.respond_to?(:bytesize)
def bytesize(string)
string.bytesize
end
else
def bytesize(string)
string.size
end
end
module_function :bytesize
# Context allows the use of a compatible middleware at different points
# in a request handling stack. A compatible middleware must define
# #context which should take the arguments env and app. The first of which
# would be the request environment. The second of which would be the rack
# application that the request would be forwarded to.
class Context
attr_reader :for, :app
def initialize(app_f, app_r)
raise 'running context does not respond to #context' unless app_f.respond_to? :context
@for, @app = app_f, app_r
end
def call(env)
@for.context(env, @app)
end
def recontext(app)
self.class.new(@for, app)
end
def context(env, app=@app)
recontext(app).call(env)
end
end
# A case-insensitive Hash that preserves the original case of a
# header when set.
class HeaderHash < Hash
def initialize(hash={})
@names = {}
hash.each { |k, v| self[k] = v }
end
def to_hash
inject({}) do |hash, (k,v)|
if v.respond_to? :to_ary
hash[k] = v.to_ary.join("\n")
else
hash[k] = v
end
hash
end
end
def [](k)
super @names[k.downcase]
end
def []=(k, v)
delete k
@names[k.downcase] = k
super k, v
end
def delete(k)
super @names.delete(k.downcase)
end
def include?(k)
@names.has_key? k.downcase
end
alias_method :has_key?, :include?
alias_method :member?, :include?
alias_method :key?, :include?
def merge!(other)
other.each { |k, v| self[k] = v }
self
end
def merge(other)
hash = dup
hash.merge! other
end
end
# Every standard HTTP code mapped to the appropriate message.
# Stolen from Mongrel.
HTTP_STATUS_CODES = {
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Large',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported'
}
# Responses with HTTP status codes that should not have an entity body
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
# A multipart form data parser, adapted from IOWA.
#
# Usually, Rack::Request#POST takes care of calling this.
module Multipart
EOL = "\r\n"
def self.parse_multipart(env)
unless env['CONTENT_TYPE'] =~
%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
nil
else
boundary = "--#{$1}"
params = {}
buf = ""
content_length = env['CONTENT_LENGTH'].to_i
input = env['rack.input']
boundary_size = boundary.size + EOL.size
bufsize = 16384
content_length -= boundary_size
status = input.read(boundary_size)
raise EOFError, "bad content body" unless status == boundary + EOL
rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
loop {
head = nil
body = ''
filename = content_type = name = nil
until head && buf =~ rx
if !head && i = buf.index("\r\n\r\n")
head = buf.slice!(0, i+2) # First \r\n
buf.slice!(0, 2) # Second \r\n
filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
content_type = head[/Content-Type: (.*)\r\n/ni, 1]
name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
if filename
body = Tempfile.new("RackMultipart")
body.binmode if body.respond_to?(:binmode)
end
next
end
# Save the read body part.
if head && (boundary_size+4 < buf.size)
body << buf.slice!(0, buf.size - (boundary_size+4))
end
c = input.read(bufsize < content_length ? bufsize : content_length)
raise EOFError, "bad content body" if c.nil? || c.empty?
buf << c
content_length -= c.size
end
# Save the rest.
if i = buf.index(rx)
body << buf.slice!(0, i)
buf.slice!(0, boundary_size+2)
content_length = -1 if $1 == "--"
end
if filename == ""
# filename is blank which means no file has been selected
data = nil
elsif filename
body.rewind
# Take the basename of the upload's original filename.
# This handles the full Windows paths given by Internet Explorer
# (and perhaps other broken user agents) without affecting
# those which give the lone filename.
filename =~ /^(?:.*[:\\\/])?(.*)/m
filename = $1
data = {:filename => filename, :type => content_type,
:name => name, :tempfile => body, :head => head}
else
data = body
end
Utils.normalize_params(params, name, data) unless data.nil?
break if buf.empty? || content_length == -1
}
begin
input.rewind if input.respond_to?(:rewind)
rescue Errno::ESPIPE
# Handles exceptions raised by input streams that cannot be rewound
# such as when using plain CGI under Apache
end
params
end
end
end
end
end

View File

@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 2
TINY = 4
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -11,7 +11,7 @@ module ActionView #:nodoc:
autoload :FormHelper, 'action_view/helpers/form_helper'
autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper'
autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
autoload :JavascriptHelper, 'action_view/helpers/javascript_helper'
autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper'
autoload :NumberHelper, 'action_view/helpers/number_helper'
autoload :PrototypeHelper, 'action_view/helpers/prototype_helper'
autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper'

View File

@@ -272,14 +272,17 @@ module ActionView
# javascript_include_tag :all, :cache => true, :recursive => true
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
cache = options.delete("cache")
concat = options.delete("concat")
cache = concat || options.delete("cache")
recursive = options.delete("recursive")
if ActionController::Base.perform_caching && cache
if concat || (ActionController::Base.perform_caching && cache)
joined_javascript_name = (cache == true ? "all" : cache) + ".js"
joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name)
write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path)
unless ActionController::Base.perform_caching && File.exists?(joined_javascript_path)
write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive))
end
javascript_src_tag(joined_javascript_name, options)
else
expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
@@ -410,16 +413,25 @@ module ActionView
# The <tt>:recursive</tt> option is also available for caching:
#
# stylesheet_link_tag :all, :cache => true, :recursive => true
#
# To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
# you have too many stylesheets for IE to load.
#
# stylesheet_link_tag :all, :concat => true
#
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
cache = options.delete("cache")
concat = options.delete("concat")
cache = concat || options.delete("cache")
recursive = options.delete("recursive")
if ActionController::Base.perform_caching && cache
if concat || (ActionController::Base.perform_caching && cache)
joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : STYLESHEETS_DIR, joined_stylesheet_name)
write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path)
unless ActionController::Base.perform_caching && File.exists?(joined_stylesheet_path)
write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive))
end
stylesheet_tag(joined_stylesheet_name, options)
else
expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")
@@ -679,4 +691,4 @@ module ActionView
end
end
end
end
end

View File

@@ -98,7 +98,7 @@ module ActionView
options[:schema_date] = "2005" # The Atom spec copyright date
end
xml = options[:xml] || eval("xml", block.binding)
xml = options.delete(:xml) || eval("xml", block.binding)
xml.instruct!
if options[:instruct]
options[:instruct].each do |target,attrs|

View File

@@ -493,7 +493,8 @@ module ActionView
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
# it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
# onto the HTML as an HTML element attribute as in the example shown.
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
# target labels for radio_button tags (where the value is used in the ID of the input tag).
#
# ==== Examples
# label(:post, :title)
@@ -505,6 +506,9 @@ module ActionView
# label(:post, :title, "A short title", :class => "title_label")
# # => <label for="post_title" class="title_label">A short title</label>
#
# label(:post, :privacy, "Public Post", :value => "public")
# # => <label for="post_privacy_public">Public Post</label>
#
def label(object_name, method, text = nil, options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options)
end
@@ -720,8 +724,10 @@ module ActionView
def to_label_tag(text = nil, options = {})
options = options.stringify_keys
tag_value = options.delete("value")
name_and_id = options.dup
add_default_name_and_id(name_and_id)
name_and_id["id"] = name_and_id["for"]
add_default_name_and_id_for_value(tag_value, name_and_id)
options.delete("index")
options["for"] ||= name_and_id["id"]
content = (text.blank? ? nil : text.to_s) || method_name.humanize
@@ -753,11 +759,7 @@ module ActionView
checked = self.class.radio_button_checked?(value(object), tag_value)
end
options["checked"] = "checked" if checked
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
options["id"] ||= defined?(@auto_index) ?
"#{tag_id_with_index(@auto_index)}_#{pretty_tag_value}" :
"#{tag_id}_#{pretty_tag_value}"
add_default_name_and_id(options)
add_default_name_and_id_for_value(tag_value, options)
tag("input", options)
end
@@ -858,6 +860,17 @@ module ActionView
end
private
def add_default_name_and_id_for_value(tag_value, options)
unless tag_value.nil?
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
specified_id = options["id"]
add_default_name_and_id(options)
options["id"] += "_#{pretty_tag_value}" unless specified_id
else
add_default_name_and_id(options)
end
end
def add_default_name_and_id(options)
if options.has_key?("index")
options["name"] ||= tag_name_with_index(options["index"])
@@ -905,6 +918,7 @@ module ActionView
attr_accessor :object_name, :object, :options
def initialize(object_name, object, template, options, proc)
@nested_child_index = {}
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
@default_options = @options ? @options.slice(:index) : {}
if @object_name.to_s.match(/\[\]$/)
@@ -1007,7 +1021,7 @@ module ActionView
explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash)
children.map do |child|
fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index}]", child, args, block)
fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block)
end.join
else
fields_for_nested_model(name, explicit_object || association, args, block)
@@ -1025,9 +1039,9 @@ module ActionView
end
end
def nested_child_index
@nested_child_index ||= -1
@nested_child_index += 1
def nested_child_index(name)
@nested_child_index[name] ||= -1
@nested_child_index[name] += 1
end
end
end
@@ -1036,4 +1050,4 @@ module ActionView
cattr_accessor :default_form_builder
self.default_form_builder = ::ActionView::Helpers::FormBuilder
end
end
end

View File

@@ -162,6 +162,60 @@ module ActionView
InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
end
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
# or <tt>:include_blank</tt> in the +options+ hash.
#
# Parameters:
# * +object+ - The instance of the class to be used for the select tag
# * +method+ - The attribute of +object+ corresponding to the select tag
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
# array of child objects representing the <tt><option></tt> tags.
# * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
#
# Example object structure for use with this method:
# class Continent < ActiveRecord::Base
# has_many :countries
# # attribs: id, name
# end
# class Country < ActiveRecord::Base
# belongs_to :continent
# # attribs: id, name, continent_id
# end
# class City < ActiveRecord::Base
# belongs_to :country
# # attribs: id, name, country_id
# end
#
# Sample usage:
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
#
# Possible output:
# <select name="city[country_id]">
# <optgroup label="Africa">
# <option value="1">South Africa</option>
# <option value="3">Somalia</option>
# </optgroup>
# <optgroup label="Europe">
# <option value="7" selected="selected">Denmark</option>
# <option value="2">Ireland</option>
# </optgroup>
# </select>
#
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
end
# Return select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#
@@ -230,6 +284,8 @@ module ActionView
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def options_for_select(container, selected = nil)
return container if String === container
container = container.to_a if Hash === container
selected, disabled = extract_selected_and_disabled(selected)
@@ -488,6 +544,15 @@ module ActionView
)
end
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
value = value(object)
content_tag(
"select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
)
end
def to_time_zone_select_tag(priority_zones, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
@@ -506,7 +571,8 @@ module ActionView
option_tags = "<option value=\"\">#{options[:include_blank] if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
end
if value.blank? && options[:prompt]
("<option value=\"\">#{options[:prompt].kind_of?(String) ? options[:prompt] : 'Please select'}</option>\n") + option_tags
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select')
"<option value=\"\">#{prompt}</option>\n" + option_tags
else
option_tags
end
@@ -522,6 +588,10 @@ module ActionView
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
end
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
end
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
end

View File

@@ -230,6 +230,8 @@ module ActionView
# * <tt>:rows</tt> - Specify the number of rows in the textarea
# * <tt>:cols</tt> - Specify the number of columns in the textarea
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
# * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
# If you need unescaped contents, set this to false.
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
@@ -257,7 +259,10 @@ module ActionView
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
end
content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys)
escape = options.key?("escape") ? options.delete("escape") : true
content = html_escape(content) if escape
content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
# Creates a check box form input tag.
@@ -353,7 +358,8 @@ module ActionView
disable_with << ";#{options.delete('onclick')}" if options['onclick']
options["onclick"] = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }"
options["onclick"] << "else { hiddenCommit = this.cloneNode(false);hiddenCommit.setAttribute('type', 'hidden');this.form.appendChild(hiddenCommit); }"
options["onclick"] << "else { hiddenCommit = document.createElement('input');hiddenCommit.type = 'hidden';"
options["onclick"] << "hiddenCommit.value = this.value;hiddenCommit.name = this.name;this.form.appendChild(hiddenCommit); }"
options["onclick"] << "this.setAttribute('originalValue', this.value);this.disabled = true;#{disable_with};"
options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());"
options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;"
@@ -444,10 +450,10 @@ module ActionView
''
when /^post$/i, "", nil
html_options["method"] = "post"
protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : ''
protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : ''
else
html_options["method"] = "post"
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0')
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline')
end
end

Some files were not shown because too many files have changed in this diff Show More