Merge branch 'master' into joindep

* master: (44 commits)
  grammar fix (reverted in e9a1ecd)
  Revert "fixed a doc bug in the CHANGELOG.md s/does no longer depend on/no longer depends on/"
  Add missed require making `enable_warnings` available
  Prepare generated Gemfile for Capistrano 3
  Added --model-name option scaffold_controller_generator.
  read the association instead of sending
  we should have unique sponsorable ids in the fixtures at least
  simplify populating the ordering hash
  the preloader for the RHS has all the preloaded records, so ask it
  only calculate offset index once. #12537
  Remove size alias for length validation
  Fix `singleton_class?`
  Minor Refactoring to `NumberHelper#number_to_human`
  `$SAFE = 4;` has been removed with Ruby 2.1
  scope_chain should not be mutated for other reflections
  Remove `default_primary_key_type`  and extract contains of `native_database_types` to a constant since they aren't conditional now in SQLite3Adapter. Makes it more like other adapters.
  cleanup changelog entry format. [ci skip]
  Extract a function to determine if the default value is a function
  Push default_function to superclass to avoid method check
  Dump the default function when the primary key is uuid
  ...

Conflicts:
	activerecord/lib/active_record/relation/finder_methods.rb
This commit is contained in:
Aaron Patterson 2013-10-15 14:45:00 -07:00
commit ee46f1d5d2
56 changed files with 365 additions and 203 deletions

@ -531,7 +531,7 @@ def select_hour(datetime, options = {}, html_options = {})
# my_date = Time.now + 2.days
#
# # Generates a select field for days that defaults to the day for the date in my_date.
# select_day(my_time)
# select_day(my_date)
#
# # Generates a select field for days that defaults to the number given.
# select_day(5)
@ -541,7 +541,7 @@ def select_hour(datetime, options = {}, html_options = {})
#
# # Generates a select field for days that defaults to the day for the date in my_date
# # that is named 'due' rather than 'day'.
# select_day(my_time, field_name: 'due')
# select_day(my_date, field_name: 'due')
#
# # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
# # generic prompt.

