diff options
6 files changed, 127 insertions, 31 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 7035e360ec..3e15caee2d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -55,6 +55,14 @@ module ActionDispatch path = args.first end + if @scope[:module] && options[:to] + if options[:to].to_s.include?("#") + options[:to] = "#{@scope[:module]}/#{options[:to]}" + elsif @scope[:controller].nil? + options[:to] = "#{@scope[:module]}##{options[:to]}" + end + end + path = normalize_path(path) if using_match_shorthand?(path, options) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 192b365bee..80a00b2a10 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -11,21 +11,25 @@ module ActionView # Form helpers are designed to make working with resources much easier # compared to using vanilla HTML. # - # Model-based forms are created with +form_for+. That method yields a form + # Forms for models are created with +form_for+. That method yields a form # builder that knows the model the form is about. The form builder is thus # able to generate default values for input fields that correspond to model - # attributes, and also convenient element names, IDs, endpoints, etc. + # attributes, and also convenient names, IDs, endpoints, etc. # # Conventions in the generated field names allow controllers to receive form # data nicely structured in +params+ with no effort on your side. # - # For example, to create a new +Person+ resource you typically set up a new - # instance in <tt>PeopleController#new</tt> action, <tt>@person</tt>, and - # write the form in <tt>new.html.erb</tt> this way: + # For example, to create a new person you typically set up a new instance of + # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and + # pass it to +form_for+: # # <%= form_for @person do |f| %> - # <%= f.text_field :first_name %> - # <%= f.text_field :last_name %> + # <%= f.label :first_name %>: + # <%= f.text_field :first_name %><br /> + # + # <%= f.label :last_name %>: + # <%= f.text_field :last_name %><br /> + # # <%= f.submit %> # <% end %> # @@ -35,16 +39,54 @@ module ActionView # <div style="margin:0;padding:0;display:inline"> # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # </div> - # <input id="person_first_name" name="person[first_name]" size="30" type="text" /> - # <input id="person_last_name" name="person[last_name]" size="30" type="text" /> + # <label for="person_first_name">First name</label>: + # <input id="person_first_name" name="person[first_name]" size="30" type="text" /><br /> + # + # <label for="person_last_name">Last name</label>: + # <input id="person_last_name" name="person[last_name]" size="30" type="text" /><br /> + # # <input id="person_submit" name="commit" type="submit" value="Create Person" /> # </form> # - # Because of the names of the input fields, the controller gets a <tt>:person</tt> - # nested hash in +params+ with the corresponding first and last names. That hash - # is ready to be passed to <tt>Person.create</tt> like this: + # As you see, the HTML reflects knowledge about the resource in several spots, + # like the path the form should be submitted to, or the names of the input fields. + # + # In particular, thanks to the conventions followed in the generated field names, the + # controller gets a nested hash <tt>params[:person]</tt> with the person attributes + # set in the form. That hash is ready to be passed to <tt>Person.create</tt>: + # + # if @person = Person.create(params[:person]) + # # success + # else + # # error handling + # end + # + # Interestingly, the exact same view code in the previous example can be used to edit + # a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256, + # the code above as is would yield instead: + # + # <form action="/people/256" class="edit_person" id="edit_person_256" method="post"> + # <div style="margin:0;padding:0;display:inline"> + # <input name="_method" type="hidden" value="put" /> + # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> + # </div> + # <label for="person_first_name">First name</label>: + # <input id="person_first_name" name="person[first_name]" size="30" type="text" value="John" /><br /> + # + # <label for="person_last_name">Last name</label>: + # <input id="person_last_name" name="person[last_name]" size="30" type="text" value="Smith" /><br /> + # + # <input id="person_submit" name="commit" type="submit" value="Update Person" /> + # </form> + # + # Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>. + # That works that way because the involved helpers know whether the resource is a new record or not, + # and generate HTML accordingly. + # + # The controller would receive the form data again in <tt>params[:person]</tt>, ready to be + # passed to <tt>Person#update_attributes</tt>: # - # if person = Person.create(params[:person]) + # if @person.update_attributes(params[:person]) # # success # else # # error handling diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index d38c48bfd4..411a28d8ee 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -140,7 +140,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest namespace :account do match 'shorthand' - match 'description', :to => "account#description", :as => "description" + match 'description', :to => "description", :as => "description" resource :subscription, :credit, :credit_card root :to => "account#index" @@ -864,7 +864,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest with_test_routes do assert_equal '/account', account_root_path get '/account' - assert_equal 'account#index', @response.body + assert_equal 'account/account#index', @response.body 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 7169c8f3f0..ceb1adc9e0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -670,8 +670,30 @@ module ActiveRecord def tables(name = nil) query(<<-SQL, name).map { |row| row[0] } SELECT tablename + FROM pg_tables + WHERE schemaname = ANY (current_schemas(false)) + SQL + end + + def table_exists?(name) + name = name.to_s + schema, table = name.split('.', 2) + + unless table # A table was provided without a schema + table = schema + schema = nil + end + + if name =~ /^"/ # Handle quoted table names + table = name + schema = nil + end + + query(<<-SQL).first[0].to_i > 0 + SELECT COUNT(*) FROM pg_tables - WHERE schemaname = ANY (current_schemas(false)) + WHERE tablename = '#{table.gsub(/(^"|"$)/,'')}' + #{schema ? "AND schemaname = '#{schema}'" : ''} SQL end diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index a294848fa3..3ed73786a7 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -52,6 +52,21 @@ class SchemaTest < ActiveRecord::TestCase @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" end + def test_table_exists? + [Thing1, Thing2, Thing3, Thing4].each do |klass| + name = klass.table_name + assert @connection.table_exists?(name), "'#{name}' table should exist" + end + end + + def test_table_exists_wrong_schema + assert(!@connection.table_exists?("foo.things"), "table should not exist") + end + + def test_table_exists_quoted_table + assert(@connection.table_exists?('"things.table"'), "table should exist") + end + def test_with_schema_prefixed_table_name assert_nothing_raised do assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 725acad43a..d2bcd7a778 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -1,5 +1,4 @@ require 'active_support/core_ext/kernel/singleton_class' -require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/module/remove_method' class Class @@ -41,21 +40,31 @@ class Class def class_attribute(*attrs) instance_writer = !attrs.last.is_a?(Hash) || attrs.pop[:instance_writer] - s = singleton_class - attrs.each do |attr| - s.send(:define_method, attr) { } - s.send(:define_method, :"#{attr}?") { !!send(attr) } - s.send(:define_method, :"#{attr}=") do |value| - singleton_class.remove_possible_method(attr) - singleton_class.send(:define_method, attr) { value } - end + attrs.each do |name| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def self.#{name}() nil end + def self.#{name}?() !!#{name} end + + def self.#{name}=(val) + singleton_class.class_eval do + remove_possible_method(:#{name}) + define_method(:#{name}) { val } + end + end + + def #{name} + defined?(@#{name}) ? @#{name} : singleton_class.#{name} + end - define_method(attr) { self.singleton_class.send(attr) } - define_method(:"#{attr}?") { !!send(attr) } - define_method(:"#{attr}=") do |value| - singleton_class.remove_possible_method(attr) - singleton_class.send(:define_method, attr) { value } - end if instance_writer + def #{name}? + !!#{name} + end + RUBY + + if instance_writer + body = "def #{name}=(value) @#{name} = value end" + class_eval body, __FILE__, __LINE__ - 1 + end end end end |