Skip to content

Replace hashie mash with hash #1594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a908eb7
Ignore .ruby-version, .ruby-gemset and byebug_history.
Mar 10, 2017
1afdc91
replace Hashie::Mash with ActiveSupport::HashWithIndifferentAccess
msroot Oct 28, 2016
162f28c
Move hashie from gemspec to Gemfile Development dependency.
Mar 13, 2017
57f4545
Replace references to ActiveSupport::HashWithIndifferentAccess.
Mar 13, 2017
1787cb7
Deep symbolize module for converting a Hash.
Mar 12, 2017
71052c8
Create DeepMergeableHash.
Mar 16, 2017
790443b
Add parameter builder extensions.
Mar 16, 2017
d9e8ed0
Update coerce_spec for Hashie::Mash.
Mar 16, 2017
bbb88e3
Update endpoint_spec to test setting param builder.
Mar 16, 2017
eabe213
Update request_spec to expect a symbolized Hash.
Mar 16, 2017
2a21427
Add build_with to the parameters dsl to select the builder.
Mar 16, 2017
e54e2d0
Use build_with in Endpoint to pass to Grape::Request.
Mar 16, 2017
2201d15
Plug the parameter builder extensions into Grape::Request.
Mar 16, 2017
85e02c7
Ensure deep symbolization of coerced params in Hash's.
Mar 16, 2017
d7c1daa
Switch Grape::Request to use Hash as default.
Mar 16, 2017
2491071
Add build_parameters_with to Grape::Middleware::Globals
Mar 16, 2017
f4aeb9d
Update gemfiles from appraisal and exclude from Rubocop.
Mar 13, 2017
dc07f38
Change README.md to reference Hash not HashWithIndifferentAccess
Mar 16, 2017
6c2d614
Update CHANGELOG.md with PR 1594.
Mar 13, 2017
abae16c
Ensure CustomTypeCoercer works with plain Hash.
Apr 6, 2017
247869b
Switch the default params to HashWithIndifferentAccess.
Apr 6, 2017
6e5964b
Document the changes to params in README and UPGRADING.
Apr 6, 2017
2ddaa42
Some adjustments to README.md.
Apr 6, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ AllCops:
TargetRubyVersion: 2.1
Include:
- Dangerfile
- gemfiles/*.gemfile

Exclude:
- vendor/**/*
- bin/**/*
- gemfiles/*.gemfile

inherit_from: .rubocop_todo.yml

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): Make Hashie::Mash params optional - [@james2m](https://github.com/james2m).
* [#1555](https://github.com/ruby-grape/grape/pull/1555): Added code coverage w/Coveralls - [@dblock](https://github.com/dblock).
* [#1568](https://github.com/ruby-grape/grape/pull/1568): Add `proc` option to `values` validator to allow custom checks - [@jlfaber](https://github.com/jlfaber).
* [#1575](https://github.com/ruby-grape/grape/pull/1575): Include nil values for missing nested params in declared - [@thogg4](https://github.com/thogg4).
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', require: false
end

group :development do
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,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 `Hash`.

**Request**

Expand Down Expand Up @@ -562,10 +562,10 @@ 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:
The returned hash is a `Hash`.

```ruby
declared(params).user == declared(params)['user']
declared(params)[:user] == declared(params)['user']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should still work, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a HashWithIndifferentAccess so I wouldn't expect to be able to access :user as a method only with a key.

```


Expand Down Expand Up @@ -905,11 +905,13 @@ 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>
# Parameter will be a plain Ruby `Hash`:
params[:avatar][:filename] # => 'avatar.png'
params[:avatar]['avatar'] # => 'image/png'
params[:avatar][:tempfile] # => #<File>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code works here with HashWithIndifferentAccess default.

end

`params` hash keys will all be converted to symbols.
```

### First-Class `JSON` Types
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', require: false
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', require: false
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', require: false
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', require: false
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', require: false
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', require: false
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
1 change: 0 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
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: 2 additions & 5 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Grape
# blocks are executed. In other words, any methods
# on the instance level of this class may be called
# from inside a `get`, `post`, etc.
class Endpoint
class Endpoint # rubocop:disable ClassLength
include Grape::DSL::Settings
include Grape::DSL::InsideRoute

Expand Down Expand Up @@ -239,15 +239,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
19 changes: 19 additions & 0 deletions lib/grape/extensions/hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require_relative 'deep_mergeable_hash'
require_relative 'deep_symbolize_hash'
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
17 changes: 17 additions & 0 deletions lib/grape/extensions/hash_with_indifferent_access.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Grape
module Extensions
module HashWithIndifferentAccess
def params_builder
Grape::Extensions::HashWithIndifferentAccess::ParamBuilder
end

module ParamBuilder
def build_params
params = ActiveSupport::HashWithIndifferentAccess[rack_params]
params.deep_merge!(grape_routing_args) if env[Grape::Env::GRAPE_ROUTING_ARGS]
params
end
end
end
end
end
20 changes: 20 additions & 0 deletions lib/grape/extensions/hashie_mash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'hashie'
module Grape
module Extensions
module Hashie
module Mash
def params_builder
Grape::Extensions::Hashie::Mash::ParamBuilder
end

module ParamBuilder
def build_params
params = ::Hashie::Mash.new(rack_params)
params.deep_merge!(grape_routing_args) if env[Grape::Env::GRAPE_ROUTING_ARGS]
params
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/grape/middleware/globals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Grape
module Middleware
class Globals < Base
def before
request = Grape::Request.new(@env)
request = Grape::Request.new(@env, build_params_with: @options[:build_params_with])
@env[Grape::Env::GRAPE_REQUEST] = request
@env[Grape::Env::GRAPE_REQUEST_HEADERS] = request.headers
@env[Grape::Env::GRAPE_REQUEST_PARAMS] = request.params if @env[Grape::Env::RACK_INPUT]
Expand Down
22 changes: 12 additions & 10 deletions lib/grape/request.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
require 'grape/extensions/hash'
module Grape
class Request < Rack::Request
HTTP_PREFIX = 'HTTP_'.freeze

alias rack_params params

def initialize(env, build_params_with: nil)
extend build_params_with || Grape::Extensions::Hash::ParamBuilder
super(env)
end

def params
@params ||= build_params
end
Expand All @@ -14,16 +20,12 @@ def headers

private

def build_params
params = Hashie::Mash.new(rack_params)
if env[Grape::Env::GRAPE_ROUTING_ARGS]
args = env[Grape::Env::GRAPE_ROUTING_ARGS].dup
# preserve version from query string parameters
args.delete(:version)
args.delete(:route_info)
params.deep_merge!(args)
end
params
def grape_routing_args
args = env[Grape::Env::GRAPE_ROUTING_ARGS].dup
# preserve version from query string parameters
args.delete(:version)
args.delete(:route_info)
args
end

def build_headers
Expand Down
6 changes: 3 additions & 3 deletions lib/grape/validations/types/custom_type_coercer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def infer_type_check(type)
# Enforce symbolized keys for complex types
# by wrapping the coercion method such that
# any Hash objects in the immediate heirarchy
# are passed through +Hashie.symbolize_keys!+.
# have their keys recursively symbolized
# This helps common libs such as JSON to work easily.
#
# @param type see #new
Expand All @@ -162,15 +162,15 @@ def enforce_symbolized_keys(type, method)
lambda do |val|
method.call(val).tap do |new_value|
new_value.each do |item|
Hashie.symbolize_keys!(item) if item.is_a? Hash
item.with_indifferent_access if item.is_a? Hash
Copy link
Contributor Author

@james2m james2m Mar 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dblock This slipped through the net. From the description above this method is supposed to wrap the result of the method and transform any Hash to be symbolized, that's fine, I can update. There is no direct test of this method, but reading the code it looks to me like the block above should not be iterating with #each but #map to construct a new Array or Set;

    method.call(val).tap do |new_value|
      new_value.map do |item|
        item.is_a?(Hash) ? symbolize_keys(item) : item
      end
    end

Correct?

I can easily update this to symbolize the Hash without using the ActiveSupport method, but wanted to check the existing method is actually doing what's expected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's updating the values in place it looks like. If removing it doesn't cause any specs to fail I would start by writing a test that hits it, then change the behavior.

end
end
end

# Hash objects are processed directly
elsif type == Hash
lambda do |val|
Hashie.symbolize_keys! method.call(val)
method.call(val).with_indifferent_access
end

# Simple types are not processed.
Expand Down
Loading