@ -455,7 +455,7 @@ def mail_to(email_address, name = nil, html_options = {}, &block)
html_options, name = name, nil if block_given?
html_options = (html_options || {}).stringify_keys
extras = %w{ cc bcc body subject }.map { |item|
extras = %w{ cc bcc body subject }.map! { |item|
option = html_options.delete(item) || next
"#{item}=#{Rack::Utils.escape_path(option)}"
}.compact

@ -1,3 +1,47 @@
* `scope_chain` should not be mutated for other reflections.
Currently `scope_chain` uses same array for building different
`scope_chain` for different associations. During processing
these arrays are sometimes mutated and because of in-place
mutation the changed `scope_chain` impacts other reflections.
Fix is to dup the value before adding to the `scope_chain`.
Fixes #3882.
*Neeraj Singh*
* Prevent the inversed association from being reloaded on save.
Fixes #9499.
*Dmitry Polushkin*
* Generate subquery for `Relation` if it passed as array condition for `where`
method.
Example:
# Before
Blog.where('id in (?)', Blog.where(id: 1))
# => SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = 1
# => SELECT "blogs".* FROM "blogs" WHERE (id IN (1))
# After
Blog.where('id in (?)', Blog.where(id: 1).select(:id))
# => SELECT "blogs".* FROM "blogs"
# WHERE "blogs"."id" IN (SELECT "blogs"."id" FROM "blogs" WHERE "blogs"."id" = 1)
Fixes #12415.
*Paul Nikitochkin*
* For missed association exception message
which is raised in `ActiveRecord::Associations::Preloader` class
added owner record class name in order to simplify to find problem code.
*Paul Nikitochkin*
* `has_and_belongs_to_many` is now transparently implemented in terms of
`has_many :through`. Behavior should remain the same, if not, it is a bug.
@ -199,6 +243,12 @@
*Yves Senn*
* Fixes bug when using includes combined with select, the select statement was overwritten.
Fixes #11773
*Edo Balvers*
* Load fixtures from linked folders.
*Kassio Borges*
@ -538,7 +588,7 @@
*Neeraj Singh*
* Fixture setup does no longer depend on `ActiveRecord::Base.configurations`.
* Fixture setup no longer depends on `ActiveRecord::Base.configurations`.
This is relevant when `ENV["DATABASE_URL"]` is used in place of a `database.yml`.
*Yves Senn*

@ -17,6 +17,7 @@ module Associations
# HasManyThroughAssociation + ThroughAssociation
class Association #:nodoc:
attr_reader :owner, :target, :reflection
attr_accessor :inversed
delegate :options, :to => :reflection
@ -42,6 +43,7 @@ def reset
@loaded = false
@target = nil
@stale_state = nil
@inversed = false
end
# Reloads the \target and returns +self+ on success.
@ -59,8 +61,9 @@ def loaded?
# Asserts the \target has been loaded setting the \loaded flag to +true+.
def loaded!
@loaded = true
@loaded = true
@stale_state = stale_state
@inversed = false
end
# The target is stale if the target no longer points to the record(s) that the
@ -70,7 +73,7 @@ def loaded!
#
# Note that if the target has not been loaded, it is not considered stale.
def stale_target?
loaded? && @stale_state != stale_state
!inversed && loaded? && @stale_state != stale_state
end
# Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@ -104,6 +107,7 @@ def set_inverse_instance(record)
if record && invertible_for?(record)
inverse = record.association(inverse_reflection_for(record).name)
inverse.target = owner
inverse.inversed = true
end
end

@ -195,9 +195,7 @@ def destroy_all
# Count all records using SQL. Construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
def count(column_name = nil)
relation = scope
if association_scope.distinct_value
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.

@ -669,8 +669,8 @@ def distinct
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
def count(column_name = nil, options = {})
@association.count(column_name, options)
def count(column_name = nil)
@association.count(column_name)
end
# Returns the size of the collection. If the collection hasn't been loaded,

@ -153,13 +153,13 @@ def records_by_reflection(association, records)
records.group_by do |record|
reflection = record.class.reflect_on_association(association)
reflection || raise_config_error(association)
reflection || raise_config_error(record, association)
end
end
def raise_config_error(association)
def raise_config_error(record, association)
raise ActiveRecord::ConfigurationError,
"Association named '#{association}' was not found; " \
"Association named '#{association}' was not found on #{record.class.name}; " \
"perhaps you misspelled it?"
end

@ -15,7 +15,7 @@ def associated_records_by_owner(preloader)
through_reflection.name,
through_scope)
through_records = owners.map do |owner, h|
through_records = owners.map do |owner|
association = owner.association through_reflection.name
[owner, Array(association.reader)]
@ -29,29 +29,30 @@ def associated_records_by_owner(preloader)
source_reflection.name,
reflection_scope)
@preloaded_records = preloaders.flat_map(&:preloaded_records)
middle_to_pl = preloaders.each_with_object({}) do |pl,h|
pl.owners.each { |middle|
h[middle] = pl
}
end
record_offset = {}
@preloaded_records.each_with_index do |record,i|
record_offset[record] = i
end
through_records.each_with_object({}) { |(lhs,center),records_by_owner|
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
rhs_records = middles.flat_map { |r|
r.send(source_reflection.name)
association = r.association source_reflection.name
association.reader
}.compact
loaded_records = pl.preloaded_records
i = 0
record_index = loaded_records.each_with_object({}) { |r,indexes|
indexes[r] = i
i += 1
}
records = rhs_records.sort_by { |rhs| record_index[rhs] }
@preloaded_records.concat rhs_records
records
rhs_records.sort_by { |rhs| record_offset[rhs] }
end
}
end

@ -13,7 +13,7 @@ module Format
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
end
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function
attr_accessor :primary, :coder
alias :encoded? :coder
@ -27,16 +27,17 @@ module Format
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
# +null+ determines if this column allows +NULL+ values.
def initialize(name, default, sql_type = nil, null = true)
@name = name
@sql_type = sql_type
@null = null
@limit = extract_limit(sql_type)
@precision = extract_precision(sql_type)
@scale = extract_scale(sql_type)
@type = simplified_type(sql_type)
@default = extract_default(default)
@primary = nil
@coder = nil
@name = name
@sql_type = sql_type
@null = null
@limit = extract_limit(sql_type)
@precision = extract_precision(sql_type)
@scale = extract_scale(sql_type)
@type = simplified_type(sql_type)
@default = extract_default(default)
@default_function = nil
@primary = nil
@coder = nil
end
# Returns +true+ if the column is either of type string or text.

