aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb24
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb17
-rw-r--r--actionview/test/template/form_helper/form_with_test.rb116
-rw-r--r--activejob/test/helper.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/column.rb3
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb2
-rw-r--r--activerecord/test/cases/primary_keys_test.rb6
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb7
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb2
-rw-r--r--activesupport/test/core_ext/duration_test.rb2
-rw-r--r--guides/Rakefile5
-rw-r--r--guides/rails_guides/kindle.rb12
-rw-r--r--guides/source/generators.md10
15 files changed, 161 insertions, 52 deletions
diff --git a/Gemfile b/Gemfile
index e30858a124..f56e31b096 100644
--- a/Gemfile
+++ b/Gemfile
@@ -44,7 +44,7 @@ group :doc do
gem "sdoc", "1.0.0.beta2"
gem "redcarpet", "~> 3.2.3", platforms: :ruby
gem "w3c_validators"
- gem "kindlerb", ">= 1.0.1"
+ gem "kindlerb", "~> 1.2.0"
end
# Active Support.
diff --git a/Gemfile.lock b/Gemfile.lock
index 0763efc001..3a616402f1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -213,7 +213,7 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.0.2)
- kindlerb (1.0.1)
+ kindlerb (1.2.0)
mustache
nokogiri
libxml-ruby (2.9.0)
@@ -396,7 +396,7 @@ DEPENDENCIES
hiredis
jquery-rails
json (>= 2.0.0)
- kindlerb (>= 1.0.1)
+ kindlerb (~> 1.2.0)
libxml-ruby
listen (>= 3.0.5, < 3.2)
minitest (< 5.3.4)
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 6c632b8e75..a66a56d441 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -513,6 +513,17 @@ module ActionView
# <input type="text" name="post[title]" value="<the title of the post>">
# </form>
#
+ # # Though the fields don't have to correspond to model attributes:
+ # <%= form_with model: Cat.new do |form| %>
+ # <%= form.text_field :cats_dont_have_gills %>
+ # <%= form.text_field :but_in_forms_they_can %>
+ # <% end %>
+ # # =>
+ # <form action="/cats" method="post" data-remote="true">
+ # <input type="text" name="cat[cats_dont_have_gills]">
+ # <input type="text" name="cat[but_in_forms_they_can]">
+ # </form>
+ #
# The parameters in the forms are accessible in controllers according to
# their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
@@ -521,7 +532,7 @@ module ActionView
# By default +form_with+ attaches the <tt>data-remote</tt> attribute
# submitting the form via an XMLHTTPRequest in the background if an
# Unobtrusive JavaScript driver, like rails-ujs, is used. See the
- # <tt>:remote</tt> option for more.
+ # <tt>:local</tt> option for more.
#
# For ease of comparison the examples above left out the submit button,
# as well as the auto generated hidden fields that enable UTF-8 support
@@ -700,6 +711,7 @@ module ActionView
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
# end
def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
+ options[:allow_method_names_outside_object] = true
options[:skip_default_ids] = true
if model
@@ -976,14 +988,14 @@ module ActionView
# <%= fields :comment do |fields| %>
# <%= fields.text_field :body %>
# <% end %>
- # # => <input type="text" name="comment[body] id="comment_body">
+ # # => <input type="text" name="comment[body]>
#
# # Using a model infers the scope and assigns field values:
# <%= fields model: Comment.new(body: "full bodied") do |fields| %<
# <%= fields.text_field :body %>
# <% end %>
# # =>
- # <input type="text" name="comment[body] id="comment_body" value="full bodied">
+ # <input type="text" name="comment[body] value="full bodied">
#
# # Using +fields+ with +form_with+:
# <%= form_with model: @post do |form| %>
@@ -1025,6 +1037,7 @@ module ActionView
# to work with an object as a base, like
# FormOptionHelper#collection_select and DateHelper#datetime_select.
def fields(scope = nil, model: nil, **options, &block)
+ options[:allow_method_names_outside_object] = true
options[:skip_default_ids] = true
if model
@@ -1492,7 +1505,7 @@ module ActionView
private
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: false,
skip_enforcing_utf8: false, **options)
- html_options = options.except(:index, :include_id, :skip_default_ids, :builder).merge(html)
+ html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
html_options[:enforce_utf8] = !skip_enforcing_utf8
@@ -1626,7 +1639,7 @@ module ActionView
def initialize(object_name, object, template, options)
@nested_child_index = {}
@object_name, @object, @template, @options = object_name, object, template, options
- @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids) : {}
+ @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {}
convert_to_legacy_options(@options)
@@ -1933,6 +1946,7 @@ module ActionView
# See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
def fields(scope = nil, model: nil, **options, &block)
+ options[:allow_method_names_outside_object] = true
options[:skip_default_ids] = true
convert_to_legacy_options(options)
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index b8c446cbed..74d6324771 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -14,6 +14,7 @@ module ActionView
@object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
@object = retrieve_object(options.delete(:object))
@skip_default_ids = options.delete(:skip_default_ids)
+ @allow_method_names_outside_object = options.delete(:allow_method_names_outside_object)
@options = options
@auto_index = Regexp.last_match ? retrieve_autoindex(Regexp.last_match.pre_match) : nil
end
@@ -26,7 +27,11 @@ module ActionView
private
def value(object)
- object.public_send @method_name if object
+ if @allow_method_names_outside_object
+ object.public_send @method_name if object && object.respond_to?(@method_name)
+ else
+ object.public_send @method_name if object
+ end
end
def value_before_type_cast(object)
@@ -93,7 +98,10 @@ module ActionView
def tag_name(multiple = false, index = nil)
# a little duplication to construct less strings
- if index
+ case
+ when @object_name.empty?
+ "#{sanitized_method_name}#{"[]" if multiple}"
+ when index
"#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
else
"#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
@@ -102,7 +110,10 @@ module ActionView
def tag_id(index = nil)
# a little duplication to construct less strings
- if index
+ case
+ when @object_name.empty?
+ sanitized_method_name.dup
+ when index
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
else
"#{sanitized_object_name}_#{sanitized_method_name}"
diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb
index c078b47e14..dd0883e071 100644
--- a/actionview/test/template/form_helper/form_with_test.rb
+++ b/actionview/test/template/form_helper/form_with_test.rb
@@ -323,6 +323,75 @@ class FormWithActsLikeFormForTest < FormWithTest
assert_dom_equal expected, output_buffer
end
+ def test_form_with_only_url_on_create
+ form_with(url: "/posts") do |f|
+ concat f.label :title, "Label me"
+ concat f.text_field :title
+ end
+
+ expected = whole_form("/posts") do
+ '<label for="title">Label me</label>' +
+ '<input type="text" name="title">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_only_url_on_update
+ form_with(url: "/posts/123") do |f|
+ concat f.label :title, 'Label me'
+ concat f.text_field :title
+ end
+
+ expected = whole_form("/posts/123") do
+ '<label for="title">Label me</label>' +
+ '<input type="text" name="title">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_general_attributes
+ form_with(url: "/posts/123") do |f|
+ concat f.text_field :no_model_to_back_this_badboy
+ end
+
+ expected = whole_form("/posts/123") do
+ '<input type="text" name="no_model_to_back_this_badboy">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_attribute_not_on_model
+ form_with(model: @post) do |f|
+ concat f.text_field :this_dont_exist_on_post
+ end
+
+ expected = whole_form("/posts/123", method: :patch) do
+ '<input type="text" name="post[this_dont_exist_on_post]">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_doesnt_call_private_or_protected_properties_on_form_object_skipping_value
+ obj = Class.new do
+ private def private_property
+ "That would be great."
+ end
+
+ protected def protected_property
+ "I believe you have my stapler."
+ end
+ end.new
+
+ form_with(model: obj, scope: "other_name", url: "/", id: "edit-other-name") do |f|
+ assert_dom_equal '<input type="hidden" name="other_name[private_property]">', f.hidden_field(:private_property)
+ assert_dom_equal '<input type="hidden" name="other_name[protected_property]">', f.hidden_field(:protected_property)
+ end
+ end
+
def test_form_with_with_collection_radio_buttons
post = Post.new
def post.active; false; end
@@ -599,18 +668,6 @@ class FormWithActsLikeFormForTest < FormWithTest
assert_dom_equal expected, output_buffer
end
- def test_form_tags_do_not_call_private_properties_on_form_object
- obj = Class.new do
- private def private_property
- raise "This method should not be called."
- end
- end.new
-
- form_with(model: obj, scope: "other_name", url: "/", id: "edit-other-name") do |f|
- assert_raise(NoMethodError) { f.hidden_field(:private_property) }
- end
- end
-
def test_form_with_with_method_as_part_of_html_options
form_with(model: @post, url: "/", id: "create-post", html: { method: :delete }) do |f|
concat f.text_field(:title)
@@ -878,6 +935,41 @@ class FormWithActsLikeFormForTest < FormWithTest
end
end
+ def test_fields_with_attributes_not_on_model
+ form_with(model: @post) do |f|
+ concat f.fields(:comment) { |c|
+ concat c.text_field :dont_exist_on_model
+ }
+ end
+
+ expected = whole_form("/posts/123", method: :patch) do
+ '<input type="text" name="post[comment][dont_exist_on_model]">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_attributes_not_on_model_deep_nested
+ @comment.save
+ form_with(scope: :posts) do |f|
+ f.fields("post[]", model: @post) do |f2|
+ f2.text_field(:id)
+ @post.comments.each do |comment|
+ concat f2.fields("comment[]", model: comment) { |c|
+ concat c.text_field(:dont_exist_on_model)
+ }
+ end
+ end
+ end
+
+ expected = whole_form do
+ '<input name="posts[post][0][comment][1][dont_exist_on_model]" type="text">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+
def test_nested_fields
@comment.body = "Hello World"
form_with(model: @post) do |f|
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index 758506b3c0..776f7788de 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -5,6 +5,7 @@ ActiveSupport.halt_callback_chains_on_return_false = false
GlobalID.app = "aj"
@adapter = ENV["AJ_ADAPTER"] || "inline"
+puts "Using #{@adapter}"
if ENV["AJ_INTEGRATION_TESTS"]
require "support/integration/helper"
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/column.rb b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
index c66d543752..1499c1681f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
@@ -5,8 +5,7 @@ module ActiveRecord
delegate :extra, to: :sql_type_metadata, allow_nil: true
def unsigned?
- # enum and set types do not allow being defined as unsigned.
- !/\A(?:enum|set)\b/.match?(sql_type) && /\bunsigned\b/.match?(sql_type)
+ /\bunsigned(?: zerofill)?\z/.match?(sql_type)
end
def case_sensitive?
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 268800d538..a0823be143 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -15,6 +15,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
t.bigint :unsigned_bigint, unsigned: true
t.float :unsigned_float, unsigned: true
t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2
+ t.column :unsigned_zerofill, "int unsigned zerofill"
end
end
@@ -48,7 +49,6 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
t.unsigned_bigint :unsigned_bigint_t
t.unsigned_float :unsigned_float_t
t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2
- t.column :unsigned_zerofill, "int unsigned zerofill"
end
@connection.columns("unsigned_types").select { |c| /^unsigned_/.match?(c.name) }.each do |column|
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index b434f4a6b6..1d72899102 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -129,12 +129,6 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
end
- def test_supports_primary_key
- assert_nothing_raised do
- ActiveRecord::Base.connection.supports_primary_key?
- end
- end
-
if ActiveRecord::Base.connection.supports_primary_key?
def test_primary_key_returns_value_if_it_exists
klass = Class.new(ActiveRecord::Base) do
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 97096e31e7..bea78d2a95 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -148,12 +148,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_4.*limit: 4}, output
end
- if current_adapter?(:SQLite3Adapter)
- 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
- elsif current_adapter?(:OracleAdapter)
+ if current_adapter?(:SQLite3Adapter, :OracleAdapter)
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
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 08c9ef8f02..b94368df14 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -21,8 +21,6 @@ module I18n
I18n::Railtie.initialize_i18n(app)
end
- protected
-
@i18n_inited = false
# Setup i18n configuration.
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 26a9ea4aee..6f7f16da5b 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -21,7 +21,7 @@ class DurationTest < ActiveSupport::TestCase
end
def test_instance_of
- assert 1.minute.instance_of?(Fixnum)
+ assert 1.minute.instance_of?(1.class)
assert 2.days.instance_of?(ActiveSupport::Duration)
assert !3.second.instance_of?(Numeric)
end
diff --git a/guides/Rakefile b/guides/Rakefile
index bf501f6a64..f241cf5918 100644
--- a/guides/Rakefile
+++ b/guides/Rakefile
@@ -13,8 +13,9 @@ namespace :guides do
desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/gp/feature.html?docId=1000765211"
task :kindle do
- unless `kindlerb -v 2> /dev/null` =~ /kindlerb 1.0.1/
- abort "Please `gem install kindlerb` and make sure you have `kindlegen` in your PATH"
+ require 'kindlerb'
+ unless Kindlerb.kindlegen_available?
+ abort "Please run `setupkindlerb` to install kindlegen"
end
unless `convert` =~ /convert/
abort "Please install ImageMagick`"
diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb
index 4b73ae9518..5ac802c817 100644
--- a/guides/rails_guides/kindle.rb
+++ b/guides/rails_guides/kindle.rb
@@ -1,9 +1,6 @@
#!/usr/bin/env ruby
-unless `which kindlerb`
- abort "Please gem install kindlerb"
-end
-
+require 'kindlerb'
require "nokogiri"
require "fileutils"
require "yaml"
@@ -28,10 +25,9 @@ module Kindle
generate_document_metadata(mobi_outfile)
puts "Creating MOBI document with kindlegen. This may take a while."
- cmd = "kindlerb . > #{File.absolute_path logfile} 2>&1"
- puts cmd
- system(cmd)
- puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}"
+ if Kindlerb.run(output_dir)
+ puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}"
+ end
end
end
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 56edfcc8ae..d0b6cef3fd 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -208,7 +208,15 @@ $ bin/rails generate scaffold User name:string
Looking at this output, it's easy to understand how generators work in Rails 3.0 and above. The scaffold generator doesn't actually generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication.
-Our first customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds. We can achieve that by changing our configuration to the following:
+If we want to avoid generating the default `app/assets/stylesheets/scaffolds.scss` file when scaffolding a new resource we can disable `scaffold_stylesheet`:
+
+```ruby
+ config.generators do |g|
+ g.scaffold_stylesheet false
+ end
+```
+
+The next customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds altogether. We can achieve that by changing our configuration to the following:
```ruby
config.generators do |g|