Compare commits

...

99 Commits

Author SHA1 Message Date
Thomas Fuchs
37e0d5de89 Update stable to scriptaculous 1.5 final
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3300 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 18:41:10 +00:00
Jeremy Kemper
e8c7d7af48 r3465@asus: jeremy | 2005-12-13 10:15:33 -0800
Apply [3298] to stable.  Don't used defined? on a scoped constant since it results in a const_missing call.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3299 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 18:37:32 +00:00
Sam Stephenson
cad37c3eaf Update to Prototype 1.4.0 final
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3297 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 17:51:17 +00:00
Jeremy Kemper
da66198fcd r3458@asus: jeremy | 2005-12-13 09:33:50 -0800
Roll back [3245] on stable.  References #3116.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3296 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 17:34:09 +00:00
Jeremy Kemper
2670371690 r3457@asus: jeremy | 2005-12-13 08:52:39 -0800
Apply [3293] to stable.  MySQL: allow encoding option for mysql.rb driver.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3295 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 17:34:00 +00:00
David Heinemeier Hansson
5da86f8c27 Made ready for 1.0 launch
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3292 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 16:41:44 +00:00
Marcel Molina
11598ac77b Apply [3290] to stable. Fix typo in benchmarker usage string.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3291 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 02:33:56 +00:00
Marcel Molina
eeb00ea334 Apply [3288] to stable. Fix typo in profiler usage string.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3289 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-13 02:26:15 +00:00
Marcel Molina
d5bfd181d7 Remove duplicate entry from chagnelogs.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3278 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-11 05:09:52 +00:00
Marcel Molina
921c5a7fdc Apply [3276] to stable. Update instructions on how to find and install generators. Closes #3172.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3277 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-11 05:07:39 +00:00
Jeremy Kemper
4ef417cf96 r3435@asus: jeremy | 2005-12-10 13:40:43 -0800
Apply [3274] to stable.  MySQL: fixes for the bundled mysql.rb driver.  Closes #3160.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3275 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-10 21:41:16 +00:00
David Heinemeier Hansson
65e93a523f Close h1 with h1 not h2 #3150
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3273 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-10 19:58:02 +00:00
Jeremy Kemper
2018b2a8d2 r3429@asus: jeremy | 2005-12-09 16:55:16 -0800
Apply [3270] SQLServer rollup to stable.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3271 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-10 01:44:03 +00:00
Jeremy Kemper
596fe49697 r3421@asus: jeremy | 2005-12-09 15:42:40 -0800
Apply [3265] to stable.  Name vendor/generators source differently from lib/generators source.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3268 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 23:43:20 +00:00
Jeremy Kemper
4e2e1a1bc6 r3420@asus: jeremy | 2005-12-09 15:38:30 -0800
Apply [3264] to stable.  MySQL: ensure that @config is set.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3267 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 23:43:16 +00:00
Jeremy Kemper
36619f2084 r3419@asus: jeremy | 2005-12-09 15:36:50 -0800
Apply [3165] to stable. MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. References #2978.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3266 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 23:43:09 +00:00
Jeremy Kemper
d2b0cc7bf4 r3416@asus: jeremy | 2005-12-09 15:07:21 -0800
Apply [3262] to stable.  Generator looks in vendor/generators also.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3263 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 23:07:28 +00:00
Jeremy Kemper
eb79743b13 r3413@asus: jeremy | 2005-12-09 15:03:42 -0800
Apply [3260] to stable.  Fix bundled mysql.rb to correctly check for PROTO_41.  Fixed scramble41 with nil password.  Fixed change_user with PROTO_41.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3261 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 23:03:47 +00:00
Jeremy Kemper
256d28d330 r3410@asus: jeremy | 2005-12-09 10:48:32 -0800
Apply [3258] to stable.  Fix shebang handling for empty files.  Closes #2927.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3259 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 18:48:38 +00:00
Jeremy Kemper
1b4470c82f r3407@asus: jeremy | 2005-12-09 10:39:00 -0800
Apply [3256] to stable.  Generator copies files in binary mode.  Closes #3156.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3257 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-09 18:39:15 +00:00
Jeremy Kemper
69e4c3c474 r3404@asus: jeremy | 2005-12-08 15:24:44 -0800
Apply [3254] to stable.  Fix some test failures due to MySQL assumptions.  References #3149.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3255 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 23:24:52 +00:00
Jeremy Kemper
94a25200a1 r3401@asus: jeremy | 2005-12-07 23:30:11 -0800
Apply [3252] to stable.  Oracle: active? performs a select instead of a commit.  Closes #3133.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3253 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 07:30:18 +00:00
David Heinemeier Hansson
93bd3b00e1 Prepare versions for release
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3250 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 05:56:23 +00:00
David Heinemeier Hansson
57e992dc0f Remove insignificant classes from docs
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3248 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 05:43:54 +00:00
Jeremy Kemper
a9f4205b21 r3392@asus: jeremy | 2005-12-07 21:10:42 -0800
Apply [3246] to stable.  MySQL: more robust test for nullified result hashes.  Closes #3124.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3247 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 05:11:38 +00:00
Jeremy Kemper
8a19d148b7 r3389@asus: jeremy | 2005-12-07 20:52:39 -0800
Apply [3244] to stable.  SQLite: find database file when RAILS_ROOT is a symlink.  Closes #3116.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3245 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:52:51 +00:00
Jeremy Kemper
bd9b27df8b r3387@asus: jeremy | 2005-12-07 20:48:42 -0800
Apply [3242] to stable.  Reloading an instance refreshes its aggregations as well as its associations.  Closes #3024.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3243 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:48:50 +00:00
David Heinemeier Hansson
c6120acc00 Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3241 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:46:40 +00:00
Jeremy Kemper
60d68aab53 r3379@asus: jeremy | 2005-12-07 20:42:31 -0800
Don't warn on keep_flash in test.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3239 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:42:54 +00:00
Jeremy Kemper
67cbb0f04c r3378@asus: jeremy | 2005-12-07 20:41:47 -0800
Apply [3237] to stable.  More robust relative url root discovery for SCGI compatibility.  This solves the 'SCGI routes problem' -- you no longer need to prefix all your routes with the name of the SCGI mountpoint.  Closes #3070.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3238 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:42:47 +00:00
Jeremy Kemper
0bb6521649 r3375@asus: jeremy | 2005-12-07 20:36:13 -0800
Apply [3235] to stable.  PostgreSQL: more robust sequence name discovery.  Closes #3087.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3236 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:36:22 +00:00
Jeremy Kemper
966b027c71 r3372@asus: jeremy | 2005-12-07 20:25:20 -0800
Apply [3233] to stable.  Oracle: use syntax compatible with Oracle 8.  Closes #3131.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3234 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 04:25:36 +00:00
David Heinemeier Hansson
2c5d342809 Warn people not to change boot.rb
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3232 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 03:54:53 +00:00
David Heinemeier Hansson
d45ca84858 Fixed RDoc warnings [DHH]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3229 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-08 03:23:54 +00:00
Jeremy Kemper
142304b79d r3363@asus: jeremy | 2005-12-06 22:26:27 -0800
Apply #428 changesets to stable: r3000, r3001, r3002, r3025, r3045, r3096, r3148, r3152, r3165, r3189, r3195, r3205, r3216, r3219, r3221, r3222.  Closes #428.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3226 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-07 06:26:54 +00:00
David Heinemeier Hansson
2deefbade0 Shouldnt overwrite Base documentation
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3225 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-06 02:46:18 +00:00
Sam Stephenson
66cd1330a5 Add builtin/ to the gemspec. Closes #3047.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3224 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-06 00:32:57 +00:00
Marcel Molina
cf656ec1f7 Apply [3207] to stable. Fix docs for text_area_tag. Closes #3083.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3208 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-02 04:41:03 +00:00
Jeremy Kemper
1c86c6f4d9 r3339@asus: jeremy | 2005-12-01 17:03:16 -0800
Apply [3202] to stable.  Firebird: updated for FireRuby 0.4.0.  Closes #3009.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3204 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-12-02 01:04:54 +00:00
Sam Stephenson
14c2334c84 Bring stable lighttpd server script in-line with trunk
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3199 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-30 19:26:23 +00:00
Marcel Molina
50c5b2817f Apply [3196] to stable. Make load_fixtures include csv fixtures. Closes #3053.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3197 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-28 23:37:51 +00:00
Nicholas Seckar
786726692f Apply freeze gems fix to stable
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3192 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-28 00:56:23 +00:00
Florian Weber
b762e0141c Made test_update_attributes_after_push_without_duplicate_join_table_rows pass with Oracle Adapter.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3187 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 12:54:08 +00:00
Jeremy Kemper
6865b66621 r3315@asus: jeremy | 2005-11-23 23:13:48 -0800
Apply [3184] to stable.  SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find.  Closes #2974.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3185 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 07:16:44 +00:00
Jeremy Kemper
1dc4783707 r3313@asus: jeremy | 2005-11-23 23:03:36 -0800
Apply [3182] to stable.  Reloading a model doesn't lose track of its connection.  Closes #2996.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3183 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 07:06:33 +00:00
Jeremy Kemper
98811479f0 r3307@asus: jeremy | 2005-11-23 17:52:14 -0800
Apply [3110] to stable.  PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database.  References #2964.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3180 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 01:55:39 +00:00
Jeremy Kemper
2b4dd8c6e7 r3306@asus: jeremy | 2005-11-23 17:39:24 -0800
Apply [3100], [3108] to stable.  Makes new ActiveRecordStore sessions work correctly with components.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3179 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 01:55:32 +00:00
Jeremy Kemper
3fcec37473 r3305@asus: jeremy | 2005-11-23 17:33:51 -0800
Apply [3151] to stable.  Don't put flash in session if sessions are disabled.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3178 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 01:55:27 +00:00
Jeremy Kemper
15839d56d0 r3303@asus: jeremy | 2005-11-23 17:14:31 -0800
Apply [3051], [3052], [3053], [3059] to stable.  Brings the Firebird adapter to 1.0.  References #1874.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3177 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-24 01:17:30 +00:00
Jeremy Kemper
9c05146f4b r3296@asus: jeremy | 2005-11-23 13:38:56 -0800
Apply [3170] to stable.  Credit ticket author.  References #2888.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3173 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-23 21:41:57 +00:00
Jeremy Kemper
a766ae6d84 r3285@asus: jeremy | 2005-11-22 13:33:04 -0800
Apply [3162] to stable.  Model generator: correct relative path to test_helper in unit test.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3172 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-23 21:41:48 +00:00
Jeremy Kemper
cc4deb1610 r3269@asus: jeremy | 2005-11-21 04:46:40 -0800
Apply [3148] to stable.  Simpler Mysql load test.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3171 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-23 21:41:42 +00:00
Florian Weber
b87294c671 Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3168 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-23 21:14:47 +00:00
Thomas Fuchs
7d6e3d0857 Make javascript_include_tag :default behave correctly with application.js, fixes #2986
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3164 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-22 23:18:53 +00:00
Marcel Molina
4906b32450 Apply [3159] to stable. Remove superfluous check for pluralize_table_names (use table_name which does this check when it's set).
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3160 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-22 09:06:04 +00:00
Marcel Molina
541da098f0 Apply [3157] to stable. Make ActionController's render honor the :locals option when rendering a :file. Closes #1665.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3158 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-22 08:41:59 +00:00
Marcel Molina
7bf86f2c57 Revert [3140]. Behavior of introducing Kernel.binding causes breakage.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3143 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-21 09:38:03 +00:00
Jeremy Kemper
81f3a923d7 r3258@asus: jeremy | 2005-11-21 00:33:59 -0800
Apply [3139] to stable.  Use Kernel.binding rather than binding to allow columns of that name.  Closes #2973.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3140 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-21 08:36:13 +00:00
Marcel Molina
4a9847f987 Apply [3136] to stable. Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. Closes #2931.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3138 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-21 08:04:44 +00:00
Sam Stephenson
743fb60a8a Strip out trailing &_= for raw post bodies #2868
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3137 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-21 08:04:28 +00:00
Marcel Molina
6329724293 Apply [3120] to stable. Make help for the console command more explicit about how to specify the desired environment in which to run the console. Closes #2911.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3121 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-21 05:35:54 +00:00
Marcel Molina
78e95faec5 Apply #3111 to stable. Correct docs for automatic layout assignment. Closes #2610.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3112 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-20 18:55:26 +00:00
Marcel Molina
371fa97ccc Apply [3105] to stable. Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3106 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-20 07:51:27 +00:00
Jeremy Kemper
1aac21ca24 r3201@asus: jeremy | 2005-11-19 21:31:47 -0800
Apply [3103] to stable.  Document request.env and request.host.  Strip trailing whitespace.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3104 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-20 05:33:46 +00:00
Jeremy Kemper
54977e0364 r3199@asus: jeremy | 2005-11-19 21:13:22 -0800
Apply [3101] to stable.  Allows generator to specify migrations directory.  Closes #2960.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3102 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-20 05:15:18 +00:00
Jeremy Kemper
3f2541b4ff r3190@asus: jeremy | 2005-11-19 20:20:48 -0800
Apply [3098] to stable.  Introducing the session_migration generator.  Creates an add_session_table migration.  Closes #2958.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3099 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-20 04:26:21 +00:00
Jeremy Kemper
bed4fec60e r3178@asus: jeremy | 2005-11-19 01:59:47 -0800
Apply [3094] to stable.  Use query methods rather than readers in boolean tests for [3092].  References #2949.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3095 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-19 10:01:34 +00:00
Jeremy Kemper
fe09fa278e r3174@asus: jeremy | 2005-11-19 01:53:00 -0800
Apply [3092] to stable.  Correct boolean handling in generated reader methods.  Closes #2945.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3093 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-19 09:54:55 +00:00
Sam Stephenson
d4039f5246 Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument)
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3091 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-19 06:59:07 +00:00
Jeremy Kemper
b8ea04f8e8 r3167@asus: jeremy | 2005-11-18 22:47:31 -0800
Apply [3089] to stable.  Don't generate read methods for columns whose names are not valid ruby method names.  Closes #2946.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3090 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-19 06:49:18 +00:00
Jeremy Kemper
beddd9680e r3157@asus: jeremy | 2005-11-17 21:27:39 -0800
Apply [3081] to stable.  Eliminate nil from newly generated logfiles.  Closes #2927.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3088 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-18 21:22:09 +00:00
Sam Stephenson
dcaa871695 Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3079 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-18 00:53:11 +00:00
Jeremy Kemper
9796a587b8 r3149@asus: jeremy | 2005-11-17 12:47:36 -0800
Apply [3075] to stable.  Document :force option to create_table.  Closes #2921.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3077 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-17 20:49:31 +00:00
Jeremy Kemper
de5980289c r3148@asus: jeremy | 2005-11-17 12:45:44 -0800
Apply [3074] to stable.  Don't add the same conditions twice in has_one finder sql.  Closes #2916.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3076 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-17 20:49:24 +00:00
Tobias Lütke
0023cb1b8c stable merge of 3032 -- Updated docs for in_place_editor by Justin Palmer
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3071 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-17 05:04:11 +00:00
Tobias Lütke
37664eb90c stable merge of [3069]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3070 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-17 04:59:13 +00:00
Marcel Molina
d212f03093 Apply [3067] to stable. Update from LGPL to MIT license as per Minero Aoki's permission.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3068 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-17 00:53:29 +00:00
Marcel Molina
afcd3ebcbb Apply [3065] to stable. Rename Version constant to VERSION. Closes #2802.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3066 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-17 00:38:59 +00:00
Jeremy Kemper
b35d05f594 r3130@asus: jeremy | 2005-11-16 14:37:47 -0800
Apply [3063] to stable.  Remove CHANGELOG from apidoc Rake task since it isn't included with new apps anymore.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3064 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-16 22:39:01 +00:00
Jeremy Kemper
1ec7fb609d r3120@asus: jeremy | 2005-11-16 13:34:45 -0800
Apply [3060] to stable.  Remove the unused, slow response_dump and session_dump variables from error pages.  Closes #1222.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3061 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-16 21:36:12 +00:00
Nicholas Seckar
113c815c6a Change Inflector#constantize to use eval instead of const_get
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3050 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-16 01:17:28 +00:00
Jeremy Kemper
9decb1a46f r3077@asus: jeremy | 2005-11-14 14:28:21 -0800
Apply [3035] to stable.  SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY.  Closes #2866.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3037 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 22:29:27 +00:00
Thomas Fuchs
49de64e0a4 Update stable to latest script.aculo.us
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3034 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 20:50:49 +00:00
Jeremy Kemper
58c7f2a744 r3073@asus: jeremy | 2005-11-13 23:42:32 -0800
Apply [3021] to stable.  Correct documentation for Base.delete_all.  Closes #1568.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3022 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 07:43:03 +00:00
Jeremy Kemper
e24096d616 r3070@asus: jeremy | 2005-11-13 20:54:50 -0800
Apply [3019] to stable.  Oracle: test case for column default parsing.  Closes #2788.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3020 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 04:55:18 +00:00
Jeremy Kemper
902cb21124 r3066@asus: jeremy | 2005-11-13 20:24:18 -0800
Apply [3017] to stable.  Update documentation for Migrations.  Closes #2861.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3018 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 04:24:52 +00:00
Jeremy Kemper
e01febbe68 r3063@asus: jeremy | 2005-11-13 16:13:51 -0800
Apply [3015] to stable.  Update documentation for render :file.  Closes #2858.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3016 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 00:14:37 +00:00
Jeremy Kemper
364f6d645a r3058@asus: jeremy | 2005-11-13 16:09:17 -0800
Apply [3013] to stable.  Note that the ruby-memcache bindings are required to use the memcache store.  Closes #2857.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3014 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-14 00:09:42 +00:00
Jeremy Kemper
971b40f59f r3055@asus: jeremy | 2005-11-13 14:48:48 -0800
Apply [3009] to stable:  Reapply [2942] which was elided by [2997].  References #2788.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3010 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 22:49:19 +00:00
Jeremy Kemper
af0ad4d50b r3053@asus: jeremy | 2005-11-13 14:37:39 -0800
Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb.  Closes #2855.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@3008 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 22:38:39 +00:00
Jeremy Kemper
f0b5630dce r3037@asus: jeremy | 2005-11-13 00:11:26 -0800
Apply [2998] to stable.  PostgreSQL: last_insert_id uses select_value rather than using @connection.exec directly.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2999 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 08:12:22 +00:00
Jeremy Kemper
532d4e8782 r3033@asus: jeremy | 2005-11-12 23:27:13 -0800
Apply [2996] to stable.  Much faster Oracle column reflection.  Closes #2848.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2997 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 07:29:02 +00:00
Jeremy Kemper
b970d2055a r3027@asus: jeremy | 2005-11-12 22:37:45 -0800
Apply [2994] to stable.  PostgreSQL: correct the sequence discovery fallback query.  References #2594.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2995 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 06:38:35 +00:00
Jeremy Kemper
9403f471b4 r3022@asus: jeremy | 2005-11-12 18:40:42 -0800
merge to stable
 r3024@asus:  jeremy | 2005-11-12 19:00:49 -0800
 Apply [2992] to stable.  SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment.  References #2846.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2993 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 03:01:19 +00:00