@ -45,12 +45,12 @@ def postgresql_connection(config)
module ConnectionAdapters
# PostgreSQL-specific extensions to column definitions in a table.
class PostgreSQLColumn < Column #:nodoc:
attr_accessor :array, :default_function
attr_accessor :array
# Instantiates a new PostgreSQL column definition in a table.
def initialize(name, default, oid_type, sql_type = nil, null = true)
@oid_type = oid_type
default_value = self.class.extract_value_from_default(default)
@default_function = default if !default_value && default && default =~ /.+\(.*\)/
if sql_type =~ /\[\]$/
@array = true
super(name, default_value, sql_type[0..sql_type.length - 3], null)
@ -58,6 +58,8 @@ def initialize(name, default, oid_type, sql_type = nil, null = true)
@array = false
super(name, default_value, sql_type, null)
end
@default_function = default if has_default_function?(default_value, default)
end
# :stopdoc:
@ -148,6 +150,10 @@ def type_cast(value)
private
def has_default_function?(default_value, default)
!default_value && (%r{\w+(.*)} === default)
end
def extract_limit(sql_type)
case sql_type
when /^bigint/i; 8
@ -442,7 +448,7 @@ def adapter_name
def prepare_column_options(column, types)
spec = super
spec[:array] = 'true' if column.respond_to?(:array) && column.array
spec[:default] = "\"#{column.default_function}\"" if column.respond_to?(:default_function) && column.default_function
spec[:default] = "\"#{column.default_function}\"" if column.default_function
spec
end

@ -55,6 +55,21 @@ def binary_to_string(value)
class SQLite3Adapter < AbstractAdapter
include Savepoints
NATIVE_DATABASE_TYPES = {
primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
string: { name: "varchar", limit: 255 },
text: { name: "text" },
integer: { name: "integer" },
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "datetime" },
timestamp: { name: "datetime" },
time: { name: "time" },
date: { name: "date" },
binary: { name: "blob" },
boolean: { name: "boolean" }
}
class Version
include Comparable
@ -183,11 +198,6 @@ def supports_count_distinct? #:nodoc:
true
end
# Returns true
def supports_autoincrement? #:nodoc:
true
end
def supports_index_sort_order?
true
end
@ -200,20 +210,7 @@ def allowed_index_name_length
end
def native_database_types #:nodoc:
{
:primary_key => default_primary_key_type,
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "integer" },
:float => { :name => "float" },
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "time" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "boolean" }
}
NATIVE_DATABASE_TYPES
end
# Returns the current database encoding format as a string, eg: 'UTF-8'
@ -596,14 +593,6 @@ def sqlite_version
@sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
end
def default_primary_key_type
if supports_autoincrement?
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
else
'INTEGER PRIMARY KEY NOT NULL'
end
end
def translate_exception(exception, message)
case exception.message
when /column(s)? .* (is|are) not unique/

@ -50,7 +50,7 @@ def sum(*)
0
end
def calculate(_operation, _column_name, _options = {})
def calculate(_operation, _column_name)
if _operation == :count
0
else

@ -287,6 +287,7 @@ db_namespace = namespace :db do
if ActiveRecord::Base.connection.supports_migrations?
File.open(filename, "a") do |f|
f.puts ActiveRecord::Base.connection.dump_schema_information
f.print "\n"
end
end
db_namespace['structure:dump'].reenable

@ -574,7 +574,7 @@ def scope_chain
# Add to it the scope from this reflection (if any)
scope_chain.first << scope if scope
through_scope_chain = through_reflection.scope_chain
through_scope_chain = through_reflection.scope_chain.map(&:dup)
if options[:source_type]
through_scope_chain.first <<

