diff options
111 files changed, 2026 insertions, 856 deletions
diff --git a/.gitignore b/.gitignore index 96a1c2117a..bba7d5dce8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ debug.log -doc +doc/rdoc activeresource/doc activerecord/doc actionpack/doc @@ -13,7 +13,7 @@ end desc 'Run all tests by default' task :default => :test -%w(test rdoc package release).each do |task_name| +%w(test rdoc pgem package release).each do |task_name| desc "Run #{task_name} task for all projects" task task_name do PROJECTS.each do |project| @@ -25,14 +25,14 @@ end desc "Generate documentation for the Rails framework" Rake::RDocTask.new do |rdoc| - rdoc.rdoc_dir = 'doc' - rdoc.title = "Rails Framework Documentation" + rdoc.rdoc_dir = 'doc/rdoc' + rdoc.title = "Ruby on Rails Documentation" rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : './doc/template/horo' rdoc.rdoc_files.include('railties/CHANGELOG') rdoc.rdoc_files.include('railties/MIT-LICENSE') @@ -68,13 +68,13 @@ end # Enhance rdoc task to copy referenced images also task :rdoc do - FileUtils.mkdir_p "doc/files/examples/" - FileUtils.copy "activerecord/examples/associations.png", "doc/files/examples/associations.png" + FileUtils.mkdir_p "doc/rdoc/files/examples/" + FileUtils.copy "activerecord/examples/associations.png", "doc/rdoc/files/examples/associations.png" end desc "Publish API docs for Rails as a whole and for each component" task :pdoc => :rdoc do - Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc").upload + Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc/rdoc").upload PROJECTS.each do |project| system %(cd #{project} && #{env} #{$0} pdoc) end diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index b003f4d559..612bd27774 100755 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -35,7 +35,7 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Action Mailer -- Easy email delivery and testing" rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('lib/action_mailer.rb') rdoc.rdoc_files.include('lib/action_mailer/*.rb') @@ -77,6 +77,7 @@ end desc "Publish the API documentation" task :pgem => [:package] do Rake::SshFilePublisher.new("wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload + `ssh wrath.rubyonrails.org './gemupdate.sh'` end desc "Publish the API documentation" diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 4a6e70a153..165db916ee 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,11 @@ *Edge* +* time_zone_select: support for regexp matching of priority zones. Resolves #195 [Ernie Miller] + +* Made ActionView::Base#render_file private [Josh Peek] + +* Fix polymorphic_url with singleton resources. #461 [Tammer Saleh] + * Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek] * Added block-call style to link_to [Sam Stephenson/DHH]. Example: diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 86a2f3af14..1880f21f67 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -49,7 +49,7 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Action Pack -- On rails from request to response" rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' if ENV['DOC_FILES'] rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/)) else diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index bf34edcd85..7cc670289d 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -859,8 +859,7 @@ module ActionController #:nodoc: elsif inline = options[:inline] add_variables_to_assigns - tmpl = ActionView::InlineTemplate.new(@template, options[:inline], options[:locals], options[:type]) - render_for_text(@template.render_template(tmpl), options[:status]) + render_for_text(@template.render(options), options[:status]) elsif action_name = options[:action] template = default_template_name(action_name.to_s) @@ -1095,7 +1094,7 @@ module ActionController #:nodoc: def render_for_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc: add_variables_to_assigns logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger - render_for_text(@template.render_file(template_path, use_full_path, locals), status) + render_for_text(@template.render(:file => template_path, :use_full_path => use_full_path, :locals => locals), status) end def render_for_text(text = nil, status = nil, append_response = false) #:nodoc: diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index e4f5de44ab..57b31ec9d1 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -61,16 +61,18 @@ module ActionController #:nodoc: end def fragment_for(block, name = {}, options = nil) #:nodoc: - unless perform_caching then block.call; return end - - buffer = yield - - if cache = read_fragment(name, options) - buffer.concat(cache) + if perform_caching + buffer = yield + + if cache = read_fragment(name, options) + buffer.concat(cache) + else + pos = buffer.length + block.call + write_fragment(name, buffer[pos..-1], options) + end else - pos = buffer.length block.call - write_fragment(name, buffer[pos..-1], options) end end diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index 0721f71498..d0c717ff67 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -254,7 +254,7 @@ module ActionController #:nodoc: @template.instance_variable_set("@content_for_layout", content_for_layout) response.layout = layout status = template_with_options ? options[:status] : nil - render_for_text(@template.render_file(layout, true), status) + render_for_text(@template.render(layout), status) else render_with_no_layout(options, extra_options, &block) end diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index 509fa6a08e..7c30bf0778 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -48,6 +48,9 @@ module ActionController # # # calls post_url(post) # polymorphic_url(post) # => "http://example.com/posts/1" + # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1" + # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1" + # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1" # # ==== Options # @@ -83,8 +86,6 @@ module ActionController else [ record_or_hash_or_array ] end - args << format if format - inflection = case when options[:action].to_s == "new" @@ -96,6 +97,9 @@ module ActionController else :singular end + + args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)} + args << format if format named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options) send!(named_route, *args) @@ -136,11 +140,19 @@ module ActionController else record = records.pop route = records.inject("") do |string, parent| - string << "#{RecordIdentifier.send!("singular_class_name", parent)}_" + if parent.is_a?(Symbol) || parent.is_a?(String) + string << "#{parent}_" + else + string << "#{RecordIdentifier.send!("singular_class_name", parent)}_" + end end end - route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_" + if record.is_a?(Symbol) || record.is_a?(String) + route << "#{record}_" + else + route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_" + end action_prefix(options) + namespace + route + routing_type(options).to_s end @@ -163,16 +175,17 @@ module ActionController end end + # Remove the first symbols from the array and return the url prefix + # implied by those symbols. def extract_namespace(record_or_hash_or_array) - returning "" do |namespace| - if record_or_hash_or_array.is_a?(Array) - record_or_hash_or_array.delete_if do |record_or_namespace| - if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol) - namespace << "#{record_or_namespace}_" - end - end - end + return "" unless record_or_hash_or_array.is_a?(Array) + + namespace_keys = [] + while (key = record_or_hash_or_array.first) && key.is_a?(String) || key.is_a?(Symbol) + namespace_keys << record_or_hash_or_array.shift end + + namespace_keys.map {|k| "#{k}_"}.join end end end diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 40ef4ea044..163ed87fbb 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -178,7 +178,7 @@ module ActionController #:nodoc: @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub"))) @template.send!(:assign_variables_from_controller) - @template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false)) + @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception), :use_full_path => false)) response.content_type = Mime::HTML render_for_file(rescues_path("layout"), response_code_for_rescue(exception)) diff --git a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb index 032f945ed2..385c6c1b09 100644 --- a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb +++ b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb @@ -6,6 +6,6 @@ </h1> <pre><%=h @exception.clean_message %></pre> -<%= render_file(@rescues_path + "/_trace.erb", false) %> +<%= render(:file => @rescues_path + "/_trace.erb", :use_full_path => false) %> -<%= render_file(@rescues_path + "/_request_and_response.erb", false) %> +<%= render(:file => @rescues_path + "/_request_and_response.erb", :use_full_path => false) %> diff --git a/actionpack/lib/action_controller/templates/rescues/template_error.erb b/actionpack/lib/action_controller/templates/rescues/template_error.erb index eda64db3e9..4aecc68d18 100644 --- a/actionpack/lib/action_controller/templates/rescues/template_error.erb +++ b/actionpack/lib/action_controller/templates/rescues/template_error.erb @@ -15,7 +15,7 @@ <% @real_exception = @exception @exception = @exception.original_exception || @exception %> -<%= render_file(@rescues_path + "/_trace.erb", false) %> +<%= render(:file => @rescues_path + "/_trace.erb", :use_full_path => false) %> <% @exception = @real_exception %> -<%= render_file(@rescues_path + "/_request_and_response.erb", false) %> +<%= render(:file => @rescues_path + "/_request_and_response.erb", :use_full_path => false) %> diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb index 607fd186b9..b8d73c350d 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb @@ -17,7 +17,7 @@ module HTML #:nodoc: @root = Node.new(nil) node_stack = [ @root ] while token = tokenizer.next - node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token) + node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token, strict) node_stack.last.children << node unless node.tag? && node.closing == :close if node.tag? diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 4f3cc46a14..40b808a61e 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -161,10 +161,9 @@ module ActionView #:nodoc: attr_accessor :output_buffer - # Specify trim mode for the ERB compiler. Defaults to '-'. - # See ERb documentation for suitable values. - @@erb_trim_mode = '-' - cattr_accessor :erb_trim_mode + class << self + delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' + end # Specify whether file modification times should be checked to see if a template needs recompilation @@cache_template_loading = false @@ -229,26 +228,6 @@ module ActionView #:nodoc: @view_paths = ViewLoadPaths.new(Array(paths)) end - # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true, - # it's relative to the view_paths array, otherwise it's absolute. The hash in <tt>local_assigns</tt> - # is made available as local variables. - def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc: - if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/") - raise ActionViewError, <<-END_ERROR -Due to changes in ActionMailer, you need to provide the mailer_name along with the template name. - - render "user_mailer/signup" - render :file => "user_mailer/signup" - -If you are rendering a subtemplate, you must now use controller-like partial syntax: - - render :partial => 'signup' # no mailer_name necessary - END_ERROR - end - - Template.new(self, template_path, use_full_path, local_assigns).render_template - end - # Renders the template present at <tt>template_path</tt> (relative to the view_paths array). # The hash in <tt>local_assigns</tt> is made available as local variables. def render(options = {}, local_assigns = {}, &block) #:nodoc: @@ -277,16 +256,11 @@ If you are rendering a subtemplate, you must now use controller-like partial syn elsif options[:partial] render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]) elsif options[:inline] - template = InlineTemplate.new(self, options[:inline], options[:locals], options[:type]) - render_template(template) + render_inline(options[:inline], options[:locals], options[:type]) end end end - def render_template(template) #:nodoc: - template.render_template - end - # Returns true is the file may be rendered implicitly. def file_public?(template_path)#:nodoc: template_path.split('/').last[0,1] != '_' @@ -324,6 +298,30 @@ If you are rendering a subtemplate, you must now use controller-like partial syn end private + # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true, + # it's relative to the view_paths array, otherwise it's absolute. The hash in <tt>local_assigns</tt> + # is made available as local variables. + def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc: + if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/") + raise ActionViewError, <<-END_ERROR + Due to changes in ActionMailer, you need to provide the mailer_name along with the template name. + + render "user_mailer/signup" + render :file => "user_mailer/signup" + + If you are rendering a subtemplate, you must now use controller-like partial syntax: + + render :partial => 'signup' # no mailer_name necessary + END_ERROR + end + + Template.new(self, template_path, use_full_path, local_assigns).render_template + end + + def render_inline(text, local_assigns = {}, type = nil) + InlineTemplate.new(self, text, local_assigns, type).render_template + end + def wrap_content_for_layout(content) original_content_for_layout, @content_for_layout = @content_for_layout, content yield diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 9cd9d3d06a..990c30b90d 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -31,10 +31,13 @@ module ActionView # </body></html> # def capture(*args, &block) - if output_buffer + # Return captured buffer in erb. + if block_called_from_erb?(block) with_output_buffer { block.call(*args) } + + # Return block result otherwise, but protect buffer also. else - block.call(*args) + with_output_buffer { return block.call(*args) } end end @@ -117,6 +120,7 @@ module ActionView ivar = "@content_for_#{name}" content = capture(&block) if block_given? instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}") + nil end private diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 7ed6272898..1aee9ef0a2 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -696,15 +696,15 @@ module ActionView class FormBuilder def date_select(method, options = {}, html_options = {}) - @template.date_select(@object_name, method, options.merge(:object => @object)) + @template.date_select(@object_name, method, options.merge(:object => @object), html_options) end def time_select(method, options = {}, html_options = {}) - @template.time_select(@object_name, method, options.merge(:object => @object)) + @template.time_select(@object_name, method, options.merge(:object => @object), html_options) end def datetime_select(method, options = {}, html_options = {}) - @template.datetime_select(@object_name, method, options.merge(:object => @object)) + @template.datetime_select(@object_name, method, options.merge(:object => @object), html_options) end end end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index b3f8e63c1b..0ca5cebcba 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -150,7 +150,8 @@ module ActionView # You can also supply an array of TimeZone objects # as +priority_zones+, so that they will be listed above the rest of the # (long) list. (You can use TimeZone.us_zones as a convenience for - # obtaining a list of the US time zones.) + # obtaining a list of the US time zones, or a Regexp to select the zones + # of your choice) # # Finally, this method supports a <tt>:default</tt> option, which selects # a default TimeZone if the object's time zone is +nil+. @@ -164,6 +165,8 @@ module ActionView # # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ]) # + # time_zone_select( "user", 'time_zone', /Australia/) + # # time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone) def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options) @@ -292,7 +295,8 @@ module ActionView # selected option tag. You can also supply an array of TimeZone objects # as +priority_zones+, so that they will be listed above the rest of the # (long) list. (You can use TimeZone.us_zones as a convenience for - # obtaining a list of the US time zones.) + # obtaining a list of the US time zones, or a Regexp to select the zones + # of your choice) # # The +selected+ parameter must be either +nil+, or a string that names # a TimeZone. @@ -311,6 +315,9 @@ module ActionView convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } } if priority_zones + if priority_zones.is_a?(Regexp) + priority_zones = model.all.find_all {|z| z =~ priority_zones} + end zone_options += options_for_select(convert_zones[priority_zones], selected) zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n" diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 3a97f1390f..ccebec3692 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -6,7 +6,7 @@ module ActionView # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like # FormHelper does. Instead, you provide the names and values manually. # - # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying + # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying # <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>. module FormTagHelper # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like @@ -20,15 +20,15 @@ module ActionView # * A list of parameters to feed to the URL the form will be posted to. # # ==== Examples - # form_tag('/posts') + # form_tag('/posts') # # => <form action="/posts" method="post"> # - # form_tag('/posts/1', :method => :put) + # form_tag('/posts/1', :method => :put) # # => <form action="/posts/1" method="put"> # - # form_tag('/upload', :multipart => true) + # form_tag('/upload', :multipart => true) # # => <form action="/upload" method="post" enctype="multipart/form-data"> - # + # # <% form_tag '/posts' do -%> # <div><%= submit_tag 'Save' %></div> # <% end -%> @@ -88,7 +88,7 @@ module ActionView # * <tt>:size</tt> - The number of visible characters that will fit in the input. # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter. # * Any other key creates standard HTML attributes for the tag. - # + # # ==== Examples # text_field_tag 'name' # # => <input id="name" name="name" type="text" /> @@ -146,13 +146,13 @@ module ActionView # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" /> # # hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')" - # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')" + # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')" # # type="hidden" value="" /> def hidden_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "hidden")) end - # Creates a file upload field. If you are using file uploads then you will also need + # Creates a file upload field. If you are using file uploads then you will also need # to set the multipart option for the form tag: # # <%= form_tag { :action => "post" }, { :multipart => true } %> @@ -160,7 +160,7 @@ module ActionView # <%= submit_tag %> # <%= end_form_tag %> # - # The specified URL will then be passed a File object containing the selected file, or if the field + # The specified URL will then be passed a File object containing the selected file, or if the field # was left blank, a StringIO object. # # ==== Options @@ -181,7 +181,7 @@ module ActionView # # => <input id="resume" name="resume" type="file" value="~/resume.doc" /> # # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg' - # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" /> + # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" /> # # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html' # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" /> @@ -286,7 +286,7 @@ module ActionView tag :input, html_options end - # Creates a radio button; use groups of radio buttons named the same to allow users to + # Creates a radio button; use groups of radio buttons named the same to allow users to # select from a group of options. # # ==== Options @@ -313,14 +313,14 @@ module ActionView tag :input, html_options end - # Creates a submit button with the text <tt>value</tt> as the caption. + # Creates a submit button with the text <tt>value</tt> as the caption. # # ==== Options # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm # prompt with the question specified. If the user accepts, the form is # processed normally, otherwise no action is taken. # * <tt>:disabled</tt> - If true, the user will not be able to use this input. - # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version + # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version # of the submit button when the form is submitted. # * Any other key creates standard HTML options for the tag. # @@ -335,7 +335,7 @@ module ActionView # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" /> # # submit_tag "Complete sale", :disable_with => "Please wait..." - # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();" + # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();" # # type="submit" value="Complete sale" /> # # submit_tag nil, :class => "form_submit" @@ -346,7 +346,7 @@ module ActionView # # name="commit" type="submit" value="Edit" /> def submit_tag(value = "Save changes", options = {}) options.stringify_keys! - + if disable_with = options.delete("disable_with") options["onclick"] = [ "this.setAttribute('originalValue', this.value)", @@ -358,15 +358,15 @@ module ActionView "return result;", ].join(";") end - + if confirm = options.delete("confirm") options["onclick"] ||= '' options["onclick"] += "return #{confirm_javascript_function(confirm)};" end - + tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys) end - + # Displays an image which when clicked will submit the form. # # <tt>source</tt> is passed to AssetTagHelper#image_path @@ -412,7 +412,7 @@ module ActionView concat(content) concat("</fieldset>") end - + private def html_options_for_form(url_for_options, options, *parameters_for_url) returning options.stringify_keys do |html_options| @@ -420,7 +420,7 @@ module ActionView html_options["action"] = url_for(url_for_options, *parameters_for_url) end end - + def extra_tags_for_form(html_options) case method = html_options.delete("method").to_s when /^get$/i # must be case-insentive, but can't use downcase as might be nil @@ -434,12 +434,12 @@ module ActionView content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0') end end - + def form_tag_html(html_options) extra_tags = extra_tags_for_form(html_options) tag(:form, html_options, true) + extra_tags end - + def form_tag_in_block(html_options, &block) content = capture(&block) concat(form_tag_html(html_options)) diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 7404a251e4..f89b6c2f70 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -4,10 +4,10 @@ require 'action_view/helpers/prototype_helper' module ActionView module Helpers # Provides functionality for working with JavaScript in your views. - # + # # == Ajax, controls and visual effects - # - # * For information on using Ajax, see + # + # * For information on using Ajax, see # ActionView::Helpers::PrototypeHelper. # * For information on using controls and visual effects, see # ActionView::Helpers::ScriptaculousHelper. @@ -20,22 +20,22 @@ module ActionView # and ActionView::Helpers::ScriptaculousHelper), you must do one of the # following: # - # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD - # section of your page (recommended): This function will return + # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD + # section of your page (recommended): This function will return # references to the JavaScript files created by the +rails+ command in # your <tt>public/javascripts</tt> directory. Using it is recommended as - # the browser can then cache the libraries instead of fetching all the + # the browser can then cache the libraries instead of fetching all the # functions anew on every request. - # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but + # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but # will only include the Prototype core library, which means you are able - # to use all basic AJAX functionality. For the Scriptaculous-based - # JavaScript helpers, like visual effects, autocompletion, drag and drop + # to use all basic AJAX functionality. For the Scriptaculous-based + # JavaScript helpers, like visual effects, autocompletion, drag and drop # and so on, you should use the method described above. # * Use <tt><%= define_javascript_functions %></tt>: this will copy all the # JavaScript support functions within a single script block. Not # recommended. # - # For documentation on +javascript_include_tag+ see + # For documentation on +javascript_include_tag+ see # ActionView::Helpers::AssetTagHelper. module JavaScriptHelper unless const_defined? :JAVASCRIPT_PATH @@ -43,13 +43,13 @@ module ActionView end include PrototypeHelper - - # Returns a link that will trigger a JavaScript +function+ using the + + # Returns a link that will trigger a JavaScript +function+ using the # onclick handler and return false after the fact. # # The +function+ argument can be omitted in favor of an +update_page+ # block, which evaluates to a string when the template is rendered - # (instead of making an Ajax request first). + # (instead of making an Ajax request first). # # Examples: # link_to_function "Greeting", "alert('Hello world!')" @@ -70,36 +70,31 @@ module ActionView # <a href="#" id="more_link" onclick="try { # $("details").visualEffect("toggle_blind"); # $("more_link").update("Show me less"); - # } - # catch (e) { - # alert('RJS error:\n\n' + e.toString()); + # } + # catch (e) { + # alert('RJS error:\n\n' + e.toString()); # alert('$(\"details\").visualEffect(\"toggle_blind\"); # \n$(\"more_link\").update(\"Show me less\");'); - # throw e + # throw e # }; # return false;">Show me more</a> # def link_to_function(name, *args, &block) - html_options = args.extract_options! - function = args[0] || '' - - html_options.symbolize_keys! - function = update_page(&block) if block_given? - content_tag( - "a", name, - html_options.merge({ - :href => html_options[:href] || "#", - :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function}; return false;" - }) - ) + html_options = args.extract_options!.symbolize_keys + + function = block_given? ? update_page(&block) : args[0] || '' + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" + href = html_options[:href] || '#' + + content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) end - - # Returns a button that'll trigger a JavaScript +function+ using the + + # Returns a button that'll trigger a JavaScript +function+ using the # onclick handler. # # The +function+ argument can be omitted in favor of an +update_page+ # block, which evaluates to a string when the template is rendered - # (instead of making an Ajax request first). + # (instead of making an Ajax request first). # # Examples: # button_to_function "Greeting", "alert('Hello world!')" @@ -111,45 +106,56 @@ module ActionView # page[:details].visual_effect :toggle_slide # end def button_to_function(name, *args, &block) - html_options = args.extract_options! - function = args[0] || '' - - html_options.symbolize_keys! - function = update_page(&block) if block_given? - tag(:input, html_options.merge({ - :type => "button", :value => name, - :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};" - })) + html_options = args.extract_options!.symbolize_keys + + function = block_given? ? update_page(&block) : args[0] || '' + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" + + tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) end - # Includes the Action Pack JavaScript libraries inside a single <script> + # Includes the Action Pack JavaScript libraries inside a single <script> # tag. The function first includes prototype.js and then its core extensions, # (determined by filenames starting with "prototype"). # Afterwards, any additional scripts will be included in undefined order. # # Note: The recommended approach is to copy the contents of # lib/action_view/helpers/javascripts/ into your application's - # public/javascripts/ directory, and use +javascript_include_tag+ to + # public/javascripts/ directory, and use +javascript_include_tag+ to # create remote <script> links. def define_javascript_functions javascript = "<script type=\"#{Mime::JS}\">" - - # load prototype.js and its extensions first + + # load prototype.js and its extensions first prototype_libs = Dir.glob(File.join(JAVASCRIPT_PATH, 'prototype*')).sort.reverse - prototype_libs.each do |filename| + prototype_libs.each do |filename| javascript << "\n" << IO.read(filename) end - + # load other libraries - (Dir.glob(File.join(JAVASCRIPT_PATH, '*')) - prototype_libs).each do |filename| + (Dir.glob(File.join(JAVASCRIPT_PATH, '*')) - prototype_libs).each do |filename| javascript << "\n" << IO.read(filename) end javascript << '</script>' end + + JS_ESCAPE_MAP = { + '\\' => '\\\\', + '</' => '<\/', + "\r\n" => '\n', + "\n" => '\n', + "\r" => '\n', + '"' => '\\"', + "'" => "\\'" } + # Escape carrier returns and single and double quotes for JavaScript segments. def escape_javascript(javascript) - (javascript || '').gsub('\\','\0\0').gsub('</','<\/').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" } + if javascript + javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] } + else + '' + end end # Returns a JavaScript tag with the +content+ inside. Example: @@ -163,7 +169,7 @@ module ActionView # </script> # # +html_options+ may be a hash of attributes for the <script> tag. Example: - # javascript_tag "alert('All is good')", :defer => 'defer' + # javascript_tag "alert('All is good')", :defer => 'defer' # # => <script defer="defer" type="text/javascript">alert('All is good')</script> # # Instead of passing the content as an argument, you can also use a block @@ -180,30 +186,37 @@ module ActionView content_or_options_with_block end - tag = content_tag("script", javascript_cdata_section(content), html_options.merge(:type => Mime::JS)) + tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS)) - block_given? ? concat(tag) : tag + if block_called_from_erb?(block) + concat(tag) + else + tag + end end def javascript_cdata_section(content) #:nodoc: "\n//#{cdata_section("\n#{content}\n//")}\n" end - + protected def options_for_javascript(options) - '{' + options.map {|k, v| "#{k}:#{v}"}.sort.join(', ') + '}' + if options.empty? + '{}' + else + "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" + end end - + def array_or_string_for_javascript(option) - js_option = if option.kind_of?(Array) + if option.kind_of?(Array) "['#{option.join('\',\'')}']" elsif !option.nil? "'#{option}'" end - js_option end end - + JavascriptHelper = JavaScriptHelper unless const_defined? :JavascriptHelper end end diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index e1abec1847..aeafd3906d 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -12,14 +12,14 @@ module ActionView BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple).to_set BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym)) - # Returns an empty HTML tag of type +name+ which by default is XHTML - # compliant. Set +open+ to true to create an open tag compatible - # with HTML 4.0 and below. Add HTML attributes by passing an attributes + # Returns an empty HTML tag of type +name+ which by default is XHTML + # compliant. Set +open+ to true to create an open tag compatible + # with HTML 4.0 and below. Add HTML attributes by passing an attributes # hash to +options+. Set +escape+ to false to disable attribute value # escaping. # # ==== Options - # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and + # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use # symbols or strings for the attribute names. # @@ -30,7 +30,7 @@ module ActionView # tag("br", nil, true) # # => <br> # - # tag("input", { :type => 'text', :disabled => true }) + # tag("input", { :type => 'text', :disabled => true }) # # => <input type="text" disabled="disabled" /> # # tag("img", { :src => "open & shut.png" }) @@ -43,13 +43,13 @@ module ActionView end # Returns an HTML block tag of type +name+ surrounding the +content+. Add - # HTML attributes by passing an attributes hash to +options+. + # HTML attributes by passing an attributes hash to +options+. # Instead of passing the content as an argument, you can also use a block # in which case, you pass your +options+ as the second parameter. # Set escape to false to disable attribute value escaping. # # ==== Options - # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and + # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use # symbols or strings for the attribute names. # @@ -68,7 +68,13 @@ module ActionView def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block) if block_given? options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash) - concat(content_tag_string(name, capture(&block), options, escape)) + content_tag = content_tag_string(name, capture(&block), options, escape) + + if block_called_from_erb?(block) + concat(content_tag) + else + content_tag + end else content_tag_string(name, content_or_options_with_block, options, escape) end @@ -102,6 +108,16 @@ module ActionView end private + BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template' + + # Check whether we're called from an erb template. + # We'd return a string in any other case, but erb <%= ... %> + # can't take an <% end %> later on, so we have to use <% ... %> + # and implicitly concat. + def block_called_from_erb?(block) + block && eval(BLOCK_CALLED_FROM_ERB, block) + end + def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options "<#{name}#{tag_options}>#{content}</#{name}>" diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index a1a91f6b3d..a6c48737e9 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -30,11 +30,7 @@ module ActionView ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.") end - if output_buffer && string - output_buffer << string - else - string - end + output_buffer << string end if RUBY_VERSION < '1.9' @@ -472,7 +468,7 @@ module ActionView [-\w]+ # subdomain or domain (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port - (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path + (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path (?:\?[\w\+@%&=.;-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index baecd304cd..d5b7255642 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -535,7 +535,7 @@ module ActionView when method "#{method_javascript_function(method, url, href)}return false;" when popup - popup_javascript_function(popup) + 'return false;' + "#{popup_javascript_function(popup)}return false;" else html_options["onclick"] end diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb index 1aef81ba1a..783456ab53 100644 --- a/actionpack/lib/action_view/template_handlers/compilable.rb +++ b/actionpack/lib/action_view/template_handlers/compilable.rb @@ -106,7 +106,13 @@ module ActionView locals_code << "#{key} = local_assigns[:#{key}]\n" end - "def #{render_symbol}(local_assigns)\nold_output_buffer = output_buffer;#{locals_code}#{body}\nensure\nself.output_buffer = old_output_buffer\nend" + <<-end_src + def #{render_symbol}(local_assigns) + old_output_buffer = output_buffer;#{locals_code}#{body} + ensure + self.output_buffer = old_output_buffer + end + end_src end # Return true if the given template was compiled for a superset of the keys in local_assigns diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index ad4ccc7c42..ac715e30c2 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -42,12 +42,20 @@ module ActionView class ERB < TemplateHandler include Compilable + # Specify trim mode for the ERB compiler. Defaults to '-'. + # See ERb documentation for suitable values. + cattr_accessor :erb_trim_mode + self.erb_trim_mode = '-' + def compile(template) - ::ERB.new(template.source, nil, @view.erb_trim_mode, '@output_buffer').src + src = ::ERB.new(template.source, nil, erb_trim_mode, '@output_buffer').src + "__in_erb_template=true;#{src}" end def cache_fragment(block, name = {}, options = nil) #:nodoc: - @view.fragment_for(block, name, options) { @view.response.template.output_buffer ||= '' } + @view.fragment_for(block, name, options) do + @view.response.template.output_buffer + end end end end diff --git a/actionpack/lib/action_view/view_load_paths.rb b/actionpack/lib/action_view/view_load_paths.rb index e873d96aa0..f23ac665f1 100644 --- a/actionpack/lib/action_view/view_load_paths.rb +++ b/actionpack/lib/action_view/view_load_paths.rb @@ -6,19 +6,15 @@ module ActionView #:nodoc: class LoadPath #:nodoc: attr_reader :path, :paths - delegate :to_s, :inspect, :to => :path + delegate :to_s, :to_str, :inspect, :to => :path def initialize(path) @path = path.freeze reload! end - def eql?(view_path) - view_path.is_a?(ViewPath) && @path == view_path.path - end - - def hash - @path.hash + def ==(path) + to_str == path.to_str end # Rebuild load path directory cache diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index fa1c3293b4..70f6a28a9c 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -22,6 +22,7 @@ ActiveSupport::Deprecation.debug = true ActionController::Base.logger = nil ActionController::Routing::Routes.reload rescue nil +FIXTURE_LOAD_PATH = ActionView::ViewLoadPaths::LoadPath.new(File.join(File.dirname(__FILE__), 'fixtures')) # Wrap tests that use Mocha and skip if unavailable. def uses_mocha(test_name) diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb index a7d526850e..a377ccad24 100644 --- a/actionpack/test/active_record_unit.rb +++ b/actionpack/test/active_record_unit.rb @@ -30,7 +30,6 @@ end $stderr.flush - # Define the rest of the connector class ActiveRecordTestConnector class << self @@ -48,46 +47,45 @@ class ActiveRecordTestConnector end private - - def setup_connection - if Object.const_defined?(:ActiveRecord) - defaults = { :database => ':memory:' } - begin - options = defaults.merge :adapter => 'sqlite3', :timeout => 500 - ActiveRecord::Base.establish_connection(options) - ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => options } - ActiveRecord::Base.connection - rescue Exception # errors from establishing a connection - $stderr.puts 'SQLite 3 unavailable; trying SQLite 2.' - options = defaults.merge :adapter => 'sqlite' - ActiveRecord::Base.establish_connection(options) - ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => options } - ActiveRecord::Base.connection + def setup_connection + if Object.const_defined?(:ActiveRecord) + defaults = { :database => ':memory:' } + begin + options = defaults.merge :adapter => 'sqlite3', :timeout => 500 + ActiveRecord::Base.establish_connection(options) + ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => options } + ActiveRecord::Base.connection + rescue Exception # errors from establishing a connection + $stderr.puts 'SQLite 3 unavailable; trying SQLite 2.' + options = defaults.merge :adapter => 'sqlite' + ActiveRecord::Base.establish_connection(options) + ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => options } + ActiveRecord::Base.connection + end + + Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE) + else + raise "Can't setup connection since ActiveRecord isn't loaded." end - - Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE) - else - raise "Can't setup connection since ActiveRecord isn't loaded." end - end - # Load actionpack sqlite tables - def load_schema - File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql| - ActiveRecord::Base.connection.execute(sql) unless sql.blank? + # Load actionpack sqlite tables + def load_schema + File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql| + ActiveRecord::Base.connection.execute(sql) unless sql.blank? + end end - end - def require_fixture_models - Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f} - end + def require_fixture_models + Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f} + end end end class ActiveRecordTestCase < ActiveSupport::TestCase # Set our fixture path if ActiveRecordTestConnector.able_to_connect - self.fixture_path = "#{File.dirname(__FILE__)}/fixtures/" + self.fixture_path = [FIXTURE_LOAD_PATH] self.use_transactional_fixtures = false end diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index ed10e72953..af2725a99b 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -40,11 +40,12 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base render :partial => @developers end end -RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] + +RenderPartialWithRecordIdentificationController.view_paths = [FIXTURE_LOAD_PATH] class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots - + def setup @controller = RenderPartialWithRecordIdentificationController.new @request = ActionController::TestRequest.new @@ -56,22 +57,22 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase get :render_with_has_many_and_belongs_to_association assert_template 'projects/_project' end - + def test_rendering_partial_with_has_many_association get :render_with_has_many_association assert_template 'replies/_reply' end - + def test_rendering_partial_with_named_scope get :render_with_named_scope assert_template 'replies/_reply' end - + def test_render_with_record get :render_with_record assert_template 'developers/_developer' end - + def test_render_with_record_collection get :render_with_record_collection assert_template 'developers/_developer' @@ -116,7 +117,8 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base render :partial => @developers end end -RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] + +RenderPartialWithRecordIdentificationController.view_paths = [FIXTURE_LOAD_PATH] class Game < Struct.new(:name, :id) def to_param @@ -134,7 +136,8 @@ module Fun render :partial => [ Game.new("Pong"), Game.new("Tank") ] end end - NestedController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] + + NestedController.view_paths = [FIXTURE_LOAD_PATH] module Serious class NestedDeeperController < ActionController::Base @@ -146,7 +149,8 @@ module Fun render :partial => [ Game.new("Chess"), Game.new("Sudoku"), Game.new("Solitaire") ] end end - NestedDeeperController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] + + NestedDeeperController.view_paths = [FIXTURE_LOAD_PATH] end end @@ -167,7 +171,6 @@ class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveReco get :render_with_record_collection_in_nested_controller assert_template 'fun/games/_game' end - end class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < ActiveRecordTestCase @@ -187,5 +190,4 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < Acti get :render_with_record_collection_in_deeper_nested_controller assert_template 'fun/serious/games/_game' end - -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index c25e9e1df6..7a90a9408e 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -157,7 +157,7 @@ module Admin def redirect_to_fellow_controller redirect_to :controller => 'user' end - + def redirect_to_top_level_named_route redirect_to top_level_url(:id => "foo") end @@ -170,7 +170,7 @@ end # tell the controller where to find its templates but start from parent # directory of test_request_response to simulate the behaviour of a # production environment -ActionPackAssertionsController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +ActionPackAssertionsController.view_paths = [FIXTURE_LOAD_PATH] # a test case to exercise the new capabilities TestRequest & TestResponse class ActionPackAssertionsControllerTest < Test::Unit::TestCase @@ -533,7 +533,6 @@ class ActionPackHeaderTest < Test::Unit::TestCase assert_equal('application/pdf; charset=utf-8', @response.headers['type']) end - def test_render_text_with_custom_content_type get :render_text_with_custom_content_type assert_equal 'application/rss+xml; charset=utf-8', @response.headers['type'] diff --git a/actionpack/test/controller/addresses_render_test.rb b/actionpack/test/controller/addresses_render_test.rb index a31734203d..df87182082 100644 --- a/actionpack/test/controller/addresses_render_test.rb +++ b/actionpack/test/controller/addresses_render_test.rb @@ -1,7 +1,6 @@ require 'abstract_unit' class Address - def Address.count(conditions = nil, join = nil) nil end @@ -20,7 +19,7 @@ class AddressesTestController < ActionController::Base def self.controller_path; "addresses"; end end -AddressesTestController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +AddressesTestController.view_paths = [FIXTURE_LOAD_PATH] class AddressesTest < Test::Unit::TestCase def setup diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 14cf0a86a1..0140654155 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -6,7 +6,7 @@ CACHE_DIR = 'test_cache' FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR) ActionController::Base.page_cache_directory = FILE_STORE_PATH ActionController::Base.cache_store = :file_store, FILE_STORE_PATH -ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/' ] +ActionController::Base.view_paths = [FIXTURE_LOAD_PATH] class PageCachingTestController < ActionController::Base caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? } @@ -631,7 +631,7 @@ class FunctionalCachingController < ActionController::Base end end -FunctionalCachingController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +FunctionalCachingController.view_paths = [FIXTURE_LOAD_PATH] class FunctionalFragmentCachingTest < Test::Unit::TestCase def setup @@ -642,6 +642,7 @@ class FunctionalFragmentCachingTest < Test::Unit::TestCase @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end + def test_fragment_caching get :fragment_cached assert_response :success diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb index 2604844b84..87f9ce8ab3 100644 --- a/actionpack/test/controller/capture_test.rb +++ b/actionpack/test/controller/capture_test.rb @@ -23,7 +23,7 @@ class CaptureController < ActionController::Base def rescue_action(e) raise end end -CaptureController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +CaptureController.view_paths = [FIXTURE_LOAD_PATH] class CaptureTest < Test::Unit::TestCase def setup diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index d262ce8103..2019b4a2d0 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -13,12 +13,12 @@ class ContentTypeController < ActionController::Base def render_content_type_from_render render :text => "hello world!", :content_type => Mime::RSS end - + def render_charset_from_body response.charset = "utf-16" render :text => "hello world!" end - + def render_default_for_rhtml end @@ -45,7 +45,7 @@ class ContentTypeController < ActionController::Base def rescue_action(e) raise end end -ContentTypeController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +ContentTypeController.view_paths = [FIXTURE_LOAD_PATH] class ContentTypeTest < Test::Unit::TestCase def setup @@ -68,7 +68,7 @@ class ContentTypeTest < Test::Unit::TestCase def test_render_changed_charset_default ContentTypeController.default_charset = "utf-16" get :render_defaults - assert_equal "utf-16", @response.charset + assert_equal "utf-16", @response.charset assert_equal Mime::HTML, @response.content_type ContentTypeController.default_charset = "utf-8" end @@ -76,13 +76,13 @@ class ContentTypeTest < Test::Unit::TestCase def test_content_type_from_body get :render_content_type_from_body assert_equal "application/rss+xml", @response.content_type - assert_equal "utf-8", @response.charset + assert_equal "utf-8", @response.charset end def test_content_type_from_render get :render_content_type_from_render assert_equal "application/rss+xml", @response.content_type - assert_equal "utf-8", @response.charset + assert_equal "utf-8", @response.charset end def test_charset_from_body @@ -94,27 +94,27 @@ class ContentTypeTest < Test::Unit::TestCase def test_default_for_rhtml get :render_default_for_rhtml assert_equal Mime::HTML, @response.content_type - assert_equal "utf-8", @response.charset + assert_equal "utf-8", @response.charset end def test_default_for_rxml get :render_default_for_rxml assert_equal Mime::XML, @response.content_type - assert_equal "utf-8", @response.charset + assert_equal "utf-8", @response.charset end def test_default_for_rjs xhr :post, :render_default_for_rjs assert_equal Mime::JS, @response.content_type - assert_equal "utf-8", @response.charset + assert_equal "utf-8", @response.charset end def test_change_for_rxml get :render_change_for_rxml assert_equal Mime::HTML, @response.content_type - assert_equal "utf-8", @response.charset + assert_equal "utf-8", @response.charset end - + def test_render_default_content_types_for_respond_to @request.env["HTTP_ACCEPT"] = Mime::HTML.to_s get :render_default_content_types_for_respond_to @@ -130,7 +130,7 @@ class ContentTypeTest < Test::Unit::TestCase get :render_default_content_types_for_respond_to assert_equal Mime::XML, @response.content_type end - + def test_render_default_content_types_for_respond_to_with_overwrite @request.env["HTTP_ACCEPT"] = Mime::RSS.to_s get :render_default_content_types_for_respond_to diff --git a/actionpack/test/controller/custom_handler_test.rb b/actionpack/test/controller/custom_handler_test.rb deleted file mode 100644 index ac484ae17e..0000000000 --- a/actionpack/test/controller/custom_handler_test.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'abstract_unit' - -class CustomHandler < ActionView::TemplateHandler - def initialize( view ) - @view = view - end - - def render( template ) - [ template.source, - template.locals, - @view ] - end -end - -class CustomHandlerTest < Test::Unit::TestCase - def setup - ActionView::Template.register_template_handler "foo", CustomHandler - ActionView::Template.register_template_handler :foo2, CustomHandler - @view = ActionView::Base.new - end - - def test_custom_render - template = ActionView::InlineTemplate.new(@view, "hello <%= one %>", { :one => "two" }, "foo") - - result = @view.render_template(template) - assert_equal( - [ "hello <%= one %>", { :one => "two" }, @view ], - result ) - end - - def test_custom_render2 - template = ActionView::InlineTemplate.new(@view, "hello <%= one %>", { :one => "two" }, "foo2") - result = @view.render_template(template) - assert_equal( - [ "hello <%= one %>", { :one => "two" }, @view ], - result ) - end - - def test_unhandled_extension - # uses the ERb handler by default if the extension isn't recognized - template = ActionView::InlineTemplate.new(@view, "hello <%= one %>", { :one => "two" }, "bar") - result = @view.render_template(template) - assert_equal "hello two", result - end -end diff --git a/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb b/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb index 8c1a8954a5..f485500b7f 100644 --- a/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb +++ b/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb @@ -2,7 +2,6 @@ require 'abstract_unit' class DeprecatedBaseMethodsTest < Test::Unit::TestCase class Target < ActionController::Base - def home_url(greeting) "http://example.com/#{greeting}" end @@ -14,7 +13,7 @@ class DeprecatedBaseMethodsTest < Test::Unit::TestCase def rescue_action(e) raise e end end - Target.view_paths = [ File.dirname(__FILE__) + "/../../fixtures" ] + Target.view_paths = [FIXTURE_LOAD_PATH] def setup @request = ActionController::TestRequest.new diff --git a/actionpack/test/controller/html-scanner/document_test.rb b/actionpack/test/controller/html-scanner/document_test.rb index 0519533dbd..1c3facb9e3 100644 --- a/actionpack/test/controller/html-scanner/document_test.rb +++ b/actionpack/test/controller/html-scanner/document_test.rb @@ -120,4 +120,29 @@ HTML assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "") assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil) end + + def test_parse_invalid_document + assert_nothing_raised do + doc = HTML::Document.new("<html> + <table> + <tr> + <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td> + </tr> + </table> + </html>") + end + end + + def test_invalid_document_raises_exception_when_strict + assert_raises RuntimeError do + doc = HTML::Document.new("<html> + <table> + <tr> + <td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td> + </tr> + </table> + </html>", true) + end + end + end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index c617cb2e84..fb2519563d 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -107,7 +107,7 @@ class RespondToController < ActionController::Base type.any(:js, :xml) { render :text => "Either JS or XML" } end end - + def handle_any_any respond_to do |type| type.html { render :text => 'HTML' } @@ -120,12 +120,12 @@ class RespondToController < ActionController::Base type.html type.js end - end - - def iphone_with_html_response_type + end + + def iphone_with_html_response_type Mime::Type.register_alias("text/html", :iphone) request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" - + respond_to do |type| type.html { @type = "Firefox" } type.iphone { @type = "iPhone" } @@ -138,7 +138,7 @@ class RespondToController < ActionController::Base def iphone_with_html_response_type_without_layout Mime::Type.register_alias("text/html", :iphone) request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - + respond_to do |type| type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" } type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" } @@ -162,7 +162,7 @@ class RespondToController < ActionController::Base end end -RespondToController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +RespondToController.view_paths = [FIXTURE_LOAD_PATH] class MimeControllerTest < Test::Unit::TestCase def setup @@ -247,7 +247,7 @@ class MimeControllerTest < Test::Unit::TestCase get :just_xml assert_equal 'XML', @response.body end - + def test_using_defaults @request.env["HTTP_ACCEPT"] = "*/*" get :using_defaults @@ -347,12 +347,12 @@ class MimeControllerTest < Test::Unit::TestCase get :handle_any_any assert_equal 'HTML', @response.body end - + def test_handle_any_any_parameter_format get :handle_any_any, {:format=>'html'} assert_equal 'HTML', @response.body end - + def test_handle_any_any_explicit_html @request.env["HTTP_ACCEPT"] = "text/html" get :handle_any_any @@ -364,7 +364,7 @@ class MimeControllerTest < Test::Unit::TestCase get :handle_any_any assert_equal 'Whatever you ask for, I got it', @response.body end - + def test_handle_any_any_xml @request.env["HTTP_ACCEPT"] = "text/xml" get :handle_any_any @@ -445,31 +445,31 @@ class MimeControllerTest < Test::Unit::TestCase get :using_defaults, :format => "xml" assert_equal "using_defaults - xml", @response.body - end - + end + def test_format_with_custom_response_type get :iphone_with_html_response_type - assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body - + assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body + get :iphone_with_html_response_type, :format => "iphone" assert_equal "text/html", @response.content_type assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body - end - + end + def test_format_with_custom_response_type_and_request_headers @request.env["HTTP_ACCEPT"] = "text/iphone" get :iphone_with_html_response_type assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body assert_equal "text/html", @response.content_type - end + end def test_format_with_custom_response_type_and_request_headers_with_only_one_layout_present get :iphone_with_html_response_type_without_layout - assert_equal '<html><div id="html_missing">Hello future from Firefox!</div></html>', @response.body + assert_equal '<html><div id="html_missing">Hello future from Firefox!</div></html>', @response.body @request.env["HTTP_ACCEPT"] = "text/iphone" assert_raises(ActionView::MissingTemplate) { get :iphone_with_html_response_type_without_layout } - end + end end class AbstractPostController < ActionController::Base @@ -497,7 +497,7 @@ class PostController < AbstractPostController end end -class SuperPostController < PostController +class SuperPostController < PostController def index respond_to do |type| type.html @@ -514,25 +514,24 @@ class MimeControllerLayoutsTest < Test::Unit::TestCase @controller = PostController.new @request.host = "www.example.com" end - + def test_missing_layout_renders_properly get :index - assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body + assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body @request.env["HTTP_ACCEPT"] = "text/iphone" get :index assert_equal 'Hello iPhone', @response.body end - + def test_format_with_inherited_layouts @controller = SuperPostController.new - + get :index assert_equal 'Super Firefox', @response.body - + @request.env["HTTP_ACCEPT"] = "text/iphone" get :index assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body end end - diff --git a/actionpack/test/controller/new_render_test.rb b/actionpack/test/controller/new_render_test.rb index b77b3ceffa..deda47f352 100644 --- a/actionpack/test/controller/new_render_test.rb +++ b/actionpack/test/controller/new_render_test.rb @@ -45,11 +45,11 @@ class NewRenderTestController < ActionController::Base def render_action_hello_world_as_symbol render :action => :hello_world end - + def render_text_hello_world render :text => "hello world" end - + def render_text_hello_world_with_layout @variable_for_layout = ", I'm here!" render :text => "hello world", :layout => true @@ -68,7 +68,7 @@ class NewRenderTestController < ActionController::Base path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb') render :file => path end - + def render_file_from_template @secret = 'in the sauce' @path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb')) @@ -76,14 +76,14 @@ class NewRenderTestController < ActionController::Base def render_file_with_locals path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb') - render :file => path, :locals => {:secret => 'in the sauce'} + 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_file_not_using_full_path_with_dot_in_path @secret = 'in the sauce' render :file => 'test/dot.directory/render_file_with_ivar', :use_full_path => true @@ -105,7 +105,7 @@ class NewRenderTestController < ActionController::Base def layout_test_with_different_layout render :action => "hello_world", :layout => "standard" end - + def rendering_without_layout render :action => "hello_world", :layout => false end @@ -113,11 +113,11 @@ class NewRenderTestController < ActionController::Base def layout_overriding_layout render :action => "hello_world", :layout => "standard" end - + def rendering_nothing_on_layout render :nothing => true end - + def builder_layout_test render :action => "hello" end @@ -135,9 +135,9 @@ class NewRenderTestController < ActionController::Base def partial_only_with_layout render :partial => "partial_only", :layout => true end - + def partial_with_locals - render :partial => "customer", :locals => { :customer => Customer.new("david") } + render :partial => "customer", :locals => { :customer => Customer.new("david") } end def partial_with_form_builder @@ -151,11 +151,11 @@ class NewRenderTestController < ActionController::Base def partial_collection render :partial => "customer", :collection => [ Customer.new("david"), Customer.new("mary") ] end - + def partial_collection_with_spacer render :partial => "customer", :spacer_template => "partial_only", :collection => [ Customer.new("david"), Customer.new("mary") ] end - + def partial_collection_with_counter render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ] end @@ -186,33 +186,33 @@ class NewRenderTestController < ActionController::Base def empty_partial_collection render :partial => "customer", :collection => [] end - + def partial_with_hash_object render :partial => "hash_object", :object => {:first_name => "Sam"} end - + def partial_hash_collection render :partial => "hash_object", :collection => [ {:first_name => "Pratik"}, {:first_name => "Amy"} ] end - + def partial_hash_collection_with_locals render :partial => "hash_greeting", :collection => [ {:first_name => "Pratik"}, {:first_name => "Amy"} ], :locals => { :greeting => "Hola" } end - + def partial_with_implicit_local_assignment @customer = Customer.new("Marcel") render :partial => "customer" end - + def missing_partial render :partial => 'thisFileIsntHere' end - + def hello_in_a_string @customers = [ Customer.new("david"), Customer.new("mary") ] render :text => "How's there? " << render_to_string(:template => "test/list") end - + def render_to_string_with_assigns @before = "i'm before the render" render_to_string :text => "foo" @@ -222,14 +222,14 @@ class NewRenderTestController < ActionController::Base def render_to_string_with_partial @partial_only = render_to_string :partial => "partial_only" - @partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") } - render :action => "test/hello_world" - end - + @partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") } + render :action => "test/hello_world" + end + def render_to_string_with_exception render_to_string :file => "exception that will not be caught - this will certainly not work", :use_full_path => true end - + def render_to_string_with_caught_exception @before = "i'm before the render" begin @@ -282,7 +282,7 @@ class NewRenderTestController < ActionController::Base render :text => "hello" redirect_to :action => "double_render" end - + def render_to_string_and_render @stuff = render_to_string :text => "here is some cached stuff" render :text => "Hi web users! #{@stuff}" @@ -329,7 +329,7 @@ class NewRenderTestController < ActionController::Base def render_with_location render :xml => "<hello/>", :location => "http://example.com", :status => 201 end - + def render_with_object_location customer = Customer.new("Some guy", 1) render :xml => "<customer/>", :location => customer_url(customer), :status => :created @@ -341,12 +341,12 @@ class NewRenderTestController < ActionController::Base "<i-am-xml/>" end end.new - + render :xml => to_xmlable end helper NewRenderTestHelper - helper do + helper do def rjs_helper_method(value) page.visual_effect :highlight, value end @@ -383,7 +383,7 @@ class NewRenderTestController < ActionController::Base page.visual_effect :highlight, 'balance' end end - + def update_page_with_instance_variables @money = '$37,000,000.00' @div_id = 'balance' @@ -426,12 +426,12 @@ class NewRenderTestController < ActionController::Base def render_using_layout_around_block_in_main_layout_and_within_content_for_layout render :action => "using_layout_around_block" end - + def rescue_action(e) raise end - + private def determine_layout - case action_name + case action_name when "hello_world", "layout_test", "rendering_without_layout", "rendering_nothing_on_layout", "render_text_hello_world", "render_text_hello_world_with_layout", @@ -443,7 +443,7 @@ class NewRenderTestController < ActionController::Base "render_js_with_explicit_template", "render_js_with_explicit_action_template", "delete_with_js", "update_page", "update_page_with_instance_variables" - + "layouts/standard" when "builder_layout_test" "layouts/builder" @@ -457,8 +457,8 @@ class NewRenderTestController < ActionController::Base end end -NewRenderTestController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] -Fun::GamesController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +NewRenderTestController.view_paths = [FIXTURE_LOAD_PATH] +Fun::GamesController.view_paths = [FIXTURE_LOAD_PATH] class NewRenderTest < Test::Unit::TestCase def setup @@ -527,7 +527,7 @@ class NewRenderTest < Test::Unit::TestCase end def test_render_file_not_using_full_path - get :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 @@ -540,7 +540,7 @@ class NewRenderTest < Test::Unit::TestCase get :render_file_with_locals assert_equal "The secret is in the sauce\n", @response.body end - + def test_render_file_from_template get :render_file_from_template assert_equal "The secret is in the sauce\n", @response.body @@ -661,7 +661,7 @@ EOS assert_not_deprecated { get :hello_in_a_string } assert_equal "How's there? goodbyeHello: davidHello: marygoodbye\n", @response.body end - + def test_render_to_string_doesnt_break_assigns get :render_to_string_with_assigns assert_equal "i'm before the render", assigns(:before) @@ -672,12 +672,12 @@ EOS get :render_to_string_with_partial assert_equal "only partial", assigns(:partial_only) assert_equal "Hello: david", assigns(:partial_with_locals) - end + end def test_bad_render_to_string_still_throws_exception assert_raises(ActionView::MissingTemplate) { get :render_to_string_with_exception } end - + def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns assert_nothing_raised { get :render_to_string_with_caught_exception } assert_equal "i'm before the render", assigns(:before) @@ -715,7 +715,7 @@ EOS def test_render_and_redirect assert_raises(ActionController::DoubleRenderError) { get :render_and_redirect } end - + # specify the one exception to double render rule - render_to_string followed by render def test_render_to_string_and_render get :render_to_string_and_render @@ -736,7 +736,7 @@ EOS get :partials_list assert_equal "goodbyeHello: davidHello: marygoodbye\n", @response.body end - + def test_partial_with_locals get :partial_with_locals assert_equal "Hello: david", @response.body @@ -758,17 +758,17 @@ EOS get :partial_collection assert_equal "Hello: davidHello: mary", @response.body end - + def test_partial_collection_with_counter get :partial_collection_with_counter assert_equal "david0mary1", @response.body end - + def test_partial_collection_with_locals get :partial_collection_with_locals assert_equal "Bonjour: davidBonjour: mary", @response.body end - + def test_partial_collection_with_spacer get :partial_collection_with_spacer assert_equal "Hello: davidonly partialHello: mary", @response.body @@ -793,12 +793,12 @@ EOS get :partial_with_hash_object assert_equal "Sam\nmaS\n", @response.body end - + def test_hash_partial_collection get :partial_hash_collection assert_equal "Pratik\nkitarP\nAmy\nymA\n", @response.body end - + def test_partial_hash_collection_with_locals get :partial_hash_collection_with_locals assert_equal "Hola: PratikHola: Amy", @response.body @@ -808,25 +808,25 @@ EOS get :partial_with_implicit_local_assignment assert_equal "Hello: Marcel", @response.body end - + def test_render_missing_partial_template assert_raises(ActionView::MissingTemplate) do get :missing_partial end end - + def test_render_text_with_assigns get :render_text_with_assigns assert_equal "world", assigns["hello"] end - + def test_update_page get :update_page assert_template nil assert_equal 'text/javascript; charset=utf-8', @response.headers['type'] assert_equal 2, @response.body.split($/).length end - + def test_update_page_with_instance_variables get :update_page_with_instance_variables assert_template nil @@ -834,7 +834,7 @@ EOS assert_match /balance/, @response.body assert_match /\$37/, @response.body end - + def test_yield_content_for assert_not_deprecated { get :yield_content_for } assert_equal "<title>Putting stuff in the title!</title>\n\nGreat stuff!\n", @response.body @@ -906,12 +906,12 @@ EOS get :render_with_location assert_equal "http://example.com", @response.headers["Location"] end - + def test_rendering_xml_should_call_to_xml_if_possible get :render_with_to_xml assert_equal "<i-am-xml/>", @response.body end - + def test_rendering_with_object_location_should_set_header_with_url_for ActionController::Routing::Routes.draw do |map| map.resources :customers @@ -941,5 +941,4 @@ EOS get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body end - end diff --git a/actionpack/test/controller/polymorphic_routes_test.rb b/actionpack/test/controller/polymorphic_routes_test.rb index 4ec0d3cd4e..3f52526f08 100644 --- a/actionpack/test/controller/polymorphic_routes_test.rb +++ b/actionpack/test/controller/polymorphic_routes_test.rb @@ -118,6 +118,39 @@ uses_mocha 'polymorphic URL helpers' do polymorphic_url([:site, :admin, @article, @response, @tag]) end + def test_nesting_with_array_ending_in_singleton_resource + expects(:article_response_url).with(@article) + polymorphic_url([@article, :response]) + end + + def test_nesting_with_array_containing_singleton_resource + @tag = Tag.new + @tag.save + expects(:article_response_tag_url).with(@article, @tag) + polymorphic_url([@article, :response, @tag]) + end + + def test_nesting_with_array_containing_namespace_and_singleton_resource + @tag = Tag.new + @tag.save + expects(:admin_article_response_tag_url).with(@article, @tag) + polymorphic_url([:admin, @article, :response, @tag]) + end + + def test_nesting_with_array_containing_singleton_resource_and_format + @tag = Tag.new + @tag.save + expects(:formatted_article_response_tag_url).with(@article, @tag, :pdf) + formatted_polymorphic_url([@article, :response, @tag, :pdf]) + end + + def test_nesting_with_array_containing_singleton_resource_and_format_option + @tag = Tag.new + @tag.save + expects(:article_response_tag_url).with(@article, @tag, :pdf) + polymorphic_url([@article, :response, @tag], :format => :pdf) + end + # TODO: Needs to be updated to correctly know about whether the object is in a hash or not def xtest_with_hash expects(:article_url).with(@article) diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 066fa6acd4..65862c6b14 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -23,11 +23,11 @@ class TestController < ActionController::Base def render_hello_world_with_forward_slash render :template => "/test/hello_world" end - + def render_template_in_top_directory render :template => 'shared' end - + def render_template_in_top_directory_with_slash render :template => '/shared' end @@ -86,7 +86,7 @@ class TestController < ActionController::Base def render_nothing_with_appendix render :text => "appended" end - + def render_invalid_args render("test/hello") end @@ -171,7 +171,7 @@ class TestController < ActionController::Base def partial_dot_html render :partial => 'partial.html.erb' end - + def partial_as_rjs render :update do |page| page.replace :foo, :partial => 'partial' @@ -217,8 +217,8 @@ class TestController < ActionController::Base end end -TestController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] -Fun::GamesController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +TestController.view_paths = [FIXTURE_LOAD_PATH] +Fun::GamesController.view_paths = [FIXTURE_LOAD_PATH] class RenderTest < Test::Unit::TestCase def setup @@ -251,13 +251,13 @@ class RenderTest < Test::Unit::TestCase get :render_hello_world_with_forward_slash assert_template "test/hello_world" end - + def test_render_in_top_directory get :render_template_in_top_directory assert_template "shared" assert_equal "Elastica", @response.body end - + def test_render_in_top_directory_with_slash get :render_template_in_top_directory_with_slash assert_template "shared" @@ -336,11 +336,11 @@ class RenderTest < Test::Unit::TestCase assert_response 200 assert_equal 'appended', @response.body end - + def test_attempt_to_render_with_invalid_arguments assert_raises(ActionController::RenderError) { get :render_invalid_args } end - + def test_attempt_to_access_object_method assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone } end @@ -467,17 +467,17 @@ class RenderTest < Test::Unit::TestCase get :formatted_html_erb assert_equal 'formatted html erb', @response.body end - + def test_should_render_formatted_xml_erb_template get :formatted_xml_erb, :format => :xml assert_equal '<test>passed formatted xml erb</test>', @response.body end - + def test_should_render_formatted_html_erb_template get :formatted_xml_erb assert_equal '<test>passed formatted html erb</test>', @response.body end - + def test_should_render_formatted_html_erb_template_with_faulty_accepts_header @request.env["HTTP_ACCEPT"] = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*" get :formatted_xml_erb @@ -520,7 +520,6 @@ class RenderTest < Test::Unit::TestCase end protected - def etag_for(text) %("#{Digest::MD5.hexdigest(text)}") end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 406825fe59..ddec51d173 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -1,13 +1,11 @@ require 'abstract_unit' - module TestFileUtils def file_name() File.basename(__FILE__) end def file_path() File.expand_path(__FILE__) end def file_data() File.open(file_path, 'rb') { |f| f.read } end end - class SendFileController < ActionController::Base include TestFileUtils layout "layouts/standard" # to make sure layouts don't interfere @@ -21,7 +19,7 @@ class SendFileController < ActionController::Base def rescue_action(e) raise end end -SendFileController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] +SendFileController.view_paths = [FIXTURE_LOAD_PATH] class SendFileTest < Test::Unit::TestCase include TestFileUtils diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index 1b4c1fae2f..9401c87d10 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -1,9 +1,7 @@ require 'abstract_unit' class ViewLoadPathsTest < Test::Unit::TestCase - LOAD_PATH_ROOT = File.join(File.dirname(__FILE__), '..', 'fixtures') - - ActionController::Base.view_paths = [LOAD_PATH_ROOT] + ActionController::Base.view_paths = [FIXTURE_LOAD_PATH] class TestController < ActionController::Base def self.controller_path() "test" end @@ -16,7 +14,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase private def add_view_path - prepend_view_path "#{LOAD_PATH_ROOT}/override" + prepend_view_path "#{FIXTURE_LOAD_PATH}/override" end end @@ -47,35 +45,35 @@ class ViewLoadPathsTest < Test::Unit::TestCase end def test_template_load_path_was_set_correctly - assert_equal [ LOAD_PATH_ROOT ], @controller.view_paths.map(&:to_s) + assert_equal [FIXTURE_LOAD_PATH], @controller.view_paths end def test_controller_appends_view_path_correctly @controller.append_view_path 'foo' - assert_equal [LOAD_PATH_ROOT, 'foo'], @controller.view_paths.map(&:to_s) + assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths @controller.append_view_path(%w(bar baz)) - assert_equal [LOAD_PATH_ROOT, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s) + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths - @controller.append_view_path(LOAD_PATH_ROOT) - assert_equal ['foo', 'bar', 'baz', LOAD_PATH_ROOT], @controller.view_paths.map(&:to_s) + @controller.append_view_path(FIXTURE_LOAD_PATH) + assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths - @controller.append_view_path([LOAD_PATH_ROOT]) - assert_equal ['foo', 'bar', 'baz', LOAD_PATH_ROOT], @controller.view_paths.map(&:to_s) + @controller.append_view_path([FIXTURE_LOAD_PATH]) + assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths end def test_controller_prepends_view_path_correctly @controller.prepend_view_path 'baz' - assert_equal ['baz', LOAD_PATH_ROOT], @controller.view_paths.map(&:to_s) + assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths @controller.prepend_view_path(%w(foo bar)) - assert_equal ['foo', 'bar', 'baz', LOAD_PATH_ROOT], @controller.view_paths.map(&:to_s) + assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths - @controller.prepend_view_path(LOAD_PATH_ROOT) - assert_equal [LOAD_PATH_ROOT, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s) + @controller.prepend_view_path(FIXTURE_LOAD_PATH) + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths - @controller.prepend_view_path([LOAD_PATH_ROOT]) - assert_equal [LOAD_PATH_ROOT, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s) + @controller.prepend_view_path([FIXTURE_LOAD_PATH]) + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths end def test_template_appends_view_path_correctly @@ -83,10 +81,10 @@ class ViewLoadPathsTest < Test::Unit::TestCase class_view_paths = TestController.view_paths @controller.append_view_path 'foo' - assert_equal [LOAD_PATH_ROOT, 'foo'], @controller.view_paths.map(&:to_s) + assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths @controller.append_view_path(%w(bar baz)) - assert_equal [LOAD_PATH_ROOT, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s) + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths assert_equal class_view_paths, TestController.view_paths end @@ -95,10 +93,10 @@ class ViewLoadPathsTest < Test::Unit::TestCase class_view_paths = TestController.view_paths @controller.prepend_view_path 'baz' - assert_equal ['baz', LOAD_PATH_ROOT], @controller.view_paths.map(&:to_s) + assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths @controller.prepend_view_path(%w(foo bar)) - assert_equal ['foo', 'bar', 'baz', LOAD_PATH_ROOT], @controller.view_paths.map(&:to_s) + assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths assert_equal class_view_paths, TestController.view_paths end @@ -109,7 +107,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase end def test_view_paths_override - TestController.prepend_view_path "#{LOAD_PATH_ROOT}/override" + TestController.prepend_view_path "#{FIXTURE_LOAD_PATH}/override" get :hello_world assert_response :success assert_equal "Hello overridden world!", @response.body @@ -117,7 +115,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase def test_view_paths_override_for_layouts_in_controllers_with_a_module @controller = Test::SubController.new - Test::SubController.view_paths = [ "#{LOAD_PATH_ROOT}/override", LOAD_PATH_ROOT, "#{LOAD_PATH_ROOT}/override2" ] + Test::SubController.view_paths = [ "#{FIXTURE_LOAD_PATH}/override", FIXTURE_LOAD_PATH, "#{FIXTURE_LOAD_PATH}/override2" ] get :hello_world assert_response :success assert_equal "layout: Hello overridden world!", @response.body @@ -140,13 +138,13 @@ class ViewLoadPathsTest < Test::Unit::TestCase A.view_paths = ['a/path'] - assert_equal ['a/path'], A.view_paths.map(&:to_s) + assert_equal ['a/path'], A.view_paths assert_equal A.view_paths, B.view_paths assert_equal original_load_paths, C.view_paths C.view_paths = [] assert_nothing_raised { C.view_paths << 'c/path' } - assert_equal ['c/path'], C.view_paths.map(&:to_s) + assert_equal ['c/path'], C.view_paths end def test_find_template_file_for_path diff --git a/actionpack/test/fixtures/test/hello.builder b/actionpack/test/fixtures/test/hello.builder index 82a4a310d3..86a8bb3d7b 100644 --- a/actionpack/test/fixtures/test/hello.builder +++ b/actionpack/test/fixtures/test/hello.builder @@ -1,4 +1,4 @@ xml.html do xml.p "Hello #{@name}" - xml << render_file("test/greeting") + xml << render("test/greeting") end
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/non_erb_block_content_for.builder b/actionpack/test/fixtures/test/non_erb_block_content_for.builder index 6ff6db0f95..a94643561c 100644 --- a/actionpack/test/fixtures/test/non_erb_block_content_for.builder +++ b/actionpack/test/fixtures/test/non_erb_block_content_for.builder @@ -1,4 +1,4 @@ content_for :title do 'Putting stuff in the title!' end -xml << "\nGreat stuff!"
\ No newline at end of file +xml << "\nGreat stuff!" diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 11b3bdb3fa..3faa363459 100755 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -1155,6 +1155,30 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, date_select("post", "written_on", {}, :class => 'selector') end + def test_date_select_with_html_options_within_fields_for + @post = Post.new + @post.written_on = Date.new(2004, 6, 15) + + fields_for :post, @post do |f| + concat f.date_select(:written_on, {}, :class => 'selector') + end + + expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n} + expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n} + expected << "</select>\n" + + expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="selector">\n} + expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n} + expected << "</select>\n" + + expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="selector">\n} + expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n} + + expected << "</select>\n" + + assert_dom_equal expected, output_buffer + end + def test_time_select @post = Post.new @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) @@ -1216,6 +1240,29 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, time_select("post", "written_on", {}, :class => 'selector') end + def test_time_select_with_html_options_within_fields_for + @post = Post.new + @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) + + fields_for :post, @post do |f| + concat f.time_select(:written_on, {}, :class => 'selector') + end + + expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n} + expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n} + expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n} + + expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="selector">\n) + 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) } + expected << "</select>\n" + expected << " : " + expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="selector">\n) + 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) } + expected << "</select>\n" + + assert_dom_equal expected, output_buffer + end + def test_datetime_select @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 16, 35) @@ -1281,21 +1328,21 @@ class DateHelperTest < ActionView::TestCase end end - def test_datetime_select_within_fields_for + def test_datetime_select_with_html_options_within_fields_for @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 16, 35) fields_for :post, @post do |f| - concat f.datetime_select(:updated_at) + concat f.datetime_select(:updated_at, {}, :class => 'selector') end - expected = "<select id='post_updated_at_1i' name='post[updated_at(1i)]'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n" - expected << "<select id='post_updated_at_2i' name='post[updated_at(2i)]'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n" - expected << "<select id='post_updated_at_3i' name='post[updated_at(3i)]'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n" - expected << " — <select id='post_updated_at_4i' name='post[updated_at(4i)]'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option selected='selected' value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n</select>\n" - expected << " : <select id='post_updated_at_5i' name='post[updated_at(5i)]'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n<option value='32'>32</option>\n<option value='33'>33</option>\n<option value='34'>34</option>\n<option selected='selected' value='35'>35</option>\n<option value='36'>36</option>\n<option value='37'>37</option>\n<option value='38'>38</option>\n<option value='39'>39</option>\n<option value='40'>40</option>\n<option value='41'>41</option>\n<option value='42'>42</option>\n<option value='43'>43</option>\n<option value='44'>44</option>\n<option value='45'>45</option>\n<option value='46'>46</option>\n<option value='47'>47</option>\n<option value='48'>48</option>\n<option value='49'>49</option>\n<option value='50'>50</option>\n<option value='51'>51</option>\n<option value='52'>52</option>\n<option value='53'>53</option>\n<option value='54'>54</option>\n<option value='55'>55</option>\n<option value='56'>56</option>\n<option value='57'>57</option>\n<option value='58'>58</option>\n<option value='59'>59</option>\n</select>\n" + expected = "<select id='post_updated_at_1i' name='post[updated_at(1i)]' class='selector'>\n<option value='1999'>1999</option>\n<option value='2000'>2000</option>\n<option value='2001'>2001</option>\n<option value='2002'>2002</option>\n<option value='2003'>2003</option>\n<option selected='selected' value='2004'>2004</option>\n<option value='2005'>2005</option>\n<option value='2006'>2006</option>\n<option value='2007'>2007</option>\n<option value='2008'>2008</option>\n<option value='2009'>2009</option>\n</select>\n" + expected << "<select id='post_updated_at_2i' name='post[updated_at(2i)]' class='selector'>\n<option value='1'>January</option>\n<option value='2'>February</option>\n<option value='3'>March</option>\n<option value='4'>April</option>\n<option value='5'>May</option>\n<option selected='selected' value='6'>June</option>\n<option value='7'>July</option>\n<option value='8'>August</option>\n<option value='9'>September</option>\n<option value='10'>October</option>\n<option value='11'>November</option>\n<option value='12'>December</option>\n</select>\n" + expected << "<select id='post_updated_at_3i' name='post[updated_at(3i)]' class='selector'>\n<option value='1'>1</option>\n<option value='2'>2</option>\n<option value='3'>3</option>\n<option value='4'>4</option>\n<option value='5'>5</option>\n<option value='6'>6</option>\n<option value='7'>7</option>\n<option value='8'>8</option>\n<option value='9'>9</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option selected='selected' value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n</select>\n" + expected << " — <select id='post_updated_at_4i' name='post[updated_at(4i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option selected='selected' value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n</select>\n" + expected << " : <select id='post_updated_at_5i' name='post[updated_at(5i)]' class='selector'>\n<option value='00'>00</option>\n<option value='01'>01</option>\n<option value='02'>02</option>\n<option value='03'>03</option>\n<option value='04'>04</option>\n<option value='05'>05</option>\n<option value='06'>06</option>\n<option value='07'>07</option>\n<option value='08'>08</option>\n<option value='09'>09</option>\n<option value='10'>10</option>\n<option value='11'>11</option>\n<option value='12'>12</option>\n<option value='13'>13</option>\n<option value='14'>14</option>\n<option value='15'>15</option>\n<option value='16'>16</option>\n<option value='17'>17</option>\n<option value='18'>18</option>\n<option value='19'>19</option>\n<option value='20'>20</option>\n<option value='21'>21</option>\n<option value='22'>22</option>\n<option value='23'>23</option>\n<option value='24'>24</option>\n<option value='25'>25</option>\n<option value='26'>26</option>\n<option value='27'>27</option>\n<option value='28'>28</option>\n<option value='29'>29</option>\n<option value='30'>30</option>\n<option value='31'>31</option>\n<option value='32'>32</option>\n<option value='33'>33</option>\n<option value='34'>34</option>\n<option selected='selected' value='35'>35</option>\n<option value='36'>36</option>\n<option value='37'>37</option>\n<option value='38'>38</option>\n<option value='39'>39</option>\n<option value='40'>40</option>\n<option value='41'>41</option>\n<option value='42'>42</option>\n<option value='43'>43</option>\n<option value='44'>44</option>\n<option value='45'>45</option>\n<option value='46'>46</option>\n<option value='47'>47</option>\n<option value='48'>48</option>\n<option value='49'>49</option>\n<option value='50'>50</option>\n<option value='51'>51</option>\n<option value='52'>52</option>\n<option value='53'>53</option>\n<option value='54'>54</option>\n<option value='55'>55</option>\n<option value='56'>56</option>\n<option value='57'>57</option>\n<option value='58'>58</option>\n<option value='59'>59</option>\n</select>\n" - assert_dom_equal(expected, output_buffer) + assert_dom_equal expected, output_buffer end def test_date_select_with_zero_value_and_no_start_year diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 3f89a5e426..1db3a4d7d8 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -1300,6 +1300,24 @@ COUNTRIES html end + uses_mocha "time_zone_select_with_priority_zones_as_regexp" do + def test_time_zone_select_with_priority_zones_as_regexp + @firm = Firm.new("D") + MockTimeZone.any_instance.stubs(:=~).returns(true,false,false,true,false) + + html = time_zone_select("firm", "time_zone", /A|D/) + assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" + + "<option value=\"A\">A</option>\n" + + "<option value=\"D\" selected=\"selected\">D</option>" + + "<option value=\"\" disabled=\"disabled\">-------------</option>\n" + + "<option value=\"B\">B</option>\n" + + "<option value=\"C\">C</option>\n" + + "<option value=\"E\">E</option>" + + "</select>", + html + end + end + def test_time_zone_select_with_default_time_zone_and_nil_value @firm = Firm.new() @firm.time_zone = nil diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 47b3605849..eabbe9c8a0 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -35,21 +35,23 @@ class FormTagHelperTest < ActionView::TestCase expected = %(<form action="http://www.example.com" method="post"><div style='margin:0;padding:0'><input type="hidden" name="_method" value="put" /></div>) assert_dom_equal expected, actual end - + def test_form_tag_with_method_delete actual = form_tag({}, { :method => :delete }) expected = %(<form action="http://www.example.com" method="post"><div style='margin:0;padding:0'><input type="hidden" name="_method" value="delete" /></div>) assert_dom_equal expected, actual end - def test_form_tag_with_block + def test_form_tag_with_block_in_erb + __in_erb_template = '' form_tag("http://example.com") { concat "Hello world!" } expected = %(<form action="http://example.com" method="post">Hello world!</form>) assert_dom_equal expected, output_buffer end - def test_form_tag_with_block_and_method + def test_form_tag_with_block_and_method_in_erb + __in_erb_template = '' form_tag("http://example.com", :method => :put) { concat "Hello world!" } expected = %(<form action="http://example.com" method="post"><div style='margin:0;padding:0'><input type="hidden" name="_method" value="put" /></div>Hello world!</form>) @@ -88,11 +90,11 @@ class FormTagHelperTest < ActionView::TestCase actual = radio_button_tag("gender", "m") + radio_button_tag("gender", "f") expected = %(<input id="gender_m" name="gender" type="radio" value="m" /><input id="gender_f" name="gender" type="radio" value="f" />) assert_dom_equal expected, actual - + actual = radio_button_tag("opinion", "-1") + radio_button_tag("opinion", "1") expected = %(<input id="opinion_-1" name="opinion" type="radio" value="-1" /><input id="opinion_1" name="opinion" type="radio" value="1" />) assert_dom_equal expected, actual - + actual = radio_button_tag("person[gender]", "m") expected = %(<input id="person_gender_m" name="person[gender]" type="radio" value="m" />) assert_dom_equal expected, actual @@ -103,13 +105,13 @@ class FormTagHelperTest < ActionView::TestCase expected = %(<select id="people" name="people"><option>david</option></select>) assert_dom_equal expected, actual end - + def test_select_tag_with_multiple actual = select_tag "colors", "<option>Red</option><option>Blue</option><option>Green</option>", :multiple => :true expected = %(<select id="colors" multiple="multiple" name="colors"><option>Red</option><option>Blue</option><option>Green</option></select>) assert_dom_equal expected, actual end - + def test_select_tag_disabled actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :disabled => :true expected = %(<select id="places" disabled="disabled" name="places"><option>Home</option><option>Work</option><option>Pub</option></select>) @@ -145,37 +147,37 @@ class FormTagHelperTest < ActionView::TestCase expected = %(<input class="admin" id="title" name="title" type="text" value="Hello!" />) assert_dom_equal expected, actual end - + def test_text_field_tag_size_symbol actual = text_field_tag "title", "Hello!", :size => 75 expected = %(<input id="title" name="title" size="75" type="text" value="Hello!" />) assert_dom_equal expected, actual end - + def test_text_field_tag_size_string actual = text_field_tag "title", "Hello!", "size" => "75" expected = %(<input id="title" name="title" size="75" type="text" value="Hello!" />) assert_dom_equal expected, actual end - + def test_text_field_tag_maxlength_symbol actual = text_field_tag "title", "Hello!", :maxlength => 75 expected = %(<input id="title" name="title" maxlength="75" type="text" value="Hello!" />) assert_dom_equal expected, actual end - + def test_text_field_tag_maxlength_string actual = text_field_tag "title", "Hello!", "maxlength" => "75" expected = %(<input id="title" name="title" maxlength="75" type="text" value="Hello!" />) assert_dom_equal expected, actual end - + def test_text_field_disabled actual = text_field_tag "title", "Hello!", :disabled => :true expected = %(<input id="title" name="title" disabled="disabled" type="text" value="Hello!" />) assert_dom_equal expected, actual end - + def test_text_field_tag_with_multiple_options actual = text_field_tag "title", "Hello!", :size => 70, :maxlength => 80 expected = %(<input id="title" name="title" size="70" maxlength="80" type="text" value="Hello!" />) @@ -226,12 +228,13 @@ class FormTagHelperTest < ActionView::TestCase submit_tag("Save", :confirm => "Are you sure?") ) end - + def test_pass assert_equal 1, 1 end - def test_field_set_tag + def test_field_set_tag_in_erb + __in_erb_template = '' field_set_tag("Your details") { concat "Hello world!" } expected = %(<fieldset><legend>Your details</legend>Hello world!</fieldset>) diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 8c649ea544..d6d398d224 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -48,7 +48,7 @@ class JavaScriptHelperTest < ActionView::TestCase end def test_link_to_function_with_href - assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>), + assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>), link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/') end @@ -95,12 +95,14 @@ class JavaScriptHelperTest < ActionView::TestCase javascript_tag("alert('hello')", :id => "the_js_tag") end - def test_javascript_tag_with_block + def test_javascript_tag_with_block_in_erb + __in_erb_template = '' javascript_tag { concat "alert('hello')" } assert_dom_equal "<script type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>", output_buffer end - def test_javascript_tag_with_block_and_options + def test_javascript_tag_with_block_and_options_in_erb + __in_erb_template = '' javascript_tag(:id => "the_js_tag") { concat "alert('hello')" } assert_dom_equal "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('hello')\n//]]>\n</script>", output_buffer end diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index a5be0d2789..60b83b476d 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -54,7 +54,7 @@ class PrototypeHelperBaseTest < ActionView::TestCase end def create_generator - block = Proc.new { |*args| yield *args if block_given? } + block = Proc.new { |*args| yield *args if block_given? } JavaScriptGenerator.new self, &block end end @@ -70,7 +70,7 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>), link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }}, { :class => "fine" }) assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>), - link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot" }) + link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot" }) assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>), link_to_remote("Remote outauthor", :success => "alert(request.responseText)", :url => { :action => "whatnot" }) assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>), @@ -78,12 +78,12 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>), link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) end - + def test_link_to_remote_html_options assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>), link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } }) end - + def test_link_to_remote_url_quote_escaping assert_dom_equal %(<a href="#" onclick="new Ajax.Request('http://www.example.com/whatnot\\\'s', {asynchronous:true, evalScripts:true}); return false;">Remote</a>), link_to_remote("Remote", { :url => { :action => "whatnot's" } }) @@ -93,14 +93,14 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_dom_equal %(<script type="text/javascript">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Updater('schremser_bier', 'http://www.example.com/mehr_bier', {asynchronous:true, evalScripts:true})}, 10)\n//]]>\n</script>), periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" }) end - + def test_periodically_call_remote_with_frequency assert_dom_equal( "<script type=\"text/javascript\">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true})}, 2)\n//]]>\n</script>", periodically_call_remote(:frequency => 2) ) end - + def test_form_remote_tag assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) @@ -117,21 +117,22 @@ class PrototypeHelperTest < PrototypeHelperBaseTest form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put }) end - def test_form_remote_tag_with_block + def test_form_remote_tag_with_block_in_erb + __in_erb_template = '' form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" } assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">Hello world!</form>), output_buffer end def test_remote_form_for_with_record_identification_with_new_record remote_form_for(@record, {:html => { :id => 'create-author' }}) {} - + expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' id='create-author' method='post'></form>) assert_dom_equal expected, output_buffer end def test_remote_form_for_with_record_identification_without_html_options remote_form_for(@record) {} - + expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' method='post' id='new_author'></form>) assert_dom_equal expected, output_buffer end @@ -139,23 +140,23 @@ class PrototypeHelperTest < PrototypeHelperBaseTest def test_remote_form_for_with_record_identification_with_existing_record @record.save remote_form_for(@record) {} - + expected = %(<form action='#{author_path(@record)}' id='edit_author_1' method='post' onsubmit="new Ajax.Request('#{author_path(@record)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_author'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div></form>) assert_dom_equal expected, output_buffer end def test_remote_form_for_with_new_object_in_list remote_form_for([@author, @article]) {} - + expected = %(<form action='#{author_articles_path(@author)}' onsubmit="new Ajax.Request('#{author_articles_path(@author)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_article' method='post' id='new_article'></form>) assert_dom_equal expected, output_buffer end - + def test_remote_form_for_with_existing_object_in_list @author.save @article.save remote_form_for([@author, @article]) {} - + expected = %(<form action='#{author_article_path(@author, @article)}' id='edit_article_1' method='post' onsubmit="new Ajax.Request('#{author_article_path(@author, @article)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_article'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div></form>) assert_dom_equal expected, output_buffer end @@ -172,18 +173,18 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">), form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();") end - + #HTTP status codes 200 up to 599 have callbacks #these should work 100.upto(599) do |callback| assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") end - + #test 200 and 404 assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, parameters:Form.serialize(this)}); return false;">), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();") - + #these shouldn't 1.upto(99) do |callback| assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">), @@ -193,44 +194,44 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") end - + #test ultimate combo assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, onComplete:function(request){c();}, onFailure:function(request){f();}, onLoading:function(request){c1()}, onSuccess:function(request){s()}, parameters:Form.serialize(this)}); return false;\">), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();") - + end - + def test_submit_to_remote assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)}); return false;\" type=\"button\" value=\"1000000\" />), submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle") end - + def test_observe_field assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/reorder_if_empty', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>), observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" }) end - + def test_observe_field_using_with_option expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(value)})})\n//]]>\n</script>) assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id') assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)") end - + def test_observe_field_using_json_in_with_option expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:{'id':value}})})\n//]]>\n</script>) - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}") + assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}") end - + def test_observe_field_using_function_for_callback assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {alert('Element changed')})\n//]]>\n</script>), observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')") end - + def test_observe_form assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {new Ajax.Request('http://www.example.com/cart_changed', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>), observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" }) end - + def test_observe_form_using_function_for_callback assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {alert('Form changed')})\n//]]>\n</script>), observe_form("cart", :frequency => 2, :function => "alert('Form changed')") @@ -245,7 +246,7 @@ class PrototypeHelperTest < PrototypeHelperBaseTest block = Proc.new { |page| page.replace_html('foo', 'bar') } assert_equal create_generator(&block).to_s, update_page(&block) end - + def test_update_page_tag block = Proc.new { |page| page.replace_html('foo', 'bar') } assert_equal javascript_tag(create_generator(&block).to_s), update_page_tag(&block) @@ -261,15 +262,15 @@ class PrototypeHelperTest < PrototypeHelperBaseTest def author_path(record) "/authors/#{record.id}" end - + def authors_path "/authors" end - + def author_articles_path(author) "/authors/#{author.id}/articles" end - + def author_article_path(author, article) "/authors/#{author.id}/articles/#{article.id}" end @@ -280,7 +281,7 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest super @generator = create_generator end - + def test_insert_html_with_string assert_equal 'new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");', @generator.insert_html(:top, 'element', '<p>This is a test</p>') @@ -291,56 +292,56 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest assert_equal 'new Insertion.After("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");', @generator.insert_html(:after, 'element', '<p>This is a test</p>') end - + def test_replace_html_with_string assert_equal 'Element.update("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");', @generator.replace_html('element', '<p>This is a test</p>') end - + def test_replace_element_with_string assert_equal 'Element.replace("element", "\\u003Cdiv id=\"element\"\\u003E\\u003Cp\\u003EThis is a test\\u003C/p\\u003E\\u003C/div\\u003E");', @generator.replace('element', '<div id="element"><p>This is a test</p></div>') end - + def test_remove assert_equal 'Element.remove("foo");', @generator.remove('foo') assert_equal '["foo", "bar", "baz"].each(Element.remove);', @generator.remove('foo', 'bar', 'baz') end - + def test_show assert_equal 'Element.show("foo");', @generator.show('foo') assert_equal '["foo", "bar", "baz"].each(Element.show);', - @generator.show('foo', 'bar', 'baz') + @generator.show('foo', 'bar', 'baz') end - + def test_hide assert_equal 'Element.hide("foo");', @generator.hide('foo') assert_equal '["foo", "bar", "baz"].each(Element.hide);', - @generator.hide('foo', 'bar', 'baz') + @generator.hide('foo', 'bar', 'baz') end - + def test_toggle assert_equal 'Element.toggle("foo");', @generator.toggle('foo') assert_equal '["foo", "bar", "baz"].each(Element.toggle);', - @generator.toggle('foo', 'bar', 'baz') + @generator.toggle('foo', 'bar', 'baz') end - + def test_alert assert_equal 'alert("hello");', @generator.alert('hello') end - + def test_redirect_to assert_equal 'window.location.href = "http://www.example.com/welcome";', @generator.redirect_to(:action => 'welcome') assert_equal 'window.location.href = "http://www.example.com/welcome?a=b&c=d";', @generator.redirect_to("http://www.example.com/welcome?a=b&c=d") end - + def test_reload assert_equal 'window.location.reload();', @generator.reload @@ -350,16 +351,16 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest @generator.delay(20) do @generator.hide('foo') end - + assert_equal "setTimeout(function() {\n;\nElement.hide(\"foo\");\n}, 20000);", @generator.to_s end - + def test_to_s @generator.insert_html(:top, 'element', '<p>This is a test</p>') @generator.insert_html(:bottom, 'element', '<p>This is a test</p>') @generator.remove('foo', 'bar') @generator.replace_html('baz', '<p>This is a test</p>') - + assert_equal <<-EOS.chomp, @generator.to_s new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); new Insertion.Bottom("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); @@ -381,12 +382,12 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); @generator['hello'].hide assert_equal %($("hello").hide();), @generator.to_s end - + def test_element_proxy_variable_access @generator['hello']['style'] assert_equal %($("hello").style;), @generator.to_s end - + def test_element_proxy_variable_access_with_assignment @generator['hello']['style']['color'] = 'red' assert_equal %($("hello").style.color = "red";), @generator.to_s @@ -401,7 +402,7 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); @generator['hello'].hide("first").clean_whitespace assert_equal %($("hello").hide("first").cleanWhitespace();), @generator.to_s end - + def test_select_access assert_equal %($$("div.hello");), @generator.select('div.hello') end @@ -410,29 +411,29 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); @generator.select('p.welcome b').first.hide assert_equal %($$("p.welcome b").first().hide();), @generator.to_s end - + def test_visual_effect - assert_equal %(new Effect.Puff("blah",{});), + assert_equal %(new Effect.Puff("blah",{});), @generator.visual_effect(:puff,'blah') - end - + end + def test_visual_effect_toggle - assert_equal %(Effect.toggle("blah",'appear',{});), + assert_equal %(Effect.toggle("blah",'appear',{});), @generator.visual_effect(:toggle_appear,'blah') end - + def test_sortable - assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});), + assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});), @generator.sortable('blah', :url => { :action => "order" }) end - + def test_draggable - assert_equal %(new Draggable("blah", {});), + assert_equal %(new Draggable("blah", {});), @generator.draggable('blah') end - + def test_drop_receiving - assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});), + assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});), @generator.drop_receiving('blah', :url => { :action => "order" }) end @@ -534,7 +535,7 @@ return array.reverse(); }); EOS end - + def test_collection_proxy_with_find_all @generator.select('p').find_all 'a' do |value, index| @generator << '(value.className == "welcome")' @@ -546,7 +547,7 @@ return (value.className == "welcome"); }); EOS end - + def test_collection_proxy_with_in_groups_of @generator.select('p').in_groups_of('a', 3) @generator.select('p').in_groups_of('a', 3, 'x') @@ -555,13 +556,13 @@ var a = $$("p").inGroupsOf(3); var a = $$("p").inGroupsOf(3, "x"); EOS end - + def test_collection_proxy_with_each_slice @generator.select('p').each_slice('a', 3) @generator.select('p').each_slice('a', 3) do |group, index| group.reverse end - + assert_equal <<-EOS.strip, @generator.to_s var a = $$("p").eachSlice(3); var a = $$("p").eachSlice(3, function(value, index) { @@ -569,7 +570,7 @@ return value.reverse(); }); EOS end - + def test_debug_rjs ActionView::Base.debug_rjs = true @generator['welcome'].replace_html 'Welcome' @@ -577,7 +578,7 @@ return value.reverse(); ensure ActionView::Base.debug_rjs = false end - + def test_literal literal = @generator.literal("function() {}") assert_equal "function() {}", literal.to_json @@ -588,7 +589,7 @@ return value.reverse(); @generator.form.focus('my_field') assert_equal "Form.focus(\"my_field\");", @generator.to_s end - + def test_call_with_block @generator.call(:before) @generator.call(:my_method) do |p| @@ -601,7 +602,7 @@ return value.reverse(); end assert_equal "before();\nmy_method(function() { $(\"one\").show();\n$(\"two\").hide(); });\nin_between();\nmy_method_with_arguments(true, \"hello\", function() { $(\"three\").visualEffect(\"highlight\"); });", @generator.to_s end - + def test_class_proxy_call_with_block @generator.my_object.my_method do |p| p[:one].show diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 441dc6b720..34a200b933 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -2,7 +2,7 @@ require 'abstract_unit' class Post def id - 45 + 45 end def body "What a wonderful world!" @@ -15,35 +15,36 @@ class RecordTagHelperTest < ActionView::TestCase def setup @post = Post.new end - + def test_content_tag_for expected = %(<li class="post bar" id="post_45"></li>) actual = content_tag_for(:li, @post, :class => 'bar') { } assert_dom_equal expected, actual end - + def test_content_tag_for_prefix expected = %(<ul class="post" id="archived_post_45"></ul>) actual = content_tag_for(:ul, @post, :archived) { } - assert_dom_equal expected, actual + assert_dom_equal expected, actual end - + def test_content_tag_for_with_extra_html_tags expected = %(<tr class="post bar" id="post_45" style='background-color: #f0f0f0'></tr>) actual = content_tag_for(:tr, @post, {:class => "bar", :style => "background-color: #f0f0f0"}) { } - assert_dom_equal expected, actual + assert_dom_equal expected, actual end - - def test_block_works_with_content_tag_for + + def test_block_works_with_content_tag_for_in_erb + __in_erb_template = '' expected = %(<tr class="post" id="post_45">#{@post.body}</tr>) actual = content_tag_for(:tr, @post) { concat @post.body } - assert_dom_equal expected, actual + assert_dom_equal expected, actual end - - def test_div_for + + def test_div_for_in_erb + __in_erb_template = '' expected = %(<div class="post bar" id="post_45">#{@post.body}</div>) actual = div_for(@post, :class => "bar") { concat @post.body } assert_dom_equal expected, actual - end - + end end diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb new file mode 100644 index 0000000000..54e4bbda1b --- /dev/null +++ b/actionpack/test/template/render_test.rb @@ -0,0 +1,122 @@ +require 'abstract_unit' +require 'controller/fake_models' + +class ViewRenderTest < Test::Unit::TestCase + def setup + @assigns = { :secret => 'in the sauce' } + @view = ActionView::Base.new([FIXTURE_LOAD_PATH], @assigns) + end + + def test_render_file + assert_equal "Hello world!", @view.render("test/hello_world.erb") + end + + def test_render_file_not_using_full_path + assert_equal "Hello world!", @view.render(:file => "test/hello_world.erb", :use_full_path => true) + end + + def test_render_file_without_specific_extension + assert_equal "Hello world!", @view.render("test/hello_world") + end + + def test_render_file_with_full_path + template_path = File.join(File.dirname(__FILE__), '../fixtures/test/hello_world.erb') + assert_equal "Hello world!", @view.render(:file => template_path, :use_full_path => false) + end + + def test_render_file_with_instance_variables + assert_equal "The secret is in the sauce\n", @view.render("test/render_file_with_ivar.erb") + end + + def test_render_file_with_locals + locals = { :secret => 'in the sauce' } + assert_equal "The secret is in the sauce\n", @view.render("test/render_file_with_locals.erb", locals) + end + + def test_render_file_not_using_full_path_with_dot_in_path + assert_equal "The secret is in the sauce\n", @view.render("test/dot.directory/render_file_with_ivar") + end + + def test_render_update + # TODO: You should not have to stub out template because template is self! + @view.instance_variable_set(:@template, @view) + assert_equal 'alert("Hello, World!");', @view.render(:update) { |page| page.alert('Hello, World!') } + end + + def test_render_partial + assert_equal "only partial", @view.render(:partial => "test/partial_only") + end + + def test_render_partial_with_errors + assert_raise(ActionView::TemplateError) { @view.render(:partial => "test/raise") } + end + + def test_render_partial_collection + assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ]) + end + + # TODO: The reason for this test is unclear, improve documentation + def test_render_partial_and_fallback_to_layout + assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" }) + end + + # TODO: The reason for this test is unclear, improve documentation + def test_render_js_partial_and_fallback_to_erb_layout + @view.template_format = :js + assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" }) + end + + # TODO: The reason for this test is unclear, improve documentation + def test_render_missing_xml_partial_and_raise_missing_template + @view.template_format = :xml + assert_raise(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") } + end + + def test_render_inline + assert_equal "Hello, World!", @view.render(:inline => "Hello, World!") + end + + def test_render_inline_with_locals + assert_equal "Hello, Josh!", @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }) + end + + def test_render_fallbacks_to_erb_for_unknown_types + assert_equal "Hello, World!", @view.render(:inline => "Hello, World!", :type => :foo) + end + + class CustomHandler < ActionView::TemplateHandler + def render(template) + [template.source, template.locals].inspect + end + end + + def test_render_inline_with_custom_type + ActionView::Template.register_template_handler :foo, CustomHandler + assert_equal '["Hello, World!", {}]', @view.render(:inline => "Hello, World!", :type => :foo) + end + + def test_render_inline_with_locals_and_custom_type + ActionView::Template.register_template_handler :foo, CustomHandler + assert_equal '["Hello, <%= name %>!", {:name=>"Josh"}]', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) + end + + class CompilableCustomHandler < ActionView::TemplateHandler + include ActionView::TemplateHandlers::Compilable + + def compile(template) + "@output_buffer = ''\n" + + "@output_buffer << 'locals: #{template.locals.inspect}, '\n" + + "@output_buffer << 'source: #{template.source.inspect}'\n" + end + end + + def test_render_inline_with_compilable_custom_type + ActionView::Template.register_template_handler :foo, CompilableCustomHandler + assert_equal 'locals: {}, source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo) + end + + def test_render_inline_with_locals_and_compilable_custom_type + ActionView::Template.register_template_handler :foo, CompilableCustomHandler + assert_equal 'locals: {:name=>"Josh"}, source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) + end +end diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb index 2941dfe217..fc49d340ef 100644 --- a/actionpack/test/template/tag_helper_test.rb +++ b/actionpack/test/template/tag_helper_test.rb @@ -33,23 +33,40 @@ class TagHelperTest < ActionView::TestCase assert_equal content_tag("a", "Create", "href" => "create"), content_tag("a", "Create", :href => "create") end - - def test_content_tag_with_block + + def test_content_tag_with_block_in_erb + __in_erb_template = '' content_tag(:div) { concat "Hello world!" } assert_dom_equal "<div>Hello world!</div>", output_buffer end - - def test_content_tag_with_block_and_options + + def test_content_tag_with_block_and_options_in_erb + __in_erb_template = '' content_tag(:div, :class => "green") { concat "Hello world!" } assert_dom_equal %(<div class="green">Hello world!</div>), output_buffer end - - def test_content_tag_with_block_and_options_outside_of_action_view - self.output_buffer = nil + + def test_content_tag_with_block_and_options_out_of_erb + assert_dom_equal %(<div class="green">Hello world!</div>), content_tag(:div, :class => "green") { "Hello world!" } + end + + def test_content_tag_with_block_and_options_outside_out_of_erb assert_equal content_tag("a", "Create", :href => "create"), - content_tag("a", "href" => "create") { "Create" } + content_tag("a", "href" => "create") { "Create" } end - + + def test_content_tag_nested_in_content_tag_out_of_erb + assert_equal content_tag("p", content_tag("b", "Hello")), + content_tag("p") { content_tag("b", "Hello") }, + output_buffer + end + + def test_content_tag_nested_in_content_tag_in_erb + __in_erb_template = true + content_tag("p") { concat content_tag("b", "Hello") } + assert_equal '<p><b>Hello</b></p>', output_buffer + end + def test_cdata_section assert_equal "<![CDATA[<hello world>]]>", cdata_section("<hello world>") end diff --git a/actionpack/test/template/template_file_test.rb b/actionpack/test/template/template_file_test.rb deleted file mode 100644 index d14a966c1c..0000000000 --- a/actionpack/test/template/template_file_test.rb +++ /dev/null @@ -1,95 +0,0 @@ -require 'abstract_unit' - -class TemplateFileTest < Test::Unit::TestCase - LOAD_PATH_ROOT = File.join(File.dirname(__FILE__), '..', 'fixtures') - - def setup - @template = ActionView::TemplateFile.new("test/hello_world.html.erb") - @another_template = ActionView::TemplateFile.new("test/hello_world.erb") - @file_only = ActionView::TemplateFile.new("hello_world.erb") - @full_path = ActionView::TemplateFile.new("/u/app/scales/config/../app/views/test/hello_world.erb", true) - @layout = ActionView::TemplateFile.new("layouts/hello") - @multipart = ActionView::TemplateFile.new("test_mailer/implicitly_multipart_example.text.html.erb") - end - - def test_path - assert_equal "test/hello_world.html.erb", @template.path - assert_equal "test/hello_world.erb", @another_template.path - assert_equal "hello_world.erb", @file_only.path - assert_equal "/u/app/scales/config/../app/views/test/hello_world.erb", @full_path.path - assert_equal "layouts/hello", @layout.path - assert_equal "test_mailer/implicitly_multipart_example.text.html.erb", @multipart.path - end - - def test_path_without_extension - assert_equal "test/hello_world.html", @template.path_without_extension - assert_equal "test/hello_world", @another_template.path_without_extension - assert_equal "hello_world", @file_only.path_without_extension - assert_equal "layouts/hello", @layout.path_without_extension - assert_equal "test_mailer/implicitly_multipart_example.text.html", @multipart.path_without_extension - end - - def test_path_without_format_and_extension - assert_equal "test/hello_world", @template.path_without_format_and_extension - assert_equal "test/hello_world", @another_template.path_without_format_and_extension - assert_equal "hello_world", @file_only.path_without_format_and_extension - assert_equal "layouts/hello", @layout.path_without_format_and_extension - assert_equal "test_mailer/implicitly_multipart_example", @multipart.path_without_format_and_extension - end - - def test_name - assert_equal "hello_world", @template.name - assert_equal "hello_world", @another_template.name - assert_equal "hello_world", @file_only.name - assert_equal "hello_world", @full_path.name - assert_equal "hello", @layout.name - assert_equal "implicitly_multipart_example", @multipart.name - end - - def test_format - assert_equal "html", @template.format - assert_equal nil, @another_template.format - assert_equal nil, @layout.format - assert_equal "text.html", @multipart.format - end - - def test_extension - assert_equal "erb", @template.extension - assert_equal "erb", @another_template.extension - assert_equal nil, @layout.extension - assert_equal "erb", @multipart.extension - end - - def test_format_and_extension - assert_equal "html.erb", @template.format_and_extension - assert_equal "erb", @another_template.format_and_extension - assert_equal nil, @layout.format_and_extension - assert_equal "text.html.erb", @multipart.format_and_extension - end - - def test_new_file_with_extension - file = @template.dup_with_extension(:haml) - assert_equal "test/hello_world.html", file.path_without_extension - assert_equal "haml", file.extension - assert_equal "test/hello_world.html.haml", file.path - - file = @another_template.dup_with_extension(:haml) - assert_equal "test/hello_world", file.path_without_extension - assert_equal "haml", file.extension - assert_equal "test/hello_world.haml", file.path - - file = @another_template.dup_with_extension(nil) - assert_equal "test/hello_world", file.path_without_extension - assert_equal nil, file.extension - assert_equal "test/hello_world", file.path - end - - def test_freezes_entire_contents - @template.freeze - assert @template.frozen? - assert @template.base_path.frozen? - assert @template.name.frozen? - assert @template.format.frozen? - assert @template.extension.frozen? - end -end diff --git a/actionpack/test/template/template_object_test.rb b/actionpack/test/template/template_object_test.rb deleted file mode 100644 index 2cfc4523c6..0000000000 --- a/actionpack/test/template/template_object_test.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'abstract_unit' - -class TemplateObjectTest < Test::Unit::TestCase - LOAD_PATH_ROOT = File.join(File.dirname(__FILE__), '..', 'fixtures') - - class TemplateTest < Test::Unit::TestCase - def setup - @view = ActionView::Base.new(LOAD_PATH_ROOT) - @path = "test/hello_world.erb" - end - - def test_should_create_valid_template - template = ActionView::Template.new(@view, @path, true) - - assert_kind_of ActionView::TemplateHandlers::ERB, template.handler - assert_equal "test/hello_world.erb", template.path.to_s - assert_nil template.instance_variable_get(:"@source") - assert_equal "erb", template.extension - end - - uses_mocha 'Template preparation tests' do - def test_should_prepare_template_properly - template = ActionView::Template.new(@view, @path, true) - view = template.instance_variable_get(:"@view") - - view.expects(:evaluate_assigns) - template.handler.expects(:compile_template).with(template) - view.expects(:method_names).returns({}) - - template.prepare! - end - end - end - - class PartialTemplateTest < Test::Unit::TestCase - def setup - @view = ActionView::Base.new(LOAD_PATH_ROOT) - @path = "test/partial_only" - end - - def test_should_create_valid_partial_template - template = ActionView::PartialTemplate.new(@view, @path, nil) - - assert_equal "test/_partial_only", template.path.path_without_format_and_extension - assert_equal :partial_only, template.variable_name - - assert template.locals.has_key?(:object) - assert template.locals.has_key?(:partial_only) - end - - def test_partial_with_errors - template = ActionView::PartialTemplate.new(@view, 'test/raise', nil) - assert_raise(ActionView::TemplateError) { template.render_template } - end - - uses_mocha 'Partial template preparation tests' do - def test_should_prepare_on_initialization - ActionView::PartialTemplate.any_instance.expects(:prepare!) - template = ActionView::PartialTemplate.new(@view, @path, 1) - end - end - end - - class PartialTemplateFallbackTest < Test::Unit::TestCase - def setup - @view = ActionView::Base.new(LOAD_PATH_ROOT) - @path = 'test/layout_for_partial' - end - - def test_default - template = ActionView::PartialTemplate.new(@view, @path, nil) - assert_equal 'test/_layout_for_partial', template.path.path_without_format_and_extension - assert_equal 'erb', template.extension - assert_equal :html, @view.template_format - end - - def test_js - @view.template_format = :js - template = ActionView::PartialTemplate.new(@view, @path, nil) - assert_equal 'test/_layout_for_partial', template.path.path_without_format_and_extension - assert_equal 'erb', template.extension - assert_equal :html, @view.template_format - end - - def test_xml - @view.template_format = :xml - assert_raise ActionView::MissingTemplate do - ActionView::PartialTemplate.new(@view, @path, nil) - end - end - end -end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index cbb5c7ee74..4999525939 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -13,9 +13,7 @@ class TextHelperTest < ActionView::TestCase def test_concat self.output_buffer = 'foo' - concat 'bar' - assert_equal 'foobar', output_buffer - assert_nothing_raised { concat nil } + assert_equal 'foobar', concat('bar') assert_equal 'foobar', output_buffer end @@ -195,6 +193,7 @@ class TextHelperTest < ActionView::TestCase http://www.mail-archive.com/rails@lists.rubyonrails.org/ http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1 http://en.wikipedia.org/wiki/Sprite_(computer_graphics) + http://en.wikipedia.org/wiki/Texas_hold'em ) urls.each do |url| diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 0713cea8ac..3d5f7eae11 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -80,7 +80,7 @@ class UrlHelperTest < ActionView::TestCase def test_link_tag_with_straight_url assert_dom_equal "<a href=\"http://www.example.com\">Hello</a>", link_to("Hello", "http://www.example.com") end - + def test_link_tag_without_host_option ActionController::Base.class_eval { attr_accessor :url } url = {:controller => 'weblog', :action => 'show'} @@ -121,7 +121,7 @@ class UrlHelperTest < ActionView::TestCase @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'}) assert_dom_equal "<a href=\"http://www.example.com/referer\">go back</a>", link_to('go back', :back) end - + def test_link_tag_with_back_and_no_referer @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {}) assert_dom_equal "<a href=\"javascript:history.back()\">go back</a>", link_to('go back', :back) @@ -212,14 +212,14 @@ class UrlHelperTest < ActionView::TestCase assert_raises(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") } end - def test_link_tag_using_block - self.output_buffer = '' + def test_link_tag_using_block_in_erb + __in_erb_template = '' link_to("http://example.com") { concat("Example site") } assert_equal '<a href="http://example.com">Example site</a>', output_buffer end - + def test_link_to_unless assert_equal "Showing", link_to_unless(true, "Showing", :action => "show", :controller => "weblog") assert_dom_equal "<a href=\"http://www.example.com\">Listing</a>", link_to_unless(false, "Listing", :action => "list", :controller => "weblog") @@ -293,7 +293,7 @@ class UrlHelperTest < ActionView::TestCase assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain(dot)com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)") assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") end - + def protect_against_forgery? false end @@ -301,7 +301,7 @@ end class UrlHelperWithControllerTest < ActionView::TestCase class UrlHelperController < ActionController::Base - self.view_paths = [ "#{File.dirname(__FILE__)}/../fixtures/" ] + self.view_paths = [FIXTURE_LOAD_PATH] def self.controller_path; 'url_helper_with_controller' end @@ -356,7 +356,7 @@ end class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase class TasksController < ActionController::Base - self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"] + self.view_paths = [FIXTURE_LOAD_PATH] def self.controller_path; 'tasks' end @@ -420,11 +420,11 @@ class Workshop def initialize(id, new_record) @id, @new_record = id, new_record end - + def new_record? @new_record end - + def to_s id.to_s end @@ -448,7 +448,7 @@ end class PolymorphicControllerTest < ActionView::TestCase class WorkshopsController < ActionController::Base - self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"] + self.view_paths = [FIXTURE_LOAD_PATH] def self.controller_path; 'workshops' end @@ -466,7 +466,7 @@ class PolymorphicControllerTest < ActionView::TestCase end class SessionsController < ActionController::Base - self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"] + self.view_paths = [FIXTURE_LOAD_PATH] def self.controller_path; 'sessions' end diff --git a/activemodel/Rakefile b/activemodel/Rakefile index 034c2fbd01..4b60f8d682 100644 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -18,7 +18,7 @@ Rake::RDocTask.new do |rdoc| rdoc.title = "Active Model" rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'CHANGES') rdoc.rdoc_files.include('lib/**/*.rb') -end
\ No newline at end of file +end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index a1d82fb45d..d6cc589381 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,22 @@ *Edge* +* Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example: + + User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } } + Item.first :conditions => { :items => { :color => 'red' } } + +* Always treat integer :limit as byte length. #420 [Tarmo Tänav] + +* Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison] + +* Fix column collision with named_scope and :joins. #46 [Duncan Beevers, Mark Catley] + +* db:migrate:down and :up update schema_migrations. #369 [Michael Raidel, RaceCondition] + +* PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a bind variable. [Tarmo Tänav] + +* MySQL: rename_column preserves column defaults. #466 [Diego Algorta] + * Add :from option to calculations. #397 [Ben Munat] * Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. [Jan De Poorter] diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 7a5296c458..60b17e02b9 100755 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -141,7 +141,7 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Active Record -- Object-relation mapping put on rails" rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG') rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.exclude('lib/active_record/vendor/*') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index db99b7165d..4b7154043f 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -711,7 +711,8 @@ module ActiveRecord configure_dependency_for_has_many(reflection) - add_multiple_associated_save_callbacks(reflection.name) unless options[:validate] == false + add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false + add_multiple_associated_save_callbacks(reflection.name) add_association_callbacks(reflection.name, reflection.options) if options[:through] @@ -801,7 +802,7 @@ module ActiveRecord end after_save method_name - add_single_associated_save_callbacks(reflection.name) if options[:validate] == true + add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true association_accessor_methods(reflection, HasOneAssociation) association_constructor_method(:build, reflection, HasOneAssociation) association_constructor_method(:create, reflection, HasOneAssociation) @@ -940,7 +941,7 @@ module ActiveRecord ) end - add_single_associated_save_callbacks(reflection.name) if options[:validate] == true + add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true configure_dependency_for_belongs_to(reflection) end @@ -1043,7 +1044,8 @@ module ActiveRecord def has_and_belongs_to_many(association_id, options = {}, &extension) reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension) - add_multiple_associated_save_callbacks(reflection.name) unless options[:validate] == false + add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false + add_multiple_associated_save_callbacks(reflection.name) collection_accessor_methods(reflection, HasAndBelongsToManyAssociation) # Don't use a before_destroy callback since users' before_destroy @@ -1163,7 +1165,7 @@ module ActiveRecord end end - def add_single_associated_save_callbacks(association_name) + def add_single_associated_validation_callbacks(association_name) method_name = "validate_associated_records_for_#{association_name}".to_sym define_method(method_name) do association = instance_variable_get("@#{association_name}") @@ -1175,7 +1177,7 @@ module ActiveRecord validate method_name end - def add_multiple_associated_save_callbacks(association_name) + def add_multiple_associated_validation_callbacks(association_name) method_name = "validate_associated_records_for_#{association_name}".to_sym ivar = "@#{association_name}" @@ -1196,6 +1198,10 @@ module ActiveRecord end validate method_name + end + + def add_multiple_associated_save_callbacks(association_name) + ivar = "@#{association_name}" method_name = "before_save_associated_records_for_#{association_name}".to_sym define_method(method_name) do @@ -1217,7 +1223,6 @@ module ActiveRecord else [] end - records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank? # reconstruct the SQL queries now that we know the owner's id diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 11c64243a2..77fc827e11 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -85,7 +85,7 @@ module ActiveRecord end def conditions - @conditions ||= interpolate_sql(sanitize_sql(@reflection.options[:conditions])) if @reflection.options[:conditions] + @conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions end alias :sql_conditions :conditions @@ -219,6 +219,10 @@ module ActiveRecord def flatten_deeper(array) array.collect { |element| element.respond_to?(:flatten) ? element.flatten : element }.flatten end + + def owner_quoted_id + @owner.quoted_id + end end end end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 918404eac6..d516d54151 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -37,7 +37,7 @@ module ActiveRecord attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s - attrs[column.name] = @owner.quoted_id + attrs[column.name] = owner_quoted_id when @reflection.association_foreign_key.to_s attrs[column.name] = record.quoted_id else @@ -64,7 +64,7 @@ module ActiveRecord records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) } else ids = quoted_record_ids(records) - sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" + sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" @owner.connection.delete(sql) end end @@ -75,7 +75,7 @@ module ActiveRecord if @reflection.options[:finder_sql] @finder_sql = @reflection.options[:finder_sql] else - @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} " + @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} " @finder_sql << " AND (#{conditions})" if conditions end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 295beb2966..37440aa84d 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -60,7 +60,7 @@ module ActiveRecord ids = quoted_record_ids(records) @reflection.klass.update_all( "#{@reflection.primary_key_name} = NULL", - "#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})" + "#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})" ) end end @@ -76,12 +76,12 @@ module ActiveRecord when @reflection.options[:as] @finder_sql = - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " + + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" @finder_sql << " AND (#{conditions})" if conditions else - @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" + @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" @finder_sql << " AND (#{conditions})" if conditions end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 52ced36d16..e1bfff5923 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -107,12 +107,12 @@ module ActiveRecord # Associate attributes pointing to owner, quoted. def construct_quoted_owner_attributes(reflection) if as = reflection.options[:as] - { "#{as}_id" => @owner.quoted_id, + { "#{as}_id" => owner_quoted_id, "#{as}_type" => reflection.klass.quote_value( @owner.class.base_class.name.to_s, reflection.klass.columns_hash["#{as}_type"]) } else - { reflection.primary_key_name => @owner.quoted_id } + { reflection.primary_key_name => owner_quoted_id } end end @@ -183,7 +183,7 @@ module ActiveRecord when @reflection.options[:finder_sql] @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) - @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" + @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" @finder_sql << " AND (#{conditions})" if conditions else @finder_sql = construct_conditions diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index c2b3503e0d..25a268e95c 100755 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -63,10 +63,10 @@ module ActiveRecord case when @reflection.options[:as] @finder_sql = - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " + + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" else - @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}" + @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" end @finder_sql << " AND (#{conditions})" if conditions end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8fca34e524..962c2b36d9 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1479,7 +1479,7 @@ module ActiveRecord #:nodoc: def construct_finder_sql(options) scope = scope(:find) - sql = "SELECT #{options[:select] || (scope && scope[:select]) || (options[:joins] && quoted_table_name + '.*') || '*'} " + sql = "SELECT #{options[:select] || (scope && scope[:select]) || ((options[:joins] || (scope && scope[:joins])) && quoted_table_name + '.*') || '*'} " sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} " add_joins!(sql, options, scope) @@ -1999,24 +1999,28 @@ module ActiveRecord #:nodoc: # # => "age BETWEEN 13 AND 18" # { 'other_records.id' => 7 } # # => "`other_records`.`id` = 7" + # { :other_records => { :id => 7 } } + # # => "`other_records`.`id` = 7" # And for value objects on a composed_of relationship: # { :address => Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" - def sanitize_sql_hash_for_conditions(attrs) + def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name) attrs = expand_hash_conditions_for_aggregates(attrs) conditions = attrs.map do |attr, value| - attr = attr.to_s + unless value.is_a?(Hash) + attr = attr.to_s + + # Extract table name from qualified attribute names. + if attr.include?('.') + table_name, attr = attr.split('.', 2) + table_name = connection.quote_table_name(table_name) + end - # Extract table name from qualified attribute names. - if attr.include?('.') - table_name, attr = attr.split('.', 2) - table_name = connection.quote_table_name(table_name) + "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" else - table_name = quoted_table_name + sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) end - - "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" end.join(' AND ') replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) @@ -2055,9 +2059,10 @@ module ActiveRecord #:nodoc: end def replace_named_bind_variables(statement, bind_vars) #:nodoc: - statement.gsub(/:([a-zA-Z]\w*)/) do - match = $1.to_sym - if bind_vars.include?(match) + statement.gsub(/(:?):([a-zA-Z]\w*)/) do + if $1 == ':' # skip postgresql casts + $& # return the whole match + elsif bind_vars.include?(match = $2.to_sym) quote_bound_value(bind_vars[match]) else raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}" @@ -2069,6 +2074,8 @@ module ActiveRecord #:nodoc: expanded = [] bind_vars.each do |var| + next if var.is_a?(Hash) + if var.is_a?(Range) expanded << var.first expanded << var.last @@ -2169,11 +2176,11 @@ module ActiveRecord #:nodoc: def cache_key case when new_record? - "#{self.class.name.tableize}/new" - when self[:updated_at] - "#{self.class.name.tableize}/#{id}-#{updated_at.to_s(:number)}" + "#{self.class.model_name.cache_key}/new" + when timestamp = self[:updated_at] + "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}" else - "#{self.class.name.tableize}/#{id}" + "#{self.class.model_name.cache_key}/#{id}" end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index f968b9b173..2c03de0f17 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -30,11 +30,11 @@ module ActiveRecord end def text? - [:string, :text].include? type + type == :string || type == :text end def number? - [:float, :integer, :decimal].include? type + type == :integer || type == :float || type == :decimal end # Returns the Ruby class that corresponds to the abstract data type. @@ -304,8 +304,7 @@ module ActiveRecord # # Available options are (none of these exists by default): # * <tt>:limit</tt> - - # Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>, - # <tt>:binary</tt> or <tt>:integer</tt> columns only) + # Requests a maximum column length. This is number of characters for <tt>:string</tt> and <tt>:text</tt> columns and number of bytes for :binary and :integer columns. # * <tt>:default</tt> - # The column's default value. Use nil for NULL. # * <tt>:null</tt> - diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 93aafaaad1..c5962764f5 100755 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -99,7 +99,8 @@ module ActiveRecord end def extract_limit(sql_type) - if sql_type =~ /blob|text/i + case sql_type + when /blob|text/i case sql_type when /tiny/i 255 @@ -110,6 +111,11 @@ module ActiveRecord else super # we could return 65535 here, but we leave it undecorated by default end + when /^bigint/i; 8 + when /^int/i; 4 + when /^mediumint/i; 3 + when /^smallint/i; 2 + when /^tinyint/i; 1 else super end @@ -168,7 +174,7 @@ module ActiveRecord :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze, :string => { :name => "varchar", :limit => 255 }, :text => { :name => "text" }, - :integer => { :name => "int"}, + :integer => { :name => "int", :limit => 4 }, :float => { :name => "float" }, :decimal => { :name => "decimal" }, :datetime => { :name => "datetime" }, @@ -450,8 +456,16 @@ module ActiveRecord end def rename_column(table_name, column_name, new_column_name) #:nodoc: + options = {} + if column = columns(table_name).find { |c| c.name == column_name.to_s } + options[:default] = column.default + else + raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" + end current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"] - execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" + rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" + add_column_options!(rename_column_sql, options) + execute(rename_column_sql) end # Maps logical Rails types to MySQL-specific data types. @@ -459,14 +473,12 @@ module ActiveRecord return super unless type.to_s == 'integer' case limit - when 0..3 - "smallint(#{limit})" - when 4..8 - "int(#{limit})" - when 9..20 - "bigint(#{limit})" - else - 'int(11)' + when 1; 'tinyint' + when 2; 'smallint' + when 3; 'mediumint' + when nil, 4, 11; 'int(11)' # compatibility with MySQL default + when 5..8; 'bigint' + else raise(ActiveRecordError, "No integer type has byte size #{limit}") end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 294f4c1929..2e2d50ccf4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -47,6 +47,15 @@ module ActiveRecord end private + def extract_limit(sql_type) + case sql_type + when /^integer/i; 4 + when /^bigint/i; 8 + when /^smallint/i; 2 + else super + end + end + # Extracts the scale from PostgreSQL-specific data types. def extract_scale(sql_type) # Money type has a fixed scale of 2. @@ -324,12 +333,7 @@ module ActiveRecord end def supports_insert_with_returning? - unless defined? @supports_insert_with_returning - @supports_insert_with_returning = - @connection.respond_to?(:server_version) && - @connection.server_version >= 80200 - end - @supports_insert_with_returning + postgresql_version >= 80200 end # Returns the configured supported identifier length supported by PostgreSQL, @@ -553,7 +557,15 @@ module ActiveRecord # Example: # drop_database 'matt_development' def drop_database(name) #:nodoc: - execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}" + if postgresql_version >= 80200 + execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}" + else + begin + execute "DROP DATABASE #{quote_table_name(name)}" + rescue ActiveRecord::StatementInvalid + @logger.warn "#{name} database doesn't exist." if @logger + end + end end @@ -782,15 +794,14 @@ module ActiveRecord def type_to_sql(type, limit = nil, precision = nil, scale = nil) return super unless type.to_s == 'integer' - if limit.nil? || limit == 4 - 'integer' - elsif limit < 4 - 'smallint' - else - 'bigint' + case limit + when 1..2; 'smallint' + when 3..4, nil; 'integer' + when 5..8; 'bigint' + else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.") end end - + # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. # # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index b32e17e990..a7d767486c 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -142,9 +142,11 @@ module ActiveRecord def field_changed?(attr, old, value) if column = column_for_attribute(attr) - if column.type == :integer && column.null && old.nil? + if column.type == :integer && column.null && (old.nil? || old == 0) # For nullable integer columns, NULL gets stored in database for blank (i.e. '') values. # Hence we don't record it as a change if the value changes from nil to ''. + # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll + # be typecast back to 0 (''.to_i => 0) value = nil if value.blank? else value = column.type_cast(value) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index c66034d18b..ff9899d032 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -68,6 +68,7 @@ module ActiveRecord def update_with_lock(attribute_names = @attributes.keys) #:nodoc: return update_without_lock(attribute_names) unless locking_enabled? + return 0 if attribute_names.empty? lock_col = self.class.locking_column previous_value = send(lock_col).to_i diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b47b01e99a..e095b3c766 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -399,7 +399,10 @@ module ActiveRecord def run target = migrations.detect { |m| m.version == @target_version } raise UnknownMigrationVersionError.new(@target_version) if target.nil? - target.migrate(@direction) + unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i)) + target.migrate(@direction) + record_version_state_after_migrating(target.version) + end end def migrate diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index b0c8a8b815..eac61e9e43 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -82,6 +82,7 @@ module ActiveRecord # expected_options = { :conditions => { :colored => 'red' } } # assert_equal expected_options, Shirt.colored('red').proxy_options def named_scope(name, options = {}, &block) + name = name.to_sym scopes[name] = lambda do |parent_scope, *args| Scope.new(parent_scope, case options when Hash diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 8614ef8751..3f74c03714 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -112,6 +112,10 @@ module ActiveRecord name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record end + def sanitized_conditions #:nodoc: + @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions] + end + private def derive_class_name name.to_s.camelize diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index b638143c5a..1e21614f45 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -345,7 +345,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_invalid_adding_with_validate_false firm = Firm.find(:first) client = Client.new - firm.unvalidated_clients_of_firm << Client.new + firm.unvalidated_clients_of_firm << client assert firm.valid? assert !client.valid? @@ -353,6 +353,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert client.new_record? end + def test_valid_adding_with_validate_false + no_of_clients = Client.count + + firm = Firm.find(:first) + client = Client.new("name" => "Apple") + + assert firm.valid? + assert client.valid? + assert client.new_record? + + firm.unvalidated_clients_of_firm << client + + assert firm.save + assert !client.new_record? + assert_equal no_of_clients+1, Client.count + end + def test_build company = companies(:first_firm) new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") } diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index c011ffaf57..e5e022050d 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -2,6 +2,7 @@ require 'cases/helper' require 'models/topic' # For booleans require 'models/pirate' # For timestamps require 'models/parrot' +require 'models/person' # For optimistic locking class Pirate # Just reopening it, not defining it attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected @@ -54,6 +55,33 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_zero_to_blank_marked_as_changed + pirate = Pirate.new + pirate.catchphrase = "Yarrrr, me hearties" + pirate.parrot_id = 1 + pirate.save + + # check the change from 1 to '' + pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties") + pirate.parrot_id = '' + assert pirate.parrot_id_changed? + assert_equal([1, nil], pirate.parrot_id_change) + pirate.save + + # check the change from nil to 0 + pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties") + pirate.parrot_id = 0 + assert pirate.parrot_id_changed? + assert_equal([nil, 0], pirate.parrot_id_change) + pirate.save + + # check the change from 0 to '' + pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties") + pirate.parrot_id = '' + assert pirate.parrot_id_changed? + assert_equal([0, nil], pirate.parrot_id_change) + end + def test_object_should_be_changed_if_any_attribute_is_changed pirate = Pirate.new assert !pirate.changed? @@ -125,6 +153,24 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_partial_update_with_optimistic_locking + person = Person.new(:first_name => 'foo') + old_lock_version = 1 + + with_partial_updates Person, false do + assert_queries(2) { 2.times { person.save! } } + Person.update_all({ :first_name => 'baz' }, :id => person.id) + end + + with_partial_updates Person, true do + assert_queries(0) { 2.times { person.save! } } + assert_equal old_lock_version, person.reload.lock_version + + assert_queries(1) { person.first_name = 'bar'; person.save! } + assert_not_equal old_lock_version, person.reload.lock_version + end + end + def test_changed_attributes_should_be_preserved_if_save_failure pirate = Pirate.new pirate.parrot_id = 1 diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 80936d51f3..b97db73b68 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/author' +require 'models/categorization' require 'models/comment' require 'models/company' require 'models/topic' @@ -199,6 +200,23 @@ class FinderTest < ActiveRecord::TestCase assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) } end + def test_find_on_hash_conditions_with_hashed_table_name + assert Topic.find(1, :conditions => {:topics => { :approved => false }}) + assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) } + end + + def test_find_with_hash_conditions_on_joined_table + firms = Firm.all :joins => :account, :conditions => {:accounts => { :credit_limit => 50 }} + assert_equal 1, firms.size + assert_equal companies(:first_firm), firms.first + end + + def test_find_with_hash_conditions_on_joined_table_and_with_range + firms = DependentFirm.all :joins => :account, :conditions => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }} + assert_equal 1, firms.size + assert_equal companies(:rails_core), firms.first + end + def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate david = customers(:david) assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address }) @@ -394,6 +412,12 @@ class FinderTest < ActiveRecord::TestCase assert_equal '1,1,1', bind('?', os) end + def test_named_bind_with_postgresql_type_casts + l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') } + assert_nothing_raised(&l) + assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call + end + def test_string_sanitation assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table") diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 7db6c570b5..701187223f 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -29,10 +29,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_equal 0, p1.lock_version assert_equal 0, p2.lock_version + p1.first_name = 'stu' p1.save! assert_equal 1, p1.lock_version assert_equal 0, p2.lock_version + p2.first_name = 'sue' assert_raises(ActiveRecord::StaleObjectError) { p2.save! } end @@ -42,11 +44,14 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_equal 0, p1.lock_version assert_equal 0, p2.lock_version + p1.first_name = 'stu' p1.save! assert_equal 1, p1.lock_version assert_equal 0, p2.lock_version + p2.first_name = 'sue' assert_raises(ActiveRecord::StaleObjectError) { p2.save! } + p2.first_name = 'sue2' assert_raises(ActiveRecord::StaleObjectError) { p2.save! } end @@ -54,15 +59,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase p1 = Person.new(:first_name => 'anika') assert_equal 0, p1.lock_version + p1.first_name = 'anika2' p1.save! p2 = Person.find(p1.id) assert_equal 0, p1.lock_version assert_equal 0, p2.lock_version + p1.first_name = 'anika3' p1.save! assert_equal 1, p1.lock_version assert_equal 0, p2.lock_version + p2.first_name = 'sue' assert_raises(ActiveRecord::StaleObjectError) { p2.save! } end @@ -81,10 +89,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_equal 0, t1.version assert_equal 0, t2.version + t1.tps_report_number = 700 t1.save! assert_equal 1, t1.version assert_equal 0, t2.version + t2.tps_report_number = 800 assert_raises(ActiveRecord::StaleObjectError) { t2.save! } end @@ -93,6 +103,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_equal 0, p1.lock_version assert_equal p1.lock_version, Person.new(p1.attributes).lock_version + p1.first_name = 'bianca2' p1.save! assert_equal 1, p1.lock_version assert_equal p1.lock_version, Person.new(p1.attributes).lock_version @@ -146,6 +157,15 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert ref.save end + # Useful for partial updates, don't only update the lock_version if there + # is nothing else being updated. + def test_update_without_attributes_does_not_only_update_lock_version + assert_nothing_raised do + p1 = Person.new(:first_name => 'anika') + p1.send(:update_with_lock, []) + end + end + private def add_counter_column_to(model) diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 1a9a875730..d6b3e341df 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -87,6 +87,16 @@ class MethodScopingTest < ActiveRecord::TestCase assert_equal 1, scoped_developers.size end + def test_scoped_find_joins + scoped_developers = Developer.with_scope(:find => { :joins => 'JOIN developers_projects ON id = developer_id' } ) do + Developer.find(:all, :conditions => 'developers_projects.project_id = 2') + end + assert scoped_developers.include?(developers(:david)) + assert !scoped_developers.include?(developers(:jamis)) + assert_equal 1, scoped_developers.size + assert_equal developers(:david).attributes, scoped_developers.first.attributes + end + def test_scoped_count_include # with the include, will retrieve only developers for the given project Developer.with_scope(:find => { :include => :projects }) do diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index f36255e209..4482b487dd 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -153,9 +153,10 @@ if ActiveRecord::Base.connection.supports_migrations? t.column :default_int, :integer - t.column :one_int, :integer, :limit => 1 - t.column :four_int, :integer, :limit => 4 - t.column :eight_int, :integer, :limit => 8 + t.column :one_int, :integer, :limit => 1 + t.column :four_int, :integer, :limit => 4 + t.column :eight_int, :integer, :limit => 8 + t.column :eleven_int, :integer, :limit => 11 end end @@ -167,12 +168,20 @@ if ActiveRecord::Base.connection.supports_migrations? one = columns.detect { |c| c.name == "one_int" } four = columns.detect { |c| c.name == "four_int" } eight = columns.detect { |c| c.name == "eight_int" } + eleven = columns.detect { |c| c.name == "eleven_int" } if current_adapter?(:PostgreSQLAdapter) assert_equal 'integer', default.sql_type assert_equal 'smallint', one.sql_type assert_equal 'integer', four.sql_type assert_equal 'bigint', eight.sql_type + assert_equal 'integer', eleven.sql_type + elsif current_adapter?(:MysqlAdapter) + assert_match 'int(11)', default.sql_type + assert_match 'tinyint', one.sql_type + assert_match 'int', four.sql_type + assert_match 'bigint', eight.sql_type + assert_match 'int(11)', eleven.sql_type elsif current_adapter?(:OracleAdapter) assert_equal 'NUMBER(38)', default.sql_type assert_equal 'NUMBER(1)', one.sql_type @@ -483,6 +492,32 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_rename_column_preserves_default_value_not_null + begin + default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default + assert_equal 70000, default_before + Developer.connection.rename_column "developers", "salary", "anual_salary" + Developer.reset_column_information + assert Developer.column_names.include?("anual_salary") + default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default + assert_equal 70000, default_after + ensure + Developer.connection.rename_column "developers", "anual_salary", "salary" + Developer.reset_column_information + end + end + + def test_rename_nonexistent_column + ActiveRecord::Base.connection.create_table(:hats) do |table| + table.column :hat_name, :string, :default => nil + end + assert_raises(ActiveRecord::ActiveRecordError) do + Person.connection.rename_column "hats", "nonexistent", "should_fail" + end + ensure + ActiveRecord::Base.connection.drop_table(:hats) + end + def test_rename_column_with_sql_reserved_word begin assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" } @@ -799,6 +834,21 @@ if ActiveRecord::Base.connection.supports_migrations? assert !Reminder.table_exists? end + def test_migrator_double_up + assert_equal(0, ActiveRecord::Migrator.current_version) + ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) + assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) } + assert_equal(1, ActiveRecord::Migrator.current_version) + end + + def test_migrator_double_down + assert_equal(0, ActiveRecord::Migrator.current_version) + ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) + ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) + assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) } + assert_equal(0, ActiveRecord::Migrator.current_version) + end + def test_finds_migrations migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations [['1', 'people_have_last_names'], @@ -888,16 +938,6 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") assert_equal(0, ActiveRecord::Migrator.current_version) end - - def test_migrator_run - assert_equal(0, ActiveRecord::Migrator.current_version) - ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 3) - assert_equal(0, ActiveRecord::Migrator.current_version) - - assert_equal(0, ActiveRecord::Migrator.current_version) - ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 3) - assert_equal(0, ActiveRecord::Migrator.current_version) - end def test_schema_migrations_table_name ActiveRecord::Base.table_name_prefix = "prefix_" @@ -1206,10 +1246,10 @@ if ActiveRecord::Base.connection.supports_migrations? end def integer_column - if current_adapter?(:SQLite3Adapter) || current_adapter?(:SQLiteAdapter) || current_adapter?(:PostgreSQLAdapter) - "integer" - else + if current_adapter?(:MysqlAdapter) 'int(11)' + else + 'integer' end end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index d890cf7936..7d73541ee1 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -6,7 +6,7 @@ require 'models/reply' require 'models/author' class NamedScopeTest < ActiveRecord::TestCase - fixtures :posts, :authors, :topics, :comments + fixtures :posts, :authors, :topics, :comments, :author_addresses def test_implements_enumerable assert !Topic.find(:all).empty? @@ -59,6 +59,16 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal Topic.count(:conditions => {:approved => true}), Topic.approved.count end + def test_scopes_with_string_name_can_be_composed + # NOTE that scopes defined with a string as a name worked on their own + # but when called on another scope the other scope was completely replaced + assert_equal Topic.replied.approved, Topic.replied.approved_as_string + end + + def test_scopes_can_be_specified_with_deep_hash_conditions + assert_equal Topic.replied.approved, Topic.replied.approved_as_hash_condition + end + def test_scopes_are_composable assert_equal (approved = Topic.find(:all, :conditions => {:approved => true})), Topic.approved assert_equal (replied = Topic.find(:all, :conditions => 'replies_count > 0')), Topic.replied @@ -77,6 +87,25 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal topics_written_before_the_second, Topic.written_before(topics(:second).written_on) end + def test_scopes_with_joins + address = author_addresses(:david_address) + posts_with_authors_at_address = Post.find( + :all, :joins => 'JOIN authors ON authors.id = posts.author_id', + :conditions => [ 'authors.author_address_id = ?', address.id ] + ) + assert_equal posts_with_authors_at_address, Post.with_authors_at_address(address) + end + + def test_scopes_with_joins_respects_custom_select + address = author_addresses(:david_address) + posts_with_authors_at_address_titles = Post.find(:all, + :select => 'title', + :joins => 'JOIN authors ON authors.id = posts.author_id', + :conditions => [ 'authors.author_address_id = ?', address.id ] + ) + assert_equal posts_with_authors_at_address_titles, Post.with_authors_at_address(address).find(:all, :select => 'title') + end + def test_extensions assert_equal 1, Topic.anonymous_extension.one assert_equal 2, Topic.named_extension.two diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index c42b0efba0..ee7e285a73 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -72,6 +72,52 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{:null => false}, output end + def test_schema_dump_includes_limit_constraint_for_integer_columns + stream = StringIO.new + + ActiveRecord::SchemaDumper.ignore_tables = [/^(?!integer_limits)/] + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) + output = stream.string + + if current_adapter?(:PostgreSQLAdapter) + assert_match %r{c_int_1.*:limit => 2}, output + assert_match %r{c_int_2.*:limit => 2}, output + + # int 3 is 4 bytes in postgresql + assert_match %r{c_int_3.*}, output + assert_no_match %r{c_int_3.*:limit}, output + + assert_match %r{c_int_4.*}, output + assert_no_match %r{c_int_4.*:limit}, output + elsif current_adapter?(:MysqlAdapter) + assert_match %r{c_int_1.*:limit => 1}, output + assert_match %r{c_int_2.*:limit => 2}, output + assert_match %r{c_int_3.*:limit => 3}, output + + assert_match %r{c_int_4.*}, output + assert_no_match %r{c_int_4.*:limit}, output + elsif current_adapter?(:SQLiteAdapter) + assert_match %r{c_int_1.*:limit => 1}, output + assert_match %r{c_int_2.*:limit => 2}, output + assert_match %r{c_int_3.*:limit => 3}, output + assert_match %r{c_int_4.*:limit => 4}, output + end + assert_match %r{c_int_without_limit.*}, output + assert_no_match %r{c_int_without_limit.*:limit}, output + + if current_adapter?(:SQLiteAdapter) + assert_match %r{c_int_5.*:limit => 5}, output + assert_match %r{c_int_6.*:limit => 6}, output + assert_match %r{c_int_7.*:limit => 7}, output + assert_match %r{c_int_8.*:limit => 8}, output + else + assert_match %r{c_int_5.*:limit => 8}, output + assert_match %r{c_int_6.*:limit => 8}, output + assert_match %r{c_int_7.*:limit => 8}, output + assert_match %r{c_int_8.*:limit => 8}, output + end + end + def test_schema_dump_with_string_ignored_table stream = StringIO.new diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index d9101706b5..3adbc0ce1f 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -1,6 +1,11 @@ class Post < ActiveRecord::Base named_scope :containing_the_letter_a, :conditions => "body LIKE '%a%'" - + named_scope :with_authors_at_address, lambda { |address| { + :conditions => [ 'authors.author_address_id = ?', address.id ], + :joins => 'JOIN authors ON authors.id = posts.author_id' + } + } + belongs_to :author do def greeting "hello" diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index f63e862cfd..47b2eec938 100755 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -4,6 +4,8 @@ class Topic < ActiveRecord::Base { :conditions => ['written_on < ?', time] } } named_scope :approved, :conditions => {:approved => true} + named_scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}} + named_scope 'approved_as_string', :conditions => {:approved => true} named_scope :replied, :conditions => ['replies_count > 0'] named_scope :anonymous_extension do def one diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 423929fd55..234c43494a 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -407,6 +407,13 @@ ActiveRecord::Schema.define do t.column :key, :string end + create_table :integer_limits, :force => true do |t| + t.integer :"c_int_without_limit" + (1..8).each do |i| + t.integer :"c_int_#{i}", :limit => i + end + end + except 'SQLite' do # fk_test_has_fk should be before fk_test_has_pk create_table :fk_test_has_fk, :force => true do |t| diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 89901292a5..8c3ad36a02 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -42,7 +42,7 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Active Resource -- Object-oriented REST services" rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.exclude('lib/activeresource.rb') diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index d87558aba9..a540150329 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,12 +1,14 @@ *Edge* +* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller] + * Added Array#second through Array#tenth as aliases for Array#[1] through Array#[9] [DHH] * Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields] * Added Object#present? which is equivalent to !Object#blank? [DHH] -* Added Enumberable#many? to encapsulate collection.size > 1 [DHH] +* Added Enumberable#many? to encapsulate collection.size > 1 [DHH/Damian Janowski] * Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell] diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 74d2750011..f2885c69a4 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -33,7 +33,7 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Active Support -- Utility classes and standard library extensions from Rails" rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('lib/active_support.rb') rdoc.rdoc_files.include('lib/active_support/**/*.rb') diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 2f1143e610..3e3dc18263 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -19,19 +19,19 @@ module ActiveSupport def self.expand_cache_key(key, namespace = nil) expanded_cache_key = namespace ? "#{namespace}/" : "" - + if ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] expanded_cache_key << "#{ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]}/" end expanded_cache_key << case - when key.respond_to?(:cache_key) - key.cache_key - when key.is_a?(Array) - key.collect { |element| expand_cache_key(element) }.to_param - when key.respond_to?(:to_param) - key.to_param - end + when key.respond_to?(:cache_key) + key.cache_key + when key.is_a?(Array) + key.collect { |element| expand_cache_key(element) }.to_param + when key + key.to_param + end.to_s expanded_cache_key end diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index a9882828ca..49ada8f174 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -27,7 +27,7 @@ module ActiveSupport #:nodoc: # Calls <tt>to_param</tt> on all its elements and joins the result with # slashes. This is used by <tt>url_for</tt> in Action Pack. def to_param - map(&:to_param).join '/' + collect { |e| e.to_param }.join '/' end # Converts an array into a string suitable for use as a URL query string, @@ -35,7 +35,8 @@ module ActiveSupport #:nodoc: # # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" def to_query(key) - collect { |value| value.to_query("#{key}[]") } * '&' + prefix = "#{key}[]" + collect { |value| value.to_query(prefix) }.join '&' end def self.included(base) #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 9647797ec2..e451e9933a 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -79,7 +79,9 @@ module Enumerable end # Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1. - def many? + # Works with a block too ala any?, so people.many? { |p| p.age > 26 } # => returns true if more than 1 person is over 26. + def many?(&block) + size = block_given? ? select(&block).size : self.size size > 1 end end diff --git a/activesupport/lib/active_support/core_ext/module/model_naming.rb b/activesupport/lib/active_support/core_ext/module/model_naming.rb index 26e76ab556..5518f5417b 100644 --- a/activesupport/lib/active_support/core_ext/module/model_naming.rb +++ b/activesupport/lib/active_support/core_ext/module/model_naming.rb @@ -1,12 +1,13 @@ module ActiveSupport class ModelName < String - attr_reader :singular, :plural, :partial_path + attr_reader :singular, :plural, :cache_key, :partial_path def initialize(name) super @singular = underscore.tr('/', '_').freeze @plural = @singular.pluralize.freeze - @partial_path = "#{tableize}/#{demodulize.underscore}".freeze + @cache_key = tableize.freeze + @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze end end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 7a8c4d0326..d3d9ff9de4 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -299,7 +299,7 @@ module ActiveSupport #:nodoc: # Will the provided constant descriptor be unloaded? def will_unload?(const_desc) - autoloaded?(desc) || + autoloaded?(const_desc) || explicitly_unloadable_constants.include?(to_constant_name(const_desc)) end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 2317171467..5f2027eb3b 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -18,7 +18,7 @@ module ActiveSupport { :benchmark => false, :runs => 1, :min_percent => 0.02, - :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time], + :metrics => [:process_time, :memory, :objects], :formats => [:flat, :graph_html, :call_tree], :output => 'tmp/performance' } end @@ -99,7 +99,7 @@ module ActiveSupport def report rate = @total / profile_options[:runs] - '%20s: %s/run' % [@metric.name, @metric.format(rate)] + '%20s: %s' % [@metric.name, @metric.format(rate)] end protected @@ -288,7 +288,7 @@ module ActiveSupport end class CpuTime < Time - Mode = RubyProf::CPU_TIME + Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) def initialize(*args) # FIXME: yeah my CPU is 2.33 GHz @@ -302,7 +302,7 @@ module ActiveSupport end class Memory < Base - Mode = RubyProf::MEMORY + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) # ruby-prof wrapper if RubyProf.respond_to?(:measure_memory) @@ -335,7 +335,7 @@ module ActiveSupport end class Objects < Base - Mode = RubyProf::ALLOCATIONS + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) if RubyProf.respond_to?(:measure_allocations) def measure @@ -353,7 +353,7 @@ module ActiveSupport end class GcRuns < Base - Mode = RubyProf::GC_RUNS + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) if RubyProf.respond_to?(:measure_gc_runs) def measure @@ -375,7 +375,7 @@ module ActiveSupport end class GcTime < Base - Mode = RubyProf::GC_TIME + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) if RubyProf.respond_to?(:measure_gc_time) def measure diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 5b2d42aa3c..788d40bfa8 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -201,6 +201,12 @@ module ActiveSupport result end + # Compare #name and TZInfo identifier to a supplied regexp, returning true + # if a match is found. + def =~(re) + return true if name =~ re || MAPPING[name] =~ re + end + # Returns a textual representation of this time zone. def to_s "(GMT#{formatted_offset}) #{name}" diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 09b56525e0..f3220d27aa 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -1,5 +1,11 @@ require 'abstract_unit' +class CacheKeyTest < Test::Unit::TestCase + def test_expand_cache_key + assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) + end +end + class CacheStoreSettingTest < Test::Unit::TestCase def test_file_fragment_cache_store store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory" diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index fb9b9b8916..2315d8f3db 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -63,10 +63,15 @@ class EnumerableTests < Test::Unit::TestCase assert_equal({ 5 => payments[0], 15 => payments[1], 10 => payments[2] }, payments.index_by { |p| p.price }) end - - def test_several + + def test_many assert ![].many? assert ![ 1 ].many? assert [ 1, 2 ].many? + + assert ![].many? {|x| x > 1 } + assert ![ 2 ].many? {|x| x > 1 } + assert ![ 1, 2 ].many? {|x| x > 1 } + assert [ 1, 2, 2 ].many? {|x| x > 1 } end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index b42dcd17f8..515ffcf0bf 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -250,6 +250,13 @@ class TimeZoneTest < Test::Unit::TestCase assert zone1 == zone1 end + def test_zone_match + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + assert zone =~ /Eastern/ + assert zone =~ /New_York/ + assert zone !~ /Nonexistent_Place/ + end + def test_to_s assert_equal "(GMT+03:00) Moscow", ActiveSupport::TimeZone['Moscow'].to_s end diff --git a/doc/template/horo.rb b/doc/template/horo.rb new file mode 100644 index 0000000000..e028422a2e --- /dev/null +++ b/doc/template/horo.rb @@ -0,0 +1,613 @@ +# Horo RDoc template +# Author: Hongli Lai - http://izumi.plan99.net/blog/ +# +# Based on the Jamis template: +# http://weblog.jamisbuck.org/2005/4/8/rdoc-template + +if defined?(RDoc::Diagram) + RDoc::Diagram.class_eval do + remove_const(:FONT) + const_set(:FONT, "\"Bitstream Vera Sans\"") + end +end + +module RDoc +module Page + +FONTS = "\"Bitstream Vera Sans\", Verdana, Arial, Helvetica, sans-serif" + +STYLE = <<CSS +a { + color: #00F; + text-decoration: none; +} + +a:hover { + color: #77F; + text-decoration: underline; +} + +body, td, p { + font-family: %fonts%; + background: #FFF; + color: #000; + margin: 0px; + font-size: small; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +#content { + margin: 2em; + margin-left: 3.5em; + margin-right: 3.5em; +} + +#description p { + margin-bottom: 0.5em; +} + +.sectiontitle { + margin-top: 1em; + margin-bottom: 1em; + padding: 0.5em; + padding-left: 2em; + background: #005; + color: #FFF; + font-weight: bold; +} + +.attr-rw { + padding-left: 1em; + padding-right: 1em; + text-align: center; + color: #055; +} + +.attr-name { + font-weight: bold; +} + +.attr-desc { +} + +.attr-value { + font-family: monospace; +} + +.file-title-prefix { + font-size: large; +} + +.file-title { + font-size: large; + font-weight: bold; + background: #005; + color: #FFF; +} + +.banner { + background: #005; + color: #FFF; + border: 1px solid black; + padding: 1em; +} + +.banner td { + background: transparent; + color: #FFF; +} + +h1 a, h2 a, .sectiontitle a, .banner a { + color: #FF0; +} + +h1 a:hover, h2 a:hover, .sectiontitle a:hover, .banner a:hover { + color: #FF7; +} + +.dyn-source { + display: none; + background: #fffde8; + color: #000; + border: #ffe0bb dotted 1px; + margin: 0.5em 2em 0.5em 2em; + padding: 0.5em; +} + +.dyn-source .cmt { + color: #00F; + font-style: italic; +} + +.dyn-source .kw { + color: #070; + font-weight: bold; +} + +.method { + margin-left: 1em; + margin-right: 1em; + margin-bottom: 1em; +} + +.description pre { + padding: 0.5em; + border: #ffe0bb dotted 1px; + background: #fffde8; +} + +.method .title { + font-family: monospace; + font-size: large; + border-bottom: 1px dashed black; + margin-bottom: 0.3em; + padding-bottom: 0.1em; +} + +.method .description, .method .sourcecode { + margin-left: 1em; +} + +.description p, .sourcecode p { + margin-bottom: 0.5em; +} + +.method .sourcecode p.source-link { + text-indent: 0em; + margin-top: 0.5em; +} + +.method .aka { + margin-top: 0.3em; + margin-left: 1em; + font-style: italic; + text-indent: 2em; +} + +h1 { + padding: 1em; + margin-left: -1.5em; + font-size: x-large; + font-weight: bold; + color: #FFF; + background: #007; +} + +h2 { + padding: 0.5em 1em 0.5em 1em; + margin-left: -1.5em; + font-size: large; + font-weight: bold; + color: #FFF; + background: #009; +} + +h3, h4, h5, h6 { + color: #220088; + border-bottom: #5522bb solid 1px; +} + +.sourcecode > pre { + padding: 0.5em; + border: 1px dotted black; + background: #FFE; +} + +dt { + font-weight: bold +} + +dd { + margin-bottom: 0.7em; +} +CSS + +XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?> +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +} + +XHTML_FRAMESET_PREAMBLE = %{ +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> +} + +HEADER = XHTML_PREAMBLE + <<ENDHEADER +<html> + <head> + <title>%title%</title> + <meta http-equiv="Content-Type" content="text/html; charset=%charset%" /> + <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" /> + + <script language="JavaScript" type="text/javascript"> + // <![CDATA[ + + function toggleSource( id ) + { + var elem + var link + + if( document.getElementById ) + { + elem = document.getElementById( id ) + link = document.getElementById( "l_" + id ) + } + else if ( document.all ) + { + elem = eval( "document.all." + id ) + link = eval( "document.all.l_" + id ) + } + else + return false; + + if( elem.style.display == "block" ) + { + elem.style.display = "none" + link.innerHTML = "show source" + } + else + { + elem.style.display = "block" + link.innerHTML = "hide source" + } + } + + function openCode( url ) + { + window.open( url, "SOURCE_CODE", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=480,width=750" ).focus(); + } + // ]]> + </script> + </head> + + <body> +ENDHEADER + +FILE_PAGE = <<HTML +<table border='0' cellpadding='0' cellspacing='0' width="100%" class='banner'> + <tr><td> + <table width="100%" border='0' cellpadding='0' cellspacing='0'><tr> + <td class="file-title" colspan="2"><span class="file-title-prefix">File</span><br />%short_name%</td> + <td align="right"> + <table border='0' cellspacing="0" cellpadding="2"> + <tr> + <td>Path:</td> + <td>%full_path% +IF:cvsurl + (<a href="%cvsurl%">CVS</a>) +ENDIF:cvsurl + </td> + </tr> + <tr> + <td>Modified:</td> + <td>%dtm_modified%</td> + </tr> + </table> + </td></tr> + </table> + </td></tr> +</table><br /> +HTML + +################################################################### + +CLASS_PAGE = <<HTML +<table width="100%" border='0' cellpadding='0' cellspacing='0' class='banner'><tr> + <td class="file-title"><span class="file-title-prefix">%classmod%</span><br />%full_name%</td> + <td align="right"> + <table cellspacing="0" cellpadding="2"> + <tr valign="top"> + <td>In:</td> + <td> +START:infiles +HREF:full_path_url:full_path: +IF:cvsurl + (<a href="%cvsurl%">CVS</a>) +ENDIF:cvsurl +END:infiles + </td> + </tr> +IF:parent + <tr> + <td>Parent:</td> + <td> +IF:par_url + <a href="%par_url%"> +ENDIF:par_url +%parent% +IF:par_url + </a> +ENDIF:par_url + </td> + </tr> +ENDIF:parent + </table> + </td> + </tr> + </table> +HTML + +################################################################### + +METHOD_LIST = <<HTML + <div id="content"> +IF:diagram + <table cellpadding='0' cellspacing='0' border='0' width="100%"><tr><td align="center"> + %diagram% + </td></tr></table> +ENDIF:diagram + +IF:description + <div class="description">%description%</div> +ENDIF:description + +IF:requires + <div class="sectiontitle">Required Files</div> + <ul> +START:requires + <li>HREF:aref:name:</li> +END:requires + </ul> +ENDIF:requires + +IF:toc + <div class="sectiontitle">Contents</div> + <ul> +START:toc + <li><a href="#%href%">%secname%</a></li> +END:toc + </ul> +ENDIF:toc + +IF:methods + <div class="sectiontitle">Methods</div> + <ul> +START:methods + <li>HREF:aref:name:</li> +END:methods + </ul> +ENDIF:methods + +IF:includes +<div class="sectiontitle">Included Modules</div> +<ul> +START:includes + <li>HREF:aref:name:</li> +END:includes +</ul> +ENDIF:includes + +START:sections +IF:sectitle +<div class="sectiontitle"><a name="%secsequence%">%sectitle%</a></div> +IF:seccomment +<div class="description"> +%seccomment% +</div> +ENDIF:seccomment +ENDIF:sectitle + +IF:classlist + <div class="sectiontitle">Classes and Modules</div> + %classlist% +ENDIF:classlist + +IF:constants + <div class="sectiontitle">Constants</div> + <table border='0' cellpadding='5'> +START:constants + <tr valign='top'> + <td class="attr-name">%name%</td> + <td>=</td> + <td class="attr-value">%value%</td> + </tr> +IF:desc + <tr valign='top'> + <td> </td> + <td colspan="2" class="attr-desc">%desc%</td> + </tr> +ENDIF:desc +END:constants + </table> +ENDIF:constants + +IF:attributes + <div class="sectiontitle">Attributes</div> + <table border='0' cellpadding='5'> +START:attributes + <tr valign='top'> + <td class='attr-rw'> +IF:rw +[%rw%] +ENDIF:rw + </td> + <td class='attr-name'>%name%</td> + <td class='attr-desc'>%a_desc%</td> + </tr> +END:attributes + </table> +ENDIF:attributes + +IF:method_list +START:method_list +IF:methods +<div class="sectiontitle">%type% %category% methods</div> +START:methods +<div class="method"> + <div class="title"> +IF:callseq + <a name="%aref%"></a><b>%callseq%</b> +ENDIF:callseq +IFNOT:callseq + <a name="%aref%"></a><b>%name%</b>%params% +ENDIF:callseq +IF:codeurl +[ <a href="%codeurl%" target="SOURCE_CODE" onclick="javascript:openCode('%codeurl%'); return false;">source</a> ] +ENDIF:codeurl + </div> +IF:m_desc + <div class="description"> + %m_desc% + </div> +ENDIF:m_desc +IF:aka +<div class="aka"> + This method is also aliased as +START:aka + <a href="%aref%">%name%</a> +END:aka +</div> +ENDIF:aka +IF:sourcecode +<div class="sourcecode"> + <p class="source-link">[ <a href="javascript:toggleSource('%aref%_source')" id="l_%aref%_source">show source</a> ]</p> + <div id="%aref%_source" class="dyn-source"> +<pre> +%sourcecode% +</pre> + </div> +</div> +ENDIF:sourcecode +</div> +END:methods +ENDIF:methods +END:method_list +ENDIF:method_list +END:sections +</div> +HTML + +FOOTER = <<ENDFOOTER + </body> +</html> +ENDFOOTER + +BODY = HEADER + <<ENDBODY + !INCLUDE! <!-- banner header --> + + <div id="bodyContent"> + #{METHOD_LIST} + </div> + + #{FOOTER} +ENDBODY + +########################## Source code ########################## + +SRC_PAGE = XHTML_PREAMBLE + <<HTML +<html> +<head><title>%title%</title> +<meta http-equiv="Content-Type" content="text/html; charset=%charset%" /> +<style type="text/css"> +.ruby-comment { color: green; font-style: italic } +.ruby-constant { color: #4433aa; font-weight: bold; } +.ruby-identifier { color: #222222; } +.ruby-ivar { color: #2233dd; } +.ruby-keyword { color: #3333FF; font-weight: bold } +.ruby-node { color: #777777; } +.ruby-operator { color: #111111; } +.ruby-regexp { color: #662222; } +.ruby-value { color: #662222; font-style: italic } + .kw { color: #3333FF; font-weight: bold } + .cmt { color: green; font-style: italic } + .str { color: #662222; font-style: italic } + .re { color: #662222; } +</style> +</head> +<body bgcolor="white"> +<pre>%code%</pre> +</body> +</html> +HTML + +########################## Index ################################ + +FR_INDEX_BODY = <<HTML +!INCLUDE! +HTML + +FILE_INDEX = XHTML_PREAMBLE + <<HTML +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=%charset%" /> +<title>Index</title> +<style type="text/css"> +<!-- + body { + background-color: #EEE; + font-family: #{FONTS}; + color: #000; + margin: 0px; + } + .banner { + background: #005; + color: #FFF; + padding: 0.2em; + font-size: small; + font-weight: bold; + text-align: center; + } + .entries { + margin: 0.25em 1em 0 1em; + font-size: x-small; + } + a { + color: #00F; + text-decoration: none; + white-space: nowrap; + } + a:hover { + color: #77F; + text-decoration: underline; + } +--> +</style> +<base target="docwin" /> +</head> +<body> +<div class="banner">%list_title%</div> +<div class="entries"> +START:entries +<a href="%href%">%name%</a><br /> +END:entries +</div> +</body></html> +HTML + +CLASS_INDEX = FILE_INDEX +METHOD_INDEX = FILE_INDEX + +INDEX = XHTML_FRAMESET_PREAMBLE + <<HTML +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <title>%title%</title> + <meta http-equiv="Content-Type" content="text/html; charset=%charset%" /> +</head> + +<frameset cols="20%,*"> + <frameset rows="15%,55%,30%"> + <frame src="fr_file_index.html" title="Files" name="Files" /> + <frame src="fr_class_index.html" name="Classes" /> + <frame src="fr_method_index.html" name="Methods" /> + </frameset> + <frame src="%initial_page%" name="docwin" /> + <noframes> + <body bgcolor="white"> + Click <a href="html/index.html">here</a> for a non-frames + version of this page. + </body> + </noframes> +</frameset> + +</html> +HTML + +end +end + diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 27d4313862..a37dc6c0eb 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,9 @@ *Edge* +* Added Thin support to script/server. #488 [Bob Klosinski] + +* Fix script/about in production mode. #370 [Cheah Chu Yeow, Xavier Noria, David Krmpotic] + * Add the gem load paths before the framework is loaded, so certain gems like RedCloth and BlueCloth can be frozen. * Fix discrepancies with loading rails/init.rb from gems. diff --git a/railties/Rakefile b/railties/Rakefile index 78fe8e6662..f64b60b0dd 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -264,7 +264,7 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Railties -- Gluing the Engine to the Rails" rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('lib/*.rb') rdoc.rdoc_files.include('lib/rails/*.rb') diff --git a/railties/bin/about b/railties/bin/about index cd38a32abf..ed8deb0dfc 100644 --- a/railties/bin/about +++ b/railties/bin/about @@ -1,3 +1,4 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' -require 'commands/about' +$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" +require 'commands/about'
\ No newline at end of file diff --git a/railties/lib/commands/about.rb b/railties/lib/commands/about.rb index 313bc18c6a..7f53ac8a2e 100644 --- a/railties/lib/commands/about.rb +++ b/railties/lib/commands/about.rb @@ -1,2 +1,3 @@ require 'environment' +require 'rails/info' puts Rails::Info diff --git a/railties/lib/commands/server.rb b/railties/lib/commands/server.rb index 40ffdd1167..7306c248fb 100644 --- a/railties/lib/commands/server.rb +++ b/railties/lib/commands/server.rb @@ -13,11 +13,19 @@ rescue Exception # Mongrel not available end +begin + require_library_or_gem 'thin' +rescue Exception + # Thin not available +end + server = case ARGV.first - when "lighttpd", "mongrel", "new_mongrel", "webrick" + when "lighttpd", "mongrel", "new_mongrel", "webrick", "thin" ARGV.shift else - if defined?(Mongrel) + if defined?(Thin) + "thin" + elsif defined?(Mongrel) "mongrel" elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `lighttpd -version` }.blank? && defined?(FCGI) "lighttpd" @@ -33,6 +41,8 @@ case server puts "=> Booting lighttpd (use 'script/server webrick' to force WEBrick)" when "mongrel", "new_mongrel" puts "=> Booting Mongrel (use 'script/server webrick' to force WEBrick)" + when "thin" + puts "=> Booting Thin (use 'script/server webrick' to force WEBrick)" end %w(cache pids sessions sockets).each { |dir_to_make| FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) } diff --git a/railties/lib/commands/servers/thin.rb b/railties/lib/commands/servers/thin.rb new file mode 100644 index 0000000000..833469cab1 --- /dev/null +++ b/railties/lib/commands/servers/thin.rb @@ -0,0 +1,25 @@ +require 'rbconfig' +require 'commands/servers/base' +require 'thin' + + +options = ARGV.clone +options.insert(0,'start') unless Thin::Runner.commands.include?(options[0]) + +thin = Thin::Runner.new(options) + +puts "=> Rails #{Rails.version} application starting on http://#{thin.options[:address]}:#{thin.options[:port]}" +puts "=> Ctrl-C to shutdown server" + +log = Pathname.new("#{File.expand_path(RAILS_ROOT)}/log/#{RAILS_ENV}.log").cleanpath +open(log, (File::WRONLY | File::APPEND | File::CREAT)) unless File.exist? log +tail_thread = tail(log) +trap(:INT) { exit } + +begin + thin.run! +ensure + tail_thread.kill if tail_thread + puts 'Exiting' +end + diff --git a/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb b/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb index cae38e9a2a..03f6d5666e 100644 --- a/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb +++ b/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class <%= class_name %>ObserverTest < Test::Unit::TestCase +class <%= class_name %>ObserverTest < ActiveSupport::TestCase # Replace this with your real tests. test "the truth" do assert true diff --git a/railties/lib/rails_generator/generators/components/performance_test/USAGE b/railties/lib/rails_generator/generators/components/performance_test/USAGE new file mode 100644 index 0000000000..d84051eb02 --- /dev/null +++ b/railties/lib/rails_generator/generators/components/performance_test/USAGE @@ -0,0 +1,8 @@ +Description: + Stubs out a new performance test. Pass the name of the test, either + CamelCased or under_scored, as an argument. The new test class is + generated in test/performance/testname_test.rb + +Example: + `./script/generate performance_test GeneralStories` creates a GeneralStories + performance test in test/performance/general_stories_test.rb diff --git a/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb b/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb new file mode 100644 index 0000000000..fbcc1cf683 --- /dev/null +++ b/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb @@ -0,0 +1,16 @@ +class PerformanceTestGenerator < Rails::Generator::NamedBase + default_options :skip_migration => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_path, class_name, "#{class_name}Test" + + # performance test directory + m.directory File.join('test/performance', class_path) + + # performance test stub + m.template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb") + end + end +end diff --git a/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb b/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb new file mode 100644 index 0000000000..352ff48054 --- /dev/null +++ b/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb @@ -0,0 +1,8 @@ +require 'performance/test_helper' + +class <%= class_name %>Test < ActionController::PerformanceTest + # Replace this with your real tests. + def test_homepage + get '/' + end +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb index e2902bf4b9..5fecfe0167 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +++ b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb @@ -1,5 +1,5 @@ class ScaffoldGenerator < Rails::Generator::NamedBase - default_options :skip_timestamps => false, :skip_migration => false + default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false attr_reader :controller_name, :controller_class_path, @@ -16,6 +16,11 @@ class ScaffoldGenerator < Rails::Generator::NamedBase def initialize(runtime_args, runtime_options = {}) super + if @name == @name.pluralize && !options[:force_plural] + logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural." + @name = @name.singularize + end + @controller_name = @name.pluralize base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) @@ -81,6 +86,8 @@ class ScaffoldGenerator < Rails::Generator::NamedBase "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } opt.on("--skip-migration", "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + opt.on("--force-plural", + "Forces the generation of a plural ModelName") { |v| options[:force_plural] = v } end def scaffold_views diff --git a/railties/test/generators/rails_scaffold_generator_test.rb b/railties/test/generators/rails_scaffold_generator_test.rb index 220f9e372e..de6b38dafe 100644 --- a/railties/test/generators/rails_scaffold_generator_test.rb +++ b/railties/test/generators/rails_scaffold_generator_test.rb @@ -1,4 +1,5 @@ require 'generators/generator_test_helper' +require 'abstract_unit' class RailsScaffoldGeneratorTest < GeneratorTestCase @@ -104,4 +105,45 @@ class RailsScaffoldGeneratorTest < GeneratorTestCase assert_added_route_for :products end + uses_mocha("scaffold_force_plural_names") do + def test_scaffolded_plural_names + Rails::Generator::Base.logger.expects(:warning) + g = Rails::Generator::Base.instance('scaffold', %w(ProductLines)) + assert_equal "ProductLines", g.controller_name + assert_equal "ProductLines", g.controller_class_name + assert_equal "ProductLine", g.controller_singular_name + assert_equal "product_lines", g.controller_plural_name + assert_equal "product_lines", g.controller_file_name + assert_equal "product_lines", g.controller_table_name + end + end + + def test_scaffold_plural_model_name_without_force_plural_generates_singular_model + run_generator('scaffold', %w(Products name:string)) + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_plural_model_name_with_force_plural_forces_plural_model + run_generator('scaffold', %w(Products name:string --force-plural)) + + assert_generated_model_for :products + assert_generated_functional_test_for :products + assert_generated_unit_test_for :products + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end + end |