Jeremy Kemper
17836f4819 r3008@asus (orig r2978): david | 2005-11-11 01:50:42 -0800
Make sure that legacy db tasks also reference :database for SQLite (closes #2830) [kazuhiko@fdiary.net]
 r3009@asus (orig r2979):  david | 2005-11-11 02:02:44 -0800
 Changelogging
 r3011@asus (orig r2981):  bitsweat | 2005-11-11 10:49:01 -0800
 Include the Enumerable module in ActiveRecord::Errors.
 r3012@asus (orig r2982):  bitsweat | 2005-11-11 15:45:02 -0800
 SQLServer: don't report limits for unsupported field types.  Closes #2835.
 r3014@asus (orig r2984):  minam | 2005-11-11 21:09:05 -0800
 Make Validations#create! use the current scope
 
 r3015@asus (orig r2985):  bitsweat | 2005-11-12 03:59:54 -0800
  r4325@asus:  jeremy | 2005-11-12 03:57:46 -0800
  PostgreSQL: correctly discover custom primary key sequences.  PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence.  Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing).  Base.define_attr_method allows nil values.  References #2594.
 
 r3017@asus (orig r2987):  david | 2005-11-12 08:26:23 -0800
 Pulled auto-starting browser: More pain than gain
 r3019@asus (orig r2989):  bitsweat | 2005-11-12 14:28:38 -0800
 PostgreSQL: min_messages = warning for AR tests.
 r3020@asus (orig r2990):  bitsweat | 2005-11-12 17:12:48 -0800
 SQLite: the clone_structure_to_test Rake task should always use the test environment.  References #2846.


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2991 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-13 01:16:59 +00:00
Jamis Buck
e823ef8336 merge from trunk (include Enumerable in AR::Errors, make Validations#create! use current scope)
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2988 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-12 17:41:59 +00:00
David Heinemeier Hansson
ce6545e574 Pulled auto-starting browser: More pain than gain
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2986 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-12 16:26:12 +00:00
Jeremy Kemper
e8086859c5 Merge [2982] to stable. SQLServer: don't report limits for unsupported field types. References #2835.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2983 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-12 02:56:11 +00:00
David Heinemeier Hansson
e0ef631055 Added stable branch to prepare for 1.0 release
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/stable@2980 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2005-11-11 10:07:24 +00:00
128 changed files with 3467 additions and 1782 deletions

View File

@@ -1,3 +1,13 @@
*1.1.5* (December 13th, 2005)
* Become part of Rails 1.0
*1.1.4* (December 7th, 2005)
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
*1.1.3* (November 7th, 2005)
* Allow Mailers to have custom initialize methods that set default instance variables for all mail actions #2563 [mrj@bigpond.net.au]

View File

@@ -9,7 +9,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
PKG_NAME = 'actionmailer'
PKG_VERSION = ActionMailer::Version::STRING + PKG_BUILD
PKG_VERSION = ActionMailer::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -53,7 +53,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 1.11.0' + PKG_BUILD)
s.add_dependency('actionpack', '= 1.11.2' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'

View File

@@ -1,8 +1,8 @@
module ActionMailer
module Version #:nodoc:
module VERSION #:nodoc:
MAJOR = 1
MINOR = 1
TINY = 3
TINY = 5
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -1,4 +1,43 @@
*SVN*
*1.1.2* (December 13th, 2005)
* Update to script.aculo.us 1.5.0 final (equals 1.5.0_rc6) [Thomas Fuchs]
* Update to Prototype 1.4.0 final [Sam Stephenson]
* Become part of Rails 1.0
*1.11.1* (December 7th, 2005)
* More robust relative url root discovery for SCGI compatibility. This solves the 'SCGI routes problem' -- you no longer need to prefix all your routes with the name of the SCGI mountpoint. #3070 [Dave Ringoen]
* Fix docs for text_area_tag. #3083. [Christopher Cotton]
* Make ActionController's render honor the :locals option when rendering a :file. #1665. [Emanuel Borsboom, Marcel Molina Jr.]
* Don't put flash in session if sessions are disabled. [Jeremy Kemper]
* Strip out trailing &_= for raw post bodies. Closes #2868. [Sam Stephenson]
* Correct docs for automatic layout assignment. #2610. [Charles M. Gerungan]
* Always create new AR sessions rather than trying too hard to avoid database traffic. #2731 [Jeremy Kemper]
* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). [Sam Stephenson]
* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. [Sam Stephenson]
* Updated docs for in_place_editor, fixes a couple bugs and offers extended support for external controls [Justin Palmer]
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
* Remove the unused, slow response_dump and session_dump variables from error pages. #1222 [lmarlow@yahoo.com]
* Update to latest script.aculo.us version (as of [3031])
* Update documentation for render :file. #2858 [Tom Werner]
* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb. #2855 [court3nay@gmail.com]
* options_for_select allows any objects which respond_to? :first and :last rather than restricting to Array and Range. #2824 [Jacob Robbins <jrobbins@cmj.com>, Jeremy Kemper]

View File

@@ -9,7 +9,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
PKG_NAME = 'actionpack'
PKG_VERSION = ActionPack::Version::STRING + PKG_BUILD
PKG_VERSION = ActionPack::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -62,7 +62,7 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
s.add_dependency('activesupport', '= 1.2.3' + PKG_BUILD)
s.add_dependency('activesupport', '= 1.2.5' + PKG_BUILD)
s.require_path = 'lib'
s.autorequire = 'action_controller'

View File

@@ -1,7 +1,7 @@
require 'test/unit'
require 'test/unit/assertions'
require 'rexml/document'
require "#{File.dirname(__FILE__)}/vendor/html-scanner/html/document"
require File.dirname(__FILE__) + "/vendor/html-scanner/html/document"
module Test #:nodoc:
module Unit #:nodoc:

View File

@@ -459,9 +459,12 @@ module ActionController #:nodoc:
self.class.controller_name
end
def session_enabled?
request.session_options[:disabled] != false
end
protected
# Renders the content that'll be returned to the browser as the response body. This can just be as regular text, but is
# more often the compilation of a template.
# Renders the content that will be returned to the browser as the response body.
#
# === Rendering an action
#
@@ -521,14 +524,19 @@ module ActionController #:nodoc:
#
# === Rendering a file
#
# File rendering works just like action rendering except that it takes an absolute path.
# The current layout is not applied automatically.
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
# is assumed to be absolute, and the current layout is not applied.
#
# # Renders the template located in /path/to/some/template.r(html|xml)
# render :file => "/path/to/some/template"
# # Renders the template located at the absolute filesystem path
# render :file => "/path/to/some/template.rhtml"
# render :file => "c:/path/to/some/template.rhtml"
#
# # Renders the same template within the current layout, but with a 404 status code
# render :file => "/path/to/some/template", :layout => true, :status => 404
# # Renders a template within the current layout, and with a 404 status code
# render :file => "/path/to/some/template.rhtml", :layout => true, :status => 404
# render :file => "c:/path/to/some/template.rhtml", :layout => true, :status => 404
#
# # Renders a template relative to the template root and chooses the proper file extension
# render :file => "some/template", :use_full_path => true
#
# _Deprecation_ _notice_: This used to have the signature <tt>render_file(path, status = 200)</tt>
#
@@ -592,7 +600,7 @@ module ActionController #:nodoc:
else
if file = options[:file]
render_file(file, options[:status], options[:use_full_path])
render_file(file, options[:status], options[:use_full_path], options[:locals] || {})
elsif template = options[:template]
render_file(template, options[:status], true)
@@ -640,11 +648,11 @@ module ActionController #:nodoc:
end
end
def render_file(template_path, status = nil, use_full_path = false)
def render_file(template_path, status = nil, use_full_path = false, locals = {})
add_variables_to_assigns
assert_existance_of_template_file(template_path) if use_full_path
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
render_text(@template.render_file(template_path, use_full_path), status)
render_text(@template.render_file(template_path, use_full_path, locals), status)
end
def render_template(template, status = nil, type = :rhtml, local_assigns = {})

View File

@@ -236,6 +236,7 @@ module ActionController #:nodoc:
# * DRbStore: Keeps the fragments in the memory of a separate, shared DRb process. This works for all environments and only keeps one cache
# around for all processes, but requires that you run and manage a separate DRb process.
# * MemCacheStore: Works like DRbStore, but uses Danga's MemCache instead.
# Requires the ruby-memcache library: gem install ruby-memcache.
#
# Configuration examples (MemoryStore is the default):
#
@@ -513,8 +514,8 @@ module ActionController #:nodoc:
end
end
end
if defined?(ActiveRecord::Observer)
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
class Sweeper < ActiveRecord::Observer #:nodoc:
attr_accessor :controller

View File

@@ -54,6 +54,7 @@ class CGI #:nodoc:
content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
# fix for Safari Ajax postings that always append \000
content.chop! if content[-1] == 0
content.gsub! /&_=$/, ''
env_table['RAW_POST_DATA'] = content.freeze
end

View File

@@ -132,22 +132,31 @@ module ActionController #:nodoc:
end
end
end
protected
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or
# <tt>flash["notice"] = "hello"</tt> to put a new one.
# Note that if sessions are disabled only flash.now will work.
def flash #:doc:
@session['flash'] ||= FlashHash.new
# @session = Hash.new if sessions are disabled
if @session.is_a?(Hash)
@__flash ||= FlashHash.new
# otherwise, @session is a CGI::Session or a TestSession
else
@session['flash'] ||= FlashHash.new
end
end
# deprecated. use <tt>flash.keep</tt> instead
def keep_flash #:doc:
warn 'keep_flash is deprecated; use flash.keep instead.'
flash.keep
end
private
private
# marks flash entries as used and expose the flash to the view
def fire_flash

View File

@@ -66,7 +66,9 @@ module ActionController #:nodoc:
# <tt>app/views/layouts/weblog.rhtml</tt> or <tt>app/views/layouts/weblog.rxml</tt> exists then it will be automatically set as
# the layout for your WeblogController. You can create a layout with the name <tt>application.rhtml</tt> or <tt>application.rxml</tt>
# and this will be set as the default controller if there is no layout with the same name as the current controller and there is
# no layout explicitly assigned with the +layout+ method. Setting a layout explicitly will always override the automatic behaviour.
# no layout explicitly assigned with the +layout+ method. Setting a layout explicitly will always override the automatic behaviour
# for the controller where the layout is set. Explicitly setting the layout in a parent class, though, will not override the
# child class's layout assignement if the child class has a layout with the same name.
#
# == Inheritance for layouts
#

View File

@@ -14,7 +14,7 @@ module ActionController
# end
#
# # View
# <%= in_place_editor_field :post, title %>
# <%= in_place_editor_field :post, 'title' %>
#
# For help on defining an in place editor in the browser,
# see ActionView::Helpers::JavaScriptHelper.

View File

@@ -2,7 +2,7 @@ module ActionController
# These methods are available in both the production and test Request objects.
class AbstractRequest
cattr_accessor :relative_url_root
# Returns both GET and POST parameters in a single hash.
def parameters
@parameters ||= request_parameters.merge(query_parameters).merge(path_parameters).with_indifferent_access
@@ -110,7 +110,7 @@ module ActionController
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
def domain(tld_length = 1)
return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil?
host.split('.').last(1 + tld_length).join('.')
end
@@ -123,13 +123,13 @@ module ActionController
parts[0..-(tld_length+2)]
end
# Receive the raw post data.
# This is useful for services such as REST, XMLRPC and SOAP
# which communicate over HTTP POST but don't use the traditional parameter format.
# Receive the raw post data.
# This is useful for services such as REST, XMLRPC and SOAP
# which communicate over HTTP POST but don't use the traditional parameter format.
def raw_post
env['RAW_POST_DATA']
end
# Returns the request URI correctly, taking into account the idiosyncracies
# of the various servers.
def request_uri
@@ -153,9 +153,9 @@ module ActionController
# Is this an SSL request?
def ssl?
env['HTTPS'] == 'on'
env['HTTPS'] == 'on'
end
# Returns the interpreted path to requested resource after all the installation directory of this application was taken into account
def path
path = (uri = request_uri) ? uri.split('?').first : ''
@@ -165,18 +165,19 @@ module ActionController
path[0, root.length] = '' if root
path || ''
end
# Returns the path minus the web server relative installation directory.
# This method returns nil unless the web server is apache.
def relative_url_root
@@relative_url_root ||= server_software == 'apache' ? File.dirname(env["SCRIPT_NAME"].to_s).gsub(/(^\.$|^\/$)/, '') : ''
@@relative_url_root ||= server_software == 'apache' ? env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '') : ''
end
# Returns the port number of this request as an integer.
def port
@port_as_int ||= env['SERVER_PORT'].to_i
end
# Returns the standard port number for this request's protocol
def standard_port
case protocol
@@ -196,12 +197,12 @@ module ActionController
def host_with_port
host + port_string
end
def path_parameters=(parameters)
@path_parameters = parameters
@symbolized_path_parameters = @parameters = nil
end
def symbolized_path_parameters
@symbolized_path_parameters ||= path_parameters.symbolize_keys
end
@@ -224,10 +225,13 @@ module ActionController
def request_parameters #:nodoc:
end
def env #:nodoc:
# Returns the hash of environment variables for this request,
# such as { 'RAILS_ENV' => 'production' }.
def env
end
def host #:nodoc:
# Returns the host for this request, such as example.com.
def host
end
def cookies #:nodoc:
@@ -237,6 +241,6 @@ module ActionController
end
def reset_session #:nodoc:
end
end
end
end

View File

@@ -80,8 +80,8 @@ class CGI
find_by_session_id(session_id)
end
def marshal(data) Base64.encode64(Marshal.dump(data)) end
def unmarshal(data) Marshal.load(Base64.decode64(data)) end
def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
def create_table!
connection.execute <<-end_sql
@@ -119,18 +119,12 @@ class CGI
# Lazy-unmarshal session state.
def data
unless @data
case d = read_attribute(@@data_column_name)
when String
@data = self.class.unmarshal(d)
else
@data = d || {}
end
end
@data
@data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
end
private
attr_writer :data
def marshal_data!
write_attribute(@@data_column_name, self.class.marshal(self.data))
end
@@ -193,8 +187,8 @@ class CGI
end
end
def marshal(data) Base64.encode64(Marshal.dump(data)) end
def unmarshal(data) Marshal.load(Base64.decode64(data)) end
def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
def create_table!
@@connection.execute <<-end_sql
@@ -230,7 +224,7 @@ class CGI
def data
unless @data
if @marshaled_data
@data, @marshaled_data = self.class.unmarshal(@marshaled_data), nil
@data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
else
@data = {}
end
@@ -284,6 +278,7 @@ class CGI
raise CGI::Session::NoSession, 'uninitialized session'
end
@session = @@session_class.new(:session_id => session_id, :data => {})
@session.save
end
end

View File

@@ -31,8 +31,6 @@
request_parameters_without_action.delete("controller")
request_dump = request_parameters_without_action.inspect.gsub(/,/, ",\n")
session_dump = @request.session.instance_variable_get("@data").inspect.gsub(/,/, ",\n")
response_dump = @response.inspect.gsub(/,/, ",\n")
%>
<h2 style="margin-top: 30px">Request</h2>

View File

@@ -1,8 +1,8 @@
module ActionPack
module Version #:nodoc:
module VERSION #:nodoc:
MAJOR = 1
MINOR = 11
TINY = 0
TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -157,13 +157,12 @@ module ActionView #:nodoc:
class ObjectWrapper < Struct.new(:value) #:nodoc:
end
def self.load_helpers(helper_dir)#:nodoc:
Dir.foreach(helper_dir) do |helper_file|
next unless helper_file =~ /_helper.rb$/
require helper_dir + helper_file
helper_module_name = helper_file.capitalize.gsub(/_([a-z])/) { |m| $1.capitalize }[0..-4]
next unless helper_file =~ /^([a-z][a-z_]*_helper).rb$/
require File.join(helper_dir, $1)
helper_module_name = $1.camelize
class_eval("include ActionView::Helpers::#{helper_module_name}") if Helpers.const_defined?(helper_module_name)
end
end

View File

@@ -35,7 +35,7 @@ module ActionView
compute_public_path(source, 'javascripts', 'js')
end
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls']
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
@@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
# Returns a script include tag per source given as argument. Examples:
@@ -60,7 +60,7 @@ module ActionView
def javascript_include_tag(*sources)
options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
if sources.first == :defaults
sources = @@javascript_default_sources
sources = @@javascript_default_sources.dup
if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
sources << 'application'
end

View File

@@ -95,7 +95,7 @@ module ActionView
# Options:
# * <tt>:size</tt> - A string specifying the dimensions of the textarea.
# # Outputs <textarea name="body" id="body" cols="25" rows="10"></textarea>
# <%= text_area_tag "body", nil, :size => 25x10 %>
# <%= text_area_tag "body", nil, :size => "25x10" %>
def text_area_tag(name, content = nil, options = {})
options = options.stringify_keys
if options["size"]

View File

@@ -28,21 +28,23 @@ module ActionView
# be sent after the user presses "ok".
#
# Addtional +options+ are:
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
# <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
# <tt>:ok_text</tt>:: The text on the save link. (default: "ok")
# <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
# <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
# in the AJAX call, +form+ is an implicit parameter
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
# <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
# <tt>:save_text</tt>:: The text on the save link. (default: "ok")
# <tt>:external_control</tt>:: The id of an external control used to enter edit mode.
# <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
# <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
# in the AJAX call, +form+ is an implicit parameter
def in_place_editor(field_id, options = {})
function = "new Ajax.InPlaceEditor("
function << "'#{field_id}', "
function << "'#{url_for(options[:url])}'"
js_options = {}
js_options['cancelText'] = options[:cancel_text] if options[:cancel_text]
js_options['okText'] = options[:save_text] if options[:save_text]
js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
js_options['rows'] = options[:rows] if options[:rows]
js_options['externalControl'] = options[:external_control] if options[:external_control]
js_options['ajaxOptions'] = options[:options] if options[:options]
js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
@@ -59,7 +61,7 @@ module ActionView
tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
tag.to_content_tag(tag_options[:tag], tag_options) +
tag.to_content_tag(tag_options.delete(:tag), tag_options) +
in_place_editor(tag_options[:id], in_place_editor_options)
end

View File

@@ -80,7 +80,10 @@ Autocompleter.Base.prototype = {
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
if(!this.iefix &&
(navigator.appVersion.indexOf('MSIE')>0) &&
(navigator.userAgent.indexOf('Opera')<0) &&
(Element.getStyle(this.update, 'position')=='absolute')) {
new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
@@ -718,4 +721,30 @@ Ajax.InPlaceEditor.prototype = {
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
}
}
};
// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
Form.Element.DelayedObserver = Class.create();
Form.Element.DelayedObserver.prototype = {
initialize: function(element, delay, callback) {
this.delay = delay || 0.5;
this.element = $(element);
this.callback = callback;
this.timer = null;
this.lastValue = $F(this.element);
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
},
delayedListener: function(event) {
if(this.lastValue == $F(this.element)) return;
if(this.timer) clearTimeout(this.timer);
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
this.lastValue = $F(this.element);
},
onTimerEvent: function() {
this.timer = null;
this.callback(this.element, $F(this.element));
}
};

View File

