cp-sparrow 0.0.12 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.ruby-version +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +96 -13
- data/lib/sparrow.rb +21 -4
- data/lib/sparrow/configuration.rb +23 -2
- data/lib/sparrow/core_ext/hash.rb +2 -0
- data/lib/sparrow/dependencies.rb +15 -0
- data/lib/sparrow/http_message.rb +103 -0
- data/lib/sparrow/logger.rb +53 -0
- data/lib/sparrow/middleware.rb +44 -63
- data/lib/sparrow/request_http_message.rb +22 -0
- data/lib/sparrow/request_middleware.rb +8 -10
- data/lib/sparrow/response_http_message.rb +49 -0
- data/lib/sparrow/response_middleware.rb +32 -28
- data/lib/sparrow/response_steward.rb +13 -0
- data/lib/sparrow/route_parser.rb +28 -5
- data/lib/sparrow/steward.rb +125 -0
- data/lib/sparrow/strategies.rb +6 -0
- data/lib/sparrow/strategies/form_hash.rb +27 -11
- data/lib/sparrow/strategies/ignore.rb +33 -5
- data/lib/sparrow/strategies/json_format_strategies.rb +4 -0
- data/lib/sparrow/strategies/json_format_strategies/array_json_format_strategy.rb +23 -0
- data/lib/sparrow/strategies/json_format_strategies/default_json_format_strategy.rb +11 -2
- data/lib/sparrow/strategies/json_format_strategies/json_format_strategy.rb +21 -6
- data/lib/sparrow/strategies/json_format_strategies/rack_body_json_format_strategy.rb +22 -0
- data/lib/sparrow/strategies/key_transformation.rb +2 -0
- data/lib/sparrow/strategies/key_transformation/camelize_key.rb +45 -17
- data/lib/sparrow/strategies/key_transformation/underscore_key.rb +17 -6
- data/lib/sparrow/strategies/raw_input.rb +29 -7
- data/lib/sparrow/strategies/transform_params.rb +46 -21
- data/lib/sparrow/transformable.rb +28 -20
- data/lib/sparrow/version.rb +1 -1
- data/sparrow.gemspec +1 -1
- data/spec/integration/apps/rails_app/config/boot.rb +1 -1
- data/spec/integration/rack/camel_caser_spec.rb +2 -2
- data/spec/unit/camelize_key_spec.rb +9 -7
- data/spec/unit/configuration_spec.rb +3 -0
- data/spec/unit/http_message.rb +66 -0
- data/spec/unit/logger_spec.rb +6 -0
- data/spec/unit/{camel_caser_spec.rb → sparrow_spec.rb} +8 -2
- data/spec/unit/steward_spec.rb +31 -0
- metadata +24 -13
- data/lib/sparrow/path_normalizer.rb +0 -10
- data/lib/sparrow/strategies/json_format_strategies/array_strategy.rb +0 -17
- data/lib/sparrow/strategies/json_format_strategies/rack_body.rb +0 -17
- data/lib/sparrow/strategies/key_transformation/key_normalizer.rb +0 -9
- data/spec/unit/path_normalizer_spec.rb +0 -23
@@ -1,17 +1,33 @@
|
|
1
1
|
module Sparrow
|
2
2
|
module Strategies
|
3
|
+
##
|
4
|
+
# This strategy is called when the HTTP message shall +not+ be transformed,
|
5
|
+
# i.e. ignored.
|
6
|
+
# This is inspired by the NullObject-Pattern to be convenient.
|
3
7
|
class Ignore
|
4
8
|
include Transformable
|
5
9
|
|
10
|
+
##
|
11
|
+
# Create a new IgnoreStrategy
|
12
|
+
# @param [Hash] env the Rack environment
|
13
|
+
# @param [Symbol] type HTTP message type. Must be either :request or
|
14
|
+
# :response
|
15
|
+
# @param [Hash] params The HTTP message params if not given in the env
|
6
16
|
def initialize(env, type = :request, params = nil)
|
7
17
|
@env = env
|
8
18
|
@params = params
|
9
19
|
@type = type
|
10
20
|
end
|
11
21
|
|
22
|
+
##
|
23
|
+
# Although we are ignoring any kind of conversion we still need to read
|
24
|
+
# the parameters from the environment to be convenient with all other
|
25
|
+
# calls in the chains and architecture *sigh*
|
26
|
+
# Checks env['rack.input']
|
27
|
+
# @return [Hash] the params
|
12
28
|
def params
|
13
|
-
ret = @params || @env[
|
14
|
-
@env[
|
29
|
+
ret = @params || @env[HttpMessage::RACK_INPUT_KEY].send(:read)
|
30
|
+
@env[HttpMessage::RACK_INPUT_KEY].rewind
|
15
31
|
ret
|
16
32
|
end
|
17
33
|
|
@@ -19,25 +35,37 @@ module Sparrow
|
|
19
35
|
new(env, type).handle
|
20
36
|
end
|
21
37
|
|
38
|
+
##
|
39
|
+
# handles the conversion, i.e. here "do nothing"
|
40
|
+
# Which is not strictly true - at write the rack.input to the form hash
|
41
|
+
# key for convenience reasons to enable further middlewares to work with
|
42
|
+
# it
|
43
|
+
# @return [Hash] the rack env
|
22
44
|
def handle
|
23
45
|
# synchronize rack.input and form hash values
|
24
|
-
input = @env[
|
46
|
+
input = @env[HttpMessage::RACK_INPUT_KEY].gets
|
25
47
|
|
26
48
|
begin
|
27
|
-
@env[
|
49
|
+
@env[HttpMessage::FORM_HASH_KEY] = MultiJson.load(input)
|
28
50
|
rescue MultiJson::ParseError
|
29
51
|
# ignore
|
30
52
|
ensure
|
31
|
-
@env[
|
53
|
+
@env[HttpMessage::RACK_INPUT_KEY].rewind
|
32
54
|
end if input.present?
|
33
55
|
|
34
56
|
@env
|
35
57
|
end
|
36
58
|
|
59
|
+
##
|
60
|
+
# Alias for #params
|
61
|
+
# @see #params
|
37
62
|
def json_body
|
38
63
|
params
|
39
64
|
end
|
40
65
|
|
66
|
+
##
|
67
|
+
# Transforms the params to a Ruby JSON Hash representation
|
68
|
+
# @return [Hash] the JSON
|
41
69
|
def transform_params
|
42
70
|
ensure_json
|
43
71
|
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
require 'sparrow/strategies/json_format_strategies/default_json_format_strategy'
|
2
|
+
require 'sparrow/strategies/json_format_strategies/json_format_strategy'
|
3
|
+
require 'sparrow/strategies/json_format_strategies/array_json_format_strategy'
|
4
|
+
require 'sparrow/strategies/json_format_strategies/rack_body_json_format_strategy'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Sparrow
|
2
|
+
module Strategies
|
3
|
+
class ArrayJsonFormatStrategy < JsonFormatStrategy
|
4
|
+
register_json_format
|
5
|
+
|
6
|
+
##
|
7
|
+
# Matches if input is an Array
|
8
|
+
# @param [Object] input the JSON object
|
9
|
+
# @return true if the input is an Array
|
10
|
+
def match?(input)
|
11
|
+
input.is_a? Array
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Takes the first element from the Array and returns it
|
16
|
+
# @param [Array] input the input Array
|
17
|
+
# @return [String] the first element of the input as a String
|
18
|
+
def convert(input)
|
19
|
+
input.first.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,14 +1,23 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
|
3
1
|
module Sparrow
|
4
2
|
module Strategies
|
3
|
+
##
|
4
|
+
# NullObject JSON Format Strategy
|
5
|
+
# Does no direct conversion except casting the given object to a string
|
5
6
|
class DefaultJsonFormatStrategy
|
6
7
|
include Singleton
|
7
8
|
|
9
|
+
##
|
10
|
+
# Matches always since this is the NullObjectStrategy and thus the
|
11
|
+
# fallback.
|
12
|
+
# @param [Object] input the JSON input
|
13
|
+
# @return [Boolean] True
|
8
14
|
def match?(input)
|
9
15
|
true
|
10
16
|
end
|
11
17
|
|
18
|
+
##
|
19
|
+
# @param [#to_s] input the JSON object
|
20
|
+
# @return [String] the input as a String
|
12
21
|
def convert(input)
|
13
22
|
input.to_s
|
14
23
|
end
|
@@ -1,22 +1,37 @@
|
|
1
|
-
require 'sparrow/strategies/json_format_strategies/default_json_format_strategy'
|
2
|
-
require 'active_support/core_ext/object/blank'
|
3
|
-
|
4
1
|
module Sparrow
|
5
2
|
module Strategies
|
3
|
+
##
|
4
|
+
# Superclass for all JSON format strategies.
|
5
|
+
# Contains no own instance logic, but keeps track of the registration
|
6
|
+
# of all JSON format strategies with its Singleton class methods.
|
7
|
+
# @abstract Not exactly a abstract class but contains no own logic but
|
8
|
+
# singleton class methods
|
6
9
|
class JsonFormatStrategy
|
10
|
+
##
|
11
|
+
# Empty constructor. Does nothing.
|
7
12
|
def initialize(*args)
|
8
|
-
|
9
13
|
end
|
10
14
|
|
15
|
+
##
|
16
|
+
# Register a new JSON Format strategy
|
17
|
+
# @param [Object] args the arguments for the new strategy
|
18
|
+
# @return [Array] args the updated registered JSON Format strategies
|
19
|
+
# available
|
11
20
|
def self.register_json_format(*args)
|
12
21
|
init(args)
|
13
22
|
@@json_format_strategies << self.new(args)
|
14
23
|
end
|
15
24
|
|
25
|
+
##
|
26
|
+
# Start a JSON conversion by its given string
|
27
|
+
# @param [Object] body a JSON object representation.
|
28
|
+
# can be any type a JSON format strategy is registered,
|
29
|
+
# i.e. an Array, a String or a RackBody
|
30
|
+
# @return [String] the formatted JSON
|
16
31
|
def self.convert(body)
|
17
|
-
strategy = json_format_strategies.
|
32
|
+
strategy = json_format_strategies.detect do |strategy|
|
18
33
|
strategy.match?(body)
|
19
|
-
end
|
34
|
+
end
|
20
35
|
strategy.convert(body)
|
21
36
|
end
|
22
37
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sparrow
|
2
|
+
module Strategies
|
3
|
+
class RackBodyJsonFormatStrategy < JsonFormatStrategy
|
4
|
+
register_json_format
|
5
|
+
|
6
|
+
##
|
7
|
+
# Checks if the input is a RackBody
|
8
|
+
# @param [#body] input the possible JSON input object
|
9
|
+
# @return [Boolean] True if the given input responds to #body
|
10
|
+
def match?(input)
|
11
|
+
input.respond_to?(:body)
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# @param [#body] input the JSON input object
|
16
|
+
# @return [String] the input body
|
17
|
+
def convert(input)
|
18
|
+
input.body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,25 +1,53 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/key_normalizer'
|
2
|
-
|
3
1
|
module Sparrow
|
4
2
|
module Strategies
|
5
|
-
|
6
|
-
|
3
|
+
module KeyTransformation
|
4
|
+
##
|
5
|
+
# Strategy class for converting JSON to a camelized format.
|
6
|
+
# Meaning snake_case => snakeCase
|
7
|
+
class CamelizeKey
|
8
|
+
##
|
9
|
+
# @return [Symbol] the camelizing strategy
|
10
|
+
# @see #initialize
|
11
|
+
attr_accessor :strategy
|
7
12
|
|
8
|
-
|
13
|
+
##
|
14
|
+
# @return [Boolean] Defines whether complete uppercased keys will be
|
15
|
+
# transformed
|
16
|
+
# @see #initialize
|
17
|
+
attr_accessor :camelize_ignore_uppercase_keys
|
9
18
|
|
10
|
-
|
11
|
-
|
12
|
-
|
19
|
+
##
|
20
|
+
# Initialize a new CamelizeKey strategy
|
21
|
+
# @param [Hash] options camelize options
|
22
|
+
# @option options [Symbol] :strategy ('lower') defines the camelizing
|
23
|
+
# strategy type. Defines how to camelize, which comes down to starting
|
24
|
+
# with a lowercased character or with an uppercased character.
|
25
|
+
# Thus possible values are :lower and :upper.
|
26
|
+
#
|
27
|
+
# @option options [Boolean] :camelize_ignore_uppercase_keys (true)
|
28
|
+
# Defines if already completely uppercased keys should not be
|
29
|
+
# transformed. I.e. JSON stays JSON if
|
30
|
+
# this is set to true. If it is set to false JSON will be transformed
|
31
|
+
# to Json.
|
32
|
+
def initialize(options = {})
|
33
|
+
self.strategy = options.fetch(:strategy, :lower)
|
34
|
+
self.camelize_ignore_uppercase_keys =
|
35
|
+
options.fetch(:camelize_ignore_uppercase_keys, true)
|
36
|
+
end
|
13
37
|
|
14
|
-
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
38
|
+
##
|
39
|
+
# Transform the given key to camelCase based on the configuration
|
40
|
+
# options set on initialization.
|
41
|
+
# @see #initialize
|
42
|
+
# @param [String, #to_s] key the key value to be transformed
|
43
|
+
# @return [String] the transformed key
|
44
|
+
def transform_key(key)
|
45
|
+
key = key.to_s
|
46
|
+
if camelize_ignore_uppercase_keys && key.upcase == key
|
47
|
+
key
|
48
|
+
else
|
49
|
+
key.camelize(strategy)
|
50
|
+
end
|
23
51
|
end
|
24
52
|
end
|
25
53
|
end
|
@@ -1,12 +1,23 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/key_normalizer'
|
2
|
-
|
3
1
|
module Sparrow
|
4
2
|
module Strategies
|
5
|
-
|
6
|
-
|
3
|
+
module KeyTransformation
|
4
|
+
##
|
5
|
+
# Strategy class for snake_casing keys
|
6
|
+
class UnderscoreKey
|
7
|
+
##
|
8
|
+
# Create a new UnderscoreKey Strategy
|
9
|
+
# Does nothing except returning a plain instance.
|
10
|
+
def initialize(*args)
|
11
|
+
# no initialization needed
|
12
|
+
end
|
7
13
|
|
8
|
-
|
9
|
-
|
14
|
+
##
|
15
|
+
# Transforms the given key to snake_case format
|
16
|
+
# @param [String] key the key to be transformed
|
17
|
+
# @return [String] the snake_cased key
|
18
|
+
def transform_key(key)
|
19
|
+
key.to_s.underscore
|
20
|
+
end
|
10
21
|
end
|
11
22
|
end
|
12
23
|
end
|
@@ -1,32 +1,54 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
|
-
|
3
1
|
module Sparrow
|
4
2
|
module Strategies
|
3
|
+
##
|
4
|
+
# Handles plain JSON input parameter conversion which is sent via
|
5
|
+
# env['rack.input']
|
5
6
|
class RawInput
|
6
7
|
include Transformable
|
7
8
|
|
8
|
-
|
9
|
+
# @return [Hash] the Rack environment
|
10
|
+
# @see #initialize
|
11
|
+
attr_reader :env
|
12
|
+
# @return [Symbol] the HTTP message type
|
13
|
+
# @see #initialize
|
14
|
+
attr_reader :type
|
9
15
|
|
16
|
+
##
|
17
|
+
# Do the transformation
|
18
|
+
# @return [Hash] the converted JSON as a Hash representation
|
10
19
|
def self.handle(env, type)
|
11
20
|
self.new(env, type).handle
|
12
21
|
end
|
13
22
|
|
23
|
+
# Create a new RawInput Strategy
|
24
|
+
# @param [Hash] env the Rack environment
|
25
|
+
# @param [Symbol] type the HTTP message type. Must be either :request or
|
26
|
+
# :response
|
27
|
+
# @param [Hash] params the HTTP message parameters to be transformed
|
28
|
+
# if not already present in the env
|
14
29
|
def initialize(env, type = :request, params = nil)
|
15
30
|
@env = env || {}
|
16
31
|
@params = params
|
17
32
|
@type = type
|
18
33
|
end
|
19
34
|
|
35
|
+
##
|
36
|
+
# Starts the conversion
|
37
|
+
# @return [Hash] the converted Rack environment
|
20
38
|
def handle
|
21
39
|
super
|
22
40
|
handle_raw_rack
|
23
41
|
end
|
24
42
|
|
43
|
+
##
|
44
|
+
# The parameters to be transformed
|
45
|
+
# @return [Hash] the JSON parameters
|
46
|
+
# @see #initialize
|
25
47
|
def params
|
26
48
|
if @params
|
27
49
|
@params
|
28
50
|
else
|
29
|
-
input_io = @env[
|
51
|
+
input_io = @env[HttpMessage::RACK_INPUT_KEY]
|
30
52
|
params = input_io.send(:read)
|
31
53
|
input_io.rewind
|
32
54
|
params
|
@@ -37,9 +59,9 @@ module Sparrow
|
|
37
59
|
|
38
60
|
def handle_raw_rack
|
39
61
|
if params.present?
|
40
|
-
new_raw_input
|
41
|
-
@env[
|
42
|
-
@env[
|
62
|
+
new_raw_input = json_body.force_encoding("BINARY")
|
63
|
+
@env[HttpMessage::RACK_INPUT_KEY] = StringIO.new(new_raw_input)
|
64
|
+
@env[HttpMessage::RACK_INPUT_KEY].rewind
|
43
65
|
@env['CONTENT_LENGTH'] = new_raw_input.length
|
44
66
|
end
|
45
67
|
end
|
@@ -1,37 +1,62 @@
|
|
1
|
-
require 'active_support/core_ext/string'
|
2
|
-
if ActiveSupport::VERSION::STRING.match(/3\.\d+\.\d+/)
|
3
|
-
require 'sparrow/core_ext/hash'
|
4
|
-
end
|
5
|
-
|
6
|
-
require 'sparrow/strategies/key_transformation/underscore_key'
|
7
|
-
require 'sparrow/strategies/key_transformation/camelize_key'
|
8
|
-
|
9
1
|
module Sparrow
|
10
2
|
module Strategies
|
3
|
+
##
|
4
|
+
# Core class to trigger the conversion
|
11
5
|
class TransformParams
|
6
|
+
##
|
7
|
+
# the strategy stating how to convert the JSON
|
8
|
+
# @return [KeyTransformation] key transformation strategy
|
9
|
+
# @see #initialize
|
12
10
|
attr_accessor :key_transformation_strategy
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
##
|
13
|
+
# Key Transformation Strategy options. Possible values differ by
|
14
|
+
# the specific strategy. E.g. for CamelizeKey options may be
|
15
|
+
# the strategy and camlize_uppercase_params
|
16
|
+
# @see KeyTransformationStrategy::CamelizeKey
|
17
|
+
# @see KeyTransformationStrategy::UnderscoreKey
|
18
|
+
attr_accessor :options
|
19
|
+
|
20
|
+
##
|
21
|
+
# Create a new TransformParams instance
|
22
|
+
# @param [String] key_transformation_strategy_buzzword the strategy
|
23
|
+
# buzzword stating how to transform. Must be either 'camelize' or
|
24
|
+
# 'underscore'
|
25
|
+
# @param [Hash] options options for the key transformation strategy
|
26
|
+
def initialize(key_transformation_strategy_buzzword, options = {})
|
27
|
+
self.options = options
|
28
|
+
key_transformation_strategy = create_key_transformation_strategy(
|
29
|
+
key_transformation_strategy_buzzword)
|
17
30
|
self.key_transformation_strategy = key_transformation_strategy
|
18
31
|
end
|
19
32
|
|
33
|
+
##
|
34
|
+
# Do the transformation
|
35
|
+
# @param [Array|Hash] collection_or_hash the object to be transformed
|
36
|
+
# if it is an Array each object inside of it will be transformed, i.e.
|
37
|
+
# each Hash's keys
|
38
|
+
# if it is a Hash each key will be transformed. Recursively.
|
39
|
+
# @return [Array|Hash] the transformed input
|
20
40
|
def transform(collection_or_hash)
|
21
41
|
case collection_or_hash
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
42
|
+
when Array
|
43
|
+
collection_or_hash.map { |element| transform(element) }
|
44
|
+
when Hash
|
45
|
+
collection_or_hash.deep_transform_keys do |key|
|
46
|
+
key_transformation_strategy.transform_key(key)
|
47
|
+
end
|
28
48
|
end
|
29
49
|
end
|
30
50
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
51
|
+
private
|
52
|
+
|
53
|
+
def create_key_transformation_strategy(key_transformation_strategy)
|
54
|
+
class_name = "#{key_transformation_strategy.to_s}_key"
|
55
|
+
class_name = class_name.camelize
|
56
|
+
full_strategy_class_name =
|
57
|
+
"Sparrow::Strategies::KeyTransformation::#{class_name}"
|
58
|
+
strategy_class = full_strategy_class_name.constantize
|
59
|
+
strategy_class.new(options)
|
35
60
|
end
|
36
61
|
end
|
37
62
|
end
|