Merge branch 'main' into 43114-add-ssl-support-for-postgresql-dbconsole

This commit is contained in:
Guillermo Iguaran 2021-08-29 13:18:58 -07:00 committed by GitHub
commit 32612c6d41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 244 additions and 100 deletions

View File

@ -1,3 +1,11 @@
* Use a static error message when raising `ActionDispatch::Http::Parameters::ParseError`
to avoid inadvertently logging the HTTP request body at the `fatal` level when it contains
malformed JSON.
Fixes #41145
*Aaron Lahey*
* Add `Middleware#delete!` to delete middleware or raise if not found.
`Middleware#delete!` works just like `Middleware#delete` but will

View File

@ -17,8 +17,8 @@ module ActionDispatch
# Raised when raw data from the request cannot be parsed by the parser
# defined for request's content MIME type.
class ParseError < StandardError
def initialize
super($!.message)
def initialize(message = $!.message)
super(message)
end
end
@ -93,7 +93,7 @@ module ActionDispatch
strategy.call(raw_post)
rescue # JSON or Ruby code block errors.
log_parse_error_once
raise ParseError
raise ParseError, "Error occurred while parsing request parameters"
end
end

View File

@ -80,7 +80,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
post "/parse", params: json, headers: { "CONTENT_TYPE" => "application/json", "action_dispatch.show_exceptions" => false }
end
assert_equal JSON::ParserError, exception.cause.class
assert_equal exception.cause.message, exception.message
assert_equal "Error occurred while parsing request parameters", exception.message
ensure
$stderr = STDERR
end

View File

@ -123,7 +123,7 @@ module ActiveModel
# user.serializable_hash(include: { notes: { only: 'title' }})
# # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
def serializable_hash(options = nil)
attribute_names = attributes.keys
attribute_names = self.attribute_names
return serializable_attributes(attribute_names) if options.blank?
@ -148,6 +148,11 @@ module ActiveModel
hash
end
# Returns an array of attribute names as strings
def attribute_names # :nodoc:
attributes.keys
end
private
# Hook method defining how an attribute value should be retrieved for
# serialization. By default this is assumed to be an instance named after

View File

