Skip to content

Commit

Permalink
Replace Hashie::Mash with ActiveSupport::HashWithIndifferentAccess.
Browse files Browse the repository at this point in the history
  • Loading branch information
James McCarthy authored and dblock committed Apr 14, 2017
1 parent ee62ea5 commit 1bd1a21
Show file tree
Hide file tree
Showing 29 changed files with 536 additions and 61 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ coverage
doc
pkg
.rvmrc
.ruby-version
.ruby-gemset
.bundle
.yardoc/*
.byebug_history
dist
Gemfile.lock
gemfiles/*.lock
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#### Features

* [#1594](https://github.com/ruby-grape/grape/pull/1594): Replace `Hashie::Mash` parameters with `ActiveSupport::HashWithIndifferentAccess` - [@james2m](https://github.com/james2m), [@dblock](https://github.com/dblock).
* Your contribution here.

#### Fixes
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
49 changes: 29 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,24 @@ In the case of conflict between either of:
* `GET`, `POST` and `PUT` parameters
* the contents of the request body on `POST` and `PUT`

route string parameters will have precedence.
Route string parameters will have precedence.

### Params Class

By default parameters are available as `ActiveSupport::HashWithIndifferentAccess`. This can be changed to, for example, Ruby `Hash` or `Hashie::Mash` for the entire API.

[TODO]

The class can be overridden on individual parameter blocks using `build_with` as follows.

```ruby
params do
build_with Grape::Extensions::Hash::ParamBuilder
optional :color, type: String
end
```

In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.

### Declared

Expand All @@ -509,7 +526,7 @@ post 'users/signup' do
end
````

If we do not specify any params, `declared` will return an empty `Hashie::Mash` instance.
If we do not specify any params, `declared` will return an empty `ActiveSupport::HashWithIndifferentAccess` hash.

**Request**

Expand All @@ -526,8 +543,8 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d

````

Once we add parameters requirements, grape will start returning only the declared params.

Once we add parameters requirements, grape will start returning only the declared params[:
]
````ruby
format :json

Expand Down Expand Up @@ -562,17 +579,11 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
}
````

The returned hash is a `Hashie::Mash` instance, allowing you to access parameters via dot notation:

```ruby
declared(params).user == declared(params)['user']
```

The returned hash is a `ActiveSupport::HashWithIndifferentAccess` hash.

The `#declared` method is not available to `before` filters, as those are evaluated prior
to parameter coercion.
The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.

### Include parent namespaces
### Include Parent Namespaces

By default `declared(params)` includes parameters that were defined in all parent namespaces. If you want to return only parameters from your current namespace, you can set `include_parent_namespaces` option to `false`.

Expand Down Expand Up @@ -897,18 +908,16 @@ end

### Multipart File Parameters

Grape makes use of `Rack::Request`'s built-in support for multipart
file parameters. Such parameters can be declared with `type: File`:
Grape makes use of `Rack::Request`'s built-in support for multipart file parameters. Such parameters can be declared with `type: File`:

```ruby
params do
requires :avatar, type: File
end
post '/' do
# Parameter will be wrapped using Hashie:
params.avatar.filename # => 'avatar.png'
params.avatar.type # => 'image/png'
params.avatar.tempfile # => #<File>
params[:avatar][:filename] # => 'avatar.png'
params[:avatar][:avatar] # => 'image/png'
params[:avatar][:tempfile] # => #<File>
end
```

Expand Down Expand Up @@ -1381,7 +1390,7 @@ class Admin < Grape::Validations::Base
# @attrs is a list containing the attribute we are currently validating
# in our sample case this method once will get called with
# @attrs being [:admin_field] and once with @attrs being [:admin_false_field]
return unless request.params.key? @attrs.first
return unless request.params.key?(@attrs.first)
# check if admin flag is set to true
return unless @option
# check if user is admin or not
Expand Down
27 changes: 27 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
Upgrading Grape
===============

### Upgrading to >= 1.0.0

#### Changes in Parameter Class

The default class for `params` has changed from `Hashie::Mash` to `ActiveSupport::HashWithIndifferentAccess` and the `hashie` dependency has been removed. To restore the behavior of prior versions, add `hashie` to your `Gemfile` and use `TODO`.

[TODO]

This behavior can also be overridden on individual parameter blocks using `build_with`.

```ruby
params do
build_with Grape::Extensions::Hash::ParamBuilder
optional :color, type: String
end
```

If you're constructing your own `Grape::Request` in a middleware, you can pass different parameter handlers to create the desired `params` class with `build_params_with`.

```ruby
def request
Grape::Request.new(env, build_params_with: Grape::Extensions::Hashie::Mash::ParamBuilder)
end
```

See [#1594](https://github.com/ruby-grape/grape/pull/1594) for more information.

### Upgrading to >= 0.19.1

#### DELETE now defaults to status code 200 for responses with a body, or 204 otherwise
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rack_1.5.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rack_edge.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_3.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_4.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_5.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_edge.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ group :development, :test do
gem 'bundler'
gem 'rake'
gem 'rubocop', '0.47.0'
gem 'hashie'
end

group :development do
Expand Down
1 change: 0 additions & 1 deletion grape.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'activesupport'
s.add_runtime_dependency 'multi_json', '>= 1.3.2'
s.add_runtime_dependency 'multi_xml', '>= 0.5.2'
s.add_runtime_dependency 'hashie', '>= 2.1.0'
s.add_runtime_dependency 'virtus', '>= 1.0.0'
s.add_runtime_dependency 'builder'

Expand Down
17 changes: 16 additions & 1 deletion lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
require 'rack/accept'
require 'rack/auth/basic'
require 'rack/auth/digest/md5'
require 'hashie'
require 'set'
require 'active_support/version'
require 'active_support/core_ext/hash/indifferent_access'
Expand Down Expand Up @@ -78,13 +77,29 @@ module Exceptions
autoload :MethodNotAllowed
end

module Extensions
extend ActiveSupport::Autoload

autoload :DeepMergeableHash
autoload :DeepSymbolizeHash
autoload :Hash
autoload :HashWithIndifferentAccess

module Hashie
extend ActiveSupport::Autoload

autoload :Mash
end
end

module Middleware
extend ActiveSupport::Autoload
autoload :Base
autoload :Versioner
autoload :Formatter
autoload :Error
autoload :Globals
autoload :Stack

module Auth
extend ActiveSupport::Autoload
Expand Down
4 changes: 2 additions & 2 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def declared_array(passed_params, options, declared_params)
end

def declared_hash(passed_params, options, declared_params)
declared_params.each_with_object(Hashie::Mash.new) do |declared_param, memo|
declared_params.each_with_object({}) do |declared_param, memo|
# If it is not a Hash then it does not have children.
# Find its value or set it to nil.
if !declared_param.is_a?(Hash)
Expand All @@ -56,7 +56,7 @@ def declared_hash(passed_params, options, declared_params)
declared_param.each_pair do |declared_parent_param, declared_children_params|
next unless options[:include_missing] || passed_params.key?(declared_parent_param)

passed_children_params = passed_params[declared_parent_param] || Hashie::Mash.new
passed_children_params = passed_params[declared_parent_param] || {}
memo[optioned_param_key(declared_parent_param, options)] = declared(passed_children_params, options, declared_children_params)
end
end
Expand Down
25 changes: 25 additions & 0 deletions lib/grape/dsl/parameters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,31 @@ module DSL
module Parameters
extend ActiveSupport::Concern

# Set the module used to build the request.params.
#
# @param build_with the ParamBuilder module to use when building request.params
# Available builders are;
# * Grape::Extensions::HashWithIndifferentAccess::ParamBuilder (default)
# * Grape::Extensions::Hash::ParamBuilder
# * Grape::Extensions::Hashie::Mash::ParamBuilder
#
# @example
#
# require 'grape/extenstions/hashie_mash'
# class API < Grape::API
# desc "Get collection"
# params do
# build_with Grape::Extensions::Hashie::Mash::ParamBuilder
# requires :user_id, type: Integer
# end
# get do
# params['user_id']
# end
# end
def build_with(build_with = nil)
@api.namespace_inheritable(:build_with, build_with)
end

# Include reusable params rules among current.
# You can define reusable params with helpers method.
#
Expand Down
7 changes: 1 addition & 6 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require 'grape/middleware/stack'

module Grape
# An Endpoint is the proxy scope in which all routing
# blocks are executed. In other words, any methods
Expand Down Expand Up @@ -239,15 +237,12 @@ def equals?(e)
def run
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
@header = {}

@request = Grape::Request.new(env)
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_with))
@params = @request.params
@headers = @request.headers

cookies.read(@request)

self.class.run_before_each(self)

run_filters befores, :before

if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
Expand Down
19 changes: 19 additions & 0 deletions lib/grape/extensions/deep_mergeable_hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Grape
module Extensions
class DeepMergeableHash < ::Hash
def deep_merge!(other_hash)
other_hash.each_pair do |current_key, other_value|
this_value = self[current_key]

self[current_key] = if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
this_value.deep_merge(other_value)
else
other_value
end
end

self
end
end
end
end
22 changes: 22 additions & 0 deletions lib/grape/extensions/deep_symbolize_hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Grape
module Extensions
module DeepSymbolizeHash
def self.deep_symbolize_keys_in(object)
case object
when ::Hash
object.each_with_object({}) do |(key, value), new_hash|
new_hash[symbolize_key(key)] = deep_symbolize_keys_in(value)
end
when ::Array
object.map { |element| deep_symbolize_keys_in(element) }
else
object
end
end

def self.symbolize_key(key)
key.respond_to?(:to_sym) ? key.to_sym : key
end
end
end
end
17 changes: 17 additions & 0 deletions lib/grape/extensions/hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Grape
module Extensions
module Hash
module ParamBuilder
def build_params
params = Grape::Extensions::DeepMergeableHash[rack_params]
params.deep_merge!(grape_routing_args) if env[Grape::Env::GRAPE_ROUTING_ARGS]
post_process_params(params)
end

def post_process_params(params)
Grape::Extensions::DeepSymbolizeHash.deep_symbolize_keys_in(params)
end
end
end
end
end
Loading

0 comments on commit 1bd1a21

Please sign in to comment.