@ -19,17 +19,16 @@ module Calculations
#
# Person.group(:city).count
# # => { 'Rome' => 5, 'Paris' => 3 }
def count(column_name = nil, options = {})
column_name, options = nil, column_name if column_name.is_a?(Hash)
calculate(:count, column_name, options)
def count(column_name = nil)
calculate(:count, column_name)
end
# Calculates the average value on a given column. Returns +nil+ if there's
# no row. See +calculate+ for examples with options.
#
# Person.average(:age) # => 35.8
def average(column_name, options = {})
calculate(:average, column_name, options)
def average(column_name)
calculate(:average, column_name)
end
# Calculates the minimum value on a given column. The value is returned
@ -37,8 +36,8 @@ def average(column_name, options = {})
# +calculate+ for examples with options.
#
# Person.minimum(:age) # => 7
def minimum(column_name, options = {})
calculate(:minimum, column_name, options)
def minimum(column_name)
calculate(:minimum, column_name)
end
# Calculates the maximum value on a given column. The value is returned
@ -46,8 +45,8 @@ def minimum(column_name, options = {})
# +calculate+ for examples with options.
#
# Person.maximum(:age) # => 93
def maximum(column_name, options = {})
calculate(:maximum, column_name, options)
def maximum(column_name)
calculate(:maximum, column_name)
end
# Calculates the sum of values on a given column. The value is returned
@ -90,15 +89,15 @@ def sum(*args)
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
#
# Person.sum("2 * age")
def calculate(operation, column_name, options = {})
def calculate(operation, column_name)
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
column_name = attribute_alias(column_name)
end
if has_include?(column_name)
construct_relation_for_association_calculations.calculate(operation, column_name, options)
construct_relation_for_association_calculations.calculate(operation, column_name)
else
perform_calculation(operation, column_name, options)
perform_calculation(operation, column_name)
end
end
@ -181,7 +180,7 @@ def has_include?(column_name)
eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?))
end
def perform_calculation(operation, column_name, options = {})
def perform_calculation(operation, column_name)
operation = operation.to_s.downcase
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)

@ -242,10 +242,9 @@ def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
def find_with_associations
join_dependency = construct_join_dependency
relation = except :select
aliases = join_dependency.aliases
relation = relation.select aliases.columns
relation = select aliases.columns
relation = apply_join_dependency(relation, join_dependency)
if block_given?

@ -127,7 +127,17 @@ def replace_bind_variables(statement, values) #:nodoc:
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
bound = values.dup
c = connection
statement.gsub('?') { quote_bound_value(bound.shift, c) }
statement.gsub('?') do
replace_bind_variable(bound.shift, c)
end
end
def replace_bind_variable(value, c = connection) #:nodoc:
if ActiveRecord::Relation === value
value.to_sql
else
quote_bound_value(value, c)
end
end
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
@ -135,7 +145,7 @@ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
if $1 == ':' # skip postgresql casts
$& # return the whole match
elsif bind_vars.include?(match = $2.to_sym)
quote_bound_value(bind_vars[match])
replace_bind_variable(bind_vars[match])
else
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
end

@ -123,6 +123,7 @@ def table(table, stream)
tbl.print %Q(, primary_key: "#{pk}")
elsif pkcol.sql_type == 'uuid'
tbl.print ", id: :uuid"
tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
end
else
tbl.print ", id: false"

@ -24,7 +24,7 @@ def setup
@connection.reconnect!
@connection.transaction do
@connection.create_table('pg_uuids', id: :uuid) do |t|
@connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t|
t.string 'name'
t.uuid 'other_uuid', default: 'uuid_generate_v4()'
end
@ -60,7 +60,7 @@ def test_pk_and_sequence_for_uuid_primary_key
def test_schema_dumper_for_uuid_primary_key
schema = StringIO.new
ActiveRecord::SchemaDumper.dump(@connection, schema)
assert_match(/\bcreate_table "pg_uuids", id: :uuid\b/, schema.string)
assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema.string)
assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema.string)
end
end

@ -603,6 +603,18 @@ def test_child_instance_should_be_shared_with_replaced_via_method_parent
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
def test_inversed_instance_should_not_be_reloaded_after_stale_state_changed
new_man = Man.new
face = Face.new
new_man.face = face
old_inversed_man = face.man
new_man.save!
new_inversed_man = face.man
assert_equal old_inversed_man.object_id, new_inversed_man.object_id
end
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
i = interests(:llama_wrangling)
m = i.polymorphic_man

@ -253,7 +253,8 @@ def test_serialized_fixtures
end
def test_fixtures_are_set_up_with_database_env_variable
ENV.stubs(:[]).with("DATABASE_URL").returns("sqlite3:///:memory:")
db_url_tmp = ENV['DATABASE_URL']
ENV['DATABASE_URL'] = "sqlite3:///:memory:"
ActiveRecord::Base.stubs(:configurations).returns({})
test_case = Class.new(ActiveRecord::TestCase) do
fixtures :accounts
@ -266,6 +267,8 @@ def test_fixtures
result = test_case.new(:test_fixtures).run
assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
ensure
ENV['DATABASE_URL'] = db_url_tmp
end
end