@@ -1,7 +1,5 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Element.Class part Copyright (c) 2005 by Rick Olson
//
// See scriptaculous.js for full license.
/*--------------------------------------------------------------------------*/
@@ -10,7 +8,7 @@ var Droppables = {
drops: [],
remove: function(element) {
this.drops = this.drops.reject(function(d) { return d.element==element });
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
},
add: function(element) {
@@ -31,6 +29,8 @@ var Droppables = {
options._containers.push($(containment));
}
}
if(options.accept) options.accept = [options.accept].flatten();
Element.makePositioned(element); // fix IE
options.element = element;
@@ -43,55 +43,50 @@ var Droppables = {
return drop._containers.detect(function(c) { return parentNode == c });
},
isAffected: function(pX, pY, element, drop) {
isAffected: function(point, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
this.isContained(element, drop)) &&
((!drop.accept) ||
(Element.Class.has_any(element, drop.accept))) &&
Position.within(drop.element, pX, pY) );
(Element.classNames(element).detect(
function(v) { return drop.accept.include(v) } ) )) &&
Position.within(drop.element, point[0], point[1]) );
},
deactivate: function(drop) {
if(drop.hoverclass)
Element.Class.remove(drop.element, drop.hoverclass);
Element.removeClassName(drop.element, drop.hoverclass);
this.last_active = null;
},
activate: function(drop) {
if(this.last_active) this.deactivate(this.last_active);
if(drop.hoverclass)
Element.Class.add(drop.element, drop.hoverclass);
Element.addClassName(drop.element, drop.hoverclass);
this.last_active = drop;
},
show: function(event, element) {
show: function(point, element) {
if(!this.drops.length) return;
var pX = Event.pointerX(event);
var pY = Event.pointerY(event);
Position.prepare();
var i = this.drops.length-1; do {
var drop = this.drops[i];
if(this.isAffected(pX, pY, element, drop)) {
if(this.last_active) this.deactivate(this.last_active);
this.drops.each( function(drop) {
if(Droppables.isAffected(point, element, drop)) {
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
if(drop.greedy) {
this.activate(drop);
return;
Droppables.activate(drop);
throw $break;
}
}
} while (i--);
if(this.last_active) this.deactivate(this.last_active);
});
},
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop)
this.last_active.onDrop(element, this.last_active.element, event);
},
@@ -103,15 +98,84 @@ var Droppables = {
}
var Draggables = {
drags: [],
observers: [],
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
activate: function(draggable) {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
},
deactivate: function(draggbale) {
this.activeDraggable = null;
},
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
// Mozilla-based browsers fire successive mousemove events with
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
},
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
addObserver: function(observer) {
this.observers.push(observer);
this.observers.push(observer);
this._cacheObserverCallbacks();
},
removeObserver: function(element) { // element instead of obsever fixes mem leaks
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
notify: function(eventName, draggable) { // 'onStart', 'onEnd'
this.observers.invoke(eventName, draggable);
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
},
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
function(o) { return o[eventName]; }
).length;
});
}
}
@@ -127,68 +191,48 @@ Draggable.prototype = {
},
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
},
endeffect: function(element) {
new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
},
zindex: 1000,
revert: false
revert: false,
snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
}, arguments[1] || {});
this.element = $(element);
this.element = $(element);
if(options.handle && (typeof options.handle == 'string'))
this.handle = Element.Class.childrenWith(this.element, options.handle)[0];
this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
Element.makePositioned(this.element); // fix IE
this.offsetX = 0;
this.offsetY = 0;
this.originalLeft = this.currentLeft();
this.originalTop = this.currentTop();
this.originalX = this.element.offsetLeft;
this.originalY = this.element.offsetTop;
this.delta = this.currentDelta();
this.options = options;
this.dragging = false;
this.options = options;
this.active = false;
this.dragging = false;
this.eventMouseDown = this.startDrag.bindAsEventListener(this);
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.update.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
this.registerEvents();
Draggables.register(this);
},
destroy: function() {
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
this.unregisterEvents();
Draggables.unregister(this);
},
registerEvents: function() {
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
currentDelta: function() {
return([
parseInt(this.element.style.left || '0'),
parseInt(this.element.style.top || '0')]);
},
unregisterEvents: function() {
//if(!this.active) return;
//Event.stopObserving(document, "mouseup", this.eventMouseUp);
//Event.stopObserving(document, "mousemove", this.eventMouseMove);
//Event.stopObserving(document, "keypress", this.eventKeypress);
},
currentLeft: function() {
return parseInt(this.element.style.left || '0');
},
currentTop: function() {
return parseInt(this.element.style.top || '0')
},
startDrag: function(event) {
if(Event.isLeftClick(event)) {
initDrag: function(event) {
if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if(src.tagName && (
@@ -196,20 +240,53 @@ Draggable.prototype = {
src.tagName=='SELECT' ||
src.tagName=='BUTTON' ||
src.tagName=='TEXTAREA')) return;
if(this.element._revert) {
this.element._revert.cancel();
this.element._revert = null;
}
// this.registerEvents();
this.active = true;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var offsets = Position.cumulativeOffset(this.element);
this.offsetX = (pointer[0] - offsets[0]);
this.offsetY = (pointer[1] - offsets[1]);
var pos = Position.cumulativeOffset(this.element);
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
Draggables.activate(this);
Event.stop(event);
}
},
startDrag: function(event) {
this.dragging = true;
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
this.element.style.zIndex = this.options.zindex;
}
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
Draggables.notify('onStart', this, event);
if(this.options.starteffect) this.options.starteffect(this.element);
},
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
Position.prepare();
Droppables.show(pointer, this.element);
Draggables.notify('onDrag', this, event);
this.draw(pointer);
if(this.options.change) this.options.change(this);
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Event.stop(event);
},
finishDrag: function(event, success) {
// this.unregisterEvents();
this.active = false;
this.dragging = false;
if(this.options.ghosting) {
@@ -219,18 +296,17 @@ Draggable.prototype = {
}
if(success) Droppables.fire(event, this.element);
Draggables.notify('onEnd', this);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
if(revert && typeof revert == 'function') revert = revert(this.element);
var d = this.currentDelta();
if(revert && this.options.reverteffect) {
this.options.reverteffect(this.element,
this.currentTop()-this.originalTop,
this.currentLeft()-this.originalLeft);
d[1]-this.delta[1], d[0]-this.delta[0]);
} else {
this.originalLeft = this.currentLeft();
this.originalTop = this.currentTop();
this.delta = d;
}
if(this.options.zindex)
@@ -239,70 +315,48 @@ Draggable.prototype = {
if(this.options.endeffect)
this.options.endeffect(this.element);
Draggables.deactivate(this);
Droppables.reset();
},
keyPress: function(event) {
if(this.active) {
if(event.keyCode==Event.KEY_ESC) {
this.finishDrag(event, false);
Event.stop(event);
}
}
if(!event.keyCode==Event.KEY_ESC) return;
this.finishDrag(event, false);
Event.stop(event);
},
endDrag: function(event) {
if(this.active && this.dragging) {
this.finishDrag(event, true);
Event.stop(event);
}
this.active = false;
this.dragging = false;
if(!this.dragging) return;
this.finishDrag(event, true);
Event.stop(event);
},
draw: function(event) {
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var offsets = Position.cumulativeOffset(this.element);
offsets[0] -= this.currentLeft();
offsets[1] -= this.currentTop();
draw: function(point) {
var pos = Position.cumulativeOffset(this.element);
var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
if(this.options.snap) {
if(typeof this.options.snap == 'function') {
p = this.options.snap(p[0],p[1]);
} else {
if(this.options.snap instanceof Array) {
p = p.map( function(v, i) {
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
} else {
p = p.map( function(v) {
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
}
}}
var style = this.element.style;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
style.left = (pointer[0] - offsets[0] - this.offsetX) + "px";
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = (pointer[1] - offsets[1] - this.offsetY) + "px";
style.top = p[1] + "px";
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
update: function(event) {
if(this.active) {
if(!this.dragging) {
var style = this.element.style;
this.dragging = true;
if(Element.getStyle(this.element,'position')=='')
style.position = "relative";
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
style.zIndex = this.options.zindex;
}
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
Draggables.notify('onStart', this);
if(this.options.starteffect) this.options.starteffect(this.element);
}
Droppables.show(event, this.element);
this.draw(event);
if(this.options.change) this.options.change(this);
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Event.stop(event);
}
}
}
@@ -315,9 +369,11 @@ SortableObserver.prototype = {
this.observer = observer;
this.lastValue = Sortable.serialize(this.element);
},
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
onEnd: function() {
Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
@@ -327,10 +383,12 @@ SortableObserver.prototype = {
var Sortable = {
sortables: new Array(),
options: function(element){
element = $(element);
return this.sortables.detect(function(s) { return s.element == element });
},
destroy: function(element){
element = $(element);
this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
@@ -340,6 +398,7 @@ var Sortable = {
});
this.sortables = this.sortables.reject(function(s) { return s.element == element });
},
create: function(element) {
element = $(element);
var options = Object.extend({
@@ -413,7 +472,7 @@ var Sortable = {
(this.findElements(element, options) || []).each( function(e) {
// handles are per-draggable
var handle = options.handle ?
Element.Class.childrenWith(e, options.handle)[0] : e;
Element.childrenWithClassName(e, options.handle)[0] : e;
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
@@ -433,8 +492,8 @@ var Sortable = {
if(!element.hasChildNodes()) return null;
var elements = [];
$A(element.childNodes).each( function(e) {
if(e.tagName && e.tagName==options.tag.toUpperCase() &&
(!options.only || (Element.Class.has(e, options.only))))
if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
(!options.only || (Element.hasClassName(e, options.only))))
elements.push(e);
if(options.tree) {
var grandchildren = this.findElements(e, options);
@@ -491,14 +550,20 @@ var Sortable = {
if(!Sortable._marker) {
Sortable._marker = $('dropmarker') || document.createElement('DIV');
Element.hide(Sortable._marker);
Element.Class.add(Sortable._marker, 'dropmarker');
Element.addClassName(Sortable._marker, 'dropmarker');
Sortable._marker.style.position = 'absolute';
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
}
var offsets = Position.cumulativeOffset(dropon);
Sortable._marker.style.top = offsets[1] + 'px';
if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
Sortable._marker.style.left = offsets[0] + 'px';
Sortable._marker.style.top = offsets[1] + 'px';
if(position=='after')
if(sortable.overlap == 'horizontal')
Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
else
Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
Element.show(Sortable._marker);
},
@@ -511,7 +576,7 @@ var Sortable = {
name: element.id,
format: sortableOptions.format || /^[^_]*_(.*)$/
}, arguments[1] || {});
return $(this.findElements(element, options) || []).collect( function(item) {
return $(this.findElements(element, options) || []).map( function(item) {
return (encodeURIComponent(options.name) + "[]=" +
encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
}).join("&");

View File

@@ -11,8 +11,8 @@
// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
color = "#";
if(this.slice(0,4) == "rgb(") {
var color = '#';
if(this.slice(0,4) == 'rgb(') {
var cols = this.slice(4,this.length-1).split(',');
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
} else {
@@ -26,8 +26,8 @@ String.prototype.parseColor = function() {
Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
var children = $(element).childNodes;
var text = "";
var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
var text = '';
var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i');
for (var i = 0; i < children.length; i++) {
if(children[i].nodeType==3) {
@@ -41,157 +41,70 @@ Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
return text;
}
Element.setStyle = function(element, style) {
element = $(element);
for(k in style) element.style[k.camelize()] = style[k];
}
Element.setContentZoom = function(element, percent) {
element = $(element);
element.style.fontSize = (percent/100) + "em";
Element.setStyle(element, {fontSize: (percent/100) + 'em'});
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
}
Element.getOpacity = function(element){
var opacity;
if (opacity = Element.getStyle(element, "opacity"))
var opacity;
if (opacity = Element.getStyle(element, 'opacity'))
return parseFloat(opacity);
if (opacity = (Element.getStyle(element, "filter") || '').match(/alpha\(opacity=(.*)\)/))
if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
if(opacity[1]) return parseFloat(opacity[1]) / 100;
return 1.0;
}
Element.setOpacity = function(element, value){
element= $(element);
var els = element.style;
if (value == 1){
els.opacity = '0.999999';
if (value == 1){
Element.setStyle(element, { opacity:
(/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : null });
if(/MSIE/.test(navigator.userAgent))
els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'');
Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
} else {
if(value < 0.00001) value = 0;
els.opacity = value;
Element.setStyle(element, {opacity: value});
if(/MSIE/.test(navigator.userAgent))
els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
"alpha(opacity="+value*100+")";
Element.setStyle(element,
{ filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')' });
}
}
Element.getInlineOpacity = function(element){
element= $(element);
var op;
op = element.style.opacity;
if (typeof op != "undefined" && op != "") return op;
return "";
return $(element).style.opacity || '';
}
Element.setInlineOpacity = function(element, value){
element= $(element);
var els = element.style;
els.opacity = value;
}
/*--------------------------------------------------------------------------*/
Element.Class = {
// Element.toggleClass(element, className) toggles the class being on/off
// Element.toggleClass(element, className1, className2) toggles between both classes,
// defaulting to className1 if neither exist
toggle: function(element, className) {
if(Element.Class.has(element, className)) {
Element.Class.remove(element, className);
if(arguments.length == 3) Element.Class.add(element, arguments[2]);
} else {
Element.Class.add(element, className);
if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
}
},
// gets space-delimited classnames of an element as an array
get: function(element) {
return $(element).className.split(' ');
},
// functions adapted from original functions by Gavin Kistner
remove: function(element) {
element = $(element);
var removeClasses = arguments;
$R(1,arguments.length-1).each( function(index) {
element.className =
element.className.split(' ').reject(
function(klass) { return (klass == removeClasses[index]) } ).join(' ');
});
},
add: function(element) {
element = $(element);
for(var i = 1; i < arguments.length; i++) {
Element.Class.remove(element, arguments[i]);
element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
}
},
// returns true if all given classes exist in said element
has: function(element) {
element = $(element);
if(!element || !element.className) return false;
var regEx;
for(var i = 1; i < arguments.length; i++) {
if((typeof arguments[i] == 'object') &&
(arguments[i].constructor == Array)) {
for(var j = 0; j < arguments[i].length; j++) {
regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
if(!regEx.test(element.className)) return false;
}
} else {
regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
if(!regEx.test(element.className)) return false;
}
}
return true;
},
// expects arrays of strings and/or strings as optional paramters
// Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
has_any: function(element) {
element = $(element);
if(!element || !element.className) return false;
var regEx;
for(var i = 1; i < arguments.length; i++) {
if((typeof arguments[i] == 'object') &&
(arguments[i].constructor == Array)) {
for(var j = 0; j < arguments[i].length; j++) {
regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
if(regEx.test(element.className)) return true;
}
} else {
regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
if(regEx.test(element.className)) return true;
}
}
return false;
},
childrenWith: function(element, className) {
var children = $(element).getElementsByTagName('*');
var elements = new Array();
for (var i = 0; i < children.length; i++)
if (Element.Class.has(children[i], className))
elements.push(children[i]);
return elements;
}
}
Element.childrenWithClassName = function(element, className) {
return $A($(element).getElementsByTagName('*')).select(
function(c) { return Element.hasClassName(c, className) });
}
Array.prototype.call = function() {
var args = arguments;
this.each(function(f){ f.apply(this, args) });
}
/*--------------------------------------------------------------------------*/
var Effect = {
tagifyText: function(element) {
var tagifyStyle = "position:relative";
if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ";zoom:1";
var tagifyStyle = 'position:relative';
if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
element = $(element);
$A(element.childNodes).each( function(child) {
if(child.nodeType==3) {
child.nodeValue.toArray().each( function(character) {
element.insertBefore(
Builder.node('span',{style: tagifyStyle},
character == " " ? String.fromCharCode(160) : character),
character == ' ' ? String.fromCharCode(160) : character),
child);
});
Element.remove(child);
@@ -211,11 +124,10 @@ var Effect = {
speed: 0.1,
delay: 0.0
}, arguments[2] || {});
var speed = options.speed;
var delay = options.delay;
var masterDelay = options.delay;
$A(elements).each( function(element, index) {
new effect(element, Object.extend(options, { delay: delay + index * speed }));
new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
});
}
};
@@ -346,13 +258,15 @@ Effect.Base.prototype = {
if(this.setup) this.setup();
this.event('afterSetup');
}
if(this.options.transition) pos = this.options.transition(pos);
pos *= (this.options.to-this.options.from);
pos += this.options.from;
this.position = pos;
this.event('beforeUpdate');
if(this.update) this.update(pos);
this.event('afterUpdate');
if(this.state == 'running') {
if(this.options.transition) pos = this.options.transition(pos);
pos *= (this.options.to-this.options.from);
pos += this.options.from;
this.position = pos;
this.event('beforeUpdate');
if(this.update) this.update(pos);
this.event('afterUpdate');
}
},
cancel: function() {
if(!this.options.sync) Effect.Queue.remove(this);
@@ -361,6 +275,9 @@ Effect.Base.prototype = {
event: function(eventName) {
if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
if(this.options[eventName]) this.options[eventName](this);
},
inspect: function() {
return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
}
}
@@ -390,7 +307,7 @@ Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
this.element = $(element);
// make this work on IE on elements without 'layout'
if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
this.element.style.zoom = 1;
Element.setStyle(this.element, {zoom: 1});
var options = Object.extend({
from: Element.getOpacity(this.element) || 0.0,
to: 1.0
@@ -414,20 +331,16 @@ Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
// Bug in Opera: Opera returns the "real" position of a static element or
// relative element that does not have top/left explicitly set.
// ==> Always set top and left for position relative elements in your stylesheets
// (to 0 if you do not need them)
// (to 0 if you do not need them)
Element.makePositioned(this.element);
this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
},
update: function(position) {
var topd = this.toTop * position + this.originalTop;
var leftd = this.toLeft * position + this.originalLeft;
this.setPosition(topd, leftd);
},
setPosition: function(topd, leftd) {
this.element.style.top = topd + "px";
this.element.style.left = leftd + "px";
Element.setStyle(this.element, {
top: this.toTop * position + this.originalTop + 'px',
left: this.toLeft * position + this.originalLeft + 'px'
});
}
});
@@ -447,33 +360,31 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
this.start(options);
},
setup: function() {
var effect = this;
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
this.elementPositioning = Element.getStyle(this.element,'position');
effect.originalStyle = {};
this.originalStyle = {};
['top','left','width','height','fontSize'].each( function(k) {
effect.originalStyle[k] = effect.element.style[k];
});
this.originalStyle[k] = this.element.style[k];
}.bind(this));
this.originalTop = this.element.offsetTop;
this.originalLeft = this.element.offsetLeft;
var fontSize = Element.getStyle(this.element,'font-size') || "100%";
var fontSize = Element.getStyle(this.element,'font-size') || '100%';
['em','px','%'].each( function(fontSizeType) {
if(fontSize.indexOf(fontSizeType)>0) {
effect.fontSize = parseFloat(fontSize);
effect.fontSizeType = fontSizeType;
this.fontSize = parseFloat(fontSize);
this.fontSizeType = fontSizeType;
}
});
}.bind(this));
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
this.dims = null;
if(this.options.scaleMode=='box')
this.dims = [this.element.clientHeight, this.element.clientWidth];
if(this.options.scaleMode=='content')
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
if(/^content/.test(this.options.scaleMode))
this.dims = [this.element.scrollHeight, this.element.scrollWidth];
if(!this.dims)
this.dims = [this.options.scaleMode.originalHeight,
@@ -482,32 +393,28 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
update: function(position) {
var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
if(this.options.scaleContent && this.fontSize)
this.element.style.fontSize = this.fontSize*currentScale + this.fontSizeType;
Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
},
finish: function(position) {
if (this.restoreAfterFinish) {
var effect = this;
['top','left','width','height','fontSize'].each( function(k) {
effect.element.style[k] = effect.originalStyle[k];
});
}
if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
},
setDimensions: function(height, width) {
var els = this.element.style;
if(this.options.scaleX) els.width = width + 'px';
if(this.options.scaleY) els.height = height + 'px';
var d = {};
if(this.options.scaleX) d.width = width + 'px';
if(this.options.scaleY) d.height = height + 'px';
if(this.options.scaleFromCenter) {
var topd = (height - this.dims[0])/2;
var leftd = (width - this.dims[1])/2;
if(this.elementPositioning == 'absolute') {
if(this.options.scaleY) els.top = this.originalTop-topd + "px";
if(this.options.scaleX) els.left = this.originalLeft-leftd + "px";
if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
} else {
if(this.options.scaleY) els.top = -topd + "px";
if(this.options.scaleX) els.left = -leftd + "px";
if(this.options.scaleY) d.top = -topd + 'px';
if(this.options.scaleX) d.left = -leftd + 'px';
}
}
Element.setStyle(this.element, d);
}
});
@@ -515,41 +422,32 @@ Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
var options = Object.extend({
startcolor: "#ffff99"
}, arguments[1] || {});
var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
this.start(options);
},
setup: function() {
// Prevent executing on elements not in the layout flow
if(this.element.style.display=='none') { this.cancel(); return; }
if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
// Disable background image during the effect
this.oldBgImage = this.element.style.backgroundImage;
this.element.style.backgroundImage = "none";
this.oldStyle = {
backgroundImage: Element.getStyle(this.element, 'background-image') };
Element.setStyle(this.element, {backgroundImage: 'none'});
if(!this.options.endcolor)
this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
if (typeof this.options.restorecolor == "undefined")
this.options.restorecolor = this.element.style.backgroundColor;
if(!this.options.restorecolor)
this.options.restorecolor = Element.getStyle(this.element, 'background-color');
// init color calculations
this.colors_base = [
parseInt(this.options.startcolor.slice(1,3),16),
parseInt(this.options.startcolor.slice(3,5),16),
parseInt(this.options.startcolor.slice(5),16) ];
this.colors_delta = [
parseInt(this.options.endcolor.slice(1,3),16)-this.colors_base[0],
parseInt(this.options.endcolor.slice(3,5),16)-this.colors_base[1],
parseInt(this.options.endcolor.slice(5),16)-this.colors_base[2]];
this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
},
update: function(position) {
var effect = this; var colors = $R(0,2).map( function(i){
return Math.round(effect.colors_base[i]+(effect.colors_delta[i]*position))
});
this.element.style.backgroundColor = "#" +
colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart();
Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
},
finish: function() {
this.element.style.backgroundColor = this.options.restorecolor;
this.element.style.backgroundImage = this.oldBgImage;
Element.setStyle(this.element, Object.extend(this.oldStyle, {
backgroundColor: this.options.restorecolor
}));
}
});
@@ -562,6 +460,7 @@ Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
setup: function() {
Position.prepare();
var offsets = Position.cumulativeOffset(this.element);
if(this.options.offset) offsets[1] += this.options.offset;
var max = window.innerHeight ?
window.height - window.innerHeight :
document.body.scrollHeight -
@@ -584,42 +483,38 @@ Effect.Fade = function(element) {
var options = Object.extend({
from: Element.getOpacity(element) || 1.0,
to: 0.0,
afterFinishInternal: function(effect)
{ if (effect.options.to == 0) {
Element.hide(effect.element);
Element.setInlineOpacity(effect.element, oldOpacity);
}
}
afterFinishInternal: function(effect) { with(Element) {
if(effect.options.to!=0) return;
hide(effect.element);
setStyle(effect.element, {opacity: oldOpacity}); }}
}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Appear = function(element) {
var options = Object.extend({
from: (Element.getStyle(element, "display") == "none" ? 0.0 : Element.getOpacity(element) || 0.0),
from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
to: 1.0,
beforeSetup: function(effect)
{ Element.setOpacity(effect.element, effect.options.from);
Element.show(effect.element); }
beforeSetup: function(effect) { with(Element) {
setOpacity(effect.element, effect.options.from);
show(effect.element); }}
}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Puff = function(element) {
element = $(element);
var oldOpacity = Element.getInlineOpacity(element);
var oldPosition = element.style.position;
var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
return new Effect.Parallel(
[ new Effect.Scale(element, 200,
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect)
{ effect.effects[0].element.style.position = 'absolute'; },
afterFinishInternal: function(effect)
{ Element.hide(effect.effects[0].element);
effect.effects[0].element.style.position = oldPosition;
Element.setInlineOpacity(effect.effects[0].element, oldOpacity); }
beforeSetupInternal: function(effect) { with(Element) {
setStyle(effect.effects[0].element, {position: 'absolute'}); }},
afterFinishInternal: function(effect) { with(Element) {
hide(effect.effects[0].element);
setStyle(effect.effects[0].element, oldStyle); }}
}, arguments[1] || {})
);
}
@@ -631,18 +526,15 @@ Effect.BlindUp = function(element) {
Object.extend({ scaleContent: false,
scaleX: false,
restoreAfterFinish: true,
afterFinishInternal: function(effect)
{
Element.hide(effect.element);
Element.undoClipping(effect.element);
}
afterFinishInternal: function(effect) { with(Element) {
[hide, undoClipping].call(effect.element); }}
}, arguments[1] || {})
);
}
Effect.BlindDown = function(element) {
element = $(element);
var oldHeight = element.style.height;
var oldHeight = Element.getStyle(element, 'height');
var elementDimensions = Element.getDimensions(element);
return new Effect.Scale(element, 100,
Object.extend({ scaleContent: false,
@@ -650,15 +542,15 @@ Effect.BlindDown = function(element) {
scaleFrom: 0,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
Element.makeClipping(effect.element);
effect.element.style.height = "0px";
Element.show(effect.element);
},
afterFinishInternal: function(effect) {
Element.undoClipping(effect.element);
effect.element.style.height = oldHeight;
}
afterSetup: function(effect) { with(Element) {
makeClipping(effect.element);
setStyle(effect.element, {height: '0px'});
show(effect.element);
}},
afterFinishInternal: function(effect) { with(Element) {
undoClipping(effect.element);
setStyle(effect.element, {height: oldHeight});
}}
}, arguments[1] || {})
);
}
@@ -674,16 +566,13 @@ Effect.SwitchOff = function(element) {
new Effect.Scale(effect.element, 1, {
duration: 0.3, scaleFromCenter: true,
scaleX: false, scaleContent: false, restoreAfterFinish: true,
beforeSetup: function(effect) {
Element.makePositioned(effect.element);
Element.makeClipping(effect.element);
},
afterFinishInternal: function(effect) {
Element.hide(effect.element);
Element.undoClipping(effect.element);
Element.undoPositioned(effect.element);
Element.setInlineOpacity(effect.element, oldOpacity);
}
beforeSetup: function(effect) { with(Element) {
[makePositioned,makeClipping].call(effect.element);
}},
afterFinishInternal: function(effect) { with(Element) {
[hide,undoClipping,undoPositioned].call(effect.element);
setStyle(effect.element, {opacity: oldOpacity});
}}
})
}
});
@@ -691,29 +580,28 @@ Effect.SwitchOff = function(element) {
Effect.DropOut = function(element) {
element = $(element);
var oldTop = element.style.top;
var oldLeft = element.style.left;
var oldOpacity = Element.getInlineOpacity(element);
var oldStyle = {
top: Element.getStyle(element, 'top'),
left: Element.getStyle(element, 'left'),
opacity: Element.getInlineOpacity(element) };
return new Effect.Parallel(
[ new Effect.MoveBy(element, 100, 0, { sync: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
Object.extend(
{ duration: 0.5,
beforeSetup: function(effect) {
Element.makePositioned(effect.effects[0].element); },
afterFinishInternal: function(effect) {
Element.hide(effect.effects[0].element);
Element.undoPositioned(effect.effects[0].element);
effect.effects[0].element.style.left = oldLeft;
effect.effects[0].element.style.top = oldTop;
Element.setInlineOpacity(effect.effects[0].element, oldOpacity); }
beforeSetup: function(effect) { with(Element) {
makePositioned(effect.effects[0].element); }},
afterFinishInternal: function(effect) { with(Element) {
[hide, undoPositioned].call(effect.effects[0].element);
setStyle(effect.effects[0].element, oldStyle); }}
}, arguments[1] || {}));
}
Effect.Shake = function(element) {
element = $(element);
var oldTop = element.style.top;
var oldLeft = element.style.left;
var oldStyle = {
top: Element.getStyle(element, 'top'),
left: Element.getStyle(element, 'left') };
return new Effect.MoveBy(element, 0, 20,
{ duration: 0.05, afterFinishInternal: function(effect) {
new Effect.MoveBy(effect.element, 0, -40,
@@ -725,39 +613,39 @@ Effect.Shake = function(element) {
new Effect.MoveBy(effect.element, 0, 40,
{ duration: 0.1, afterFinishInternal: function(effect) {
new Effect.MoveBy(effect.element, 0, -20,
{ duration: 0.05, afterFinishInternal: function(effect) {
Element.undoPositioned(effect.element);
effect.element.style.left = oldLeft;
effect.element.style.top = oldTop;
}}) }}) }}) }}) }}) }});
{ duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
undoPositioned(effect.element);
setStyle(effect.element, oldStyle);
}}}) }}) }}) }}) }}) }});
}
Effect.SlideDown = function(element) {
element = $(element);
Element.cleanWhitespace(element);
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
var oldInnerBottom = element.firstChild.style.bottom;
var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
var elementDimensions = Element.getDimensions(element);
return new Effect.Scale(element, 100,
Object.extend({ scaleContent: false,
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
scaleX: false,
scaleFrom: 0,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
Element.makePositioned(effect.element.firstChild);
if (window.opera) effect.element.firstChild.style.top = "";
Element.makeClipping(effect.element);
element.style.height = '0';
Element.show(element);
},
afterUpdateInternal: function(effect) {
effect.element.firstChild.style.bottom =
(effect.dims[0] - effect.element.clientHeight) + 'px'; },
afterFinishInternal: function(effect) {
Element.undoClipping(effect.element);
Element.undoPositioned(effect.element.firstChild);
effect.element.firstChild.style.bottom = oldInnerBottom; }
afterSetup: function(effect) { with(Element) {
makePositioned(effect.element);
makePositioned(effect.element.firstChild);
if(window.opera) setStyle(effect.element, {top: ''});
makeClipping(effect.element);
setStyle(effect.element, {height: '0px'});
show(element); }},
afterUpdateInternal: function(effect) { with(Element) {
setStyle(effect.element.firstChild, {bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
afterFinishInternal: function(effect) { with(Element) {
undoClipping(effect.element);
undoPositioned(effect.element.firstChild);
undoPositioned(effect.element);
setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
}, arguments[1] || {})
);
}
@@ -765,122 +653,111 @@ Effect.SlideDown = function(element) {
Effect.SlideUp = function(element) {
element = $(element);
Element.cleanWhitespace(element);
var oldInnerBottom = element.firstChild.style.bottom;
var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
return new Effect.Scale(element, 0,
Object.extend({ scaleContent: false,
scaleX: false,
scaleMode: 'box',
scaleFrom: 100,
restoreAfterFinish: true,
beforeStartInternal: function(effect) {
Element.makePositioned(effect.element.firstChild);
if (window.opera) effect.element.firstChild.style.top = "";
Element.makeClipping(effect.element);
Element.show(element);
},
afterUpdateInternal: function(effect) {
effect.element.firstChild.style.bottom =
(effect.dims[0] - effect.element.clientHeight) + 'px'; },
afterFinishInternal: function(effect) {
Element.hide(effect.element);
Element.undoClipping(effect.element);
Element.undoPositioned(effect.element.firstChild);
effect.element.firstChild.style.bottom = oldInnerBottom; }
beforeStartInternal: function(effect) { with(Element) {
makePositioned(effect.element);
makePositioned(effect.element.firstChild);
if(window.opera) setStyle(effect.element, {top: ''});
makeClipping(effect.element);
show(element); }},
afterUpdateInternal: function(effect) { with(Element) {
setStyle(effect.element.firstChild, {bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
afterFinishInternal: function(effect) { with(Element) {
[hide, undoClipping].call(effect.element);
undoPositioned(effect.element.firstChild);
undoPositioned(effect.element);
setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
}, arguments[1] || {})
);
}
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
// Bug in opera makes the TD containing this element expand for a instance after finish
return new Effect.Scale(element, window.opera ? 1 : 0,
{ restoreAfterFinish: true,
beforeSetup: function(effect) {
Element.makeClipping(effect.element); },
afterFinishInternal: function(effect) {
Element.hide(effect.element);
Element.undoClipping(effect.element); }
beforeSetup: function(effect) { with(Element) {
makeClipping(effect.element); }},
afterFinishInternal: function(effect) { with(Element) {
hide(effect.element);
undoClipping(effect.element); }}
});
}
Effect.Grow = function(element) {
element = $(element);
var options = arguments[1] || {};
var elementDimensions = Element.getDimensions(element);
var originalWidth = elementDimensions.width;
var originalHeight = elementDimensions.height;
var oldTop = element.style.top;
var oldLeft = element.style.left;
var oldHeight = element.style.height;
var oldWidth = element.style.width;
var oldOpacity = Element.getInlineOpacity(element);
var direction = options.direction || 'center';
var moveTransition = options.moveTransition || Effect.Transitions.sinoidal;
var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal;
var opacityTransition = options.opacityTransition || Effect.Transitions.full;
var options = Object.extend({
direction: 'center',
moveTransistion: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.full
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: Element.getInlineOpacity(element) };
var dims = Element.getDimensions(element);
var initialMoveX, initialMoveY;
var moveX, moveY;
switch (direction) {
switch (options.direction) {
case 'top-left':
initialMoveX = initialMoveY = moveX = moveY = 0;
break;
case 'top-right':
initialMoveX = originalWidth;
initialMoveX = dims.width;
initialMoveY = moveY = 0;
moveX = -originalWidth;
moveX = -dims.width;
break;
case 'bottom-left':
initialMoveX = moveX = 0;
initialMoveY = originalHeight;
moveY = -originalHeight;
initialMoveY = dims.height;
moveY = -dims.height;
break;
case 'bottom-right':
initialMoveX = originalWidth;
initialMoveY = originalHeight;
moveX = -originalWidth;
moveY = -originalHeight;
initialMoveX = dims.width;
initialMoveY = dims.height;
moveX = -dims.width;
moveY = -dims.height;
break;
case 'center':
initialMoveX = originalWidth / 2;
initialMoveY = originalHeight / 2;
moveX = -originalWidth / 2;
moveY = -originalHeight / 2;
initialMoveX = dims.width / 2;
initialMoveY = dims.height / 2;
moveX = -dims.width / 2;
moveY = -dims.height / 2;
break;
}
return new Effect.MoveBy(element, initialMoveY, initialMoveX, {
duration: 0.01,
beforeSetup: function(effect) {
Element.hide(effect.element);
Element.makeClipping(effect.element);
Element.makePositioned(effect.element);
},
beforeSetup: function(effect) { with(Element) {
hide(effect.element);
makeClipping(effect.element);
makePositioned(effect.element);
}},
afterFinishInternal: function(effect) {
new Effect.Parallel(
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }),
new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: moveTransition }),
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }),
new Effect.Scale(effect.element, 100, {
scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth },
sync: true, scaleFrom: window.opera ? 1 : 0, transition: scaleTransition, restoreAfterFinish: true})
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
effect.effects[0].element.style.height = 0;
Element.show(effect.effects[0].element);
},
afterFinishInternal: function(effect) {
var el = effect.effects[0].element;
var els = el.style;
Element.undoClipping(el);
Element.undoPositioned(el);
els.top = oldTop;
els.left = oldLeft;
els.height = oldHeight;
els.width = originalWidth + 'px';
Element.setInlineOpacity(el, oldOpacity);
}
beforeSetup: function(effect) { with(Element) {
setStyle(effect.effects[0].element, {height: '0px'});
show(effect.effects[0].element); }},
afterFinishInternal: function(effect) { with(Element) {
[undoClipping, undoPositioned].call(effect.effects[0].element);
setStyle(effect.effects[0].element, oldStyle); }}
}, options)
)
}
@@ -889,66 +766,54 @@ Effect.Grow = function(element) {
Effect.Shrink = function(element) {
element = $(element);
var options = arguments[1] || {};
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
var oldTop = element.style.top;
var oldLeft = element.style.left;
var oldHeight = element.style.height;
var oldWidth = element.style.width;
var oldOpacity = Element.getInlineOpacity(element);
var options = Object.extend({
direction: 'center',
moveTransistion: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.none
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: Element.getInlineOpacity(element) };
var direction = options.direction || 'center';
var moveTransition = options.moveTransition || Effect.Transitions.sinoidal;
var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal;
var opacityTransition = options.opacityTransition || Effect.Transitions.none;
var dims = Element.getDimensions(element);
var moveX, moveY;
switch (direction) {
switch (options.direction) {
case 'top-left':
moveX = moveY = 0;
break;
case 'top-right':
moveX = originalWidth;
moveX = dims.width;
moveY = 0;
break;
case 'bottom-left':
moveX = 0;
moveY = originalHeight;
moveY = dims.height;
break;
case 'bottom-right':
moveX = originalWidth;
moveY = originalHeight;
moveX = dims.width;
moveY = dims.height;
break;
case 'center':
moveX = originalWidth / 2;
moveY = originalHeight / 2;
moveX = dims.width / 2;
moveY = dims.height / 2;
break;
}
return new Effect.Parallel(
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }),
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: scaleTransition, restoreAfterFinish: true}),
new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition })
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition })
], Object.extend({
beforeStartInternal: function(effect) {
Element.makePositioned(effect.effects[0].element);
Element.makeClipping(effect.effects[0].element);
},
afterFinishInternal: function(effect) {
var el = effect.effects[0].element;
var els = el.style;
Element.hide(el);
Element.undoClipping(el);
Element.undoPositioned(el);
els.top = oldTop;
els.left = oldLeft;
els.height = oldHeight;
els.width = oldWidth;
Element.setInlineOpacity(el, oldOpacity);
}
beforeStartInternal: function(effect) { with(Element) {
[makePositioned, makeClipping].call(effect.effects[0].element) }},
afterFinishInternal: function(effect) { with(Element) {
[hide, undoClipping, undoPositioned].call(effect.effects[0].element);
setStyle(effect.effects[0].element, oldStyle); }}
}, options)
);
}
@@ -962,16 +827,17 @@ Effect.Pulsate = function(element) {
reverser.bind(transition);
return new Effect.Opacity(element,
Object.extend(Object.extend({ duration: 3.0, from: 0,
afterFinishInternal: function(effect) { Element.setInlineOpacity(effect.element, oldOpacity); }
afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
}, options), {transition: reverser}));
}
Effect.Fold = function(element) {
element = $(element);
var originalTop = element.style.top;
var originalLeft = element.style.left;
var originalWidth = element.style.width;
var originalHeight = element.style.height;
var oldStyle = {
top: element.style.top,
left: element.style.left,
width: element.style.width,
height: element.style.height };
Element.makeClipping(element);
return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
@@ -980,13 +846,9 @@ Effect.Fold = function(element) {
new Effect.Scale(element, 1, {
scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
Element.hide(effect.element);
Element.undoClipping(effect.element);
effect.element.style.top = originalTop;
effect.element.style.left = originalLeft;
effect.element.style.width = originalWidth;
effect.element.style.height = originalHeight;
} });
afterFinishInternal: function(effect) { with(Element) {
[hide, undoClipping].call(effect.element);
setStyle(effect.element, oldStyle);
}} });
}}, arguments[1] || {}));
}