@ -18,7 +18,7 @@
* Make schema cache methods return consistent results.
Previously the schema cache methods `primary_keys`, `columns, `columns_hash`, and `indexes`
Previously the schema cache methods `primary_keys`, `columns`, `columns_hash`, and `indexes`
would behave differently than one another when a table didn't exist and differently across
database adapters. This change unifies the behavior so each method behaves the same regardless
of adapter.

View File

@ -196,14 +196,7 @@ module ActiveRecord
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil, async: false)
materialize_transactions
mark_transaction_written_if_write(sql)
log(sql, name, async: async) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.query(sql)
end
end
raw_execute(sql, name, async)
end
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
@ -629,6 +622,17 @@ module ActiveRecord
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
end
def raw_execute(sql, name, async: false)
materialize_transactions
mark_transaction_written_if_write(sql)
log(sql, name, async: async) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.query(sql)
end
end
end
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
ER_DB_CREATE_EXISTS = 1007
ER_FILSORT_ABORT = 1028

View File

@ -37,25 +37,11 @@ module ActiveRecord
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
end
def execute(sql, name = nil, async: false)
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
super
end
alias_method :raw_execute, :execute
private :raw_execute
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil, async: false)
sql = transform_query(sql)
check_if_write_query(sql)
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
raw_execute(sql, name, async: async)
end
@ -91,6 +77,14 @@ module ActiveRecord
alias :exec_update :exec_delete
private
def raw_execute(sql, name, async: false)
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
super
end
def execute_batch(statements, name = nil)
statements = statements.map { |sql| transform_query(sql) }
combine_multi_statements(statements).each do |statement|

View File

@ -17,7 +17,7 @@ require "models/hotel"
require "models/department"
class HasManyThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase
fixtures :posts, :authors, :comments, :pirates
fixtures :posts, :authors, :comments, :pirates, :author_addresses
def setup
@author = authors(:mary)

View File

@ -5,7 +5,7 @@ require "models/author"
module ActiveRecord
class AndTest < ActiveRecord::TestCase
fixtures :authors
fixtures :authors, :author_addresses
def test_and
david, mary, bob = authors(:david, :mary, :bob)

View File

@ -10,7 +10,7 @@ require "models/categorization"
module ActiveRecord
class WhereChainTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :humans, :essays
fixtures :posts, :comments, :authors, :humans, :essays, :author_addresses
def test_associated_with_association
Post.where.associated(:author).tap do |relation|

View File

@ -1,3 +1,9 @@
* Emit Active Support instrumentation events from Active Storage analyzers.
Fixes #42930
*Shouichi Kamiya*
* Add support for byte range requests
*Tom Prats*

View File

@ -40,5 +40,9 @@ module ActiveStorage
def tmpdir # :doc:
Dir.tmpdir
end
def instrument(analyzer, &block) # :doc:
ActiveSupport::Notifications.instrument("analyze.active_storage", analyzer: analyzer, &block)
end
end
end

View File

@ -42,14 +42,16 @@ module ActiveStorage
end
def probe_from(file)
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
instrument(File.basename(ffprobe_path)) do
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
end
end
rescue Errno::ENOENT
logger.info "Skipping audio analysis because FFmpeg isn't installed"

View File

@ -12,7 +12,10 @@ module ActiveStorage
def read_image
download_blob_to_tempfile do |file|
require "mini_magick"
image = MiniMagick::Image.new(file.path)
image = instrument("mini_magick") do
MiniMagick::Image.new(file.path)
end
if image.valid?
yield image

View File

@ -12,7 +12,10 @@ module ActiveStorage
def read_image
download_blob_to_tempfile do |file|
require "ruby-vips"
image = ::Vips::Image.new_from_file(file.path, access: :sequential)
image = instrument("vips") do
::Vips::Image.new_from_file(file.path, access: :sequential)
end
if valid_image?(image)
yield image

View File

@ -121,14 +121,16 @@ module ActiveStorage
end
def probe_from(file)
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
instrument(File.basename(ffprobe_path)) do
IO.popen([ ffprobe_path,
"-print_format", "json",
"-show_streams",
"-show_format",
"-v", "error",
file.path
]) do |output|
JSON.parse(output.read)
end
end
rescue Errno::ENOENT
logger.info "Skipping video analysis because FFmpeg isn't installed"

View File

@ -42,6 +42,9 @@ module ActiveStorage
image/vnd.adobe.photoshop
image/vnd.microsoft.icon
image/webp
image/avif
image/heic
image/heif
)
config.active_storage.web_image_content_types = %w(

View File

@ -13,4 +13,14 @@ class ActiveStorage::Analyzer::AudioAnalyzerTest < ActiveSupport::TestCase
assert_equal 0.914286, metadata[:duration]
assert_equal 128000, metadata[:bit_rate]
end
test "instrumenting analysis" do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "audio.mp3", content_type: "audio/mp3")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "ffprobe" }, events.first.payload)
end
end

View File

@ -46,6 +46,18 @@ class ActiveStorage::Analyzer::ImageAnalyzer::ImageMagickTest < ActiveSupport::T
end
end
test "instrumenting analysis" do
analyze_with_image_magick do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "mini_magick" }, events.first.payload)
end
end
private
def analyze_with_image_magick
previous_processor, ActiveStorage.variant_processor = ActiveStorage.variant_processor, :mini_magick

View File

@ -46,6 +46,18 @@ class ActiveStorage::Analyzer::ImageAnalyzer::VipsTest < ActiveSupport::TestCase
end
end
test "instrumenting analysis" do
analyze_with_vips do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "vips" }, events.first.payload)
end
end
private
def analyze_with_vips
previous_processor, ActiveStorage.variant_processor = ActiveStorage.variant_processor, :vips

View File

@ -76,4 +76,14 @@ class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase
assert metadata[:video]
assert_not metadata[:audio]
end
test "instrumenting analysis" do
events = subscribe_events_from("analyze.active_storage")
blob = create_file_blob(filename: "video_without_audio_stream.mp4", content_type: "video/mp4")
blob.analyze
assert_equal 1, events.size
assert_equal({ analyzer: "ffprobe" }, events.first.payload)
end
end

View File

@ -157,6 +157,14 @@ class ActiveSupport::TestCase
ActionController::Base.raise_on_open_redirects = old_raise_on_open_redirects
ActiveStorage::Blob.service = old_service
end
def subscribe_events_from(name)
events = []
ActiveSupport::Notifications.subscribe(name) do |*args|
events << ActiveSupport::Notifications::Event.new(*args)
end
events
end
end
require "global_id"

View File

@ -17,7 +17,7 @@ After reading this guide you will know:
Active Record supports application-level encryption. It works by declaring which attributes should be encrypted and seamlessly encrypting and decrypting them when necessary. The encryption layer is placed between the database and the application. The application will access unencrypted data but the database will store it encrypted.
## Basic usage
## Basic Usage
### Setup
@ -35,7 +35,7 @@ active_record_encryption:
NOTE: These generated keys and salt are 32 bytes length. If you generate these yourself, the minimum lengths you should use are 12 bytes for the primary key (this will be used to derive the AES 32 bytes key) and 20 bytes for the salt.
### Declaration of encrypted attributes
### Declaration of Encrypted Attributes
Encryptable attributes are defined at the model level. These are regular Active Record attributes backed by a column with the same name.
@ -62,7 +62,7 @@ Encryption takes additional space in the column. You can estimate the worst-case
NOTE: The reason for the additional space are Base 64 encoding and additional metadata stored with the encrypted values.
### Deterministic and non-deterministic encryption
### Deterministic and Non-deterministic Encryption
By default, Active Record Encryption uses a non-deterministic approach to encryption. This means that encrypting the same content with the same password twice will result in different ciphertexts. This is good for security, since it makes crypto-analysis of encrypted content much harder, but it makes querying the database impossible.
@ -106,11 +106,11 @@ config.active_record.encryption.encrypt_fixtures = true
When enabled, all the encryptable attributes will be encrypted according to the encryption settings defined in the model.
#### Action text fixtures
#### Action Text Fixtures
To encrypt action text fixtures you should place them in `fixtures/action_text/encrypted_rich_texts.yml`.
### Supported types
### Supported Types
`active_record.encryption` will serialize values using the underlying type before encrypting them, but *they must be serializable as strings*. Structured types like `serialized` are supported out of the box.
@ -130,7 +130,7 @@ class Article < ApplicationRecord
end
```
### Ignoring case
### Ignoring Case
You might need to ignore case when querying deterministically encrypted data. There are two options that can help you here.
@ -150,7 +150,7 @@ class Label
end
```
### Support for unencrypted data
### Support for Unencrypted Data
To ease migrations of unencrypted data, the library includes the option `config.active_record.encryption.support_unencrypted_data`. When set to `true`:
@ -159,7 +159,7 @@ To ease migrations of unencrypted data, the library includes the option `config.
**This options is meant to be used in transition periods** while clear data and encrypted data need to coexist. Their value is `false` by default, which is the recommended goal for any application: errors will be raised when working with unencrypted data.
### Support for previous encryption schemes
### Support for Previous Encryption Schemes
Changing encryption properties of attributes can break existing data. For example, imagine you want to make a "deterministic" attribute "not deterministic". If you just change the declaration in the model, reading existing ciphertexts will fail because they are different now.
@ -173,7 +173,7 @@ You can configure previous encryption schemes:
* Globally
* On a per-attribute basis
#### Global previous encryption schemes
#### Global Previous Encryption Schemes
You can add previous encryption schemes by adding them as list of properties using the `previous` config property in your `application.rb`:
@ -181,7 +181,7 @@ You can add previous encryption schemes by adding them as list of properties usi
config.active_record.encryption.previous = [ { key_provider: MyOldKeyProvider.new } ]
```
#### Per-attribute encryption schemes
#### Per-attribute Encryption Schemes
Use `:previous` when declaring the attribute:
@ -191,7 +191,7 @@ class Article
end
```
#### Encryption schemes and deterministic attributes
#### Encryption Schemes and Deterministic Attributes
When adding previous encryption schemes:
@ -200,11 +200,11 @@ When adding previous encryption schemes:
The reason is that, with deterministic encryption, you normally want ciphertexts to remain constant. You can change this behavior by setting `deterministic: { fixed: false }`. In that case, it will use the *newest* encryption scheme for encrypting new data.
### Unique constraints
### Unique Constraints
NOTE: Unique constraints can only be used with data encrypted deterministically.
#### Unique validations
#### Unique Validations
Unique validations are supported normally as long as extended queries are enabled (`config.active_record.encryption.extend_queries = true`).
@ -219,7 +219,7 @@ They will also work when combining encrypted and unencrypted data, and when conf
NOTE: If you want to ignore case make sure to use `downcase:` or `ignore_case:` in the `encrypts` declaration. Using the `case_sensitive:` option in the validation won't work.
#### Unique indexes
#### Unique Indexes
To support unique indexes on deterministically-encrypted columns, you need to make sure their ciphertext doesn't ever change.
@ -231,7 +231,7 @@ class Person
end
```
### Filtering params named as encrypted columns
### Filtering Params Named as Encrypted Columns
By default, encrypted columns are configured to be [automatically filtered in Rails logs](https://guides.rubyonrails.org/action_controller_overview.html#parameters-filtering). You can disable this behavior by adding this to your `application.rb`:
@ -258,11 +258,11 @@ And you can disable this behavior and preserve the encoding in all cases with:
config.active_record.encryption.forced_encoding_for_deterministic_encryption = nil
```
## Key management
## Key Management
Key management strategies are implemented by key providers. You can configure key providers globally or on a per-attribute basis.
### Built-in key providers
### Built-in Key Providers
#### DerivedSecretKeyProvider
@ -289,7 +289,7 @@ config.active_record.encryption.key_provider = ActiveRecord::Encryption::Envelop
As with other built-in key providers, you can provide a list of primary keys in `active_record.encryption.primary_key`, to implement key-rotation schemes.
### Custom key providers
### Custom Key Providers
For more advanced key-management schemes, you can configure a custom key provider in an initializer:
@ -316,7 +316,7 @@ Both methods return `ActiveRecord::Encryption::Key` objects:
A key can include arbitrary tags that will be stored unencrypted with the message. You can use `ActiveRecord::Encryption::Message#headers` to examine those values when decrypting.
### Model-specific key providers
### Model-specific Key Providers
You can configure a key provider on a per-class basis with the `:key_provider` option:
@ -326,7 +326,7 @@ class Article < ApplicationRecord
end
```
### Model-specific keys
### Model-specific Keys
You can configure a given key on a per-class basis with the `:key` option:
@ -338,7 +338,7 @@ end
The key will be used internally to derive the key used to encrypt and decrypt the data.
### Rotating keys
### Rotating Keys
`active_record.encryption` can work with lists of keys to support implementing key-rotation schemes:
@ -360,7 +360,7 @@ NOTE: Rotating keys is not currently supported for deterministic encryption.
NOTE: Active Record Encryption doesn't provide automatic management of key rotation processes yet. All the pieces are there, but this hasn't been implemented yet.
### Storing key references
### Storing Key References
There is a setting `active_record.encryption.store_key_references` you can use to make `active_record.encryption` store a reference to the encryption key in the encrypted message itself.
@ -376,20 +376,20 @@ This makes for a more performant decryption since, instead of trying lists of ke
ActiveRecord encryption is meant to be used declaratively, but it presents an API for advanced usage scenarios.
#### Encrypt and decrypt
#### Encrypt and Decrypt
```ruby
article.encrypt # encrypt or re-encrypt all the encryptable attributes
article.decrypt # decrypt all the encryptable attributes
```
#### Read ciphertext
#### Read Ciphertext
```ruby
article.ciphertext_for(:title)
```
#### Check if attribute is encrypted or not
#### Check if Attribute is Encrypted or Not
```ruby
article.encrypted_attribute?(:title)
@ -397,7 +397,7 @@ article.encrypted_attribute?(:title)
## Configuration
### Configuration options
### Configuration Options
You can configure Active Record Encryption options by setting them in your `application.rb` (most common scenario) or in a specific environment config file `config/environments/<env name>.rb` if you want to set them on a per-environment basis.
@ -426,7 +426,7 @@ The available config options are:
NOTE: It's recommended to use Rails built-in credentials support to store keys. If you prefer to set them manually via config properties, make sure you don't commit them with your code (e.g: use environment variables).
### Encryption contexts
### Encryption Contexts
An encryption context defines the encryption components that are used in a given moment. There is a default encryption context based on your global configuration, but you can configure a custom context for a given attribute or when running a specific block of code.
@ -441,7 +441,7 @@ The main components of encryption contexts are:
NOTE: If you decide to build your own `message_serializer`, It's important to use safe mechanisms that can't deserialize arbitrary objects. A common supported scenario is encrypting existing unencrypted data. An attacker can leverage this to enter a tampered payload before encryption takes place and perform RCE attacks. This means custom serializers should avoid `Marshal`, `YAML.load` (use `YAML.safe_load` instead) or `JSON.load` (use `JSON.parse` instead).
#### Global encryption context
#### Global Encryption Context
The global encryption context is the one used by default and is configured as other configuration properties in your `application.rb` or environment config files.
@ -450,7 +450,7 @@ config.active_record.encryption.key_provider = ActiveRecord::Encryption::Envelop
config.active_record_encryption.encryptor = MyEncryptor.new
```
#### Per-attribute encryption contexts
#### Per-attribute Encryption Contexts
You can override encryption context params by passing them in the attribute declaration:
@ -460,7 +460,7 @@ class Attribute
end
```
#### Encryption context when running a block of code
#### Encryption Context When Running a Block of Code
You can use `ActiveRecord::Encryption.with_encryption_context` to set an encryption context for a given block of code:
@ -470,9 +470,9 @@ ActiveRecord::Encryption.with_encryption_context(encryptor: ActiveRecord::Encryp
end
```
#### Built-in encryption contexts
#### Built-in Encryption Contexts
##### Disable encryption
##### Disable Encryption
You can run code without encryption:
@ -483,7 +483,7 @@ end
```
This means that reading encrypted text will return the ciphertext and saved content will be stored unencrypted.
##### Protect encrypted data
##### Protect Encrypted Data
You can run code without encryption but preventing overwriting encrypted content:

View File

@ -690,6 +690,12 @@ INFO. The only ActiveStorage service that provides this hook so far is GCS.
#### transform.active_storage
#### analyze.active_storage
| Key | Value |
| ------------ | ------------------------------ |
| `:analyzer` | Name of analyzer e.g., ffprobe |
### Railties
#### load_config_initializer.railties

View File

@ -517,6 +517,9 @@ in controllers and views. This defaults to `false`.
the context does not change during the lifetime of the request or job execution.
Defaults to `false`.
* `config.active_record.schema_cache_ignored_tables` define the list of table that should be ignored when generating
the schema cache. It accepts an `Array` of strings, representing the table names, or regular expressions.
The MySQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns as booleans. Defaults to `true`.
@ -1047,9 +1050,9 @@ You can find more detailed configuration options in the
config.active_storage.paths[:ffprobe] = '/usr/local/bin/ffprobe'
```
* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/pjpeg image/tiff image/bmp image/vnd.adobe.photoshop image/vnd.microsoft.icon image/webp)`.
* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/pjpeg image/tiff image/bmp image/vnd.adobe.photoshop image/vnd.microsoft.icon image/webp image/avif image/heic image/heif)`.
* `config.active_storage.web_image_content_types` accepts an array of strings regarded as web image content types in which variants can be processed without being converted to the fallback PNG format. If you want to use `WebP` variants in your application you can add `image/webp` to this array. The default is `%w(image/png image/jpeg image/jpg image/gif)`.
* `config.active_storage.web_image_content_types` accepts an array of strings regarded as web image content types in which variants can be processed without being converted to the fallback PNG format. If you want to use `WebP` or `AVIF` variants in your application you can add `image/webp` or `image/avif` to this array. The default is `%w(image/png image/jpeg image/jpg image/gif)`.
* `config.active_storage.content_types_to_serve_as_binary` accepts an array of strings indicating the content types that Active Storage will always serve as an attachment, rather than inline. The default is `%w(text/html
text/javascript image/svg+xml application/postscript application/x-shockwave-flash text/xml application/xml application/xhtml+xml application/mathml+xml text/cache-manifest)`.

View File

@ -4,6 +4,10 @@
*Michael Bayucot*
* Add support for comments above gem declaration in Rails application templates, e.g. `gem("nokogiri", comment: "For XML")`.
*Linas Juškevičius*
* The setter `config.autoloader=` has been deleted. `zeitwerk` is the only
available autoloading mode.
@ -28,7 +32,7 @@
railties initializers.
Please check the [autoloading
guide](https://edgeguides.rubyonrails.org/autoloading_and_reloading_constants.html#autoloading-when-the-application-boots)
guide](https://guides.rubyonrails.org/v7.0/autoloading_and_reloading_constants.html#autoloading-when-the-application-boots)
for details.
*Xavier Noria*
@ -40,9 +44,9 @@
*Xavier Noria*
* Show Rake task description if command is run with -h.
* Show Rake task description if command is run with `-h`.
Adding `-h` (or `--help`) to a Rails command that's a Rake task, now returns
Adding `-h` (or `--help`) to a Rails command that's a Rake task now outputs
the task description instead of the general Rake help.
*Petrik de Heus*
@ -94,7 +98,7 @@
*Jean Boussier*
* Remove Rack::Runtime from the default middleware stack and deprecate
* Remove `Rack::Runtime` from the default middleware stack and deprecate
referencing it in middleware operations without adding it back.
*Hartley McGuire*
@ -121,20 +125,17 @@
*Prateek Choudhary*
* Add benchmark method that can be called from anywhere.
* The new method `Rails.benchmark` gives you a quick way to measure and log the execution time taken by a block:
This method is used as a quick way to measure & log the speed of some code.
However, it was previously available only in specific contexts, mainly views and controllers.
The new Rails.benchmark can be used in the rest of your app: services, API wrappers, models, etc.
def test
Rails.benchmark("test") { ... }
def test_expensive_stuff
Rails.benchmark("test_expensive_stuff") { ... }
end
This functionality was available in some contexts only before.
*Simon Perepelitsa*
* Removed manifest.js and application.css in app/assets
folder when --skip-sprockets option passed as flag to rails.
* Applications generated with `--skip-sprockets` no longer get `app/assets/config/manifest.js` and `app/assets/stylesheets/application.css`.
*Cindy Gao*

View File

@ -18,6 +18,7 @@ module Rails
# gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "http://gems.github.com/"
# gem "rails", "3.0", git: "https://github.com/rails/rails"
# gem "RedCloth", ">= 4.1.0", "< 4.2.0"
# gem "rspec", comment: "Put this comment above the gem declaration"
def gem(*args)
options = args.extract_options!
name, *versions = args
@ -26,6 +27,9 @@ module Rails
# otherwise use name (version).
parts, message = [ quote(name) ], name.dup
# Output a comment above the gem declaration.
comment = options.delete(:comment)
if versions = versions.any? ? versions : options.delete(:version)
_versions = Array(versions)
_versions.each do |version|
@ -40,9 +44,17 @@ module Rails
parts << quote(options) unless options.empty?
in_root do
str = "gem #{parts.join(", ")}"
str = indentation + str
append_file_with_newline "Gemfile", str, verbose: false
str = []
if comment
comment.each_line do |comment_line|
str << indentation
str << "# #{comment_line}"
end
str << "\n"
end
str << indentation
str << "gem #{parts.join(", ")}"
append_file_with_newline "Gemfile", str.join, verbose: false
end
end

View File

@ -116,6 +116,22 @@ class ActionsTest < Rails::Generators::TestCase
assert_file "Gemfile", /gem "rspec", github: "dchelimsky\/rspec", tag: "1\.2\.9\.rc1"/
end
def test_gem_should_put_the_comment_before_gem_declaration
run_generator
action :gem, "rspec", comment: "Use RSpec"
assert_file "Gemfile", /# Use RSpec\ngem "rspec"/
end
def test_gem_should_support_multiline_comments
run_generator
action :gem, "rspec", comment: "Use RSpec\nReplaces minitest"
assert_file "Gemfile", /# Use RSpec\n# Replaces minitest\ngem "rspec"/
end
def test_gem_with_non_string_options
run_generator
@ -156,6 +172,26 @@ class ActionsTest < Rails::Generators::TestCase
assert_file "Gemfile", /\n\ngroup :development, :test do\n gem "rspec-rails"\nend\n\ngroup :test do\n gem "fakeweb"\nend\n\z/
end
def test_gem_group_should_indent_comments
run_generator
action :gem_group, :test do
gem "fakeweb", comment: "Fake requests"
end
assert_file "Gemfile", /\n\ngroup :test do\n # Fake requests\n gem "fakeweb"\nend\n\z/
end
def test_gem_group_should_indent_multiline_comments
run_generator
action :gem_group, :test do
gem "fakeweb", comment: "Fake requests\nNeeded in tests"
end
assert_file "Gemfile", /\n\ngroup :test do\n # Fake requests\n # Needed in tests\n gem "fakeweb"\nend\n\z/
end
def test_github_should_create_an_indented_block
run_generator