@ -18,6 +18,11 @@
require 'models/tag'
require 'models/sponsor'
require 'models/edge'
require 'models/hotel'
require 'models/chef'
require 'models/department'
require 'models/cake_designer'
require 'models/drink_designer'
class ReflectionTest < ActiveRecord::TestCase
include ActiveRecord::Reflection
@ -227,6 +232,17 @@ def test_scope_chain
assert_equal expected, actual
end
def test_scope_chain_does_not_interfere_with_hmt_with_polymorphic_case
@hotel = Hotel.create!
@department = @hotel.departments.create!
@department.chefs.create!(employable: CakeDesigner.create!)
@department.chefs.create!(employable: DrinkDesigner.create!)
assert_equal 1, @hotel.cake_designers.size
assert_equal 1, @hotel.drink_designers.size
assert_equal 2, @hotel.chefs.size
end
def test_nested?
assert !Author.reflect_on_association(:comments).nested?
assert Author.reflect_on_association(:tags).nested?

@ -9,10 +9,11 @@ class DelegationTest < ActiveRecord::TestCase
def assert_responds(target, method)
assert target.respond_to?(method)
assert_nothing_raised do
case target.to_a.method(method).arity
when 0
method_arity = target.to_a.method(method).arity
if method_arity.zero?
target.send(method)
when -1
elsif method_arity < 0
if method == :shuffle!
target.send(method)
else

@ -295,7 +295,7 @@ def test_null_relation_content_size_methods
def test_null_relation_calculations_methods
assert_no_queries do
assert_equal 0, Developer.none.count
assert_equal 0, Developer.none.calculate(:count, nil, {})
assert_equal 0, Developer.none.calculate(:count, nil)
assert_equal nil, Developer.none.calculate(:average, 'salary')
end
end
@ -486,6 +486,14 @@ def test_default_scoping_finder_methods
assert_equal Developer.where(name: 'David').map(&:id).sort, developers
end
def test_includes_with_select
query = Post.select('comments_count AS ranking').order('ranking').includes(:comments)
.where(comments: { id: 1 })
assert_equal ['comments_count AS ranking'], query.select_values
assert_equal 1, query.to_a.size
end
def test_loading_with_one_association
posts = Post.preload(:comments)
post = posts.find { |p| p.id == 1 }
@ -626,6 +634,11 @@ def test_find_all_using_where_with_relation
relation = Author.where(:id => Author.where(:id => david.id))
assert_equal [david], relation.to_a
}
assert_queries(1) {
relation = Author.where('id in (?)', Author.where(id: david).select(:id))
assert_equal [david], relation.to_a
}
end
def test_find_all_using_where_with_relation_and_alternate_primary_key

@ -31,4 +31,10 @@ def test_sanitize_sql_array_handles_bind_variables
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_relations
assert_match(/\(\bselect\b.*?\bwhere\b.*?\)/i,
Binary.send(:sanitize_sql_array, ["id in (?)", Binary.where(id: 1)]),
"should sanitize `Relation` as subquery")
end
end

@ -421,7 +421,9 @@ def test_rollback_when_saving_a_frozen_record
topic = Topic.new(:title => 'test')
topic.freeze
e = assert_raise(RuntimeError) { topic.save }
assert_equal "can't modify frozen Hash", e.message
assert_match(/frozen/i, e.message) # Not good enough, but we can't do much
# about it since there is no specific error
# for frozen objects.
assert !topic.persisted?, 'not persisted'
assert_nil topic.id
assert topic.frozen?, 'not frozen'

@ -8,5 +8,5 @@ boring_club_sponsor_for_groucho:
sponsorable_type: Member
crazy_club_sponsor_for_groucho:
sponsor_club: crazy_club
sponsorable_id: 2
sponsorable_id: 3
sponsorable_type: Member

@ -0,0 +1,3 @@
class CakeDesigner < ActiveRecord::Base
has_one :chef, as: :employable
end

@ -0,0 +1,3 @@
class Chef < ActiveRecord::Base
belongs_to :employable, polymorphic: true
end