View File

@@ -1,4 +1,4 @@
/* Prototype JavaScript framework, version 1.4.0_rc2
/* Prototype JavaScript framework, version 1.4.0
* (c) 2005 Sam Stephenson <sam@conio.net>
*
* THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
@@ -11,7 +11,8 @@
/*--------------------------------------------------------------------------*/
var Prototype = {
Version: '1.4.0_rc2',
Version: '1.4.0',
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
emptyFunction: function() {},
K: function(x) {return x}
@@ -45,10 +46,10 @@ Object.inspect = function(object) {
}
}
Function.prototype.bind = function(object) {
var __method = this;
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, arguments);
return __method.apply(object, args.concat($A(arguments)));
}
}
@@ -143,6 +144,22 @@ Object.extend(String.prototype, {
return this.replace(/<\/?[^>]+>/gi, '');
},
stripScripts: function() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
},
extractScripts: function() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag) {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
},
evalScripts: function() {
return this.extractScripts().map(eval);
},
escapeHTML: function() {
var div = document.createElement('div');
var text = document.createTextNode(this);
@@ -214,8 +231,8 @@ var Enumerable = {
all: function(iterator) {
var result = true;
this.each(function(value, index) {
if (!(result &= (iterator || Prototype.K)(value, index)))
throw $break;
result = result && !!(iterator || Prototype.K)(value, index);
if (!result) throw $break;
});
return result;
},
@@ -223,7 +240,7 @@ var Enumerable = {
any: function(iterator) {
var result = true;
this.each(function(value, index) {
if (result &= (iterator || Prototype.K)(value, index))
if (result = !!(iterator || Prototype.K)(value, index))
throw $break;
});
return result;
@@ -376,6 +393,7 @@ Object.extend(Enumerable, {
entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
@@ -388,12 +406,19 @@ var $A = Array.from = function(iterable) {
Object.extend(Array.prototype, Enumerable);
Array.prototype._reverse = Array.prototype.reverse;
Object.extend(Array.prototype, {
_each: function(iterator) {
for (var i = 0; i < this.length; i++)
iterator(this[i]);
},
clear: function() {
this.length = 0;
return this;
},
first: function() {
return this[0];
},
@@ -425,13 +450,18 @@ Object.extend(Array.prototype, {
indexOf: function(object) {
for (var i = 0; i < this.length; i++)
if (this[i] == object) return i;
return false;
return -1;
},
reverse: function() {
var result = [];
for (var i = this.length; i > 0; i--)
result.push(this[i-1]);
reverse: function(inline) {
return (inline !== false ? this : this.toArray())._reverse();
},
shift: function() {
var result = this[0];
for (var i = 0; i < this.length - 1; i++)
this[i] = this[i + 1];
this.length--;
return result;
},
@@ -486,9 +516,9 @@ function $H(object) {
Object.extend(hash, Hash);
return hash;
}
var Range = Class.create();
Object.extend(Range.prototype, Enumerable);
Object.extend(Range.prototype, {
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
initialize: function(start, end, exclusive) {
this.start = start;
this.end = end;
@@ -513,7 +543,7 @@ Object.extend(Range.prototype, {
});
var $R = function(start, end, exclusive) {
return new Range(start, end, exclusive);
return new ObjectRange(start, end, exclusive);
}
var Ajax = {
@@ -549,8 +579,7 @@ Ajax.Responders = {
if (responder[callback] && typeof responder[callback] == 'function') {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) {
}
} catch (e) {}
}
});
}
@@ -626,8 +655,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
this.transport.send(this.options.method == 'post' ? body : null);
} catch (e) {
(this.options.onException || Prototype.emptyFunction)(this, e);
Ajax.Responders.dispatch('onException', this, e);
this.dispatchException(e);
}
},
@@ -661,12 +689,23 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
this.respondToReadyState(this.transport.readyState);
},
header: function(name) {
try {
return this.transport.getResponseHeader(name);
} catch (e) {}
},
evalJSON: function() {
try {
var json = this.transport.getResponseHeader('X-JSON'), object;
object = eval(json);
return object;
return eval(this.header('X-JSON'));
} catch (e) {}
},
evalResponse: function() {
try {
return eval(this.transport.responseText);
} catch (e) {
this.dispatchException(e);
}
},
@@ -674,22 +713,38 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
var event = Ajax.Request.Events[readyState];
var transport = this.transport, json = this.evalJSON();
if (event == 'Complete')
(this.options['on' + this.transport.status]
|| this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(transport, json);
if (event == 'Complete') {
try {
(this.options['on' + this.transport.status]
|| this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(transport, json);
} catch (e) {
this.dispatchException(e);
}
(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
Ajax.Responders.dispatch('on' + event, this, transport, json);
if ((this.header('Content-type') || '').match(/^text\/javascript/i))
this.evalResponse();
}
try {
(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
Ajax.Responders.dispatch('on' + event, this, transport, json);
} catch (e) {
this.dispatchException(e);
}
/* Avoid memory leak in MSIE: clean up the oncomplete event handler */
if (event == 'Complete')
this.transport.onreadystatechange = Prototype.emptyFunction;
},
dispatchException: function(exception) {
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Updater = Class.create();
Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
initialize: function(container, url, options) {
@@ -714,16 +769,16 @@ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
updateContent: function() {
var receiver = this.responseIsSuccess() ?
this.containers.success : this.containers.failure;
var response = this.transport.responseText;
var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
var response = this.transport.responseText.replace(match, '');
var scripts = this.transport.responseText.match(match);
if (!this.options.evalScripts)
response = response.stripScripts();
if (receiver) {
if (this.options.insertion) {
new this.options.insertion(receiver, response);
} else {
receiver.innerHTML = response;
Element.update(receiver, response);
}
}
@@ -731,14 +786,6 @@ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
if (this.onComplete)
setTimeout(this.onComplete.bind(this), 10);
}
if (this.options.evalScripts && scripts) {
match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
setTimeout((function() {
for (var i = 0; i < scripts.length; i++)
eval(scripts[i].match(match)[1]);
}).bind(this), 10);
}
}
});
@@ -830,6 +877,11 @@ Object.extend(Element, {
element.parentNode.removeChild(element);
},
update: function(element, html) {
$(element).innerHTML = html.stripScripts();
setTimeout(function() {html.evalScripts()}, 10);
},
getHeight: function(element) {
element = $(element);
return element.offsetHeight;
@@ -893,6 +945,12 @@ Object.extend(Element, {
return value == 'auto' ? null : value;
},
setStyle: function(element, style) {
element = $(element);
for (name in style)
element.style[name.camelize()] = style[name];
},
getDimensions: function(element) {
element = $(element);
if (Element.getStyle(element, 'display') != 'none')
@@ -969,7 +1027,7 @@ Abstract.Insertion = function(adjacency) {
Abstract.Insertion.prototype = {
initialize: function(element, content) {
this.element = $(element);
this.content = content;
this.content = content.stripScripts();
if (this.adjacency && this.element.insertAdjacentHTML) {
try {
@@ -986,6 +1044,8 @@ Abstract.Insertion.prototype = {
if (this.initializeRange) this.initializeRange();
this.insertContent([this.range.createContextualFragment(this.content)]);
}
setTimeout(function() {content.evalScripts()}, 10);
},
contentFromAnonymousTable: function() {
@@ -1018,7 +1078,7 @@ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
},
insertContent: function(fragments) {
fragments.reverse().each((function(fragment) {
fragments.reverse(false).each((function(fragment) {
this.element.insertBefore(fragment, this.element.firstChild);
}).bind(this));
}
@@ -1079,7 +1139,7 @@ Element.ClassNames.prototype = {
if (!this.include(classNameToRemove)) return;
this.set(this.select(function(className) {
return className != classNameToRemove;
}));
}).join(' '));
},
toString: function() {
@@ -1109,8 +1169,10 @@ var Field = {
},
activate: function(element) {
$(element).focus();
$(element).select();
element = $(element);
element.focus();
if (element.select)
element.select();
}
}
@@ -1178,16 +1240,15 @@ var Form = {
}
},
findFirstElement: function(form) {
return Form.getElements(form).find(function(element) {
return element.type != 'hidden' && !element.disabled &&
['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
});
},
focusFirstElement: function(form) {
form = $(form);
var elements = Form.getElements(form);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element.type != 'hidden' && !element.disabled) {
Field.activate(element);
break;
}
}
Field.activate(Form.findFirstElement(form));
},
reset: function(form) {
@@ -1201,9 +1262,17 @@ Form.Element = {
var method = element.tagName.toLowerCase();
var parameter = Form.Element.Serializers[method](element);
if (parameter)
return encodeURIComponent(parameter[0]) + '=' +
encodeURIComponent(parameter[1]);
if (parameter) {
var key = encodeURIComponent(parameter[0]);
if (key.length == 0) return;
if (parameter[1].constructor != Array)
parameter[1] = [parameter[1]];
return parameter[1].map(function(value) {
return key + '=' + encodeURIComponent(value);
}).join('&');
}
},
getValue: function(element) {
@@ -1349,24 +1418,14 @@ Abstract.EventObserver.prototype = {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':
element.target = this;
element.prev_onclick = element.onclick || Prototype.emptyFunction;
element.onclick = function() {
this.prev_onclick();
this.target.onElementEvent();
}
Event.observe(element, 'click', this.onElementEvent.bind(this));
break;
case 'password':
case 'text':
case 'textarea':
case 'select-one':
case 'select-multiple':
element.target = this;
element.prev_onchange = element.onchange || Prototype.emptyFunction;
element.onchange = function() {
this.prev_onchange();
this.target.onElementEvent();
}
Event.observe(element, 'change', this.onElementEvent.bind(this));
break;
}
}

View File

@@ -23,6 +23,7 @@ end
# Set up company fixtures.
$LOAD_PATH << "#{path_to_ar}/test"
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
require 'fixtures/company'
File.read("#{path_to_ar}/test/fixtures/db_definitions/sqlite.sql").split(';').each do |sql|
ActiveRecord::Base.connection.execute(sql) unless sql.blank?

View File

@@ -15,12 +15,12 @@ require 'action_controller/session/active_record_store'
#ActiveRecord::Base.logger = Logger.new($stdout)
begin
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite3', :dbfile => ':memory:')
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
CGI::Session::ActiveRecordStore::Session.connection
rescue Object
$stderr.puts 'SQLite 3 unavailable; falling back to SQLite 2.'
begin
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite', :dbfile => ':memory:')
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite', :database => ':memory:')
CGI::Session::ActiveRecordStore::Session.connection
rescue Object
$stderr.puts 'SQLite 2 unavailable; skipping ActiveRecordStore test suite.'
@@ -68,10 +68,16 @@ class ActiveRecordStoreTest < Test::Unit::TestCase
ENV['REQUEST_METHOD'] = 'GET'
CGI::Session::ActiveRecordStore.session_class = session_class
@new_session = CGI::Session.new(CGI.new, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
@cgi = CGI.new
@new_session = CGI::Session.new(@cgi, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
@new_session['foo'] = 'bar'
end
def test_another_instance
@another = CGI::Session.new(@cgi, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
assert_equal @new_session.session_id, @another.session_id
end
def test_model_attribute
assert_kind_of CGI::Session::ActiveRecordStore::Session, @new_session.model
assert_equal({ 'foo' => 'bar' }, @new_session.model.data)

View File

@@ -31,7 +31,7 @@ class FlashTest < Test::Unit::TestCase
def use_flash_and_keep_it
@flash_copy = {}.update flash
@flashy = flash["that"]
keep_flash
silence_warnings { keep_flash }
render :inline => "hello"
end

View File

@@ -43,6 +43,22 @@ class NewRenderTestController < ActionController::Base
def render_custom_code
render :text => "hello world", :status => "404 Moved"
end
def render_file_with_instance_variables
@secret = 'in the sauce'
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.rhtml')
render :file => path
end
def render_file_with_locals
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.rhtml')
render :file => path, :locals => {:secret => 'in the sauce'}
end
def render_file_not_using_full_path
@secret = 'in the sauce'
render :file => 'test/render_file_with_ivar', :use_full_path => true
end
def render_xml_hello
@name = "David"
@@ -242,6 +258,21 @@ class NewRenderTest < Test::Unit::TestCase
assert_response :missing
end
def test_render_file_with_instance_variables
get :render_file_with_instance_variables
assert_equal "The secret is in the sauce\n", @response.body
end
def test_render_file_not_using_full_path
get :render_file_not_using_full_path
assert_equal "The secret is in the sauce\n", @response.body
end
def test_render_file_with_locals
get :render_file_with_locals
assert_equal "The secret is in the sauce\n", @response.body
end
def test_attempt_to_access_object_method
assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
end

View File

@@ -100,6 +100,11 @@ class RequestTest < Test::Unit::TestCase
@request.relative_url_root = nil
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
assert_equal "/collaboration/hieraki", @request.relative_url_root
# apache/scgi case
@request.relative_url_root = nil
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki"
assert_equal "/collaboration/hieraki", @request.relative_url_root
end
def test_request_uri

View File

@@ -0,0 +1 @@
The secret is <%= @secret %>

View File

@@ -0,0 +1 @@
The secret is <%= secret %>

View File

@@ -1,7 +1,17 @@
*SVN*
*1.0.0* (December 13th, 2005)
* Become part of Rails 1.0
*0.9.4* (December 7th, 2005)
* Update from LGPL to MIT license as per Minero Aoki's permission. [Marcel Molina Jr.]
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
* Fix that XML-RPC date/time values did not have well-defined behaviour (#2516, #2534). This fix has one caveat, in that we can't support pre-1970 dates from XML-RPC clients.
*0.9.3* (November 7th, 2005)
* Upgraded to Action Pack 1.11.0 and Active Record 1.13.0

View File

@@ -10,7 +10,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'action_web_service', 'version'
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
PKG_NAME = 'actionwebservice'
PKG_VERSION = ActionWebService::Version::STRING + PKG_BUILD
PKG_VERSION = ActionWebService::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
@@ -63,8 +63,8 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "aws"
s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 1.11.0' + PKG_BUILD)
s.add_dependency('activerecord', '= 1.13.0' + PKG_BUILD)
s.add_dependency('actionpack', '= 1.11.2' + PKG_BUILD)
s.add_dependency('activerecord', '= 1.13.2' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'

View File

@@ -1,8 +1,8 @@
module ActionWebService
module Version
MAJOR = 0
MINOR = 9
TINY = 3
module VERSION
MAJOR = 1
MINOR = 0
TINY = 0
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -3,9 +3,28 @@
#
# Copyright (c) 2000-2004 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
#
unless Enumerable.method_defined?(:map) # Ruby 1.4.6

View File

@@ -1,4 +1,92 @@
*SVN*
*
*1.13.2* (December 13th, 2005)
* Become part of Rails 1.0
* MySQL: allow encoding option for mysql.rb driver. [Jeremy Kemper]
* MySQL: fixes for the bundled mysql.rb driver. #3160 [Justin Forder]
* MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. #2978 [Shugo Maeda]
* SQLServer: fix obscure optimistic locking bug. #3068 [kajism@yahoo.com]
* SQLServer: support uniqueidentifier columns. #2930 [keithm@infused.org]
* SQLServer: cope with tables names qualified by owner. #3067 [jeff@ministrycentered.com]
* SQLServer: cope with columns with "desc" in the name. #1950 [Ron Lusk, Ryan Tomayko]
* SQLServer: cope with primary keys with "select" in the name. #3057 [rdifrango@captechventures.com]
* Oracle: active? performs a select instead of a commit. #3133 [Michael Schoen]
*1.13.1* (December 7th, 2005)
* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausolei]
* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
* PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
* Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? [Jeremy Kemper]
* Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz <kennethkunz@gmail.com>]
* Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz <kennethkunz@gmail.com>]
* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz <kennethkunz@gmail.com>]
* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
* Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
* Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
* Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
* Document :force option to create_table. #2921 [Blair Zajac <blair@orcaware.com>]
* Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
* Correct documentation for Base.delete_all. #1568 [Newhydra]
* Oracle: test case for column default parsing. #2788 [Michael Schoen <schoenm@earthlink.net>]
* Update documentation for Migrations. #2861 [Tom Werner <tom@cube6media.com>]
* Oracle: Much faster column reflection. #2848 [Michael Schoen <schoenm@earthlink.net>]
* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]

View File

@@ -9,7 +9,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
PKG_NAME = 'activerecord'
PKG_VERSION = ActiveRecord::Version::STRING + PKG_BUILD
PKG_VERSION = ActiveRecord::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -27,7 +27,7 @@ task :default => [ :test_mysql, :test_sqlite, :test_postgresql ]
# Run the unit tests
for adapter in %w( mysql postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2 oci )
for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oci )
Rake::TestTask.new("test_#{adapter}") { |t|
t.libs << "test" << "test/connections/native_#{adapter}"
t.pattern = "test/*_test{,_#{adapter}}.rb"
@@ -35,6 +35,41 @@ for adapter in %w( mysql postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2
}
end
SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
desc 'Build the MySQL test databases'
task :build_mysql_databases do
%x( mysqladmin create activerecord_unittest )
%x( mysqladmin create activerecord_unittest2 )
%x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
%x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql2.sql')} )
end
desc 'Drop the MySQL test databases'
task :drop_mysql_databases do
%x( mysqladmin -f drop activerecord_unittest )
%x( mysqladmin -f drop activerecord_unittest2 )
end
desc 'Rebuild the MySQL test databases'
task :rebuild_mysql_databases => [:drop_mysql_databases, :build_mysql_databases]
desc 'Build the PostgreSQL test databases'
task :build_postgresql_databases do
%x( createdb activerecord_unittest )
%x( createdb activerecord_unittest2 )
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} )
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} )
end
desc 'Drop the PostgreSQL test databases'
task :drop_postgresql_databases do
%x( dropdb activerecord_unittest )
%x( dropdb activerecord_unittest2 )
end
desc 'Rebuild the PostgreSQL test databases'
task :rebuild_postgresql_databases => [:drop_postgresql_databases, :build_postgresql_databases]
# Generate the RDoc documentation
@@ -71,7 +106,7 @@ spec = Gem::Specification.new do |s|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
s.add_dependency('activesupport', '= 1.2.3' + PKG_BUILD)
s.add_dependency('activesupport', '= 1.2.5' + PKG_BUILD)
s.files.delete "test/fixtures/fixture_database.sqlite"
s.files.delete "test/fixtures/fixture_database_2.sqlite"

View File

@@ -66,11 +66,11 @@ ActiveRecord::Base.class_eval do
end
unless defined?(RAILS_CONNECTION_ADAPTERS)
RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite sqlserver db2 oci)
RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite firebird sqlserver db2 oci)
end
RAILS_CONNECTION_ADAPTERS.each do |adapter|
require "active_record/connection_adapters/#{adapter}_adapter"
require "active_record/connection_adapters/" + adapter + "_adapter"
end
require 'active_record/query_cache'

View File

@@ -5,6 +5,12 @@ module ActiveRecord
base.extend(ClassMethods)
end
def clear_aggregation_cache #:nodoc:
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
instance_variable_set "@#{assoc.name}", nil
end unless self.new_record?
end
# Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
# as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
# composed of [an] address". Each call to the macro adds a description of how the value objects are created from the

View File

@@ -809,6 +809,9 @@ module ActiveRecord
records_to_save.each { |record| association.send(:insert_record, record) }
association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
end
@new_record_before_save = false
true
end_eval
# Doesn't use after_save as that would save associations added in after_create/after_update twice
@@ -977,9 +980,10 @@ module ActiveRecord
end
def include_eager_conditions?(options)
return false unless options[:conditions]
options[:conditions].scan(/ ([^.]+)\.[^.]+ /).flatten.any? do |condition_table_name|
conditions = options[:conditions]
return false unless conditions
conditions = conditions.first if conditions.is_a?(Array)
conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name|
condition_table_name != table_name
end
end
@@ -999,7 +1003,7 @@ module ActiveRecord
end
def column_aliases(schema_abbreviations)
schema_abbreviations.collect { |cn, tc| "#{tc.join(".")} AS #{cn}" }.join(", ")
schema_abbreviations.collect { |cn, tc| "#{tc[0]}.#{connection.quote_column_name tc[1]} AS #{cn}" }.join(", ")
end
def association_join(reflection)

View File

@@ -65,7 +65,7 @@ module ActiveRecord
end
def construct_sql
@finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@options[:conditions] ? " AND " + @options[:conditions] : ""}"
@finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
@finder_sql << " AND (#{sanitize_sql(@options[:conditions])})" if @options[:conditions]
@finder_sql
end

View File

@@ -243,24 +243,6 @@ module ActiveRecord #:nodoc:
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
cattr_accessor :logger
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work unrelated
# to any of the specific Active Records.
def self.connection
if allow_concurrency
retrieve_connection
else
@connection ||= retrieve_connection
end
end
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work that isn't
# easily done without going straight to SQL.
def connection
self.class.connection
end
def self.inherited(child) #:nodoc:
@@subclasses[self] ||= []
@@subclasses[self] << child
@@ -513,7 +495,7 @@ module ActiveRecord #:nodoc:
# Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
# calling the destroy method). Example:
# Post.destroy_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
# Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
def delete_all(conditions = nil)
sql = "DELETE FROM #{table_name} "
add_conditions!(sql, conditions)
@@ -646,9 +628,16 @@ module ActiveRecord #:nodoc:
"type"
end
# Default sequence_name. Use set_sequence_name to override.
# Lazy-set the sequence name to the connection's default. This method
# is only ever called once since set_sequence_name overrides it.
def sequence_name
connection.default_sequence_name(table_name, primary_key)
reset_sequence_name
end
def reset_sequence_name
default = connection.default_sequence_name(table_name, primary_key)
set_sequence_name(default)
default
end
# Sets the table name to use to the given value, or (if the value
@@ -699,9 +688,11 @@ module ActiveRecord #:nodoc:
# given block. This is required for Oracle and is useful for any
# database which relies on sequences for primary key generation.
#
# Setting the sequence name when using other dbs will have no effect.
# If a sequence name is not explicitly set when using Oracle, it will
# default to the commonly used pattern of: #{table_name}_seq
# If a sequence name is not explicitly set when using Oracle or Firebird,
# it will default to the commonly used pattern of: #{table_name}_seq
#
# If a sequence name is not explicitly set when using PostgreSQL, it
# will discover the sequence corresponding to your primary key for you.
#
# Example:
#
@@ -955,8 +946,9 @@ module ActiveRecord #:nodoc:
end
def type_condition
type_condition = subclasses.inject("#{table_name}.#{inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
condition << "OR #{table_name}.#{inheritance_column} = '#{subclass.name.demodulize}' "
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
type_condition = subclasses.inject("#{table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
condition << "OR #{table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
end
" (#{type_condition}) "
@@ -1010,7 +1002,7 @@ module ActiveRecord #:nodoc:
def construct_conditions_from_arguments(attribute_names, arguments)
conditions = []
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{name} #{attribute_condition(arguments[idx])} " }
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{connection.quote_column_name(name)} #{attribute_condition(arguments[idx])} " }
[ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
end
@@ -1053,12 +1045,12 @@ module ActiveRecord #:nodoc:
def define_attr_method(name, value=nil, &block)
sing = class << self; self; end
sing.send :alias_method, "original_#{name}", name
if value
if block_given?
sing.send :define_method, name, &block
else
# use eval instead of a block to work around a memory leak in dev
# mode in fcgi
sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
else
sing.send :define_method, name, &block
end
end
@@ -1312,6 +1304,7 @@ module ActiveRecord #:nodoc:
# Reloads the attributes of this object from the database.
def reload
clear_aggregation_cache
clear_association_cache
@attributes.update(self.class.find(self.id).instance_variable_get('@attributes'))
self
@@ -1450,6 +1443,10 @@ module ActiveRecord #:nodoc:
# Creates a new record with values matching those of the instance attributes.
def create
if self.id.nil? and connection.prefetch_primary_key?(self.class.table_name)
self.id = connection.next_sequence_value(self.class.sequence_name)
end
self.id = connection.insert(
"INSERT INTO #{self.class.table_name} " +
"(#{quoted_column_names.join(', ')}) " +
@@ -1549,7 +1546,16 @@ module ActiveRecord #:nodoc:
self.class.read_methods << attr_name
end
self.class.class_eval("def #{symbol}; #{access_code}; end")
begin
self.class.class_eval("def #{symbol}; #{access_code}; end")
rescue SyntaxError => err
self.class.read_methods.delete(attr_name)
if logger
logger.warn "Exception occured during reader method compilation."
logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
logger.warn "#{err.message}"
end
end
end
# Returns true if the attribute is of a text column and marked for serialization.

View File

@@ -1,5 +1,4 @@
module ActiveRecord
# The root class of all active record objects.
class Base
class ConnectionSpecification #:nodoc:
attr_reader :config, :adapter_method
@@ -11,6 +10,28 @@ module ActiveRecord
# The class -> [adapter_method, config] map
@@defined_connections = {}
# The class -> thread id -> adapter cache.
@@connection_cache = Hash.new { |h, k| h[k] = Hash.new }
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work unrelated
# to any of the specific Active Records.
def self.connection
@@connection_cache[Thread.current.object_id][name] ||= retrieve_connection
end
# Clears the cache which maps classes to connections.
def self.clear_connection_cache!
@@connection_cache.clear
end
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work that isn't
# easily done without going straight to SQL.
def connection
self.class.connection
end
# Establishes the connection to the database. Accepts a hash as input where
# the :adapter key must be specified with the name of a database adapter (in lower-case)
# example for regular databases (MySQL, Postgresql, etc):
@@ -44,7 +65,7 @@ module ActiveRecord
raise AdapterNotSpecified unless defined? RAILS_ENV
establish_connection(RAILS_ENV)
when ConnectionSpecification
@@defined_connections[self] = spec
@@defined_connections[name] = spec
when Symbol, String
if configuration = configurations[spec.to_s]
establish_connection(configuration)
@@ -77,9 +98,11 @@ module ActiveRecord
klass = self
ar_super = ActiveRecord::Base.superclass
until klass == ar_super
if conn = active_connections[klass]
if conn = active_connections[klass.name]
# Reconnect if the connection is inactive.
conn.reconnect! unless conn.active?
return conn
elsif conn = @@defined_connections[klass]
elsif conn = @@defined_connections[klass.name]
klass.connection = conn
return self.connection
end
@@ -92,7 +115,7 @@ module ActiveRecord
def self.connected?
klass = self
until klass == ActiveRecord::Base.superclass
if active_connections[klass]
if active_connections[klass.name]
return true
else
klass = klass.superclass
@@ -106,9 +129,10 @@ module ActiveRecord
# can be used as argument for establish_connection, for easy
# re-establishing of the connection.
def self.remove_connection(klass=self)
conn = @@defined_connections[klass]
@@defined_connections.delete(klass)
active_connections[klass] = nil
conn = @@defined_connections[klass.name]
@@defined_connections.delete(klass.name)
@@connection_cache[Thread.current.object_id].delete(klass.name)
active_connections.delete(klass.name)
@connection = nil
conn.config if conn
end
@@ -116,7 +140,7 @@ module ActiveRecord
# Set the connection for the class.
def self.connection=(spec)
if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
active_connections[self] = spec
active_connections[name] = spec
elsif spec.kind_of?(ConnectionSpecification)
self.connection = self.send(spec.adapter_method, spec.config)
elsif spec.nil?

View File

@@ -59,7 +59,7 @@ module ActiveRecord
when :time then self.class.string_to_dummy_time(value)
when :date then self.class.string_to_date(value)
when :binary then self.class.binary_to_string(value)
when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
when :boolean then self.class.value_to_boolean(value)
else value
end
end
@@ -75,7 +75,7 @@ module ActiveRecord
when :time then "#{self.class.name}.string_to_dummy_time(#{var_name})"
when :date then "#{self.class.name}.string_to_date(#{var_name})"
when :binary then "#{self.class.name}.binary_to_string(#{var_name})"
when :boolean then "(#{var_name} == true or (#{var_name} =~ /^t(?:true)?$/i) == 0 or #{var_name}.to_s == '1')"
when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
else nil
end
end
@@ -120,6 +120,15 @@ module ActiveRecord
Time.send(Base.default_timezone, *time_array) rescue nil
end
# convert something to a boolean
def self.value_to_boolean(value)
return value if value==true || value==false
case value.to_s.downcase
when "true", "t", "1" then true
else false
end
end
private
def extract_limit(sql_type)
$1.to_i if sql_type =~ /\((.*)\)/

View File

@@ -43,6 +43,9 @@ module ActiveRecord
# Any extra options you want appended to the table definition.
# [<tt>:temporary</tt>]
# Make a temporary table.
# [<tt>:force</tt>]
# Set to true or false to drop the table before creating it.
# Defaults to false.
#
# ===== Examples
# ====== Add a backend specific option to the generated SQL (MySQL)

View File

@@ -32,40 +32,60 @@ module ActiveRecord
def adapter_name
'Abstract'
end
# Does this adapter support migrations? Backend specific, as the
# abstract adapter always returns +false+.
def supports_migrations?
false
end
def reset_runtime #:nodoc:
rt = @runtime
@runtime = 0
return rt
# Should primary key values be selected from their corresponding
# sequence before the insert statement? If true, next_sequence_value
# is called before each insert to set the record's primary key.
# This is false for all adapters but Firebird.
def prefetch_primary_key?(table_name = nil)
false
end
protected
def reset_runtime #:nodoc:
rt, @runtime = @runtime, 0
rt
end
# CONNECTION MANAGEMENT ====================================
# Is this connection active and ready to perform queries?
def active?
true
end
# Close this connection and open a new one in its place.
def reconnect!
end
protected
def log(sql, name)
begin
if block_given?
if @logger and @logger.level <= Logger::INFO
result = nil
seconds = Benchmark.realtime { result = yield }
@runtime += seconds
log_info(sql, name, seconds)
result
else
yield
end
if block_given?
if @logger and @logger.level <= Logger::INFO
result = nil
seconds = Benchmark.realtime { result = yield }
@runtime += seconds
log_info(sql, name, seconds)
result
else
log_info(sql, name, 0)
nil
yield
end
rescue Exception => e
log_info("#{e.message}: #{sql}", name, 0)
raise ActiveRecord::StatementInvalid, "#{e.message}: #{sql}"
else
log_info(sql, name, 0)
nil
end
rescue Exception => e
# Log message and raise exception.
message = "#{e.class.name}: #{e.message}: #{sql}"
log_info(message, name, 0)
raise ActiveRecord::StatementInvalid, message
end
def log_info(sql, name, runtime)

View File

@@ -0,0 +1,414 @@
# Author: Ken Kunz <kennethkunz@gmail.com>
require 'active_record/connection_adapters/abstract_adapter'
module FireRuby # :nodoc: all
class Database
def self.new_from_params(database, host, port, service)
db_string = ""
if host
db_string << host
db_string << "/#{service || port}" if service || port
db_string << ":"
end
db_string << database
new(db_string)
end
end
end
module ActiveRecord
class << Base
def firebird_connection(config) # :nodoc:
require_library_or_gem 'fireruby'
unless defined? FireRuby::SQLType
raise AdapterNotFound,
'The Firebird adapter requires FireRuby version 0.4.0 or greater; you appear ' <<
'to be running an older version -- please update FireRuby (gem install fireruby).'
end
config = config.symbolize_keys
unless config.has_key?(:database)
raise ArgumentError, "No database specified. Missing argument: database."
end
options = config[:charset] ? { CHARACTER_SET => config[:charset] } : {}
connection_params = [config[:username], config[:password], options]
db = FireRuby::Database.new_from_params(*config.values_at(:database, :host, :port, :service))
connection = db.connect(*connection_params)
ConnectionAdapters::FirebirdAdapter.new(connection, logger, connection_params)
end
end
module ConnectionAdapters
class FirebirdColumn < Column # :nodoc:
VARCHAR_MAX_LENGTH = 32_765
BLOB_MAX_LENGTH = 32_767
def initialize(name, domain, type, sub_type, length, precision, scale, default_source, null_flag)
@firebird_type = FireRuby::SQLType.to_base_type(type, sub_type).to_s
super(name.downcase, nil, @firebird_type, !null_flag)
@default = parse_default(default_source) if default_source
@limit = type == 'BLOB' ? BLOB_MAX_LENGTH : length
@domain, @sub_type, @precision, @scale = domain, sub_type, precision, scale
end
def type
if @domain =~ /BOOLEAN/
:boolean
elsif @type == :binary and @sub_type == 1
:text
else
@type
end
end
# Submits a _CAST_ query to the database, casting the default value to the specified SQL type.
# This enables Firebird to provide an actual value when context variables are used as column
# defaults (such as CURRENT_TIMESTAMP).
def default
if @default
sql = "SELECT CAST(#{@default} AS #{column_def}) FROM RDB$DATABASE"
connection = ActiveRecord::Base.active_connections.values.detect { |conn| conn && conn.adapter_name == 'Firebird' }
if connection
type_cast connection.execute(sql).to_a.first['CAST']
else
raise ConnectionNotEstablished, "No Firebird connections established."
end
end
end
def type_cast(value)
if type == :boolean
value == true or value == ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain[:true]
else
super
end
end
private
def parse_default(default_source)
default_source =~ /^\s*DEFAULT\s+(.*)\s*$/i
return $1 unless $1.upcase == "NULL"
end
def column_def
case @firebird_type
when 'BLOB' then "VARCHAR(#{VARCHAR_MAX_LENGTH})"
when 'CHAR', 'VARCHAR' then "#{@firebird_type}(#{@limit})"
when 'NUMERIC', 'DECIMAL' then "#{@firebird_type}(#{@precision},#{@scale.abs})"
when 'DOUBLE' then "DOUBLE PRECISION"
else @firebird_type
end
end
def simplified_type(field_type)
if field_type == 'TIMESTAMP'
:datetime
else
super
end
end
end
# The Firebird adapter relies on the FireRuby[http://rubyforge.org/projects/fireruby/]
# extension, version 0.4.0 or later (available as a gem or from
# RubyForge[http://rubyforge.org/projects/fireruby/]). FireRuby works with
# Firebird 1.5.x on Linux, OS X and Win32 platforms.
#
# == Usage Notes
#
# === Sequence (Generator) Names
# The Firebird adapter supports the same approach adopted for the Oracle
# adapter. See ActiveRecord::Base#set_sequence_name for more details.
#
# Note that in general there is no need to create a <tt>BEFORE INSERT</tt>
# trigger corresponding to a Firebird sequence generator when using
# ActiveRecord. In other words, you don't have to try to make Firebird
# simulate an <tt>AUTO_INCREMENT</tt> or +IDENTITY+ column. When saving a
# new record, ActiveRecord pre-fetches the next sequence value for the table
# and explicitly includes it in the +INSERT+ statement. (Pre-fetching the
# next primary key value is the only reliable method for the Firebird
# adapter to report back the +id+ after a successful insert.)
#
# === BOOLEAN Domain
# Firebird 1.5 does not provide a native +BOOLEAN+ type. But you can easily
# define a +BOOLEAN+ _domain_ for this purpose, e.g.:
#
# CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1));
#
# When the Firebird adapter encounters a column that is based on a domain
# that includes "BOOLEAN" in the domain name, it will attempt to treat
# the column as a +BOOLEAN+.
#
# By default, the Firebird adapter will assume that the BOOLEAN domain is
# defined as above. This can be modified if needed. For example, if you
# have a legacy schema with the following +BOOLEAN+ domain defined:
#
# CREATE DOMAIN BOOLEAN AS CHAR(1) CHECK (VALUE IN ('T', 'F'));
#
# ...you can add the following line to your <tt>environment.rb</tt> file:
#
# ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain = { :true => 'T', :false => 'F' }
#
# === BLOB Elements
# The Firebird adapter currently provides only limited support for +BLOB+
# columns. You cannot currently retrieve or insert a +BLOB+ as an IO stream.
# When selecting a +BLOB+, the entire element is converted into a String.
# When inserting or updating a +BLOB+, the entire value is included in-line
# in the SQL statement, limiting you to values <= 32KB in size.
#
# === Column Name Case Semantics
# Firebird and ActiveRecord have somewhat conflicting case semantics for
# column names.
#
# [*Firebird*]
# The standard practice is to use unquoted column names, which can be
# thought of as case-insensitive. (In fact, Firebird converts them to
# uppercase.) Quoted column names (not typically used) are case-sensitive.
# [*ActiveRecord*]
# Attribute accessors corresponding to column names are case-sensitive.
# The defaults for primary key and inheritance columns are lowercase, and
# in general, people use lowercase attribute names.
#
# In order to map between the differing semantics in a way that conforms
# to common usage for both Firebird and ActiveRecord, uppercase column names
# in Firebird are converted to lowercase attribute names in ActiveRecord,
# and vice-versa. Mixed-case column names retain their case in both
# directions. Lowercase (quoted) Firebird column names are not supported.
# This is similar to the solutions adopted by other adapters.
#
# In general, the best approach is to use unqouted (case-insensitive) column
# names in your Firebird DDL (or if you must quote, use uppercase column
# names). These will correspond to lowercase attributes in ActiveRecord.
#
# For example, a Firebird table based on the following DDL:
#
# CREATE TABLE products (
# id BIGINT NOT NULL PRIMARY KEY,
# "TYPE" VARCHAR(50),
# name VARCHAR(255) );
#
# ...will correspond to an ActiveRecord model class called +Product+ with
# the following attributes: +id+, +type+, +name+.
#
# ==== Quoting <tt>"TYPE"</tt> and other Firebird reserved words:
# In ActiveRecord, the default inheritance column name is +type+. The word
# _type_ is a Firebird reserved word, so it must be quoted in any Firebird
# SQL statements. Because of the case mapping described above, you should
# always reference this column using quoted-uppercase syntax
# (<tt>"TYPE"</tt>) within Firebird DDL or other SQL statements (as in the
# example above). This holds true for any other Firebird reserved words used
# as column names as well.
#
# === Migrations
# The Firebird adapter does not currently support Migrations. I hope to
# add this feature in the near future.
#
# == Connection Options
# The following options are supported by the Firebird adapter. None of the
# options have default values.
#
# <tt>:database</tt>::
# <i>Required option.</i> Specifies one of: (i) a Firebird database alias;
# (ii) the full path of a database file; _or_ (iii) a full Firebird
# connection string. <i>Do not specify <tt>:host</tt>, <tt>:service</tt>
# or <tt>:port</tt> as separate options when using a full connection
# string.</i>
# <tt>:host</tt>::
# Set to <tt>"remote.host.name"</tt> for remote database connections.
# May be omitted for local connections if a full database path is
# specified for <tt>:database</tt>. Some platforms require a value of
# <tt>"localhost"</tt> for local connections when using a Firebird
# database _alias_.
# <tt>:service</tt>::
# Specifies a service name for the connection. Only used if <tt>:host</tt>
# is provided. Required when connecting to a non-standard service.
# <tt>:port</tt>::
# Specifies the connection port. Only used if <tt>:host</tt> is provided
# and <tt>:service</tt> is not. Required when connecting to a non-standard
# port and <tt>:service</tt> is not defined.
# <tt>:username</tt>::
# Specifies the database user. May be omitted or set to +nil+ (together
# with <tt>:password</tt>) to use the underlying operating system user
# credentials on supported platforms.
# <tt>:password</tt>::
# Specifies the database password. Must be provided if <tt>:username</tt>
# is explicitly specified; should be omitted if OS user credentials are
# are being used.
# <tt>:charset</tt>::
# Specifies the character set to be used by the connection. Refer to
# Firebird documentation for valid options.
class FirebirdAdapter < AbstractAdapter
@@boolean_domain = { :true => 1, :false => 0 }
cattr_accessor :boolean_domain
def initialize(connection, logger, connection_params=nil)
super(connection, logger)
@connection_params = connection_params
end
def adapter_name # :nodoc:
'Firebird'
end
# Returns true for Firebird adapter (since Firebird requires primary key
# values to be pre-fetched before insert). See also #next_sequence_value.
def prefetch_primary_key?(table_name = nil)
true
end
def default_sequence_name(table_name, primary_key) # :nodoc:
"#{table_name}_seq"
end
# QUOTING ==================================================
def quote(value, column = nil) # :nodoc:
if [Time, DateTime].include?(value.class)
"CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
else
super
end
end
def quote_string(string) # :nodoc:
string.gsub(/'/, "''")
end
def quote_column_name(column_name) # :nodoc:
%Q("#{ar_to_fb_case(column_name)}")
end
def quoted_true # :nodoc:
quote(boolean_domain[:true])
end
def quoted_false # :nodoc:
quote(boolean_domain[:false])
end
# CONNECTION MANAGEMENT ====================================
def active?
not @connection.closed?
end
def reconnect!
@connection.close
@connection = @connection.database.connect(*@connection_params)
end
# DATABASE STATEMENTS ======================================
def select_all(sql, name = nil) # :nodoc:
select(sql, name)
end
def select_one(sql, name = nil) # :nodoc:
result = select(sql, name)
result.nil? ? nil : result.first
end
def execute(sql, name = nil, &block) # :nodoc:
log(sql, name) do
if @transaction
@connection.execute(sql, @transaction, &block)
else
@connection.execute_immediate(sql, &block)
end
end
end
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
execute(sql, name)
id_value
end
alias_method :update, :execute
alias_method :delete, :execute
def begin_db_transaction() # :nodoc:
@transaction = @connection.start_transaction
end
def commit_db_transaction() # :nodoc:
@transaction.commit
ensure
@transaction = nil
end
def rollback_db_transaction() # :nodoc:
@transaction.rollback
ensure
@transaction = nil
end
def add_limit_offset!(sql, options) # :nodoc:
if options[:limit]
limit_string = "FIRST #{options[:limit]}"
limit_string << " SKIP #{options[:offset]}" if options[:offset]
sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ')
end
end
# Returns the next sequence value from a sequence generator. Not generally
# called directly; used by ActiveRecord to get the next primary key value
# when inserting a new database record (see #prefetch_primary_key?).
def next_sequence_value(sequence_name)
FireRuby::Generator.new(sequence_name, @connection).next(1)
end
# SCHEMA STATEMENTS ========================================
def columns(table_name, name = nil) # :nodoc:
sql = <<-END_SQL
SELECT r.rdb$field_name, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type,
f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale,
COALESCE(r.rdb$default_source, f.rdb$default_source) rdb$default_source,
COALESCE(r.rdb$null_flag, f.rdb$null_flag) rdb$null_flag
FROM rdb$relation_fields r
JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name
WHERE r.rdb$relation_name = '#{table_name.to_s.upcase}'
ORDER BY r.rdb$field_position
END_SQL
execute(sql, name).collect do |field|
field_values = field.values.collect do |value|
case value
when String then value.rstrip
when FireRuby::Blob then value.to_s
else value
end
end
FirebirdColumn.new(*field_values)
end
end
private
def select(sql, name = nil)
execute(sql, name).collect do |row|
hashed_row = {}
row.each do |column, value|
value = value.to_s if FireRuby::Blob === value
hashed_row[fb_to_ar_case(column)] = value
end
hashed_row
end
end
# Maps uppercase Firebird column names to lowercase for ActiveRecord;
# mixed-case columns retain their original case.
def fb_to_ar_case(column_name)
column_name =~ /[[:lower:]]/ ? column_name : column_name.downcase
end
# Maps lowercase ActiveRecord column names to uppercase for Fierbird;
# mixed-case columns retain their original case.
def ar_to_fb_case(column_name)
column_name =~ /[[:upper:]]/ ? column_name : column_name.upcase
end
end
end
end

View File

@@ -5,23 +5,19 @@ module ActiveRecord
# Establishes a connection to the database that's used by all Active Record objects.
def self.mysql_connection(config) # :nodoc:
# Only include the MySQL driver if one hasn't already been loaded
unless self.class.const_defined?(:Mysql)
unless defined? Mysql
begin
require_library_or_gem 'mysql'
# The C version of mysql returns null fields in each_hash if Mysql::VERSION is defined
ConnectionAdapters::MysqlAdapter.null_values_in_each_hash = Mysql.const_defined?(:VERSION)
rescue LoadError => cannot_require_mysql
# Only use the supplied backup Ruby/MySQL driver if no driver is already in place
begin
require 'active_record/vendor/mysql'
# The ruby version of mysql returns null fields in each_hash
ConnectionAdapters::MysqlAdapter.null_values_in_each_hash = true
rescue LoadError
raise cannot_require_mysql
end
end
end
config = config.symbolize_keys
host = config[:host]
@@ -38,7 +34,7 @@ module ActiveRecord
mysql = Mysql.init
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]
ConnectionAdapters::MysqlAdapter.new(mysql.real_connect(host, username, password, database, port, socket), logger, [host, username, password, database, port, socket])
ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
end
end
@@ -77,9 +73,6 @@ module ActiveRecord
@@emulate_booleans = true
cattr_accessor :emulate_booleans
cattr_accessor :null_values_in_each_hash
@@null_values_in_each_hash = false
LOST_CONNECTION_ERROR_MESSAGES = [
"Server shutdown in progress",
"Broken pipe",
@@ -87,9 +80,11 @@ module ActiveRecord
"MySQL server has gone away"
]
def initialize(connection, logger, connection_options=nil)
def initialize(connection, logger, connection_options, config)
super(connection, logger)
@connection_options = connection_options
@connection_options, @config = connection_options, config
@null_values_in_each_hash = Mysql.const_defined?(:VERSION)
connect
end
def adapter_name #:nodoc:
@@ -119,12 +114,21 @@ module ActiveRecord
# QUOTING ==================================================
def quote(value, column = nil)
if value.kind_of?(String) && column && column.type == :binary
s = column.class.string_to_binary(value).unpack("H*")[0]
"x'#{s}'"
else
super
end
end
def quote_column_name(name) #:nodoc:
"`#{name}`"
end
def quote_string(string) #:nodoc:
Mysql::quote(string)
@connection.quote(string)
end
def quoted_true
@@ -136,6 +140,31 @@ module ActiveRecord
end
# CONNECTION MANAGEMENT ====================================
def active?
if @connection.respond_to?(:stat)
@connection.stat
else
@connection.query 'select 1'
end
# mysql-ruby doesn't raise an exception when stat fails.
if @connection.respond_to?(:errno)
@connection.errno.zero?
else
true
end
rescue Mysql::Error
false
end
def reconnect!
@connection.close rescue nil
connect
end
# DATABASE STATEMENTS ======================================
def select_all(sql, name = nil) #:nodoc:
@@ -148,22 +177,10 @@ module ActiveRecord
end
def execute(sql, name = nil, retries = 2) #:nodoc:
unless @logger
@connection.query(sql)
else
log(sql, name) { @connection.query(sql) }
end
log(sql, name) { @connection.query(sql) }
rescue ActiveRecord::StatementInvalid => exception
if LOST_CONNECTION_ERROR_MESSAGES.any? { |msg| exception.message.split(":").first =~ /^#{msg}/ }
@connection.real_connect(*@connection_options)
unless @logger
@connection.query(sql)
else
@logger.info "Retrying invalid statement with reopened connection"
log(sql, name) { @connection.query(sql) }
end
elsif exception.message.split(":").first =~ /Packets out of order/
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem update mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information."
if exception.message.split(":").first =~ /Packets out of order/
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
else
raise
end
@@ -291,11 +308,20 @@ module ActiveRecord
private
def connect
encoding = @config[:encoding]
if encoding
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
end
@connection.real_connect(*@connection_options)
execute("SET NAMES '#{encoding}'") if encoding
end
def select(sql, name = nil)
@connection.query_with_result = true
result = execute(sql, name)
rows = []
if @@null_values_in_each_hash
if @null_values_in_each_hash
result.each_hash { |row| rows << row }
else
all_fields = result.fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }

View File

@@ -23,6 +23,7 @@
# portions Copyright 2005 Graham Jenkins
require 'active_record/connection_adapters/abstract_adapter'
require 'delegate'
begin
require_library_or_gem 'oci8' unless self.class.const_defined? :OCI8
@@ -30,11 +31,8 @@ begin
module ActiveRecord
class Base
def self.oci_connection(config) #:nodoc:
conn = OCI8.new config[:username], config[:password], config[:host]
conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'}
conn.autocommit = true
ConnectionAdapters::OCIAdapter.new conn, logger
# Use OCI8AutoRecover instead of normal OCI8 driver.
ConnectionAdapters::OCIAdapter.new OCI8AutoRecover.new(config), logger
end
# Enable the id column to be bound into the sql later, by the adapter's insert method.
@@ -118,7 +116,7 @@ begin
return value if value.is_a? Time
time_array = ParseDate.parsedate value
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
Time.send Base.default_timezone, *time_array
Time.send(Base.default_timezone, *time_array) rescue nil
end
def guess_date_or_time(value)
@@ -213,6 +211,27 @@ begin
end
# CONNECTION MANAGEMENT ====================================#
# Returns true if the connection is active.
def active?
# Pings the connection to check if it's still good. Note that an
# #active? method is also available, but that simply returns the
# last known state, which isn't good enough if the connection has
# gone stale since the last use.
@connection.ping
rescue OCIError
false
end
# Reconnects to the database.
def reconnect!
@connection.reset!
rescue OCIError => e
@logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}"
end
# DATABASE STATEMENTS ======================================
#
# see: abstract/database_statements.rb
@@ -316,23 +335,28 @@ begin
table_name = table_name.to_s.upcase
owner = table_name.include?('.') ? "'#{table_name.split('.').first}'" : "user"
table = "'#{table_name.split('.').last}'"
scope = (owner == "user" ? "user" : "all")
select_all(%Q{
select column_name, data_type, data_default, nullable,
case when data_type = 'NUMBER' then data_precision
when data_type = 'VARCHAR2' then data_length
else null end as length,
case when data_type = 'NUMBER' then data_scale
else null end as scale
from all_catalog cat, all_synonyms syn, all_tab_columns col
where cat.owner = #{owner}
and cat.table_name = #{table}
and syn.owner (+)= cat.owner
and syn.synonym_name (+)= cat.table_name
and col.owner = nvl(syn.table_owner, cat.owner)
and col.table_name = nvl(syn.table_name, cat.table_name)
}).map do |row|
row['data_default'].gsub!(/^'(.*)'\s*$/, '\1') if row['data_default']
table_cols = %Q{
select column_name, data_type, data_default, nullable,
decode(data_type, 'NUMBER', data_precision,
'VARCHAR2', data_length,
null) as length,
decode(data_type, 'NUMBER', data_scale, null) as scale
from #{scope}_catalog cat, #{scope}_synonyms syn, all_tab_columns col
where cat.table_name = #{table}
and syn.synonym_name (+)= cat.table_name
and col.table_name = nvl(syn.table_name, cat.table_name)
and col.owner = nvl(syn.table_owner, #{(scope == "all" ? "cat.owner" : "user")}) }
if scope == "all"
table_cols << %Q{
and cat.owner = #{owner}
and syn.owner (+)= cat.owner }
end
select_all(table_cols, name).map do |row|
row['data_default'].sub!(/^'(.*)'\s*$/, '\1') if row['data_default']
OCIColumn.new(
oci_downcase(row['column_name']),
row['data_default'],
@@ -479,10 +503,99 @@ begin
when 187 : @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
else define_a_column_pre_ar i
end
end
end
end
end
# The OCIConnectionFactory factors out the code necessary to connect and
# configure an OCI connection.
class OCIConnectionFactory #:nodoc:
def new_connection(username, password, host)
conn = OCI8.new username, password, host
conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil
conn.autocommit = true
conn
end
end
# The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
# reset functionality. If a call to #exec fails, and autocommit is turned on
# (ie., we're not in the middle of a longer transaction), it will
# automatically reconnect and try again. If autocommit is turned off,
# this would be dangerous (as the earlier part of the implied transaction
# may have failed silently if the connection died) -- so instead the
# connection is marked as dead, to be reconnected on it's next use.
class OCI8AutoRecover < DelegateClass(OCI8) #:nodoc:
attr_accessor :active
alias :active? :active
cattr_accessor :auto_retry
class << self
alias :auto_retry? :auto_retry
end
@@auto_retry = false
def initialize(config, factory = OCIConnectionFactory.new)
@active = true
@username, @password, @host = config[:username], config[:password], config[:host]
@factory = factory
@connection = @factory.new_connection @username, @password, @host
super @connection
end
# Checks connection, returns true if active. Note that ping actively
# checks the connection, while #active? simply returns the last
# known state.
def ping
@connection.exec("select 1 from dual") { |r| nil }
@active = true
rescue
@active = false
raise
end
# Resets connection, by logging off and creating a new connection.
def reset!
logoff rescue nil
begin
@connection = @factory.new_connection @username, @password, @host
__setobj__ @connection
@active = true
rescue
@active = false
raise
end
end
# ORA-00028: your session has been killed
# ORA-01012: not logged on
# ORA-03113: end-of-file on communication channel
# ORA-03114: not connected to ORACLE
LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
# Adds auto-recovery functionality.
#
# See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
def exec(sql, *bindvars)
should_retry = self.class.auto_retry? && autocommit?
begin
@connection.exec(sql, *bindvars)
rescue OCIError => e
raise unless LOST_CONNECTION_ERROR_CODES.include?(e.code)
@active = false
raise unless should_retry
should_retry = false
reset! rescue nil
retry
end
end
end
rescue LoadError
# OCI8 driver is unavailable.
end

View File

@@ -12,7 +12,6 @@ module ActiveRecord
username = config[:username].to_s
password = config[:password].to_s
encoding = config[:encoding]
min_messages = config[:min_messages]
if config.has_key?(:database)
@@ -22,12 +21,10 @@ module ActiveRecord
end
pga = ConnectionAdapters::PostgreSQLAdapter.new(
PGconn.connect(host, port, "", "", database, username, password), logger
PGconn.connect(host, port, "", "", database, username, password), logger, config
)
pga.schema_search_path = config[:schema_search_path] || config[:schema_order]
pga.execute("SET client_encoding TO '#{encoding}'") if encoding
pga.execute("SET client_min_messages TO '#{min_messages}'") if min_messages
pga
end
@@ -52,6 +49,33 @@ module ActiveRecord
'PostgreSQL'
end
def initialize(connection, logger, config = {})
super(connection, logger)
@config = config
configure_connection
end
# Is this connection alive and ready for queries?
def active?
if @connection.respond_to?(:status)
@connection.status == PGconn::CONNECTION_OK
else
@connection.query 'SELECT 1'
true
end
rescue PGError
false
end
# Close then reopen the connection.
def reconnect!
# TODO: postgres-pr doesn't have PGconn#reset.
if @connection.respond_to?(:reset)
@connection.reset
configure_connection
end
end
def native_database_types
{
:primary_key => "serial primary key",
@@ -102,7 +126,7 @@ module ActiveRecord
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
execute(sql, name)
table = sql.split(" ", 4)[2]
id_value || last_insert_id(table, sequence_name)
id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
end
def query(sql, name = nil) #:nodoc:
@@ -199,24 +223,35 @@ module ActiveRecord
@schema_search_path ||= query('SHOW search_path')[0][0]
end
def default_sequence_name(table_name, pk = 'id')
"#{table_name}_#{pk}_seq"
def default_sequence_name(table_name, pk = nil)
default_pk, default_seq = pk_and_sequence_for(table_name)
default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
end
# Set the sequence to the max value of the table's pk.
def reset_pk_sequence!(table)
pk, sequence = pk_and_sequence_for(table)
if pk and sequence
select_value <<-end_sql, 'Reset sequence'
SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
end_sql
# Resets sequence to the max value of the table's pk if present.
def reset_pk_sequence!(table, pk = nil, sequence = nil)
unless pk and sequence
default_pk, default_sequence = pk_and_sequence_for(table)
pk ||= default_pk
sequence ||= default_sequence
end
if pk
if sequence
select_value <<-end_sql, 'Reset sequence'
SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
end_sql
else
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
end
end
end
# Find a table's primary key and sequence.
def pk_and_sequence_for(table)
execute(<<-end_sql, 'Find pk sequence')[0]
SELECT attr.attname, (name.nspname || '.' || seq.relname)
# First try looking for a sequence with a dependency on the
# given table's primary key.
result = execute(<<-end_sql, 'PK and serial sequence')[0]
SELECT attr.attname, name.nspname, seq.relname
FROM pg_class seq,
pg_attribute attr,
pg_depend dep,
@@ -232,11 +267,30 @@ module ActiveRecord
AND cons.contype = 'p'
AND dep.refobjid = '#{table}'::regclass
end_sql
if result.nil? or result.empty?
# If that fails, try parsing the primary key's default value.
# Support the 7.x and 8.0 nextval('foo'::text) as well as
# the 8.1+ nextval('foo'::regclass).
# TODO: assumes sequence is in same schema as table.
result = execute(<<-end_sql, 'PK and custom sequence')[0]
SELECT attr.attname, name.nspname, split_part(def.adsrc, '\\\'', 2)
FROM pg_class t
JOIN pg_namespace name ON (t.relnamespace = name.oid)
JOIN pg_attribute attr ON (t.oid = attrelid)
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
WHERE t.oid = '#{table}'::regclass
AND cons.contype = 'p'
AND def.adsrc ~* 'nextval'
end_sql
end
# check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
result.last['.'] ? [result.first, result.last] : [result.first, "#{result[1]}.#{result[2]}"]
rescue
nil
end
def rename_table(name, new_name)
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
end
@@ -276,14 +330,21 @@ module ActiveRecord
execute "DROP INDEX #{index_name}"
end
private
BYTEA_COLUMN_TYPE_OID = 17
def last_insert_id(table, sequence_name)
if sequence_name
@connection.exec("SELECT currval('#{sequence_name}')")[0][0].to_i
def configure_connection
if @config[:encoding]
execute("SET client_encoding TO '#{@config[:encoding]}'")
end
if @config[:min_messages]
execute("SET client_min_messages TO '#{@config[:min_messages]}'")
end
end
def last_insert_id(table, sequence_name)
Integer(select_value("SELECT currval('#{sequence_name}')"))
end
def select(sql, name = nil)

View File

@@ -123,7 +123,7 @@ module ActiveRecord
end
def quote_column_name(name) #:nodoc:
"'#{name}'"
%Q("#{name}")
end

View File

@@ -30,16 +30,17 @@ module ActiveRecord
if mode == "ODBC"
raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
dsn = config[:dsn]
conn = DBI.connect("DBI:ODBC:#{dsn}", username, password)
driver_url = "DBI:ODBC:#{dsn}"
else
raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
database = config[:database]
host = config[:host] ? config[:host].to_s : 'localhost'
conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
driver_url = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};"
end
conn = DBI.connect(driver_url, username, password)
conn["AutoCommit"] = true
ConnectionAdapters::SQLServerAdapter.new(conn, logger)
ConnectionAdapters::SQLServerAdapter.new(conn, logger, [driver_url, username, password])
end
end # class Base
@@ -52,6 +53,8 @@ module ActiveRecord
@identity = is_identity
@is_special = sql_type =~ /text|ntext|image/i ? true : false
@scale = scale_value
# SQL Server only supports limits on *char and float types
@limit = nil unless @type == :float or @type == :string
end
def simplified_type(field_type)
@@ -65,6 +68,7 @@ module ActiveRecord
when /binary|image|varbinary/i then :binary
when /char|nchar|nvarchar|string|varchar/i then :string
when /bit/i then :boolean
when /uniqueidentifier/i then :string
end
end
@@ -170,6 +174,12 @@ module ActiveRecord
# unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.
# [Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux]
class SQLServerAdapter < AbstractAdapter
def initialize(connection, logger, connection_options=nil)
super(connection, logger)
@connection_options = connection_options
end
def native_database_types
{
:primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
@@ -194,6 +204,25 @@ module ActiveRecord
true
end
# CONNECTION MANAGEMENT ====================================#
# Returns true if the connection is active.
def active?
@connection.execute("SELECT 1") { }
true
rescue DBI::DatabaseError, DBI::InterfaceError
false
end
# Reconnects to the database, returns false if no connection could be made.
def reconnect!
@connection.disconnect rescue nil
@connection = DBI.connect(*@connection_options)
rescue DBI::DatabaseError => e
@logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
false
end
def select_all(sql, name = nil)
select(sql, name)
end
@@ -205,6 +234,9 @@ module ActiveRecord
end
def columns(table_name, name = nil)
return [] if table_name.blank?
table_name = table_name.to_s if table_name.is_a?(Symbol)
table_name = table_name.split('.')[-1] unless table_name.nil?
sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue, DATA_TYPE as ColType, COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '#{table_name}'"
# Comment out if you want to have the Columns select statment logged.
# Personnally, I think it adds unneccessary bloat to the log.
@@ -234,7 +266,7 @@ module ActiveRecord
end
log(sql, name) do
@connection.execute(sql)
select_one("SELECT @@IDENTITY AS Ident")["Ident"]
id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"]
end
ensure
if ii_enabled
@@ -319,7 +351,7 @@ module ActiveRecord
def add_limit_offset!(sql, options)
if options[:limit] and options[:offset]
total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/SELECT/i, "SELECT TOP 1000000000")}) tally")[0][:TotalRows].to_i
total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT\b/i, "SELECT TOP 1000000000")}) tally")[0][:TotalRows].to_i
if (options[:limit] + options[:offset]) >= total_rows
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
end
@@ -328,7 +360,12 @@ module ActiveRecord
if options[:order]
options[:order] = options[:order].split(',').map do |field|
parts = field.split(" ")
if sql =~ /#{parts[0]} AS (t\d_r\d\d?)/
tc = parts[0]
if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
tc.gsub!(/\./, '\\.\\[')
tc << '\\]'
end
if sql =~ /#{tc} AS (t\d_r\d\d?)/
parts[0] = $1
end
parts.join(' ')
@@ -476,9 +513,9 @@ module ActiveRecord
def change_order_direction(order)
case order
when /DESC/i then order.gsub(/DESC/i, "ASC")
when /ASC/i then order.gsub(/ASC/i, "DESC")
else String.new(order).split(',').join(' DESC,') + ' DESC'
when /\bDESC\b/i then order.gsub(/\bDESC\b/i, "ASC")
when /\bASC\b/i then order.gsub(/\bASC\b/i, "DESC")
else String.new(order).split(',').join(' DESC,') + ' DESC'
end
end

View File

@@ -508,6 +508,7 @@ module Test #:nodoc:
ActiveRecord::Base.connection.rollback_db_transaction
ActiveRecord::Base.unlock_mutex
end
ActiveRecord::Base.clear_connection_cache!
end
alias_method :teardown, :teardown_with_fixtures

View File

@@ -20,7 +20,7 @@ module ActiveRecord
# def self.up
# add_column :accounts, :ssl_enabled, :boolean, :default => 1
# end
#
#
# def self.down
# remove_column :accounts, :ssl_enabled
# end
@@ -28,7 +28,7 @@ module ActiveRecord
#
# This migration will add a boolean flag to the accounts table and remove it again, if you're backing out of the migration.
# It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
# or remove the migration. These methods can consist of both the migration specific methods, like add_column and remove_column,
# or remove the migration. These methods can consist of both the migration specific methods, like add_column and remove_column,
# but may also contain regular Ruby code for generating data needed for the transformations.
#
# Example of a more complex migration that also needs to initialize data:
@@ -42,10 +42,10 @@ module ActiveRecord
# t.column :type, :string
# t.column :position, :integer
# end
#
#
# SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
# end
#
#
# def self.down
# drop_table :system_settings
# end
@@ -79,13 +79,29 @@ module ActiveRecord
#
# == Running migrations from within Rails
#
# The Rails package has support for migrations with the <tt>script/generate migration my_new_migration</tt> command and
# with the <tt>rake migrate</tt> command that'll run all the pending migrations. It'll even create the needed schema_info
# table automatically if it's missing.
# The Rails package has several tools to help create and apply migrations.
#
# To generate a new migration, use <tt>script/generate migration MyNewMigration</tt>
# where MyNewMigration is the name of your migration. The generator will
# create a file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
# directory, where <tt>nnn</tt> is the next largest migration number.
# You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
# n MyNewMigration.
#
# To run migrations against the currently configured database, use
# <tt>rake migrate</tt>. This will update the database by running all of the
# pending migrations, creating the <tt>schema_info</tt> table if missing.
#
# To roll the database back to a previous migration version, use
# <tt>rake migrate version=X</tt> where <tt>X</tt> is the version to which
# you wish to downgrade. If any of the migrations throw an
# <tt>IrreversibleMigration</tt> exception, that step will fail and you'll
# have some manual work to do.
#
# == Database support
#
# Migrations are currently only supported in MySQL and PostgreSQL.
# Migrations are currently supported in MySQL, PostgreSQL, SQLite,
# SQL Server, and Oracle (all supported databases except DB2).
#
# == More examples
#
@@ -95,7 +111,7 @@ module ActiveRecord
# def self.up
# Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
# end
#
#
# def self.down
# # not much we can do to restore deleted data
# raise IrreversibleMigration
@@ -128,20 +144,21 @@ module ActiveRecord
# end
# end
#
# == Using the class after changing table
# == Using a model after changing its table
#
# Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
# to make a call to Base#reset_column_information in order to ensure that the class has the latest column data from
# to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
# after the new column was added. Example:
#
# class MakeJoinUnique < ActiveRecord::Migration
# class AddPeopleSalary < ActiveRecord::Migration
# def self.up
# add_column :people, :salary, :integer
# Person.reset_column_information
# Person.find(:all).each do |p|
# p.salary = SalaryCalculator.compute(p)
# end
# end
# end
# end
class Migration
class << self
def up() end

View File

@@ -17,6 +17,8 @@ module ActiveRecord
# Active Record validation is reported to and from this object, which is used by Base#save to
# determine whether the object in a valid state to be saved. See usage example in Validations.
class Errors
include Enumerable
def initialize(base) # :nodoc:
@base, @errors = base, {}
end
@@ -485,13 +487,20 @@ module ActiveRecord
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
if scope = configuration[:scope]
validates_each(attr_names,configuration) do |record, attr_name, value|
record.errors.add(attr_name, configuration[:message]) if record.class.find(:first, :conditions => (record.new_record? ? ["#{attr_name} = ? AND #{scope} = ?", record.send(attr_name), record.send(scope)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ? AND #{scope} = ?", record.send(attr_name), record.send(:id), record.send(scope)]))
validates_each(attr_names,configuration) do |record, attr_name, value|
condition_sql = "#{attr_name} #{attribute_condition(value)}"
condition_params = [value]
if scope = configuration[:scope]
scope_value = record.send(scope)
condition_sql << " AND #{scope} #{attribute_condition(scope_value)}"
condition_params << scope_value
end
else
validates_each(attr_names,configuration) do |record, attr_name, value|
record.errors.add(attr_name, configuration[:message]) if record.class.find(:first, :conditions => (record.new_record? ? ["#{attr_name} = ?", record.send(attr_name)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ?", record.send(attr_name), record.send(:id) ] ))
unless record.new_record?
condition_sql << " AND #{record.class.primary_key} <> ?"
condition_params << record.send(:id)
end
if record.class.find(:first, :conditions => [condition_sql, *condition_params])
record.errors.add(attr_name, configuration[:message])
end
end
end
@@ -658,6 +667,8 @@ module ActiveRecord
if attributes.is_a?(Array)
attributes.collect { |attr| create!(attr) }
else
attributes.reverse_merge!(scope(:create)) if scoped?(:create)
object = new(attributes)
object.save!
object

View File

@@ -127,7 +127,8 @@ class Mysql
flag |= @client_flag | CLIENT_CAPABILITIES
flag |= CLIENT_CONNECT_WITH_DB if db
if !@server_capabilities & PROTO_AUTH41
@pre_411 = (0 == @server_capabilities & PROTO_AUTH41)
if @pre_411
data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+
(user||"")+"\0"+
scramble(passwd, @scramble_buff, @protocol_version==9)
@@ -139,13 +140,10 @@ class Mysql
([8] + Array.new(23, 0)).pack("c24") + (user||"")+"\0"+
scramble41(passwd, @scramble_buff)
end
if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
if PROTO_AUTH41
data << db+"\0"
else
data << "\0"+db
end
if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0
data << "\0" if @pre_411
data << db
@db = db.dup
end
write data
@@ -205,10 +203,10 @@ class Mysql
end
def change_user(user="", passwd="", db="")
if !@server_capabilities & PROTO_AUTH41
data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
if @pre_411
data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
else
data = user+"\0"+ scramble41(passwd, @scramble_buff)
data = user+"\0"+scramble41(passwd, @scramble_buff)+db
end
command COM_CHANGE_USER, data
@user = user
@@ -270,8 +268,8 @@ class Mysql
def list_fields(table, field=nil)
command COM_FIELD_LIST, "#{table}\0#{field}", true
if !@server_capabilities & PROTO_AUTH41
f = read_rows 6
if @pre_411
f = read_rows 6
else
f = read_rows 7
end
@@ -284,8 +282,8 @@ class Mysql
def list_processes()
data = command COM_PROCESS_INFO
@field_count = get_length data
if !@server_capabilities & PROTO_AUTH41
fields = read_rows 5
if @pre_411
fields = read_rows 5
else
fields = read_rows 7
end
@@ -402,8 +400,8 @@ class Mysql
end
else
@extra_info = get_length(data, true)
if !@server_capabilities & PROTO_AUTH41
fields = read_rows 5
if @pre_411
fields = read_rows(5)
else
fields = read_rows(7)
end
@@ -416,20 +414,20 @@ class Mysql
def unpack_fields(data, long_flag_protocol)
ret = []
data.each do |f|
if !@server_capabilities & PROTO_AUTH41
table = org_table = f[0]
name = f[1]
length = f[2][0]+f[2][1]*256+f[2][2]*256*256
type = f[3][0]
if long_flag_protocol then
flags = f[4][0]+f[4][1]*256
decimals = f[4][2]
else
flags = f[4][0]
decimals = f[4][1]
end
def_value = f[5]
max_length = 0
if @pre_411
table = org_table = f[0]
name = f[1]
length = f[2][0]+f[2][1]*256+f[2][2]*256*256
type = f[3][0]
if long_flag_protocol then
flags = f[4][0]+f[4][1]*256
decimals = f[4][2]
else
flags = f[4][0]
decimals = f[4][1]
end
def_value = f[5]
max_length = 0
else
catalog = f[0]
db = f[1]
@@ -443,9 +441,9 @@ class Mysql
decimals = f[6][9]
def_value = ""
max_length = 0
end
ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
end
end
ret
end
@@ -548,16 +546,13 @@ class Mysql
end
def scramble41(password, message)
if password.length != 0
buf = [0x14]
s1 = Digest::SHA1.new(password).digest
s2 = Digest::SHA1.new(s1).digest
x = Digest::SHA1.new(message + s2).digest
(0..s1.length - 1).each {|i| buf.push(s1[i] ^ x[i])}
buf.pack("C*")
else
0x00.chr
end
return 0x00.chr if password.nil? or password.empty?
buf = [0x14]
s1 = Digest::SHA1.new(password).digest
s2 = Digest::SHA1.new(s1).digest
x = Digest::SHA1.new(message + s2).digest
(0..s1.length - 1).each {|i| buf.push(s1[i] ^ x[i])}
buf.pack("C*")
end
def error(errno)

View File

@@ -1,8 +1,8 @@
module ActiveRecord
module Version #:nodoc:
module VERSION #:nodoc:
MAJOR = 1
MINOR = 13
TINY = 0
TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -8,6 +8,8 @@ require 'active_support/binding_of_caller'
require 'active_support/breakpoint'
require 'connection'
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
class Test::Unit::TestCase #:nodoc:
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
self.use_instantiated_fixtures = false
@@ -22,3 +24,6 @@ def current_adapter?(type)
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
end
#ActiveRecord::Base.logger = Logger.new(STDOUT)
#ActiveRecord::Base.colorize_logging = false

View File

@@ -44,4 +44,23 @@ class AggregationsTest < Test::Unit::TestCase
assert_equal "39", customers(:david).gps_location.latitude
assert_equal "-110", customers(:david).gps_location.longitude
end
def test_reloaded_instance_refreshes_aggregations
assert_equal "35.544623640962634", customers(:david).gps_location.latitude
assert_equal "-105.9309951055148", customers(:david).gps_location.longitude
Customer.update_all("gps_location = '24x113'")
customers(:david).reload
assert_equal '24x113', customers(:david)['gps_location']
assert_equal GpsLocation.new('24x113'), customers(:david).gps_location
end
def test_gps_equality
assert GpsLocation.new('39x110') == GpsLocation.new('39x110')
end
def test_gps_inequality
assert GpsLocation.new('39x110') != GpsLocation.new('39x111')
end
end

View File

@@ -21,7 +21,7 @@ class EagerAssociationTest < Test::Unit::TestCase
end
def test_loading_conditions_with_or
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.type = 'SpecialComment'")
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
"expected to find only david's posts"
end
@@ -83,6 +83,12 @@ class EagerAssociationTest < Test::Unit::TestCase
assert_equal [6,7,8], comments.collect { |c| c.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
comments = Comment.find(:all, :include => :post, :conditions => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id')
assert_equal 3, comments.length
assert_equal [6,7,8], comments.collect { |c| c.id }
end
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1)
assert_equal 1, posts.length
@@ -101,6 +107,24 @@ class EagerAssociationTest < Test::Unit::TestCase
assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
end
def test_eager_with_has_many_and_limit_and_conditions
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
assert_equal 2, posts.size
assert_equal [4,5], posts.collect { |p| p.id }
end
def test_eager_with_has_many_and_limit_and_conditions_array
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
assert_equal 2, posts.size
assert_equal [4,5], posts.collect { |p| p.id }
end
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
assert_raises(ArgumentError) do
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
end
end
def test_eager_with_has_many_and_limit_with_no_results
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
assert_equal 0, posts.size
@@ -120,7 +144,7 @@ class EagerAssociationTest < Test::Unit::TestCase
assert_raises(ArgumentError) do
posts = authors(:david).posts.find(:all,
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.type = 'SpecialComment'",
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
:limit => 2
)
end
@@ -198,17 +222,23 @@ class EagerAssociationTest < Test::Unit::TestCase
def test_preconfigured_includes_with_has_many
posts = authors(:david).posts_with_comments
assert_equal 2, posts.first.comments.size
one = posts.detect { |p| p.id == 1 }
assert_equal 5, posts.size
assert_equal 2, one.comments.size
end
def test_preconfigured_includes_with_habtm
posts = authors(:david).posts_with_categories
assert_equal 2, posts.first.categories.size
one = posts.detect { |p| p.id == 1 }
assert_equal 5, posts.size
assert_equal 2, one.categories.size
end
def test_preconfigured_includes_with_has_many_and_habtm
posts = authors(:david).posts_with_comments_and_categories
assert_equal 2, posts.first.comments.size
assert_equal 2, posts.first.categories.size
one = posts.detect { |p| p.id == 1 }
assert_equal 5, posts.size
assert_equal 2, one.comments.size
assert_equal 2, one.categories.size
end
end

View File

@@ -351,7 +351,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
def test_find_all
firm = Firm.find_first
assert_equal firm.clients, firm.clients.find_all
assert_equal 2, firm.clients.find(:all, :conditions => "type = 'Client'").length
assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
end
@@ -367,16 +367,16 @@ class HasManyAssociationsTest < Test::Unit::TestCase
firm = Firm.find_first
client2 = Client.find(2)
assert_equal firm.clients.first, firm.clients.find_first
assert_equal client2, firm.clients.find_first("type = 'Client'")
assert_equal client2, firm.clients.find(:first, :conditions => "type = 'Client'")
assert_equal client2, firm.clients.find_first("#{QUOTED_TYPE} = 'Client'")
assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'")
end
def test_find_first_sanitized
firm = Firm.find_first
client2 = Client.find(2)
assert_equal client2, firm.clients.find_first(["type = ?", "Client"])
assert_equal client2, firm.clients.find(:first, :conditions => ['type = ?', 'Client'])
assert_equal client2, firm.clients.find(:first, :conditions => ['type = :type', { :type => 'Client' }])
assert_equal client2, firm.clients.find_first(["#{QUOTED_TYPE} = ?", "Client"])
assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'])
assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }])
end
def test_find_in_collection
@@ -1381,5 +1381,17 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert developer.special_projects.include?(special_project)
assert !developer.special_projects.include?(other_project)
end
def test_update_attributes_after_push_without_duplicate_join_table_rows
developer = Developer.new("name" => "Kano")
project = SpecialProject.create("name" => "Special Project")
assert developer.save
developer.projects << project
developer.update_attribute("name", "Bruza")
assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
SELECT count(*) FROM developers_projects
WHERE project_id = #{project.id}
AND developer_id = #{developer.id}
end_sql
end
end

View File

@@ -17,6 +17,7 @@ class MasterCreditCard < ActiveRecord::Base; end
class Post < ActiveRecord::Base; end
class Computer < ActiveRecord::Base; end
class NonExistentTable < ActiveRecord::Base; end
class TestOCIDefault < ActiveRecord::Base; end
class LoosePerson < ActiveRecord::Base
attr_protected :credit_rating, :administrator
@@ -205,6 +206,40 @@ class BasicsTest < Test::Unit::TestCase
topic = topics(:first)
topic.approved = false
assert !topic.approved?, "approved should be false"
topic.approved = "false"
assert !topic.approved?, "approved should be false"
end
def test_read_attribute_when_true
topic = topics(:first)
topic.approved = true
assert topic.approved?, "approved should be true"
topic.approved = "true"
assert topic.approved?, "approved should be true"
end
def test_read_write_boolean_attribute
topic = Topic.new
# puts ""
# puts "New Topic"
# puts topic.inspect
topic.approved = "false"
# puts "Expecting false"
# puts topic.inspect
assert !topic.approved?, "approved should be false"
topic.approved = "false"
# puts "Expecting false"
# puts topic.inspect
assert !topic.approved?, "approved should be false"
topic.approved = "true"
# puts "Expecting true"
# puts topic.inspect
assert topic.approved?, "approved should be true"
topic.approved = "true"
# puts "Expecting true"
# puts topic.inspect
assert topic.approved?, "approved should be true"
# puts ""
end
def test_reader_generation
@@ -220,6 +255,13 @@ class BasicsTest < Test::Unit::TestCase
end
end
def test_reader_for_invalid_column_names
# column names which aren't legal ruby ids
topic = Topic.find(:first)
topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
assert !Topic.read_methods.include?("mumub-jumbo")
end
def test_non_attribute_access_and_assignment
topic = Topic.new
assert !topic.respond_to?("mumbo")
@@ -470,6 +512,15 @@ class BasicsTest < Test::Unit::TestCase
topic = Topic.find(topic.id)
assert topic.approved?
assert_nil topic.last_read
# Oracle has some funky default handling, so it requires a bit of
# extra testing. See ticket #2788.
if current_adapter?(:OCIAdapter)
test = TestOCIDefault.new
assert_equal "X", test.test_char
assert_equal "hello", test.test_string
assert_equal 3, test.test_int
end
end
def test_utc_as_time_zone
@@ -495,7 +546,7 @@ class BasicsTest < Test::Unit::TestCase
assert_nil topic.last_read
assert_nil topic.approved
end
def test_equality
assert_equal Topic.find(1), Topic.find(2).parent
end
@@ -839,10 +890,10 @@ class BasicsTest < Test::Unit::TestCase
def test_column_name_properly_quoted
col_record = ColumnName.new
col_record.references = 40
col_record.save
assert col_record.save
col_record.references = 41
col_record.save
c2 = ColumnName.find(col_record.id)
assert col_record.save
assert_not_nil c2 = ColumnName.find(col_record.id)
assert_equal(41, c2.references)
end
@@ -993,10 +1044,10 @@ class BasicsTest < Test::Unit::TestCase
end
def test_count_with_join
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.type = 'Post'"
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
res2 = res + 1
assert_nothing_raised do
res2 = Post.count("posts.type = 'Post'",
res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
"LEFT JOIN comments ON posts.id=comments.post_id")
end
assert_equal res, res2

View File

@@ -18,9 +18,9 @@ class BinaryTest < Test::Unit::TestCase
# limited to 8KB.
#
# Without using prepared statements, it makes no sense to test
# BLOB data with DB2, because the length of a statement is
# limited to 32KB.
unless %w(SQLServer DB2 OCI).include? ActiveRecord::Base.connection.adapter_name
# BLOB data with DB2 or Firebird, because the length of a statement
# is limited to 32KB.
unless %w(SQLServer DB2 OCI Firebird).include? ActiveRecord::Base.connection.adapter_name
def test_load_save
bin = Binary.new
bin.data = @data

View File

@@ -1,5 +1,5 @@
print "Using native DB2\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -0,0 +1,24 @@
print "Using native Firebird\n"
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")
db1 = 'activerecord_unittest'
db2 = 'activerecord_unittest2'
ActiveRecord::Base.establish_connection(
:adapter => "firebird",
:host => "localhost",
:username => "rails",
:password => "rails",
:database => db1
)
Course.establish_connection(
:adapter => "firebird",
:host => "localhost",
:username => "rails",
:password => "rails",
:database => db2
)

View File

@@ -1,5 +1,5 @@
print "Using native MySQL\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -1,5 +1,5 @@
print "Using OCI Oracle\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new STDOUT

View File

@@ -1,5 +1,5 @@
print "Using native PostgreSQL\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")
@@ -9,16 +9,16 @@ db2 = 'activerecord_unittest2'
ActiveRecord::Base.establish_connection(
:adapter => "postgresql",
:host => nil,
:username => "postgres",
:password => "postgres",
:database => db1
:database => db1,
:min_messages => "warning"
)
Course.establish_connection(
:adapter => "postgresql",
:host => nil,
:username => "postgres",
:password => "postgres",
:database => db2
)
:database => db2,
:min_messages => "warning"
)

View File

@@ -1,5 +1,5 @@
print "Using native SQlite\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -1,5 +1,5 @@
print "Using native SQLite3\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -1,5 +1,5 @@
print "Using native SQLite3\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -1,5 +1,5 @@
print "Using native SQLServer\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -1,5 +1,5 @@
print "Using native SQLServer via ODBC\n"
require 'fixtures/course'
require_dependency 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")

View File

@@ -0,0 +1,16 @@
require 'abstract_unit'
require 'fixtures/default'
class DefaultTest < Test::Unit::TestCase
def test_default_timestamp
default = Default.new
assert_instance_of(Time, default.default_timestamp)
assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type)
# Variance should be small; increase if required -- e.g., if test db is on
# remote host and clocks aren't synchronized.
t1 = Time.new
accepted_variance = 1.0
assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance)
end
end

View File

@@ -312,7 +312,7 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase
end
def test_has_many_find_all
assert_equal 2, Firm.find_first.find_all_in_clients("type = 'Client'").length
assert_equal 2, Firm.find_first.find_all_in_clients("#{QUOTED_TYPE} = 'Client'").length
assert_equal 1, Firm.find_first.find_all_in_clients("name = 'Summit'").length
end

View File

@@ -350,7 +350,7 @@ class FinderTest < Test::Unit::TestCase
def test_find_by_id_with_conditions_with_or
assert_nothing_raised do
Post.find([1,2,3],
:conditions => "posts.id <= 3 OR posts.type = 'Post'")
:conditions => "posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'")
end
end

View File

@@ -6,7 +6,7 @@ class Comment < ActiveRecord::Base
end
def self.search_by_type(q)
self.find(:all, :conditions => ['type = ?', q])
self.find(:all, :conditions => ["#{QUOTED_TYPE} = ?", q])
end
end

View File

@@ -7,7 +7,9 @@ end
class Firm < Company
has_many :clients, :order => "id", :dependent => true, :counter_sql => "SELECT COUNT(*) FROM companies WHERE firm_id = 1 AND (type = 'Client' OR type = 'SpecialClient' OR type = 'VerySpecialClient' )"
has_many :clients, :order => "id", :dependent => true, :counter_sql =>
"SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
"AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => true

View File

@@ -44,4 +44,8 @@ class GpsLocation
def longitude
gps_location.split("x").last
end
def ==(other)
self.latitude == other.latitude && self.longitude == other.longitude
end
end

View File

@@ -0,0 +1,54 @@
DROP TABLE accounts;
DROP TABLE companies;
DROP TABLE topics;
DROP TABLE developers;
DROP TABLE projects;
DROP TABLE developers_projects;
DROP TABLE orders;
DROP TABLE customers;
DROP TABLE movies;
DROP TABLE subscribers;
DROP TABLE booleantests;
DROP TABLE auto_id_tests;
DROP TABLE entrants;
DROP TABLE colnametests;
DROP TABLE mixins;
DROP TABLE people;
DROP TABLE binaries;
DROP TABLE computers;
DROP TABLE posts;
DROP TABLE comments;
DROP TABLE authors;
DROP TABLE tasks;
DROP TABLE categories;
DROP TABLE categories_posts;
DROP TABLE fk_test_has_fk;
DROP TABLE fk_test_has_pk;
DROP TABLE keyboards;
DROP TABLE defaults;
DROP DOMAIN D_BOOLEAN;
DROP GENERATOR accounts_seq;
DROP GENERATOR companies_nonstd_seq;
DROP GENERATOR topics_seq;
DROP GENERATOR developers_seq;
DROP GENERATOR projects_seq;
DROP GENERATOR orders_seq;
DROP GENERATOR customers_seq;
DROP GENERATOR movies_seq;
DROP GENERATOR booleantests_seq;
DROP GENERATOR auto_id_tests_seq;
DROP GENERATOR entrants_seq;
DROP GENERATOR colnametests_seq;
DROP GENERATOR mixins_seq;
DROP GENERATOR people_seq;
DROP GENERATOR binaries_seq;
DROP GENERATOR computers_seq;
DROP GENERATOR posts_seq;
DROP GENERATOR comments_seq;
DROP GENERATOR authors_seq;
DROP GENERATOR tasks_seq;
DROP GENERATOR categories_seq;
DROP GENERATOR keyboards_seq;
DROP GENERATOR defaults_seq;

View File

@@ -0,0 +1,259 @@
CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1));
CREATE TABLE accounts (
id BIGINT NOT NULL,
firm_id BIGINT,
credit_limit INTEGER,
PRIMARY KEY (id)
);
CREATE GENERATOR accounts_seq;
SET GENERATOR accounts_seq TO 10000;
CREATE TABLE companies (
id BIGINT NOT NULL,
"TYPE" VARCHAR(50),
ruby_type VARCHAR(50),
firm_id BIGINT,
name VARCHAR(50),
client_of INTEGER,
rating INTEGER DEFAULT 1,
PRIMARY KEY (id)
);
CREATE GENERATOR companies_nonstd_seq;
SET GENERATOR companies_nonstd_seq TO 10000;
CREATE TABLE topics (
id BIGINT NOT NULL,
title VARCHAR(255),
author_name VARCHAR(255),
author_email_address VARCHAR(255),
written_on TIMESTAMP,
bonus_time TIME,
last_read DATE,
content VARCHAR(4000),
approved D_BOOLEAN DEFAULT 1,
replies_count INTEGER DEFAULT 0,
parent_id BIGINT,
"TYPE" VARCHAR(50),
PRIMARY KEY (id)
);
CREATE GENERATOR topics_seq;
SET GENERATOR topics_seq TO 10000;
CREATE TABLE developers (
id BIGINT NOT NULL,
name VARCHAR(100),
salary INTEGER DEFAULT 70000,
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (id)
);
CREATE GENERATOR developers_seq;
SET GENERATOR developers_seq TO 10000;
CREATE TABLE projects (
id BIGINT NOT NULL,
name VARCHAR(100),
"TYPE" VARCHAR(255),
PRIMARY KEY (id)
);
CREATE GENERATOR projects_seq;
SET GENERATOR projects_seq TO 10000;
CREATE TABLE developers_projects (
developer_id BIGINT NOT NULL,
project_id BIGINT NOT NULL,
joined_on DATE,
access_level SMALLINT DEFAULT 1
);
CREATE TABLE orders (
id BIGINT NOT NULL,
name VARCHAR(100),
billing_customer_id BIGINT,
shipping_customer_id BIGINT,
PRIMARY KEY (id)
);
CREATE GENERATOR orders_seq;
SET GENERATOR orders_seq TO 10000;
CREATE TABLE customers (
id BIGINT NOT NULL,
name VARCHAR(100),
balance INTEGER DEFAULT 0,
address_street VARCHAR(100),
address_city VARCHAR(100),
address_country VARCHAR(100),
gps_location VARCHAR(100),
PRIMARY KEY (id)
);
CREATE GENERATOR customers_seq;
SET GENERATOR customers_seq TO 10000;
CREATE TABLE movies (
movieid BIGINT NOT NULL,
name varchar(100),
PRIMARY KEY (movieid)
);
CREATE GENERATOR movies_seq;
SET GENERATOR movies_seq TO 10000;
CREATE TABLE subscribers (
nick VARCHAR(100) NOT NULL,
name VARCHAR(100),
PRIMARY KEY (nick)
);
CREATE TABLE booleantests (
id BIGINT NOT NULL,
"VALUE" D_BOOLEAN,
PRIMARY KEY (id)
);
CREATE GENERATOR booleantests_seq;
SET GENERATOR booleantests_seq TO 10000;
CREATE TABLE auto_id_tests (
auto_id BIGINT NOT NULL,
"VALUE" INTEGER,
PRIMARY KEY (auto_id)
);
CREATE GENERATOR auto_id_tests_seq;
SET GENERATOR auto_id_tests_seq TO 10000;
CREATE TABLE entrants (
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
course_id INTEGER NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR entrants_seq;
SET GENERATOR entrants_seq TO 10000;
CREATE TABLE colnametests (
id BIGINT NOT NULL,
"REFERENCES" INTEGER NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR colnametests_seq;
SET GENERATOR colnametests_seq TO 10000;
CREATE TABLE mixins (
id BIGINT NOT NULL,
parent_id BIGINT,
pos INTEGER,
created_at TIMESTAMP,
updated_at TIMESTAMP,
lft INTEGER,
rgt INTEGER,
root_id BIGINT,
"TYPE" VARCHAR(40),
PRIMARY KEY (id)
);
CREATE GENERATOR mixins_seq;
SET GENERATOR mixins_seq TO 10000;
CREATE TABLE people (
id BIGINT NOT NULL,
first_name VARCHAR(40),
lock_version INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR people_seq;
SET GENERATOR people_seq TO 10000;
CREATE TABLE binaries (
id BIGINT NOT NULL,
data BLOB,
PRIMARY KEY (id)
);
CREATE GENERATOR binaries_seq;
SET GENERATOR binaries_seq TO 10000;
CREATE TABLE computers (
id BIGINT NOT NULL,
developer INTEGER NOT NULL,
"extendedWarranty" INTEGER NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR computers_seq;
SET GENERATOR computers_seq TO 10000;
CREATE TABLE posts (
id BIGINT NOT NULL,
author_id BIGINT,
title VARCHAR(255) NOT NULL,
"TYPE" VARCHAR(255) NOT NULL,
body VARCHAR(3000) NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR posts_seq;
SET GENERATOR posts_seq TO 10000;
CREATE TABLE comments (
id BIGINT NOT NULL,
post_id BIGINT NOT NULL,
"TYPE" VARCHAR(255) NOT NULL,
body VARCHAR(3000) NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR comments_seq;
SET GENERATOR comments_seq TO 10000;
CREATE TABLE authors (
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR authors_seq;
SET GENERATOR authors_seq TO 10000;
CREATE TABLE tasks (
id BIGINT NOT NULL,
"STARTING" TIMESTAMP,
ending TIMESTAMP,
PRIMARY KEY (id)
);
CREATE GENERATOR tasks_seq;
SET GENERATOR tasks_seq TO 10000;
CREATE TABLE categories (
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
"TYPE" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE GENERATOR categories_seq;
SET GENERATOR categories_seq TO 10000;
CREATE TABLE categories_posts (
category_id BIGINT NOT NULL,
post_id BIGINT NOT NULL,
PRIMARY KEY (category_id, post_id)
);
CREATE TABLE fk_test_has_pk (
id BIGINT NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE fk_test_has_fk (
id BIGINT NOT NULL,
fk_id BIGINT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (fk_id) REFERENCES fk_test_has_pk(id)
);
CREATE TABLE keyboards (
key_number BIGINT NOT NULL,
name VARCHAR(50),
PRIMARY KEY (key_number)
);
CREATE GENERATOR keyboards_seq;
SET GENERATOR keyboards_seq TO 10000;
CREATE TABLE defaults (
id BIGINT NOT NULL,
default_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE GENERATOR defaults_seq;
SET GENERATOR defaults_seq TO 10000;

View File

@@ -0,0 +1,2 @@
DROP TABLE courses;
DROP GENERATOR courses_seq;

View File

@@ -0,0 +1,6 @@
CREATE TABLE courses (
id BIGINT NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE GENERATOR courses_seq;
SET GENERATOR courses_seq TO 10000;

View File

@@ -261,3 +261,11 @@ create table keyboards (
);
create sequence keyboards_seq minvalue 10000;
create table test_oci_defaults (
id integer not null primary key,
test_char char(1) default 'X' not null,
test_string varchar2(20) default 'hello' not null,
test_int integer default 3 not null
);
create sequence test_oci_defaults_seq minvalue 10000;

View File

@@ -1,10 +1,11 @@
CREATE SEQUENCE public.accounts_id_seq START 100;
CREATE TABLE accounts (
id serial,
id integer DEFAULT nextval('public.accounts_id_seq'),
firm_id integer,
credit_limit integer,
PRIMARY KEY (id)
);
SELECT setval('accounts_id_seq', 100);
CREATE SEQUENCE companies_nonstd_seq START 101;

View File

@@ -51,42 +51,44 @@ class FixturesTest < Test::Unit::TestCase
assert_nil(secondRow["author_email_address"])
end
def test_inserts_with_pre_and_suffix
ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
t.column :title, :string
t.column :author_name, :string
t.column :author_email_address, :string
t.column :written_on, :datetime
t.column :bonus_time, :time
t.column :last_read, :date
t.column :content, :text
t.column :approved, :boolean, :default => true
t.column :replies_count, :integer, :default => 0
t.column :parent_id, :integer
t.column :type, :string, :limit => 50
if ActiveRecord::Base.connection.supports_migrations?
def test_inserts_with_pre_and_suffix
ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
t.column :title, :string
t.column :author_name, :string
t.column :author_email_address, :string
t.column :written_on, :datetime
t.column :bonus_time, :time
t.column :last_read, :date
t.column :content, :text
t.column :approved, :boolean, :default => true
t.column :replies_count, :integer, :default => 0
t.column :parent_id, :integer
t.column :type, :string, :limit => 50
end
# Store existing prefix/suffix
old_prefix = ActiveRecord::Base.table_name_prefix
old_suffix = ActiveRecord::Base.table_name_suffix
# Set a prefix/suffix we can test against
ActiveRecord::Base.table_name_prefix = 'prefix_'
ActiveRecord::Base.table_name_suffix = '_suffix'
topics = create_fixtures("topics")
# Restore prefix/suffix to its previous values
ActiveRecord::Base.table_name_prefix = old_prefix
ActiveRecord::Base.table_name_suffix = old_suffix
firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
assert_equal("The First Topic", firstRow["title"])
secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
assert_nil(secondRow["author_email_address"])
ensure
ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
end
# Store existing prefix/suffix
old_prefix = ActiveRecord::Base.table_name_prefix
old_suffix = ActiveRecord::Base.table_name_suffix
# Set a prefix/suffix we can test against
ActiveRecord::Base.table_name_prefix = 'prefix_'
ActiveRecord::Base.table_name_suffix = '_suffix'
topics = create_fixtures("topics")
# Restore prefix/suffix to its previous values
ActiveRecord::Base.table_name_prefix = old_prefix
ActiveRecord::Base.table_name_suffix = old_suffix
firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
assert_equal("The First Topic", firstRow["title"])
secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
assert_nil(secondRow["author_email_address"])
ensure
ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
end
def test_insert_with_datetime
@@ -173,27 +175,45 @@ end
if Account.connection.respond_to?(:reset_pk_sequence!)
class FixturesResetPkSequenceTest < Test::Unit::TestCase
fixtures :accounts
fixtures :companies
def test_resets_to_min_pk
Account.delete_all
Account.connection.reset_pk_sequence!(Account.table_name)
def setup
@instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
end
one = Account.new(:credit_limit => 50)
one.save!
assert_equal 1, one.id
def test_resets_to_min_pk_with_specified_pk_and_sequence
@instances.each do |instance|
model = instance.class
model.delete_all
model.connection.reset_pk_sequence!(model.table_name, model.primary_key, model.sequence_name)
instance.save!
assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
end
end
def test_resets_to_min_pk_with_default_pk_and_sequence
@instances.each do |instance|
model = instance.class
model.delete_all
model.connection.reset_pk_sequence!(model.table_name)
instance.save!
assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
end
end
def test_create_fixtures_resets_sequences
# create_fixtures performs reset_pk_sequence!
max_id = create_fixtures('accounts').inject(0) do |max_id, (name, fixture)|
fixture_id = fixture['id'].to_i
fixture_id > max_id ? fixture_id : max_id
end
@instances.each do |instance|
max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
fixture_id = fixture['id'].to_i
fixture_id > max_id ? fixture_id : max_id
end
# Clone the last fixture to check that it gets the next greatest id.
another = Account.new(:credit_limit => 1200)
another.save!
assert_equal max_id + 1, another.id
# Clone the last fixture to check that it gets the next greatest id.
instance.save!
assert_equal max_id + 1, instance.id, "Sequence reset for #{instance.class.table_name} failed."
end
end
end
end

View File

@@ -11,7 +11,7 @@ class InheritanceTest < Test::Unit::TestCase
if current_adapter?(:SQLServerAdapter)
Company.connection.execute "SET IDENTITY_INSERT companies ON"
end
Company.connection.insert "INSERT INTO companies (id, type, name) VALUES(100, 'bad_class!', 'Not happening')"
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
#We then need to turn it back Off before continuing.
if current_adapter?(:SQLServerAdapter)

View File

@@ -362,6 +362,9 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Base.table_name_suffix = ""
Reminder.reset_table_name
assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
ensure
ActiveRecord::Base.table_name_prefix = ""
ActiveRecord::Base.table_name_suffix = ""
end
def test_proper_table_name
@@ -398,17 +401,20 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Base.table_name_prefix = 'prefix_'
ActiveRecord::Base.table_name_suffix = '_suffix'
Reminder.reset_table_name
Reminder.reset_sequence_name
WeNeedReminders.up
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
assert_equal "hello world", Reminder.find(:first).content
WeNeedReminders.down
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
ensure
ActiveRecord::Base.table_name_prefix = ''
ActiveRecord::Base.table_name_suffix = ''
Reminder.reset_table_name
Reminder.reset_sequence_name
end
def test_migrator_with_duplicates
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)

View File

@@ -1,7 +1,9 @@
require 'abstract_unit'
require 'fixtures/course'
require 'fixtures/entrant'
# So we can test whether Course.connection survives a reload.
require_dependency 'fixtures/course'
class MultipleDbTest < Test::Unit::TestCase
self.use_transactional_fixtures = false
@@ -45,4 +47,14 @@ class MultipleDbTest < Test::Unit::TestCase
e3 = Entrant.find(3)
assert_equal e3.course.id, c2.id
end
def test_course_connection_should_survive_dependency_reload
assert Course.connection
Dependencies.clear
Object.send(:remove_const, :Course)
require_dependency 'fixtures/course'
assert Course.connection
end
end

View File

@@ -54,6 +54,7 @@ class PrimaryKeysTest < Test::Unit::TestCase
assert_equal("jdoe", subscriber.id)
subscriber.name = "John Doe"
assert_nothing_raised { subscriber.save! }
assert_equal("jdoe", subscriber.id)
subscriberReloaded = Subscriber.find("jdoe")
assert_equal("John Doe", subscriberReloaded.name)

View File

@@ -1,5 +1,17 @@
*1.2.5* (December 13th, 2005)
* Become part of Rails 1.0
*1.2.4* (December 7th, 2005)
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
*1.2.3* (November 7th, 2005)
* Change Inflector#constantize to use eval instead of const_get. [Nicholas Seckar]
* Fix const_missing handler to ignore the trailing '.rb' on files when comparing paths. [Nicholas Seckar]
* Define kernel.rb methods in "class Object" instead of "module Kernel" to work around a Windows peculiarity [Sam Stephenson]

View File

@@ -6,7 +6,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'active_support', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
PKG_NAME = 'activesupport'
PKG_VERSION = ActiveSupport::Version::STRING + PKG_BUILD
PKG_VERSION = ActiveSupport::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
RELEASE_NAME = "REL #{PKG_VERSION}"

View File

@@ -142,9 +142,11 @@ module Inflector
end
def constantize(camel_cased_word)
camel_cased_word.split("::").inject(Object) do |final_type, part|
final_type = final_type.const_get(part)
end
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" unless
camel_cased_word.split("::").all? { |part| /^[A-Z]\w*$/ =~ part }
camel_cased_word = "::#{camel_cased_word}" unless camel_cased_word[0, 2] == '::'
Object.module_eval(camel_cased_word, __FILE__, __LINE__)
end
def ordinalize(number)

View File

@@ -1,8 +1,8 @@
module ActiveSupport
module Version #:nodoc:
module VERSION #:nodoc:
MAJOR = 1
MINOR = 2
TINY = 3
TINY = 5
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@@ -276,6 +276,11 @@ class InflectorTest < Test::Unit::TestCase
assert_equal Ace::Base::Case, Inflector.constantize("Ace::Base::Case")
assert_equal InflectorTest, Inflector.constantize("InflectorTest")
assert_raises(NameError) { Inflector.constantize("UnknownClass") }
assert_raises(NameError) { Inflector.constantize("An invalid string") }
end
def test_constantize_doesnt_look_in_parent
assert_raises(NameError) { Inflector.constantize("Ace::Base::InflectorTest") }
end
def test_ordinal

View File

@@ -1,4 +1,49 @@
*SVN*
*1.0.0* (December 13th, 2005)
* Update to script.aculo.us 1.5.0 final (equals 1.5.0_rc6) [Thomas Fuchs]
* Update to Prototype 1.4.0 final [Sam Stephenson]
* Update instructions on how to find and install generators. #3172. [Chad Fowler]
* Generator looks in vendor/generators also. [Chad Fowler]
* Generator copies files in binary mode. #3156 [minimudboy@gmail.com]
*0.14.4 (RC5)* (December 7th, 2005)
* Add builtin/ to the gemspec. Closes #3047. [Nicholas Seckar, Sam Stephenson]
* Run initialize_logger in script/lighttpd to ensure the log file exists before tailing it. [Sam Stephenson]
* Make load_fixtures include csv fixtures. #3053. [me@mdaines.com]
* Fix freeze_gems so that the latest rails version is dumped by default. [Nicholas Seckar]
* Model generator: correct relative path to test_helper in unit test. [Jeremy Kemper]
* Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. #2931. [Blair Zajac]
* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. [anonymous]
* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 [dreamer3@gmail.com]
* Introducing the session_migration generator. Creates an add_session_table migration. Allows generator to specify migrations directory. #2958, #2960 [Rick Olson]
* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). [Sam Stephenson]
* Eliminate nil from newly generated logfiles. #2927 [Blair Zajac <blair@orcaware.com>]
* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. [Sam Stephenson]
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
* Update to latest script.aculo.us version (as of [3031])
* SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment. #2846 [Rick Olson]
* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko@fdiary.net]
* Pass __FILE__ when evaluating plugins' init.rb. #2817 [james.adam@gmail.com]
@@ -8,8 +53,6 @@
* Don't detach or fork for script/server tailing [Nicholas Seckar]
* Added automatic browser launching on OS X when starting script/server [DHH]
* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path [DHH]
* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [DHH] #2660
@@ -18,6 +61,7 @@
* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. [Sam Stephenson]
*0.14.3 (RC4)* (November 7th, 2005)
* Add 'add_new_scripts' rake task for adding new rails scripts to script/* [Jamis Buck]

View File

@@ -11,7 +11,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'rails_version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
PKG_NAME = 'rails'
PKG_VERSION = Rails::Version::STRING + PKG_BUILD
PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
@@ -252,6 +252,7 @@ end
PKG_FILES = FileList[
'[a-zA-Z]*',
'bin/**/*',
'builtin/**/*',
'configs/**/*',
'doc/**/*',
'dispatches/**/*',
@@ -272,11 +273,11 @@ spec = Gem::Specification.new do |s|
EOF
s.add_dependency('rake', '>= 0.6.2')
s.add_dependency('activesupport', '= 1.2.3' + PKG_BUILD)
s.add_dependency('activerecord', '= 1.13.0' + PKG_BUILD)
s.add_dependency('actionpack', '= 1.11.0' + PKG_BUILD)
s.add_dependency('actionmailer', '= 1.1.3' + PKG_BUILD)
s.add_dependency('actionwebservice', '= 0.9.3' + PKG_BUILD)
s.add_dependency('activesupport', '= 1.2.5' + PKG_BUILD)
s.add_dependency('activerecord', '= 1.13.2' + PKG_BUILD)
s.add_dependency('actionpack', '= 1.11.2' + PKG_BUILD)
s.add_dependency('actionmailer', '= 1.1.5' + PKG_BUILD)
s.add_dependency('actionwebservice', '= 1.0.0' + PKG_BUILD)
s.rdoc_options << '--exclude' << '.'
s.has_rdoc = false

View File

@@ -12,7 +12,7 @@ end
Signal.trap("INT") { puts; exit }
require File.dirname(__FILE__) + '/../lib/rails_version'
abort "Rails #{Rails::Version::STRING}" if %w(--version -v).include? ARGV.first
abort "Rails #{Rails::VERSION::STRING}" if %w(--version -v).include? ARGV.first
require File.dirname(__FILE__) + '/../lib/rails_generator'

View File

@@ -1,3 +1,5 @@
# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
unless defined?(RAILS_ROOT)
root_path = File.join(File.dirname(__FILE__), '..')
unless RUBY_PLATFORM =~ /mswin32/

View File

@@ -197,7 +197,7 @@
window.onload = function() {
$('search-text').value = '';
$('search').onsubmit = function() {
$('search-text').value = 'site:rubyonrails.com ' + $F('search-text');
$('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
}
}
</script>
@@ -209,7 +209,7 @@
<li>
<form id="search" action="http://www.google.com/search" method="get">
<input type="hidden" name="hl" value="en" />
<input type="text" id="search-text" name="q" value="site:rubyonrails.com " />
<input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
<input type="submit" value="Search" /> the Rails site
</form>
</li>
@@ -217,23 +217,21 @@
<li>
<h3>Join the community</h3>
<ul class="links">
<li><a href="http://www.rubyonrails.com/">Ruby on Rails</a></li>
<li><a href="http://weblog.rubyonrails.com/">Official weblog</a></li>
<li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
<li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
<li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li>
<li><a href="http://wiki.rubyonrails.com/rails/pages/IRC">IRC channel</a></li>
<li><a href="http://wiki.rubyonrails.com/">Wiki</a></li>
<li><a href="http://dev.rubyonrails.com/">Bug tracker</a></li>
<li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li>
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
<li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li>
</ul>
</li>
<li>
<h3>Browse the documentation</h3>
<ul class="links">
<li><a href="http://manuals.rubyonrails.com/">Manuals</a></li>
<li><a href="http://api.rubyonrails.com/">All APIs</a></li>
<li><a href="http://ar.rubyonrails.com/">Active Record</a></li>
<li><a href="http://ap.rubyonrails.com/">Action Pack</a></li>
<li><a href="http://as.rubyonrails.com/">Active Support</a></li>
<li><a href="http://api.rubyonrails.org/">Rails API</a></li>
<li><a href="http://www.ruby-doc.org/stdlib/">Ruby standard library</a></li>
<li><a href="http://www.ruby-doc.org/core/">Ruby core</a></li>
</ul>
</li>
</ul>
@@ -241,7 +239,7 @@
<div id="content">
<div id="header">
<h1>Welcome aboard</h2>
<h1>Welcome aboard</h1>
<h2>You&rsquo;re riding the Rails!</h2>
</div>
@@ -256,18 +254,18 @@
<ol>
<li>
<h2>Create your databases and edit <tt>database.yml</tt></h2>
<p>Instructions here</p>
<h2>Create your databases and edit <tt>config/database.yml</tt></h2>
<p>Rails needs to know your login and password.</p>
</li>
<li>
<h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
<p>Instructions here</p>
<p>To see all available options, run it without parameters.</p>
</li>
<li>
<h2>Set up a default route and remove or rename this file</h2>
<p>Instructions here</p>
<p>Routes are setup in config/routes.rb.</p>
</li>
</ol>
</div>

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