Merge branch 'master' into rm-uuid-fixtures
Conflicts: activerecord/CHANGELOG.md activesupport/CHANGELOG.md
This commit is contained in:
commit
085ce4f141
|
@ -1,3 +1,4 @@
|
|||
services: memcache
|
||||
script: 'ci/travis.rb'
|
||||
before_install:
|
||||
- travis_retry gem install bundler
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -11,6 +11,7 @@ gem 'rack-cache', '~> 1.2'
|
|||
gem 'jquery-rails', '~> 3.1.0'
|
||||
gem 'turbolinks'
|
||||
gem 'coffee-rails', '~> 4.0.0'
|
||||
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: '2-1-stable'
|
||||
|
||||
# require: false so bcrypt is loaded only when has_secure_password is used.
|
||||
# This is to avoid ActiveModel (and by extension the entire framework)
|
||||
|
|
|
@ -76,7 +76,7 @@ We encourage you to contribute to Ruby on Rails! Please check out the
|
|||
|
||||
## Code Status
|
||||
|
||||
* [![Build Status](https://travis-ci.org/rails/rails.png?branch=master)](https://travis-ci.org/rails/rails)
|
||||
* [![Build Status](https://travis-ci.org/rails/rails.svg?branch=master)](https://travis-ci.org/rails/rails)
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -32,11 +32,14 @@ module ActionDispatch
|
|||
|
||||
private
|
||||
def redirect_to_https(request)
|
||||
url = URI(request.url)
|
||||
url.scheme = "https"
|
||||
url.host = @host if @host
|
||||
url.port = @port if @port
|
||||
headers = { 'Content-Type' => 'text/html', 'Location' => url.to_s }
|
||||
host = @host || request.host
|
||||
port = @port || request.port
|
||||
|
||||
location = "https://#{host}"
|
||||
location << ":#{port}" if port != 80
|
||||
location << request.fullpath
|
||||
|
||||
headers = { 'Content-Type' => 'text/html', 'Location' => location }
|
||||
|
||||
[301, headers, []]
|
||||
end
|
||||
|
|
|
@ -210,20 +210,29 @@ class FlashTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
def test_redirect_to_with_adding_flash_types
|
||||
@controller.class.add_flash_types :foo
|
||||
original_controller = @controller
|
||||
test_controller_with_flash_type_foo = Class.new(TestController) do
|
||||
add_flash_types :foo
|
||||
end
|
||||
@controller = test_controller_with_flash_type_foo.new
|
||||
get :redirect_with_foo_flash
|
||||
assert_equal "for great justice", @controller.send(:flash)[:foo]
|
||||
ensure
|
||||
@controller = original_controller
|
||||
end
|
||||
|
||||
class SubclassesTestController < TestController; end
|
||||
|
||||
def test_add_flash_type_to_subclasses
|
||||
TestController.add_flash_types :foo
|
||||
assert SubclassesTestController._flash_types.include?(:foo)
|
||||
test_controller_with_flash_type_foo = Class.new(TestController) do
|
||||
add_flash_types :foo
|
||||
end
|
||||
subclass_controller_with_no_flash_type = Class.new(test_controller_with_flash_type_foo)
|
||||
assert subclass_controller_with_no_flash_type._flash_types.include?(:foo)
|
||||
end
|
||||
|
||||
def test_do_not_add_flash_type_to_parent_class
|
||||
SubclassesTestController.add_flash_types :bar
|
||||
def test_does_not_add_flash_type_to_parent_class
|
||||
Class.new(TestController) do
|
||||
add_flash_types :bar
|
||||
end
|
||||
assert_not TestController._flash_types.include?(:bar)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -196,6 +196,13 @@ class SSLTest < ActionDispatch::IntegrationTest
|
|||
response.headers['Location']
|
||||
end
|
||||
|
||||
def test_redirect_to_host_with_port
|
||||
self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org:443")
|
||||
get "http://example.org/path?key=value"
|
||||
assert_equal "https://ssl.example.org:443/path?key=value",
|
||||
response.headers['Location']
|
||||
end
|
||||
|
||||
def test_redirect_to_secure_host_when_on_subdomain
|
||||
self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org")
|
||||
get "http://ssl.example.org/path?key=value"
|
||||
|
|
|
@ -53,7 +53,7 @@ module ActiveModel
|
|||
#
|
||||
# Configuration options:
|
||||
# * <tt>:on</tt> - Specifies when this validation is active
|
||||
# (<tt>:create</tt> or <tt>:update</tt>.
|
||||
# (<tt>:create</tt> or <tt>:update</tt>).
|
||||
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
||||
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
||||
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
|
||||
|
|
|
@ -4,6 +4,34 @@
|
|||
|
||||
*Roderick van Domburg*
|
||||
|
||||
* Fixed a problem where an enum would overwrite values of another enum
|
||||
with the same name in an unrelated class.
|
||||
|
||||
Fixes #14607.
|
||||
|
||||
*Evan Whalen*
|
||||
|
||||
* PostgreSQL and SQLite string columns no longer have a default limit of 255.
|
||||
|
||||
Fixes #13435, #9153.
|
||||
|
||||
*Vladimir Sazhin*, *Toms Mikoss*, *Yves Senn*
|
||||
|
||||
* Make possible to have an association called `records`.
|
||||
|
||||
Fixes #11645.
|
||||
|
||||
*prathamesh-sonpatki*
|
||||
|
||||
* `to_sql` on an association now matches the query that is actually executed, where it
|
||||
could previously have incorrectly accrued additional conditions (e.g. as a result of
|
||||
a previous query). CollectionProxy now always defers to the association scope's
|
||||
`arel` method so the (incorrect) inherited one should be entirely concealed.
|
||||
|
||||
Fixes #14003.
|
||||
|
||||
*Jefferson Lai*
|
||||
|
||||
* Block a few default Class methods as scope name.
|
||||
|
||||
For instance, this will raise:
|
||||
|
|
|
@ -134,11 +134,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def create(attributes = {}, &block)
|
||||
create_record(attributes, &block)
|
||||
_create_record(attributes, &block)
|
||||
end
|
||||
|
||||
def create!(attributes = {}, &block)
|
||||
create_record(attributes, true, &block)
|
||||
_create_record(attributes, true, &block)
|
||||
end
|
||||
|
||||
# Add +records+ to this association. Returns +self+ so method calls may
|
||||
|
@ -182,11 +182,11 @@ module ActiveRecord
|
|||
#
|
||||
# See delete for more info.
|
||||
def delete_all(dependent = nil)
|
||||
if dependent.present? && ![:nullify, :delete_all].include?(dependent)
|
||||
if dependent && ![:nullify, :delete_all].include?(dependent)
|
||||
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
||||
end
|
||||
|
||||
dependent = if dependent.present?
|
||||
dependent = if dependent
|
||||
dependent
|
||||
elsif options[:dependent] == :destroy
|
||||
:delete_all
|
||||
|
@ -449,13 +449,13 @@ module ActiveRecord
|
|||
persisted + memory
|
||||
end
|
||||
|
||||
def create_record(attributes, raise = false, &block)
|
||||
def _create_record(attributes, raise = false, &block)
|
||||
unless owner.persisted?
|
||||
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
||||
end
|
||||
|
||||
if attributes.is_a?(Array)
|
||||
attributes.collect { |attr| create_record(attr, raise, &block) }
|
||||
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
||||
else
|
||||
transaction do
|
||||
add_to_target(build_record(attributes)) do |record|
|
||||
|
|
|
@ -357,7 +357,7 @@ module ActiveRecord
|
|||
|
||||
# Deletes all the records from the collection. For +has_many+ associations,
|
||||
# the deletion is done according to the strategy specified by the <tt>:dependent</tt>
|
||||
# option. Returns an array with the deleted records.
|
||||
# option.
|
||||
#
|
||||
# If no <tt>:dependent</tt> option is given, then it will follow the
|
||||
# default strategy. The default strategy is <tt>:nullify</tt>. This
|
||||
|
@ -435,11 +435,6 @@ module ActiveRecord
|
|||
# # ]
|
||||
#
|
||||
# person.pets.delete_all
|
||||
# # => [
|
||||
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
||||
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
||||
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
||||
# # ]
|
||||
#
|
||||
# Pet.find(1, 2, 3)
|
||||
# # => ActiveRecord::RecordNotFound
|
||||
|
@ -860,6 +855,10 @@ module ActiveRecord
|
|||
!!@association.include?(record)
|
||||
end
|
||||
|
||||
def arel
|
||||
scope.arel
|
||||
end
|
||||
|
||||
def proxy_association
|
||||
@association
|
||||
end
|
||||
|
|
|
@ -18,11 +18,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def create(attributes = {}, &block)
|
||||
create_record(attributes, &block)
|
||||
_create_record(attributes, &block)
|
||||
end
|
||||
|
||||
def create!(attributes = {}, &block)
|
||||
create_record(attributes, true, &block)
|
||||
_create_record(attributes, true, &block)
|
||||
end
|
||||
|
||||
def build(attributes = {})
|
||||
|
@ -52,7 +52,7 @@ module ActiveRecord
|
|||
replace(record)
|
||||
end
|
||||
|
||||
def create_record(attributes, raise_error = false)
|
||||
def _create_record(attributes, raise_error = false)
|
||||
record = build_record(attributes)
|
||||
yield(record) if block_given?
|
||||
saved = record.save
|
||||
|
|
|
@ -79,11 +79,11 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def update_record(*)
|
||||
def _update_record(*)
|
||||
partial_writes? ? super(keys_for_partial_write) : super
|
||||
end
|
||||
|
||||
def create_record(*)
|
||||
def _create_record(*)
|
||||
partial_writes? ? super(keys_for_partial_write) : super
|
||||
end
|
||||
|
||||
|
|
|
@ -302,11 +302,11 @@ module ActiveRecord
|
|||
run_callbacks(:save) { super }
|
||||
end
|
||||
|
||||
def create_record #:nodoc:
|
||||
def _create_record #:nodoc:
|
||||
run_callbacks(:create) { super }
|
||||
end
|
||||
|
||||
def update_record(*) #:nodoc:
|
||||
def _update_record(*) #:nodoc:
|
||||
run_callbacks(:update) { super }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,7 +33,7 @@ module ActiveRecord
|
|||
def initialize(url)
|
||||
raise "Database URL cannot be empty" if url.blank?
|
||||
@uri = URI.parse(url)
|
||||
@adapter = @uri.scheme
|
||||
@adapter = @uri.scheme.gsub('-', '_')
|
||||
@adapter = "postgresql" if @adapter == "postgres"
|
||||
|
||||
if @uri.opaque
|
||||
|
@ -220,10 +220,10 @@ module ActiveRecord
|
|||
# an environment key or a URL spec, so we have deprecated
|
||||
# this ambiguous behaviour and in the future this function
|
||||
# can be removed in favor of resolve_url_connection.
|
||||
if configurations.key?(spec)
|
||||
if configurations.key?(spec) || spec !~ /:/
|
||||
ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \
|
||||
"for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead"
|
||||
resolve_connection(configurations[spec])
|
||||
resolve_symbol_connection(spec)
|
||||
else
|
||||
resolve_url_connection(spec)
|
||||
end
|
||||
|
|
|
@ -4,14 +4,14 @@ module ActiveRecord
|
|||
module Quoting
|
||||
# Escapes binary strings for bytea input to the database.
|
||||
def escape_bytea(value)
|
||||
PGconn.escape_bytea(value) if value
|
||||
@connection.escape_bytea(value) if value
|
||||
end
|
||||
|
||||
# Unescapes bytea output from a database to the binary string it represents.
|
||||
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
||||
# on escaped binary output from database drive.
|
||||
def unescape_bytea(value)
|
||||
PGconn.unescape_bytea(value) if value
|
||||
@connection.unescape_bytea(value) if value
|
||||
end
|
||||
|
||||
# Quotes PostgreSQL-specific data types for SQL input.
|
||||
|
|
|
@ -209,7 +209,7 @@ module ActiveRecord
|
|||
|
||||
NATIVE_DATABASE_TYPES = {
|
||||
primary_key: "serial primary key",
|
||||
string: { name: "character varying", limit: 255 },
|
||||
string: { name: "character varying" },
|
||||
text: { name: "text" },
|
||||
integer: { name: "integer" },
|
||||
float: { name: "float" },
|
||||
|
|
|
@ -63,7 +63,7 @@ module ActiveRecord
|
|||
|
||||
NATIVE_DATABASE_TYPES = {
|
||||
primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
|
||||
string: { name: "varchar", limit: 255 },
|
||||
string: { name: "varchar" },
|
||||
text: { name: "text" },
|
||||
integer: { name: "integer" },
|
||||
float: { name: "float" },
|
||||
|
|
|
@ -58,9 +58,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
class MergeAndResolveDefaultUrlConfig # :nodoc:
|
||||
def initialize(raw_configurations, url = ENV['DATABASE_URL'])
|
||||
def initialize(raw_configurations)
|
||||
@raw_config = raw_configurations.dup
|
||||
@url = url
|
||||
@env = DEFAULT_ENV.call.to_s
|
||||
end
|
||||
|
||||
# Returns fully resolved connection hashes.
|
||||
|
@ -71,33 +71,10 @@ module ActiveRecord
|
|||
|
||||
private
|
||||
def config
|
||||
if @url
|
||||
raw_merged_into_default
|
||||
else
|
||||
@raw_config
|
||||
end
|
||||
end
|
||||
|
||||
def raw_merged_into_default
|
||||
default = default_url_hash
|
||||
|
||||
@raw_config.each do |env, values|
|
||||
default[env] = values || {}
|
||||
default[env].merge!("url" => @url) { |h, v1, v2| v1 || v2 } if default[env].is_a?(Hash)
|
||||
end
|
||||
default
|
||||
end
|
||||
|
||||
# When the raw configuration is not present and ENV['DATABASE_URL']
|
||||
# is available we return a hash with the connection information in
|
||||
# the connection URL. This hash responds to any string key with
|
||||
# resolved connection information.
|
||||
def default_url_hash
|
||||
Hash.new do |hash, key|
|
||||
hash[key] = if key.is_a? String
|
||||
ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash
|
||||
else
|
||||
nil
|
||||
@raw_config.dup.tap do |cfg|
|
||||
if url = ENV['DATABASE_URL']
|
||||
cfg[@env] ||= {}
|
||||
cfg[@env]["url"] ||= url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'active_support/core_ext/object/deep_dup'
|
||||
|
||||
module ActiveRecord
|
||||
# Declare an enum attribute where the values map to integers in the database,
|
||||
# but can be queried by name. Example:
|
||||
|
@ -65,10 +67,14 @@ module ActiveRecord
|
|||
#
|
||||
# Where conditions on an enum attribute must use the ordinal value of an enum.
|
||||
module Enum
|
||||
DEFINED_ENUMS = {} # :nodoc:
|
||||
def self.extended(base)
|
||||
base.class_attribute(:defined_enums)
|
||||
base.defined_enums = {}
|
||||
end
|
||||
|
||||
def enum_mapping_for(attr_name) # :nodoc:
|
||||
DEFINED_ENUMS[attr_name.to_s]
|
||||
def inherited(base)
|
||||
base.defined_enums = defined_enums.deep_dup
|
||||
super
|
||||
end
|
||||
|
||||
def enum(definitions)
|
||||
|
@ -122,9 +128,8 @@ module ActiveRecord
|
|||
klass.send(:detect_enum_conflict!, name, value, true)
|
||||
klass.scope value, -> { klass.where name => i }
|
||||
end
|
||||
|
||||
DEFINED_ENUMS[name.to_s] = enum_values
|
||||
end
|
||||
defined_enums[name.to_s] = enum_values
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -134,7 +139,7 @@ module ActiveRecord
|
|||
mod = Module.new do
|
||||
private
|
||||
def save_changed_attribute(attr_name, value)
|
||||
if (mapping = self.class.enum_mapping_for(attr_name))
|
||||
if (mapping = self.class.defined_enums[attr_name.to_s])
|
||||
if attribute_changed?(attr_name)
|
||||
old = changed_attributes[attr_name]
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ module ActiveRecord
|
|||
send(lock_col + '=', previous_lock_value + 1)
|
||||
end
|
||||
|
||||
def update_record(attribute_names = @attributes.keys) #:nodoc:
|
||||
def _update_record(attribute_names = @attributes.keys) #:nodoc:
|
||||
return super unless locking_enabled?
|
||||
return 0 if attribute_names.empty?
|
||||
|
||||
|
|
|
@ -484,24 +484,24 @@ module ActiveRecord
|
|||
|
||||
def create_or_update
|
||||
raise ReadOnlyRecord if readonly?
|
||||
result = new_record? ? create_record : update_record
|
||||
result = new_record? ? _create_record : _update_record
|
||||
result != false
|
||||
end
|
||||
|
||||
# Updates the associated record with values matching those of the instance attributes.
|
||||
# Returns the number of affected rows.
|
||||
def update_record(attribute_names = @attributes.keys)
|
||||
def _update_record(attribute_names = @attributes.keys)
|
||||
attributes_values = arel_attributes_with_values_for_update(attribute_names)
|
||||
if attributes_values.empty?
|
||||
0
|
||||
else
|
||||
self.class.unscoped.update_record attributes_values, id, id_was
|
||||
self.class.unscoped._update_record attributes_values, id, id_was
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a record with values matching those of the instance attributes
|
||||
# and returns its id.
|
||||
def create_record(attribute_names = @attributes.keys)
|
||||
def _create_record(attribute_names = @attributes.keys)
|
||||
attributes_values = arel_attributes_with_values_for_create(attribute_names)
|
||||
|
||||
new_id = self.class.unscoped.insert attributes_values
|
||||
|
|
|
@ -22,11 +22,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def self.add_reflection(ar, name, reflection)
|
||||
ar.reflections = ar.reflections.merge(name => reflection)
|
||||
ar.reflections = ar.reflections.merge(name.to_s => reflection)
|
||||
end
|
||||
|
||||
def self.add_aggregate_reflection(ar, name, reflection)
|
||||
ar.aggregate_reflections = ar.aggregate_reflections.merge(name => reflection)
|
||||
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
||||
end
|
||||
|
||||
# \Reflection enables to interrogate Active Record classes and objects
|
||||
|
@ -48,7 +48,7 @@ module ActiveRecord
|
|||
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
||||
#
|
||||
def reflect_on_aggregation(aggregation)
|
||||
aggregate_reflections[aggregation]
|
||||
aggregate_reflections[aggregation.to_s]
|
||||
end
|
||||
|
||||
# Returns an array of AssociationReflection objects for all the
|
||||
|
@ -72,7 +72,7 @@ module ActiveRecord
|
|||
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
||||
#
|
||||
def reflect_on_association(association)
|
||||
reflections[association]
|
||||
reflections[association.to_s]
|
||||
end
|
||||
|
||||
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
||||
|
@ -617,7 +617,7 @@ module ActiveRecord
|
|||
# # => [:tag, :tags]
|
||||
#
|
||||
def source_reflection_names
|
||||
(options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq
|
||||
options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
|
||||
end
|
||||
|
||||
def source_reflection_name # :nodoc:
|
||||
|
|
|
@ -70,7 +70,7 @@ module ActiveRecord
|
|||
binds)
|
||||
end
|
||||
|
||||
def update_record(values, id, id_was) # :nodoc:
|
||||
def _update_record(values, id, id_was) # :nodoc:
|
||||
substitutes, binds = substitute_values values
|
||||
um = @klass.unscoped.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ module ActiveRecord
|
|||
|
||||
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
||||
# Postgresql doesn't like ORDER BY when there are no GROUP BY
|
||||
relation = reorder(nil)
|
||||
relation = unscope(:order)
|
||||
|
||||
column_alias = column_name
|
||||
|
||||
|
|
|
@ -139,7 +139,6 @@ module ActiveRecord
|
|||
def merge_single_values
|
||||
relation.from_value = values[:from] unless relation.from_value
|
||||
relation.lock_value = values[:lock] unless relation.lock_value
|
||||
relation.reverse_order_value = values[:reverse_order]
|
||||
|
||||
unless values[:create_with].blank?
|
||||
relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
|
||||
|
|
|
@ -825,7 +825,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def reverse_order! # :nodoc:
|
||||
self.reverse_order_value = !reverse_order_value
|
||||
orders = order_values.uniq
|
||||
orders.reject!(&:blank?)
|
||||
self.order_values = reverse_sql_order(orders)
|
||||
self
|
||||
end
|
||||
|
||||
|
@ -871,7 +873,6 @@ module ActiveRecord
|
|||
|
||||
case scope
|
||||
when :order
|
||||
self.reverse_order_value = false
|
||||
result = []
|
||||
else
|
||||
result = [] unless single_val_method
|
||||
|
@ -1031,7 +1032,6 @@ module ActiveRecord
|
|||
def build_order(arel)
|
||||
orders = order_values.uniq
|
||||
orders.reject!(&:blank?)
|
||||
orders = reverse_sql_order(orders) if reverse_order_value
|
||||
|
||||
arel.order(*orders) unless orders.empty?
|
||||
end
|
||||
|
|
|
@ -43,7 +43,7 @@ module ActiveRecord
|
|||
|
||||
private
|
||||
|
||||
def create_record
|
||||
def _create_record
|
||||
if self.record_timestamps
|
||||
current_time = current_time_from_proper_timezone
|
||||
|
||||
|
@ -57,7 +57,7 @@ module ActiveRecord
|
|||
super
|
||||
end
|
||||
|
||||
def update_record(*args)
|
||||
def _update_record(*args)
|
||||
if should_record_timestamps?
|
||||
current_time = current_time_from_proper_timezone
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def map_enum_attribute(klass, attribute, value)
|
||||
mapping = klass.enum_mapping_for(attribute.to_s)
|
||||
mapping = klass.defined_enums[attribute.to_s]
|
||||
value = mapping[value] if value && mapping
|
||||
value
|
||||
end
|
||||
|
|
|
@ -25,7 +25,7 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
|
|||
|
||||
def test_column
|
||||
assert_equal :string, @column.type
|
||||
assert_equal "character varying(255)", @column.sql_type
|
||||
assert_equal "character varying", @column.sql_type
|
||||
assert @column.array
|
||||
assert_not @column.text?
|
||||
assert_not @column.number?
|
||||
|
|
|
@ -70,6 +70,23 @@ class PostgresqlByteaTest < ActiveRecord::TestCase
|
|||
assert_equal(data, record.payload)
|
||||
end
|
||||
|
||||
def test_via_to_sql
|
||||
data = "'\u001F\\"
|
||||
record = ByteaDataType.create(payload: data)
|
||||
sql = ByteaDataType.where(payload: data).select(:payload).to_sql
|
||||
result = @connection.query(sql)
|
||||
assert_equal([[data]], result)
|
||||
end
|
||||
|
||||
def test_via_to_sql_with_complicating_connection
|
||||
Thread.new do
|
||||
other_conn = ActiveRecord::Base.connection
|
||||
other_conn.execute('SET standard_conforming_strings = off')
|
||||
end.join
|
||||
|
||||
test_via_to_sql
|
||||
end
|
||||
|
||||
def test_write_binary
|
||||
data = File.read(File.join(File.dirname(__FILE__), '..', '..', '..', 'assets', 'example.log'))
|
||||
assert(data.size > 1)
|
||||
|
|
|
@ -16,6 +16,8 @@ require 'models/essay'
|
|||
require 'models/toy'
|
||||
require 'models/invoice'
|
||||
require 'models/line_item'
|
||||
require 'models/column'
|
||||
require 'models/record'
|
||||
|
||||
class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :topics,
|
||||
|
@ -885,4 +887,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
test 'belongs_to works with model called Record' do
|
||||
record = Record.create!
|
||||
Column.create! record: record
|
||||
assert_equal 1, Column.count
|
||||
end
|
||||
end
|
||||
|
|
|
@ -387,6 +387,20 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
assert_raise(ArgumentError) { Account.count(1, 2, 3) }
|
||||
end
|
||||
|
||||
def test_count_with_order
|
||||
assert_equal 6, Account.order(:credit_limit).count
|
||||
end
|
||||
|
||||
def test_count_with_reverse_order
|
||||
assert_equal 6, Account.order(:credit_limit).reverse_order.count
|
||||
end
|
||||
|
||||
def test_count_with_where_and_order
|
||||
assert_equal 1, Account.where(firm_name: '37signals').count
|
||||
assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).count
|
||||
assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).reverse_order.count
|
||||
end
|
||||
|
||||
def test_should_sum_expression
|
||||
# Oracle adapter returns floating point value 636.0 after SUM
|
||||
if current_adapter?(:OracleAdapter)
|
||||
|
|
|
@ -25,35 +25,45 @@ module ActiveRecord
|
|||
ConnectionSpecification::Resolver.new(klass.new(config).resolve).spec(spec)
|
||||
end
|
||||
|
||||
def test_resolver_with_database_uri_and_known_key
|
||||
def test_resolver_with_database_uri_and_current_env_symbol_key
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
actual = resolve(:production, config)
|
||||
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
actual = resolve(:default_env, config)
|
||||
expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def test_resolver_with_database_uri_and_known_string_key
|
||||
def test_resolver_with_database_uri_and_and_current_env_string_key
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
actual = assert_deprecated { resolve("production", config) }
|
||||
config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
actual = assert_deprecated { resolve("default_env", config) }
|
||||
expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def test_resolver_with_database_uri_and_known_key
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
|
||||
actual = resolve(:production, config)
|
||||
expected = { "adapter"=>"not_postgres", "database"=>"not_foo", "host"=>"localhost" }
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def test_resolver_with_database_uri_and_unknown_symbol_key
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
actual = resolve(:production, config)
|
||||
expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
|
||||
assert_equal expected, actual
|
||||
assert_raises AdapterNotSpecified do
|
||||
resolve(:production, config)
|
||||
end
|
||||
end
|
||||
|
||||
def test_resolver_with_database_uri_and_unknown_string_key
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
assert_raises AdapterNotSpecified do
|
||||
spec("production", config)
|
||||
assert_deprecated do
|
||||
assert_raises AdapterNotSpecified do
|
||||
spec("production", config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -73,16 +83,24 @@ module ActiveRecord
|
|||
|
||||
def test_environment_does_not_exist_in_config_url_does_exist
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
config = { "not_default_env" => { "adapter" => "not_postgres", "database" => "not_foo" } }
|
||||
actual = klass.new(config).resolve
|
||||
expect_prod = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
|
||||
assert_equal expect_prod, actual["production"]
|
||||
assert_equal expect_prod, actual["default_env"]
|
||||
end
|
||||
|
||||
def test_url_with_hyphenated_scheme
|
||||
ENV['DATABASE_URL'] = "ibm-db://localhost/foo"
|
||||
config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
|
||||
actual = resolve(:default_env, config)
|
||||
expected = { "adapter"=>"ibm_db", "database"=>"foo", "host"=>"localhost" }
|
||||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def test_string_connection
|
||||
config = { "production" => "postgres://localhost/foo" }
|
||||
config = { "default_env" => "postgres://localhost/foo" }
|
||||
actual = klass.new(config).resolve
|
||||
expected = { "production" =>
|
||||
expected = { "default_env" =>
|
||||
{ "adapter" => "postgresql",
|
||||
"database" => "foo",
|
||||
"host" => "localhost"
|
||||
|
@ -92,9 +110,9 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def test_url_sub_key
|
||||
config = { "production" => { "url" => "postgres://localhost/foo" } }
|
||||
config = { "default_env" => { "url" => "postgres://localhost/foo" } }
|
||||
actual = klass.new(config).resolve
|
||||
expected = { "production" =>
|
||||
expected = { "default_env" =>
|
||||
{ "adapter" => "postgresql",
|
||||
"database" => "foo",
|
||||
"host" => "localhost"
|
||||
|
@ -123,9 +141,10 @@ module ActiveRecord
|
|||
expected = { "adapter" => "postgresql",
|
||||
"database" => "foo",
|
||||
"host" => "localhost" }
|
||||
assert_equal expected, actual["production"]
|
||||
assert_equal expected, actual["development"]
|
||||
assert_equal expected, actual["test"]
|
||||
assert_equal expected, actual["default_env"]
|
||||
assert_equal nil, actual["production"]
|
||||
assert_equal nil, actual["development"]
|
||||
assert_equal nil, actual["test"]
|
||||
assert_equal nil, actual[:production]
|
||||
assert_equal nil, actual[:development]
|
||||
assert_equal nil, actual[:test]
|
||||
|
@ -134,9 +153,9 @@ module ActiveRecord
|
|||
def test_url_sub_key_with_database_url
|
||||
ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO"
|
||||
|
||||
config = { "production" => { "url" => "postgres://localhost/foo" } }
|
||||
config = { "default_env" => { "url" => "postgres://localhost/foo" } }
|
||||
actual = klass.new(config).resolve
|
||||
expected = { "production" =>
|
||||
expected = { "default_env" =>
|
||||
{ "adapter" => "postgresql",
|
||||
"database" => "foo",
|
||||
"host" => "localhost"
|
||||
|
@ -148,9 +167,9 @@ module ActiveRecord
|
|||
def test_merge_no_conflicts_with_database_url
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
|
||||
config = {"production" => { "pool" => "5" } }
|
||||
config = {"default_env" => { "pool" => "5" } }
|
||||
actual = klass.new(config).resolve
|
||||
expected = { "production" =>
|
||||
expected = { "default_env" =>
|
||||
{ "adapter" => "postgresql",
|
||||
"database" => "foo",
|
||||
"host" => "localhost",
|
||||
|
@ -163,9 +182,9 @@ module ActiveRecord
|
|||
def test_merge_conflicts_with_database_url
|
||||
ENV['DATABASE_URL'] = "postgres://localhost/foo"
|
||||
|
||||
config = {"production" => { "adapter" => "NOT-POSTGRES", "database" => "NOT-FOO", "pool" => "5" } }
|
||||
config = {"default_env" => { "adapter" => "NOT-POSTGRES", "database" => "NOT-FOO", "pool" => "5" } }
|
||||
actual = klass.new(config).resolve
|
||||
expected = { "production" =>
|
||||
expected = { "default_env" =>
|
||||
{ "adapter" => "postgresql",
|
||||
"database" => "foo",
|
||||
"host" => "localhost",
|
||||
|
|
|
@ -250,4 +250,40 @@ class EnumTest < ActiveRecord::TestCase
|
|||
valid_book = klass.new(status: "written")
|
||||
assert valid_book.valid?
|
||||
end
|
||||
|
||||
test "enums are distinct per class" do
|
||||
klass1 = Class.new(ActiveRecord::Base) do
|
||||
self.table_name = "books"
|
||||
enum status: [:proposed, :written]
|
||||
end
|
||||
|
||||
klass2 = Class.new(ActiveRecord::Base) do
|
||||
self.table_name = "books"
|
||||
enum status: [:drafted, :uploaded]
|
||||
end
|
||||
|
||||
book1 = klass1.proposed.create!
|
||||
book1.status = :written
|
||||
assert_equal ['proposed', 'written'], book1.status_change
|
||||
|
||||
book2 = klass2.drafted.create!
|
||||
book2.status = :uploaded
|
||||
assert_equal ['drafted', 'uploaded'], book2.status_change
|
||||
end
|
||||
|
||||
test "enums are inheritable" do
|
||||
subklass1 = Class.new(Book)
|
||||
|
||||
subklass2 = Class.new(Book) do
|
||||
enum status: [:drafted, :uploaded]
|
||||
end
|
||||
|
||||
book1 = subklass1.proposed.create!
|
||||
book1.status = :written
|
||||
assert_equal ['proposed', 'written'], book1.status_change
|
||||
|
||||
book2 = subklass2.drafted.create!
|
||||
book2.status = :uploaded
|
||||
assert_equal ['drafted', 'uploaded'], book2.status_change
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,6 +35,14 @@ module ActiveRecord
|
|||
assert_no_column TestModel, :last_name
|
||||
end
|
||||
|
||||
def test_add_column_without_limit
|
||||
# TODO: limit: nil should work with all adapters.
|
||||
skip "MySQL wrongly enforces a limit of 255" if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
|
||||
add_column :test_models, :description, :string, limit: nil
|
||||
TestModel.reset_column_information
|
||||
assert_nil TestModel.columns_hash["description"].limit
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
|
||||
def test_unabstracted_database_dependent_types
|
||||
add_column :test_models, :intelligence_quotient, :tinyint
|
||||
|
|
|
@ -63,7 +63,7 @@ class ReflectionTest < ActiveRecord::TestCase
|
|||
|
||||
def test_column_string_type_and_limit
|
||||
assert_equal :string, @first.column_for_attribute("title").type
|
||||
assert_equal 255, @first.column_for_attribute("title").limit
|
||||
assert_equal 250, @first.column_for_attribute("title").limit
|
||||
end
|
||||
|
||||
def test_column_null_not_null
|
||||
|
@ -192,7 +192,7 @@ class ReflectionTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_reflection_should_not_raise_error_when_compared_to_other_object
|
||||
assert_nothing_raised { Firm.reflections[:clients] == Object.new }
|
||||
assert_nothing_raised { Firm.reflections['clients'] == Object.new }
|
||||
end
|
||||
|
||||
def test_has_many_through_reflection
|
||||
|
|
|
@ -107,10 +107,18 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
test 'reverse_order!' do
|
||||
assert relation.reverse_order!.equal?(relation)
|
||||
assert relation.reverse_order_value
|
||||
relation = Post.order('title ASC, comments_count DESC')
|
||||
|
||||
relation.reverse_order!
|
||||
assert !relation.reverse_order_value
|
||||
|
||||
assert_equal 'title DESC', relation.order_values.first
|
||||
assert_equal 'comments_count ASC', relation.order_values.last
|
||||
|
||||
|
||||
relation.reverse_order!
|
||||
|
||||
assert_equal 'title ASC', relation.order_values.first
|
||||
assert_equal 'comments_count DESC', relation.order_values.last
|
||||
end
|
||||
|
||||
test 'create_with!' do
|
||||
|
|
|
@ -573,6 +573,12 @@ class RelationTest < ActiveRecord::TestCase
|
|||
assert_equal expected, actual
|
||||
end
|
||||
|
||||
def test_to_sql_on_scoped_proxy
|
||||
auth = Author.first
|
||||
Post.where("1=1").written_by(auth)
|
||||
assert_not auth.posts.to_sql.include?("1=1")
|
||||
end
|
||||
|
||||
def test_loading_with_one_association_with_non_preload
|
||||
posts = Post.eager_load(:last_comment).order('comments.id DESC')
|
||||
post = posts.find { |p| p.id == 1 }
|
||||
|
@ -1418,6 +1424,18 @@ class RelationTest < ActiveRecord::TestCase
|
|||
assert_equal [], scope.references_values
|
||||
end
|
||||
|
||||
def test_order_with_reorder_nil_removes_the_order
|
||||
relation = Post.order(:title).reorder(nil)
|
||||
|
||||
assert_nil relation.order_values.first
|
||||
end
|
||||
|
||||
def test_reverse_order_with_reorder_nil_removes_the_order
|
||||
relation = Post.order(:title).reverse_order.reorder(nil)
|
||||
|
||||
assert_nil relation.order_values.first
|
||||
end
|
||||
|
||||
def test_presence
|
||||
topics = Topic.all
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
class Column < ActiveRecord::Base
|
||||
belongs_to :record
|
||||
end
|
|
@ -149,6 +149,10 @@ class Post < ActiveRecord::Base
|
|||
ranked_by_comments.limit_by(limit)
|
||||
end
|
||||
|
||||
def self.written_by(author)
|
||||
where(id: author.posts.pluck(:id))
|
||||
end
|
||||
|
||||
def self.reset_log
|
||||
@log = []
|
||||
end
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
class Record < ActiveRecord::Base
|
||||
end
|
|
@ -3,5 +3,5 @@ class Tag < ActiveRecord::Base
|
|||
has_many :taggables, :through => :taggings
|
||||
has_one :tagging
|
||||
|
||||
has_many :tagged_posts, :through => :taggings, :source => :taggable, :source_type => 'Post'
|
||||
end
|
||||
has_many :tagged_posts, :through => :taggings, :source => 'taggable', :source_type => 'Post'
|
||||
end
|
||||
|
|
|
@ -170,6 +170,10 @@ ActiveRecord::Schema.define do
|
|||
t.integer :references, null: false
|
||||
end
|
||||
|
||||
create_table :columns, force: true do |t|
|
||||
t.references :record
|
||||
end
|
||||
|
||||
create_table :comments, force: true do |t|
|
||||
t.integer :post_id, null: false
|
||||
# use VARCHAR2(4000) instead of CLOB datatype as CLOB data type has many limitations in
|
||||
|
@ -682,7 +686,7 @@ ActiveRecord::Schema.define do
|
|||
end
|
||||
|
||||
create_table :topics, force: true do |t|
|
||||
t.string :title
|
||||
t.string :title, limit: 250
|
||||
t.string :author_name
|
||||
t.string :author_email_address
|
||||
if mysql_56?
|
||||
|
@ -828,6 +832,8 @@ ActiveRecord::Schema.define do
|
|||
t.integer :department_id
|
||||
end
|
||||
|
||||
create_table :records, force: true do |t|
|
||||
end
|
||||
|
||||
except 'SQLite' do
|
||||
# fk_test_has_fk should be before fk_test_has_pk
|
||||
|
|
|
@ -3,6 +3,40 @@
|
|||
|
||||
*Roderick van Domburg*
|
||||
|
||||
* Fixed `ActiveSupport::Duration#eql?` so that `1.second.eql?(1.second)` is
|
||||
true.
|
||||
|
||||
This fixes the current situation of:
|
||||
|
||||
1.second.eql?(1.second) #=> false
|
||||
|
||||
`eql?` also requires that the other object is an `ActiveSupport::Duration`.
|
||||
This requirement makes `ActiveSupport::Duration`'s behavior consistent with
|
||||
the behavior of Ruby's numeric types:
|
||||
|
||||
1.eql?(1.0) #=> false
|
||||
1.0.eql?(1) #=> false
|
||||
|
||||
1.second.eql?(1) #=> false (was true)
|
||||
1.eql?(1.second) #=> false
|
||||
|
||||
{ 1 => "foo", 1.0 => "bar" }
|
||||
#=> { 1 => "foo", 1.0 => "bar" }
|
||||
|
||||
{ 1 => "foo", 1.second => "bar" }
|
||||
# now => { 1 => "foo", 1.second => "bar" }
|
||||
# was => { 1 => "bar" }
|
||||
|
||||
And though the behavior of these hasn't changed, for reference:
|
||||
|
||||
1 == 1.0 #=> true
|
||||
1.0 == 1 #=> true
|
||||
|
||||
1 == 1.second #=> true
|
||||
1.second == 1 #=> true
|
||||
|
||||
*Emily Dobervich*
|
||||
|
||||
* `ActiveSupport::SafeBuffer#prepend` acts like `String#prepend` and modifies
|
||||
instance in-place, returning self. `ActiveSupport::SafeBuffer#prepend!` is
|
||||
deprecated.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'active_support/core_ext/object/duplicable'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/per_thread_registry'
|
||||
|
||||
module ActiveSupport
|
||||
module Cache
|
||||
|
|
|
@ -131,8 +131,6 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
|
||||
lambda { |env|
|
||||
target = env.target
|
||||
|
@ -149,6 +147,7 @@ module ActiveSupport
|
|||
next_callback.call env
|
||||
}
|
||||
end
|
||||
private_class_method :halting_and_conditional
|
||||
|
||||
def self.halting(next_callback, user_callback, halted_lambda, filter)
|
||||
lambda { |env|
|
||||
|
@ -166,6 +165,7 @@ module ActiveSupport
|
|||
next_callback.call env
|
||||
}
|
||||
end
|
||||
private_class_method :halting
|
||||
|
||||
def self.conditional(next_callback, user_callback, user_conditions)
|
||||
lambda { |env|
|
||||
|
@ -178,6 +178,7 @@ module ActiveSupport
|
|||
next_callback.call env
|
||||
}
|
||||
end
|
||||
private_class_method :conditional
|
||||
|
||||
def self.simple(next_callback, user_callback)
|
||||
lambda { |env|
|
||||
|
@ -185,6 +186,7 @@ module ActiveSupport
|
|||
next_callback.call env
|
||||
}
|
||||
end
|
||||
private_class_method :simple
|
||||
end
|
||||
|
||||
class After
|
||||
|
@ -208,8 +210,6 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.halting_and_conditional(next_callback, user_callback, user_conditions)
|
||||
lambda { |env|
|
||||
env = next_callback.call env
|
||||
|
@ -223,6 +223,7 @@ module ActiveSupport
|
|||
env
|
||||
}
|
||||
end
|
||||
private_class_method :halting_and_conditional
|
||||
|
||||
def self.halting(next_callback, user_callback)
|
||||
lambda { |env|
|
||||
|
@ -233,6 +234,7 @@ module ActiveSupport
|
|||
env
|
||||
}
|
||||
end
|
||||
private_class_method :halting
|
||||
|
||||
def self.conditional(next_callback, user_callback, user_conditions)
|
||||
lambda { |env|
|
||||
|
@ -246,6 +248,7 @@ module ActiveSupport
|
|||
env
|
||||
}
|
||||
end
|
||||
private_class_method :conditional
|
||||
|
||||
def self.simple(next_callback, user_callback)
|
||||
lambda { |env|
|
||||
|
@ -254,6 +257,7 @@ module ActiveSupport
|
|||
env
|
||||
}
|
||||
end
|
||||
private_class_method :simple
|
||||
end
|
||||
|
||||
class Around
|
||||
|
@ -269,8 +273,6 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.halting_and_conditional(next_callback, user_callback, user_conditions)
|
||||
lambda { |env|
|
||||
target = env.target
|
||||
|
@ -288,6 +290,7 @@ module ActiveSupport
|
|||
end
|
||||
}
|
||||
end
|
||||
private_class_method :halting_and_conditional
|
||||
|
||||
def self.halting(next_callback, user_callback)
|
||||
lambda { |env|
|
||||
|
@ -305,6 +308,7 @@ module ActiveSupport
|
|||
end
|
||||
}
|
||||
end
|
||||
private_class_method :halting
|
||||
|
||||
def self.conditional(next_callback, user_callback, user_conditions)
|
||||
lambda { |env|
|
||||
|
@ -322,6 +326,7 @@ module ActiveSupport
|
|||
end
|
||||
}
|
||||
end
|
||||
private_class_method :conditional
|
||||
|
||||
def self.simple(next_callback, user_callback)
|
||||
lambda { |env|
|
||||
|
@ -332,6 +337,7 @@ module ActiveSupport
|
|||
env
|
||||
}
|
||||
end
|
||||
private_class_method :simple
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -49,6 +49,10 @@ module ActiveSupport
|
|||
end
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
other.is_a?(Duration) && self == other
|
||||
end
|
||||
|
||||
def self.===(other) #:nodoc:
|
||||
other.is_a?(Duration)
|
||||
rescue ::NoMethodError
|
||||
|
|
|
@ -31,6 +31,13 @@ class DurationTest < ActiveSupport::TestCase
|
|||
assert !(1.day == 'foo')
|
||||
end
|
||||
|
||||
def test_eql
|
||||
assert 1.minute.eql?(1.minute)
|
||||
assert 2.days.eql?(48.hours)
|
||||
assert !1.second.eql?(1)
|
||||
assert !1.eql?(1.second)
|
||||
end
|
||||
|
||||
def test_inspect
|
||||
assert_equal '0 seconds', 0.seconds.inspect
|
||||
assert_equal '1 month', 1.month.inspect
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
require 'abstract_unit'
|
||||
require 'active_support/xml_mini'
|
||||
require 'active_support/builder'
|
||||
require 'active_support/core_ext/array'
|
||||
require 'active_support/core_ext/hash'
|
||||
require 'active_support/core_ext/big_decimal'
|
||||
|
||||
module XmlMiniTest
|
||||
class RenameKeyTest < ActiveSupport::TestCase
|
||||
|
@ -88,6 +91,61 @@ module XmlMiniTest
|
|||
assert_xml "<b>Howdy</b>"
|
||||
end
|
||||
|
||||
test "#to_tag should use the type value in the options hash" do
|
||||
@xml.to_tag(:b, "blue", @options.merge(type: 'color'))
|
||||
assert_xml( "<b type=\"color\">blue</b>" )
|
||||
end
|
||||
|
||||
test "#to_tag accepts symbol types" do
|
||||
@xml.to_tag(:b, :name, @options)
|
||||
assert_xml( "<b type=\"symbol\">name</b>" )
|
||||
end
|
||||
|
||||
test "#to_tag accepts boolean types" do
|
||||
@xml.to_tag(:b, true, @options)
|
||||
assert_xml( "<b type=\"boolean\">true</b>")
|
||||
end
|
||||
|
||||
test "#to_tag accepts float types" do
|
||||
@xml.to_tag(:b, 3.14, @options)
|
||||
assert_xml( "<b type=\"float\">3.14</b>")
|
||||
end
|
||||
|
||||
test "#to_tag accepts decimal types" do
|
||||
@xml.to_tag(:b, ::BigDecimal.new("1.2"), @options)
|
||||
assert_xml( "<b type=\"decimal\">1.2</b>")
|
||||
end
|
||||
|
||||
test "#to_tag accepts date types" do
|
||||
@xml.to_tag(:b, Date.new(2001,2,3), @options)
|
||||
assert_xml( "<b type=\"date\">2001-02-03</b>")
|
||||
end
|
||||
|
||||
test "#to_tag accepts datetime types" do
|
||||
@xml.to_tag(:b, DateTime.new(2001,2,3,4,5,6,'+7'), @options)
|
||||
assert_xml( "<b type=\"dateTime\">2001-02-03T04:05:06+07:00</b>")
|
||||
end
|
||||
|
||||
test "#to_tag accepts time types" do
|
||||
@xml.to_tag(:b, Time.new(1993, 02, 24, 12, 0, 0, "+09:00"), @options)
|
||||
assert_xml( "<b type=\"dateTime\">1993-02-24T12:00:00+09:00</b>")
|
||||
end
|
||||
|
||||
test "#to_tag accepts array types" do
|
||||
@xml.to_tag(:b, ["first_name", "last_name"], @options)
|
||||
assert_xml( "<b type=\"array\"><b>first_name</b><b>last_name</b></b>" )
|
||||
end
|
||||
|
||||
test "#to_tag accepts hash types" do
|
||||
@xml.to_tag(:b, { first_name: "Bob", last_name: "Marley" }, @options)
|
||||
assert_xml( "<b><first-name>Bob</first-name><last-name>Marley</last-name></b>" )
|
||||
end
|
||||
|
||||
test "#to_tag should not add type when skip types option is set" do
|
||||
@xml.to_tag(:b, "Bob", @options.merge(skip_types: 1))
|
||||
assert_xml( "<b>Bob</b>" )
|
||||
end
|
||||
|
||||
test "#to_tag should dasherize the space when passed a string with spaces as a key" do
|
||||
@xml.to_tag("New York", 33, @options)
|
||||
assert_xml "<New---York type=\"integer\">33</New---York>"
|
||||
|
@ -97,7 +155,6 @@ module XmlMiniTest
|
|||
@xml.to_tag(:"New York", 33, @options)
|
||||
assert_xml "<New---York type=\"integer\">33</New---York>"
|
||||
end
|
||||
# TODO: test the remaining functions hidden in #to_tag.
|
||||
end
|
||||
|
||||
class WithBackendTest < ActiveSupport::TestCase
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
* Updates the maintenance policy to match the latest versions of Rails
|
||||
|
||||
*Matias Korhonen*
|
||||
|
||||
* Switched the order of `Applying a default scope` and `Merging of scopes` subsections so default scopes are introduced first.
|
||||
|
||||
*Alex Riabov*
|
||||
|
|
|
@ -245,7 +245,7 @@ When a file is referenced from a manifest or a helper, Sprockets searches the
|
|||
three default asset locations for it.
|
||||
|
||||
The default locations are: the `images`, `javascripts` and `stylesheets`
|
||||
directories under the `apps/assets` folder, but these subdirectories
|
||||
directories under the `app/assets` folder, but these subdirectories
|
||||
are not special - any path under `assets/*` will be searched.
|
||||
|
||||
For example, these files:
|
||||
|
@ -581,23 +581,8 @@ runtime. To disable this behavior you can set:
|
|||
config.assets.raise_runtime_errors = false
|
||||
```
|
||||
|
||||
When `raise_runtime_errors` is set to `false` sprockets will not check that dependencies of assets are declared properly. Here is a scenario where you must tell the asset pipeline about a dependency:
|
||||
|
||||
If you have `application.css.erb` that references `logo.png` like this:
|
||||
|
||||
```css
|
||||
#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
|
||||
```
|
||||
|
||||
Then you must declare that `logo.png` is a dependency of `application.css.erb`, so when the image gets re-compiled, the css file does as well. You can do this using the `//= depend_on_asset` declaration:
|
||||
|
||||
```css
|
||||
//= depend_on_asset "logo.png"
|
||||
#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
|
||||
```
|
||||
|
||||
Without this declaration you may experience strange behavior when pushing to production that is difficult to debug. When you have `raise_runtime_errors` set to `true`, dependencies will be checked at runtime so you can ensure that all dependencies are met.
|
||||
|
||||
When this option is true asset pipeline will check if all the assets loaded in your application
|
||||
are included in the `config.assets.precompile` list.
|
||||
|
||||
### Turning Debugging Off
|
||||
|
||||
|
@ -943,7 +928,7 @@ gem.
|
|||
```ruby
|
||||
config.assets.css_compressor = :yui
|
||||
```
|
||||
The other option for compressing CSS if you have the sass-rails gem installed is
|
||||
The other option for compressing CSS if you have the sass-rails gem installed is
|
||||
|
||||
```ruby
|
||||
config.assets.css_compressor = :sass
|
||||
|
@ -1018,7 +1003,7 @@ The X-Sendfile header is a directive to the web server to ignore the response
|
|||
from the application, and instead serve a specified file from disk. This option
|
||||
is off by default, but can be enabled if your server supports it. When enabled,
|
||||
this passes responsibility for serving the file to the web server, which is
|
||||
faster. Have a look at [send_file](http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file)
|
||||
faster. Have a look at [send_file](http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file)
|
||||
on how to use this feature.
|
||||
|
||||
Apache and nginx support this option, which can be enabled in
|
||||
|
|
|
@ -117,7 +117,7 @@ This command will install all dependencies except the MySQL and PostgreSQL Ruby
|
|||
|
||||
NOTE: If you would like to run the tests that use memcached, you need to ensure that you have it installed and running.
|
||||
|
||||
You can use homebrew to install memcached on OSX:
|
||||
You can use [Homebrew](http://brew.sh/) to install memcached on OSX:
|
||||
|
||||
```bash
|
||||
$ brew install memcached
|
||||
|
@ -210,6 +210,14 @@ FreeBSD users will have to run the following:
|
|||
# pkg_add -r postgresql92-client postgresql92-server
|
||||
```
|
||||
|
||||
You can use [Homebrew](http://brew.sh/) to install MySQL and PostgreSQL on OSX:
|
||||
|
||||
```bash
|
||||
$ brew install mysql
|
||||
$ brew install postgresql
|
||||
```
|
||||
Follow instructions given by [Homebrew](http://brew.sh/) to start these.
|
||||
|
||||
Or install them through ports (they are located under the `databases` folder).
|
||||
If you run into troubles during the installation of MySQL, please see
|
||||
[the MySQL documentation](http://dev.mysql.com/doc/refman/5.1/en/freebsd-installation.html).
|
||||
|
@ -245,10 +253,15 @@ $ bundle exec rake mysql:build_databases
|
|||
```
|
||||
|
||||
PostgreSQL's authentication works differently. A simple way to set up the development environment for example is to run with your development account
|
||||
This is not needed when installed via [Homebrew](http://brew.sh).
|
||||
|
||||
```bash
|
||||
$ sudo -u postgres createuser --superuser $USER
|
||||
```
|
||||
And for OS X (when installed via [Homebrew](http://brew.sh))
|
||||
```bash
|
||||
$ createuser --superuser $USER
|
||||
```
|
||||
|
||||
and then create the test databases with
|
||||
|
||||
|
|
|
@ -166,6 +166,7 @@ is called.
|
|||
COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)
|
||||
|
||||
def run_command!(command)
|
||||
command = parse_command(command)
|
||||
if COMMAND_WHITELIST.include?(command)
|
||||
send(command)
|
||||
else
|
||||
|
@ -178,8 +179,7 @@ With the `server` command, Rails will further run the following code:
|
|||
|
||||
```ruby
|
||||
def set_application_directory!
|
||||
Dir.chdir(File.expand_path('../../', APP_PATH)) unless
|
||||
File.exist?(File.expand_path("config.ru"))
|
||||
Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
|
||||
end
|
||||
|
||||
def server
|
||||
|
@ -187,6 +187,8 @@ def server
|
|||
require_command!("server")
|
||||
|
||||
Rails::Server.new.tap do |server|
|
||||
# We need to require application after the server sets environment,
|
||||
# otherwise the --environment option given to the server won't propagate.
|
||||
require APP_PATH
|
||||
Dir.chdir(Rails.application.root)
|
||||
server.start
|
||||
|
@ -207,6 +209,7 @@ sets up the `Rails::Server` class.
|
|||
require 'fileutils'
|
||||
require 'optparse'
|
||||
require 'action_dispatch'
|
||||
require 'rails'
|
||||
|
||||
module Rails
|
||||
class Server < ::Rack::Server
|
||||
|
@ -273,7 +276,7 @@ def parse_options(args)
|
|||
# http://www.meb.uni-bonn.de/docs/cgi/cl.html
|
||||
args.clear if ENV.include?("REQUEST_METHOD")
|
||||
|
||||
options.merge! opt_parser.parse! args
|
||||
options.merge! opt_parser.parse!(args)
|
||||
options[:config] = ::File.expand_path(options[:config])
|
||||
ENV["RACK_ENV"] = options[:environment]
|
||||
options
|
||||
|
@ -284,13 +287,16 @@ With the `default_options` set to this:
|
|||
|
||||
```ruby
|
||||
def default_options
|
||||
environment = ENV['RACK_ENV'] || 'development'
|
||||
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
|
||||
|
||||
{
|
||||
environment: ENV['RACK_ENV'] || "development",
|
||||
pid: nil,
|
||||
Port: 9292,
|
||||
Host: "0.0.0.0",
|
||||
AccessLog: [],
|
||||
config: "config.ru"
|
||||
:environment => environment,
|
||||
:pid => nil,
|
||||
:Port => 9292,
|
||||
:Host => default_host,
|
||||
:AccessLog => [],
|
||||
:config => "config.ru"
|
||||
}
|
||||
end
|
||||
```
|
||||
|
@ -348,6 +354,7 @@ private
|
|||
def print_boot_information
|
||||
...
|
||||
puts "=> Run `rails server -h` for more startup options"
|
||||
...
|
||||
puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
|
||||
end
|
||||
|
||||
|
@ -434,7 +441,11 @@ The `app` method here is defined like so:
|
|||
|
||||
```ruby
|
||||
def app
|
||||
@app ||= begin
|
||||
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
|
||||
end
|
||||
...
|
||||
private
|
||||
def build_app_and_options_from_config
|
||||
if !::File.exist? options[:config]
|
||||
abort "configuration #{options[:config]} not found"
|
||||
end
|
||||
|
@ -443,7 +454,10 @@ def app
|
|||
self.options.merge! options
|
||||
app
|
||||
end
|
||||
end
|
||||
|
||||
def build_app_from_string
|
||||
Rack::Builder.new_from_string(self.options[:builder])
|
||||
end
|
||||
```
|
||||
|
||||
The `options[:config]` value defaults to `config.ru` which contains this:
|
||||
|
@ -459,8 +473,14 @@ run <%= app_const %>
|
|||
The `Rack::Builder.parse_file` method here takes the content from this `config.ru` file and parses it using this code:
|
||||
|
||||
```ruby
|
||||
app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
|
||||
TOPLEVEL_BINDING, config
|
||||
app = new_from_string cfgfile, config
|
||||
|
||||
...
|
||||
|
||||
def self.new_from_string(builder_script, file="(rackup)")
|
||||
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
|
||||
TOPLEVEL_BINDING, file, 0
|
||||
end
|
||||
```
|
||||
|
||||
The `initialize` method of `Rack::Builder` will take the block here and execute it within an instance of `Rack::Builder`. This is where the majority of the initialization process of Rails happens. The `require` line for `config/environment.rb` in `config.ru` is the first to run:
|
||||
|
@ -473,11 +493,22 @@ require ::File.expand_path('../config/environment', __FILE__)
|
|||
|
||||
This file is the common file required by `config.ru` (`rails server`) and Passenger. This is where these two ways to run the server meet; everything before this point has been Rack and Rails setup.
|
||||
|
||||
This file begins with requiring `config/application.rb`.
|
||||
This file begins with requiring `config/application.rb`:
|
||||
|
||||
```ruby
|
||||
require File.expand_path('../application', __FILE__)
|
||||
```
|
||||
|
||||
### `config/application.rb`
|
||||
|
||||
This file requires `config/boot.rb`, but only if it hasn't been required before, which would be the case in `rails server` but **wouldn't** be the case with Passenger.
|
||||
This file requires `config/boot.rb`:
|
||||
|
||||
```ruby
|
||||
require File.expand_path('../boot', __FILE__)
|
||||
```
|
||||
|
||||
But only if it hasn't been required before, which would be the case in `rails server`
|
||||
but **wouldn't** be the case with Passenger.
|
||||
|
||||
Then the fun begins!
|
||||
|
||||
|
@ -498,11 +529,12 @@ This file is responsible for requiring all the individual frameworks of Rails:
|
|||
require "rails"
|
||||
|
||||
%w(
|
||||
active_record
|
||||
action_controller
|
||||
action_mailer
|
||||
rails/test_unit
|
||||
sprockets
|
||||
active_record
|
||||
action_controller
|
||||
action_view
|
||||
action_mailer
|
||||
rails/test_unit
|
||||
sprockets
|
||||
).each do |framework|
|
||||
begin
|
||||
require "#{framework}/railtie"
|
||||
|
@ -568,7 +600,7 @@ initializers (like building the middleware stack) are run last. The `railtie`
|
|||
initializers are the initializers which have been defined on the `Rails::Application`
|
||||
itself and are run between the `bootstrap` and `finishers`.
|
||||
|
||||
After this is done we go back to `Rack::Server`
|
||||
After this is done we go back to `Rack::Server`.
|
||||
|
||||
### Rack: lib/rack/server.rb
|
||||
|
||||
|
@ -576,7 +608,11 @@ Last time we left when the `app` method was being defined:
|
|||
|
||||
```ruby
|
||||
def app
|
||||
@app ||= begin
|
||||
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
|
||||
end
|
||||
...
|
||||
private
|
||||
def build_app_and_options_from_config
|
||||
if !::File.exist? options[:config]
|
||||
abort "configuration #{options[:config]} not found"
|
||||
end
|
||||
|
@ -585,7 +621,10 @@ def app
|
|||
self.options.merge! options
|
||||
app
|
||||
end
|
||||
end
|
||||
|
||||
def build_app_from_string
|
||||
Rack::Builder.new_from_string(self.options[:builder])
|
||||
end
|
||||
```
|
||||
|
||||
At this point `app` is the Rails app itself (a middleware), and what
|
||||
|
@ -611,40 +650,50 @@ server.run wrapped_app, options, &blk
|
|||
```
|
||||
|
||||
At this point, the implementation of `server.run` will depend on the
|
||||
server you're using. For example, if you were using Mongrel, here's what
|
||||
server you're using. For example, if you were using Puma, here's what
|
||||
the `run` method would look like:
|
||||
|
||||
```ruby
|
||||
def self.run(app, options={})
|
||||
server = ::Mongrel::HttpServer.new(
|
||||
options[:Host] || '0.0.0.0',
|
||||
options[:Port] || 8080,
|
||||
options[:num_processors] || 950,
|
||||
options[:throttle] || 0,
|
||||
options[:timeout] || 60)
|
||||
# Acts like Rack::URLMap, utilizing Mongrel's own path finding methods.
|
||||
# Use is similar to #run, replacing the app argument with a hash of
|
||||
# { path=>app, ... } or an instance of Rack::URLMap.
|
||||
if options[:map]
|
||||
if app.is_a? Hash
|
||||
app.each do |path, appl|
|
||||
path = '/'+path unless path[0] == ?/
|
||||
server.register(path, Rack::Handler::Mongrel.new(appl))
|
||||
end
|
||||
elsif app.is_a? URLMap
|
||||
app.instance_variable_get(:@mapping).each do |(host, path, appl)|
|
||||
next if !host.nil? && !options[:Host].nil? && options[:Host] != host
|
||||
path = '/'+path unless path[0] == ?/
|
||||
server.register(path, Rack::Handler::Mongrel.new(appl))
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "first argument should be a Hash or URLMap"
|
||||
end
|
||||
else
|
||||
server.register('/', Rack::Handler::Mongrel.new(app))
|
||||
...
|
||||
DEFAULT_OPTIONS = {
|
||||
:Host => '0.0.0.0',
|
||||
:Port => 8080,
|
||||
:Threads => '0:16',
|
||||
:Verbose => false
|
||||
}
|
||||
|
||||
def self.run(app, options = {})
|
||||
options = DEFAULT_OPTIONS.merge(options)
|
||||
|
||||
if options[:Verbose]
|
||||
app = Rack::CommonLogger.new(app, STDOUT)
|
||||
end
|
||||
|
||||
if options[:environment]
|
||||
ENV['RACK_ENV'] = options[:environment].to_s
|
||||
end
|
||||
|
||||
server = ::Puma::Server.new(app)
|
||||
min, max = options[:Threads].split(':', 2)
|
||||
|
||||
puts "Puma #{::Puma::Const::PUMA_VERSION} starting..."
|
||||
puts "* Min threads: #{min}, max threads: #{max}"
|
||||
puts "* Environment: #{ENV['RACK_ENV']}"
|
||||
puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
|
||||
|
||||
server.add_tcp_listener options[:Host], options[:Port]
|
||||
server.min_threads = min
|
||||
server.max_threads = max
|
||||
yield server if block_given?
|
||||
server.run.join
|
||||
|
||||
begin
|
||||
server.run.join
|
||||
rescue Interrupt
|
||||
puts "* Gracefully stopping, waiting for requests to finish"
|
||||
server.stop(true)
|
||||
puts "* Goodbye!"
|
||||
end
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
@ -654,4 +703,4 @@ the last piece of our journey in the Rails initialization process.
|
|||
This high level overview will help you understand when your code is
|
||||
executed and how, and overall become a better Rails developer. If you
|
||||
still want to know more, the Rails source code itself is probably the
|
||||
best place to go next.
|
||||
best place to go next.
|
|
@ -20,7 +20,7 @@ Only the latest release series will receive bug fixes. When enough bugs are
|
|||
fixed and its deemed worthy to release a new gem, this is the branch it happens
|
||||
from.
|
||||
|
||||
**Currently included series:** 4.0.z
|
||||
**Currently included series:** 4.1.z, 4.0.z
|
||||
|
||||
Security Issues
|
||||
---------------
|
||||
|
@ -35,7 +35,7 @@ be built from 1.2.2, and then added to the end of 1-2-stable. This means that
|
|||
security releases are easy to upgrade to if you're running the latest version
|
||||
of Rails.
|
||||
|
||||
**Currently included series:** 4.0.z, 3.2.z
|
||||
**Currently included series:** 4.1.z, 4.0.z
|
||||
|
||||
Severe Security Issues
|
||||
----------------------
|
||||
|
@ -44,7 +44,7 @@ For severe security issues we will provide new versions as above, and also the
|
|||
last major release series will receive patches and new versions. The
|
||||
classification of the security issue is judged by the core team.
|
||||
|
||||
**Currently included series:** 4.0.z, 3.2.z
|
||||
**Currently included series:** 4.1.z, 4.0.z, 3.2.z
|
||||
|
||||
Unsupported Release Series
|
||||
--------------------------
|
||||
|
|
|
@ -1003,7 +1003,7 @@ _'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS at
|
|||
* X-Content-Type-Options
|
||||
_'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file.
|
||||
* X-Content-Security-Policy
|
||||
[A powerful mechanism for controlling which sites certain content types can be loaded from](http://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html)
|
||||
[A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html)
|
||||
* Access-Control-Allow-Origin
|
||||
Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
|
||||
* Strict-Transport-Security
|
||||
|
|
|
@ -25,8 +25,6 @@ TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterp
|
|||
Upgrading from Rails 4.0 to Rails 4.1
|
||||
-------------------------------------
|
||||
|
||||
NOTE: This section is a work in progress.
|
||||
|
||||
### CSRF protection from remote `<script>` tags
|
||||
|
||||
Or, "whaaat my tests are failing!!!?"
|
||||
|
@ -79,12 +77,15 @@ secrets, you need to:
|
|||
secret_key_base:
|
||||
|
||||
production:
|
||||
secret_key_base:
|
||||
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
||||
```
|
||||
|
||||
2. Copy the existing `secret_key_base` from the `secret_token.rb` initializer to
|
||||
`secrets.yml` under the `production` section.
|
||||
|
||||
2. Use your existing `secret_key_base` from the `secret_token.rb` initializer to
|
||||
set the SECRET_KEY_BASE environment variable for whichever users run the Rails
|
||||
app in production mode. Alternately, you can simply copy the existing
|
||||
`secret_key_base` from the `secret_token.rb` initializer to `secrets.yml`
|
||||
under the `production` section, replacing '<%= ENV["SECRET_KEY_BASE"] %>'.
|
||||
|
||||
3. Remove the `secret_token.rb` initializer.
|
||||
|
||||
4. Use `rake secret` to generate new keys for the `development` and `test` sections.
|
||||
|
@ -463,7 +464,7 @@ being used, you can update your form to use the `PUT` method instead:
|
|||
<%= form_for [ :update_name, @user ], method: :put do |f| %>
|
||||
```
|
||||
|
||||
For more on PATCH and why this change was made, see [this post](http://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates/)
|
||||
For more on PATCH and why this change was made, see [this post](http://weblog.rubyonrails.org/2012/2/25/edge-rails-patch-is-the-new-primary-http-method-for-updates/)
|
||||
on the Rails blog.
|
||||
|
||||
#### A note about media types
|
||||
|
|
|
@ -27,5 +27,5 @@ Gem::Specification.new do |s|
|
|||
s.add_dependency 'railties', version
|
||||
|
||||
s.add_dependency 'bundler', '>= 1.3.0', '< 2.0'
|
||||
s.add_dependency 'sprockets-rails', '~> 2.0.0'
|
||||
s.add_dependency 'sprockets-rails', '~> 2.1'
|
||||
end
|
||||
|
|
|
@ -87,7 +87,7 @@ module Rails
|
|||
class << self
|
||||
def inherited(base)
|
||||
super
|
||||
Rails.application ||= base.instance
|
||||
base.instance
|
||||
end
|
||||
|
||||
# Makes the +new+ method public.
|
||||
|
@ -117,6 +117,8 @@ module Rails
|
|||
@railties = nil
|
||||
@message_verifiers = {}
|
||||
|
||||
Rails.application ||= self
|
||||
|
||||
add_lib_to_load_path!
|
||||
ActiveSupport.run_load_hooks(:before_configuration, self)
|
||||
|
||||
|
@ -151,14 +153,13 @@ module Rails
|
|||
def key_generator
|
||||
# number of iterations selected based on consultation with the google security
|
||||
# team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
|
||||
@caching_key_generator ||= begin
|
||||
@caching_key_generator ||=
|
||||
if secrets.secret_key_base
|
||||
key_generator = ActiveSupport::KeyGenerator.new(secrets.secret_key_base, iterations: 1000)
|
||||
ActiveSupport::CachingKeyGenerator.new(key_generator)
|
||||
else
|
||||
ActiveSupport::LegacyKeyGenerator.new(config.secret_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a message verifier object.
|
||||
|
|
|
@ -249,7 +249,7 @@ module Rails
|
|||
'Use SCSS for stylesheets')
|
||||
else
|
||||
gems << GemfileEntry.version('sass-rails',
|
||||
'~> 4.0.2',
|
||||
'~> 4.0.3',
|
||||
'Use SCSS for stylesheets')
|
||||
end
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<h2><%%= pluralize(@<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2>
|
||||
|
||||
<ul>
|
||||
<%% @<%= singular_table_name %>.errors.full_messages.each do |msg| %>
|
||||
<li><%%= msg %></li>
|
||||
<%% @<%= singular_table_name %>.errors.full_messages.each do |message| %>
|
||||
<li><%%= message %></li>
|
||||
<%% end %>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="frontbase://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -61,7 +61,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name[0,4] %>_tst
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="ibm-db://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -53,7 +53,16 @@ test:
|
|||
<<: *default
|
||||
url: jdbc:db://localhost/<%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
url: jdbc:db://localhost/<%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -26,6 +26,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
production: <%%= ENV["DATABASE_URL"] %>
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -42,7 +42,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -18,7 +18,6 @@ test:
|
|||
<<: *default
|
||||
database: db/test.sqlite3
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: db/production.sqlite3
|
||||
|
|
|
@ -32,11 +32,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Avoid production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Example:
|
||||
# mysql2://myuser:mypass@localhost/somedatabase
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -32,7 +32,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="oracle://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -59,11 +59,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Example:
|
||||
# postgres://myuser:mypass@localhost/somedatabase
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -20,13 +20,6 @@ test:
|
|||
<<: *default
|
||||
database: db/test.sqlite3
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
#
|
||||
# Examples:
|
||||
# sqlite3::memory:
|
||||
# sqlite3:db/production.sqlite3
|
||||
# sqlite3:/full/path/to/database.sqlite3
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: db/production.sqlite3
|
||||
|
|
|
@ -42,7 +42,27 @@ test:
|
|||
<<: *default
|
||||
database: <%= app_name %>_test
|
||||
|
||||
# Do not keep production credentials in the repository,
|
||||
# instead read the configuration from the environment.
|
||||
# As with config/secrets.yml, you never want to store sensitive information,
|
||||
# like your database password, in your source code. If your source code is
|
||||
# ever seen by anyone, they now have access to your database.
|
||||
#
|
||||
# Instead, provide the password as a unix environment variable when you boot
|
||||
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
|
||||
# for a full rundown on how to provide these environment variables in a
|
||||
# production deployment.
|
||||
#
|
||||
# On Heroku and other platform providers, you may have a full connection URL
|
||||
# available as an environment variable. For example:
|
||||
#
|
||||
# DATABASE_URL="sqlserver://myuser:mypass@localhost/somedatabase"
|
||||
#
|
||||
# You can use this database configuration with:
|
||||
#
|
||||
# production:
|
||||
# url: <%%= ENV['DATABASE_URL'] %>
|
||||
#
|
||||
production:
|
||||
url: <%%= ENV["DATABASE_URL"] %>
|
||||
<<: *default
|
||||
database: <%= app_name %>_production
|
||||
username: <%= app_name %>
|
||||
password: <%%= ENV['<%= app_name.upcase %>_DATABASE_PASSWORD'] %>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
Rails.application.config.action_dispatch.cookies_serializer = :json
|
||||
Rails.application.config.action_dispatch.cookies_serializer = :json
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
|
||||
# Add new mime types for use in respond_to blocks:
|
||||
# Mime::Type.register "text/richtext", :rtf
|
||||
# Mime::Type.register_alias "text/html", :iphone
|
||||
|
|
|
@ -31,7 +31,7 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|||
if @<%= orm_instance.save %>
|
||||
redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
|
||||
else
|
||||
render action: 'new'
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -40,7 +40,7 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|||
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
||||
redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
|
||||
else
|
||||
render action: 'edit'
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ abort("Abort testing: Your Rails environment is running in production mode!") if
|
|||
|
||||
require 'active_support/testing/autorun'
|
||||
require 'active_support/test_case'
|
||||
require 'action_controller'
|
||||
require 'action_controller/test_case'
|
||||
require 'action_dispatch/testing/integration'
|
||||
require 'rails/generators/test_case'
|
||||
|
|
|
@ -199,6 +199,7 @@ module ApplicationTests
|
|||
end
|
||||
|
||||
test "precompile creates a manifest file with all the assets listed" do
|
||||
app_file "app/assets/images/rails.png", "notactuallyapng"
|
||||
app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
|
||||
app_file "app/assets/javascripts/application.js", "alert();"
|
||||
# digest is default in false, we must enable it for test environment
|
||||
|
@ -260,7 +261,7 @@ module ApplicationTests
|
|||
test "precompile shouldn't use the digests present in manifest.json" do
|
||||
app_file "app/assets/images/rails.png", "notactuallyapng"
|
||||
|
||||
app_file "app/assets/stylesheets/application.css.erb", "//= depend_on rails.png\np { url: <%= asset_path('rails.png') %> }"
|
||||
app_file "app/assets/stylesheets/application.css.erb", "p { url: <%= asset_path('rails.png') %> }"
|
||||
|
||||
ENV["RAILS_ENV"] = "production"
|
||||
precompile!
|
||||
|
@ -448,23 +449,23 @@ module ApplicationTests
|
|||
|
||||
test "asset urls should be protocol-relative if no request is in scope" do
|
||||
app_file "app/assets/images/rails.png", "notreallyapng"
|
||||
app_file "app/assets/javascripts/image_loader.js.erb", 'var src="<%= image_path("rails.png") %>";'
|
||||
app_file "app/assets/javascripts/image_loader.js.erb", "var src='<%= image_path('rails.png') %>';"
|
||||
add_to_config "config.assets.precompile = %w{image_loader.js}"
|
||||
add_to_config "config.asset_host = 'example.com'"
|
||||
precompile!
|
||||
|
||||
assert_match 'src="//example.com/assets/rails.png"', File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first)
|
||||
assert_match "src='//example.com/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first)
|
||||
end
|
||||
|
||||
test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do
|
||||
ENV["RAILS_RELATIVE_URL_ROOT"] = "/sub/uri"
|
||||
app_file "app/assets/images/rails.png", "notreallyapng"
|
||||
|
||||
app_file "app/assets/javascripts/app.js.erb", 'var src="<%= image_path("rails.png") %>";'
|
||||
app_file "app/assets/javascripts/app.js.erb", "var src='<%= image_path('rails.png') %>';"
|
||||
add_to_config "config.assets.precompile = %w{app.js}"
|
||||
precompile!
|
||||
|
||||
assert_match 'src="/sub/uri/assets/rails.png"', File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
|
||||
assert_match "src='/sub/uri/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
|
||||
end
|
||||
|
||||
test "assets:cache:clean should clean cache" do
|
||||
|
|
|
@ -21,6 +21,12 @@ module ApplicationTests
|
|||
assert_equal Rails.application.config.secret_key_base, clone.config.secret_key_base, "The base secret key on the config should be the same"
|
||||
end
|
||||
|
||||
def test_inheriting_multiple_times_from_application
|
||||
new_application_class = Class.new(Rails::Application)
|
||||
|
||||
assert_not_equal Rails.application.object_id, new_application_class.instance.object_id
|
||||
end
|
||||
|
||||
def test_initialization_of_multiple_copies_of_same_application
|
||||
application1 = AppTemplate::Application.new
|
||||
application2 = AppTemplate::Application.new
|
||||
|
|
|
@ -160,13 +160,6 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
|
|||
Unknown::Generators.send :remove_const, :ActiveModel
|
||||
end
|
||||
|
||||
def test_new_hash_style
|
||||
run_generator
|
||||
assert_file "app/controllers/users_controller.rb" do |content|
|
||||
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|
|
||||
|
|
|
@ -72,6 +72,14 @@ module RailtiesTest
|
|||
assert $to_prepare
|
||||
end
|
||||
|
||||
test "railtie have access to application in before_configuration callbacks" do
|
||||
$after_initialize = false
|
||||
class Foo < Rails::Railtie ; config.before_configuration { $before_configuration = Rails.root.to_path } ; end
|
||||
assert_not $before_configuration
|
||||
require "#{app_path}/config/environment"
|
||||
assert_equal app_path, $before_configuration
|
||||
end
|
||||
|
||||
test "railtie can add after_initialize callbacks" do
|
||||
$after_initialize = false
|
||||
class Foo < Rails::Railtie ; config.after_initialize { $after_initialize = true } ; end
|
||||
|
|
Loading…
Reference in New Issue