@ -0,0 +1,4 @@
class Department < ActiveRecord::Base
has_many :chefs
belongs_to :hotel
end

@ -0,0 +1,3 @@
class DrinkDesigner < ActiveRecord::Base
has_one :chef, as: :employable
end

@ -0,0 +1,6 @@
class Hotel < ActiveRecord::Base
has_many :departments
has_many :chefs, through: :departments
has_many :cake_designers, source_type: 'CakeDesigner', source: :employable, through: :chefs
has_many :drink_designers, source_type: 'DrinkDesigner', source: :employable, through: :chefs
end

@ -787,6 +787,22 @@ def create_table(*args, &block)
t.string 'from'
end
create_table :hotels, force: true do |t|
end
create_table :departments, force: true do |t|
t.integer :hotel_id
end
create_table :cake_designers, force: true do |t|
end
create_table :drink_designers, force: true do |t|
end
create_table :chefs, force: true do |t|
t.integer :employable_id
t.string :employable_type
t.integer :department_id
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|

@ -1,9 +1,6 @@
ActiveRecord::Schema.define do
# For sqlite 3.1.0+, make a table with an autoincrement column
if supports_autoincrement?
create_table :table_with_autoincrement, :force => true do |t|
t.column :name, :string
end
create_table :table_with_autoincrement, :force => true do |t|
t.column :name, :string
end
execute "DROP TABLE fk_test_has_fk" rescue nil

@ -1,10 +1,3 @@
* Add `flatten` and `flatten!` methods to Duration objects.
Example:
Date.today + (1.month + 1.month).flatten == Date.today + 2.months
*Ionatan Wiznia*
* `require_dependency` accepts objects that respond to `to_path`, in
particular `Pathname` instances.

@ -118,7 +118,10 @@ def class_attribute(*attrs)
end
private
def singleton_class?
ancestors.first != self
unless respond_to?(:singleton_class?)
def singleton_class?
ancestors.first != self
end
end
end

@ -8,6 +8,7 @@
require 'active_support/core_ext/module/anonymous'
require 'active_support/core_ext/module/qualified_const'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
require 'active_support/core_ext/string/starts_ends_with'

@ -81,18 +81,6 @@ def as_json(options = nil) #:nodoc:
to_i
end
# Flattens all the +parts+ of the duration, and returns a
# new duration object.
def flatten
Duration.new(@value, flatten_parts)
end
# Flattens all the +parts+ of this duration.
def flatten!
@parts = flatten_parts
self
end
protected
def sum(sign, time = ::Time.current) #:nodoc:
@ -109,14 +97,6 @@ def sum(sign, time = ::Time.current) #:nodoc:
end
end
def flatten_parts
@parts.inject({}) do |result, (name, value)|
result[name] ||= 0
result[name] += value
result
end.to_a
end
private
def method_missing(method, *args, &block) #:nodoc:

@ -108,7 +108,7 @@ module NumberHelper
DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
-1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
# Formats a +number+ into a US phone number (e.g., (555)
@ -561,8 +561,6 @@ def number_to_human(number, options = {})
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
inverted_du = DECIMAL_UNITS.invert
units = options.delete :units
unit_exponents = case units
when Hash
@ -573,7 +571,7 @@ def number_to_human(number, options = {})
translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
else
raise ArgumentError, ":units must be a Hash or String translation scope."
end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
end.keys.map!{|e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by!{|e| -e}
number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0

@ -145,21 +145,6 @@ def test_to_json
assert_equal '172800', 2.days.to_json
end
def test_flatten
a = 2.months
b = (1.month + 1.month).flatten
assert_equal a.parts, b.parts
end
def test_flatten!
a = (1.month + 1.month)
b = a.flatten
a.flatten!
assert_equal a.parts, b.parts
end
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz

@ -72,17 +72,4 @@ def test_thread_variable_frozen_after_set
end
end
def test_thread_variable_security
rubinius_skip "$SAFE is not supported on Rubinius."
t = Thread.new { sleep }
assert_raises(SecurityError) do
Thread.new { $SAFE = 4; t.thread_variable_get(:foo) }.join
end
assert_raises(SecurityError) do
Thread.new { $SAFE = 4; t.thread_variable_set(:foo, :baz) }.join
end
end
end

@ -209,7 +209,7 @@ class PeopleController < ActionController::Base
# Request reply.
def update
person = current_account.people.find(params[:id])
person.update_attributes!(person_params)
person.update!(person_params)
redirect_to person
end

@ -269,7 +269,7 @@ Rails will render the `_product_ruler` partial (with no data passed to it) betwe
### Layouts
Layouts can be used to render a common view template around the results of Rails controller actions. Typically, every Rails has a couple of overall layouts that most pages are rendered within. For example, a site might have a layout for a logged in user, and a layout for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us." You would expect each layout to have a different look and feel. You can read more details about Layouts in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide.
Layouts can be used to render a common view template around the results of Rails controller actions. Typically, every Rails application has a couple of overall layouts that most pages are rendered within. For example, a site might have a layout for a logged in user, and a layout for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us." You would expect each layout to have a different look and feel. You can read more details about Layouts in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide.
Partial Layouts
---------------

@ -116,7 +116,7 @@ to Active Record instances:
locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to
a model.
* `type` - Specifies that the model uses [Single Table
Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html).
Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance).
* `(association_name)_type` - Stores the type for
[polymorphic associations](association_basics.html#polymorphic-associations).
* `(table_name)_count` - Used to cache the number of belonging objects on

@ -438,8 +438,6 @@ provide a personalized message or use `presence: true` instead. When
`:in` or `:within` have a lower limit of 1, you should either provide a
personalized message or call `presence` prior to `length`.
The `size` helper is an alias for `length`.
### `numericality`
This helper validates that your attributes have only numeric values. By
@ -528,7 +526,7 @@ If you validate the presence of an object associated via a `has_one` or
Since `false.blank?` is true, if you want to validate the presence of a boolean
field you should use `validates :field_name, inclusion: { in: [true, false] }`.
The default error message is _"can't be empty"_.
The default error message is _"can't be blank"_.
### `absence`

@ -3699,21 +3699,6 @@ Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
```
When addinng or substracting durations, the resulting duration will be equivalent to subsequent calls to since or advance, so:
```ruby
Date.new(2013,1,28) + 1.month + 1.month
# => Thu, 28 Mar 2013
```
If you want to add durations and then use them as just one call to since or advance, you can use the flatten or flatten! method:
```ruby
Date.new(2013,1,31) + (1.month + 1.month).flatten
# => Sun, 31 Mar 2013
```
Extensions to `File`
--------------------

@ -1490,8 +1490,8 @@ So first, we'll wire up the Post show template
</p>
<% end %>
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %>
<%= link_to 'Back', posts_path %>
| <%= link_to 'Edit', edit_post_path(@post) %>
```
This adds a form on the `Post` show page that creates a new comment by

@ -1,3 +1,7 @@
* Added `--model-name` scaffld\_controller\_generator option.
*yalab*
* Expose MiddlewareStack#unshift to environment configuration.
*Ben Pickles*

@ -1,6 +1,7 @@
require 'fileutils'
require 'optparse'
require 'action_dispatch'
require 'rails'
module Rails
class Server < ::Rack::Server
@ -32,7 +33,7 @@ def parse!(args)
opt_parser.parse! args
options[:log_stdout] = options[:daemonize].blank? && options[:environment] == "development"
options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
options[:server] = args.shift
options
end

@ -25,7 +25,7 @@ end
# gem 'unicorn'
# Use Capistrano for deployment
# gem 'capistrano', group: :development
# gem 'capistrano-rails', group: :development
<% unless defined?(JRUBY_VERSION) -%>
# Use debugger

@ -13,7 +13,7 @@ class ScaffoldControllerGenerator < NamedBase # :nodoc:
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_controller_files
template "controller.rb", File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb")
template "controller.rb", File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
end
hook_for :template_engine, :test_framework, as: :scaffold

@ -9,11 +9,19 @@ module ResourceHelpers # :nodoc:
def self.included(base) #:nodoc:
base.class_option :force_plural, type: :boolean, desc: "Forces the use of a plural ModelName"
base.class_option :model_name, type: :string, desc: "ModelName to be used"
end
# Set controller variables on initialization.
def initialize(*args) #:nodoc:
super
if options[:model_name]
controller_name = name
self.name = options[:model_name]
assign_names!(self.name)
else
controller_name = name
end
if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural]
unless ResourceHelpers.skip_warn
@ -24,19 +32,26 @@ def initialize(*args) #:nodoc:
assign_names!(name)
end
@controller_name = name.pluralize
assign_controller_names!(controller_name.pluralize)
end
protected
attr_reader :controller_name
attr_reader :controller_name, :controller_file_name
def controller_class_path
class_path
if options[:model_name]
@controller_class_path
else
class_path
end
end
def controller_file_name
@controller_file_name ||= file_name.pluralize
def assign_controller_names!(name)
@controller_name = name
@controller_class_path = name.include?('/') ? name.split('/') : name.split('::')
@controller_class_path.map! { |m| m.underscore }
@controller_file_name = @controller_class_path.pop
end
def controller_file_path

@ -27,26 +27,62 @@ def test_server_option_without_environment
end
def test_environment_with_rails_env
with_rails_env 'production' do
server = Rails::Server.new
assert_equal 'production', server.options[:environment]
with_rack_env nil do
with_rails_env 'production' do
server = Rails::Server.new
assert_equal 'production', server.options[:environment]
end
end
end
def test_environment_with_rack_env
with_rack_env 'production' do
server = Rails::Server.new
assert_equal 'production', server.options[:environment]
with_rails_env nil do
with_rack_env 'production' do
server = Rails::Server.new
assert_equal 'production', server.options[:environment]
end
end
end
def test_log_stdout
args = ["-e", "development"]
options = Rails::Server::Options.new.parse!(args)
assert_equal true, options[:log_stdout]
with_rack_env nil do
with_rails_env nil do
args = []
options = Rails::Server::Options.new.parse!(args)
assert_equal true, options[:log_stdout]
args = ["-e", "production"]
options = Rails::Server::Options.new.parse!(args)
assert_equal false, options[:log_stdout]
args = ["-e", "development"]
options = Rails::Server::Options.new.parse!(args)
assert_equal true, options[:log_stdout]
args = ["-e", "production"]
options = Rails::Server::Options.new.parse!(args)
assert_equal false, options[:log_stdout]
with_rack_env 'development' do
args = []
options = Rails::Server::Options.new.parse!(args)
assert_equal true, options[:log_stdout]
end
with_rack_env 'production' do
args = []
options = Rails::Server::Options.new.parse!(args)
assert_equal false, options[:log_stdout]
end
with_rails_env 'development' do
args = []
options = Rails::Server::Options.new.parse!(args)
assert_equal true, options[:log_stdout]
end
with_rails_env 'production' do
args = []
options = Rails::Server::Options.new.parse!(args)
assert_equal false, options[:log_stdout]
end
end
end
end
end

@ -1,7 +1,10 @@
require 'rails'
module EnvHelpers
private
def with_rails_env(env)
Rails.instance_variable_set :@_env, nil
switch_env 'RAILS_ENV', env do
switch_env 'RACK_ENV', nil do
yield
@ -10,6 +13,7 @@ def with_rails_env(env)
end
def with_rack_env(env)
Rails.instance_variable_set :@_env, nil
switch_env 'RACK_ENV', env do
switch_env 'RAILS_ENV', nil do
yield

@ -117,6 +117,25 @@ def test_hide_namespace
assert Rails::Generators.hidden_namespaces.include?('hidden')
end
def test_scaffold_plural_names_with_model_name_option
g = generator ['Admin::Foo'], model_name: 'User'
assert_name g, 'user', :singular_name
assert_name g, 'User', :name
assert_name g, 'user', :file_path
assert_name g, 'User', :class_name
assert_name g, 'user', :file_name
assert_name g, 'User', :human_name
assert_name g, 'users', :plural_name
assert_name g, 'user', :i18n_scope
assert_name g, 'users', :table_name
assert_name g, 'Admin::Foos', :controller_name
assert_name g, %w(admin), :controller_class_path
assert_name g, 'Admin::Foos', :controller_class_name
assert_name g, 'admin/foos', :controller_file_path
assert_name g, 'foos', :controller_file_name
assert_name g, 'admin.foos', :controller_i18n_scope
end
protected
def assert_name(generator, value, method)

@ -166,4 +166,13 @@ def test_new_hash_style
assert_match(/render action: 'new'/, content)
end
end
def test_model_name_option
run_generator ["Admin::User", "--model-name=User"]
assert_file "app/controllers/admin/users_controller.rb" do |content|
assert_instance_method :index, content do |m|
assert_match("@users = User.all", m)
end
end
end
end