cp-sparrow 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.ruby-version +1 -1
  4. data/LICENSE.txt +1 -1
  5. data/README.md +96 -13
  6. data/lib/sparrow.rb +21 -4
  7. data/lib/sparrow/configuration.rb +23 -2
  8. data/lib/sparrow/core_ext/hash.rb +2 -0
  9. data/lib/sparrow/dependencies.rb +15 -0
  10. data/lib/sparrow/http_message.rb +103 -0
  11. data/lib/sparrow/logger.rb +53 -0
  12. data/lib/sparrow/middleware.rb +44 -63
  13. data/lib/sparrow/request_http_message.rb +22 -0
  14. data/lib/sparrow/request_middleware.rb +8 -10
  15. data/lib/sparrow/response_http_message.rb +49 -0
  16. data/lib/sparrow/response_middleware.rb +32 -28
  17. data/lib/sparrow/response_steward.rb +13 -0
  18. data/lib/sparrow/route_parser.rb +28 -5
  19. data/lib/sparrow/steward.rb +125 -0
  20. data/lib/sparrow/strategies.rb +6 -0
  21. data/lib/sparrow/strategies/form_hash.rb +27 -11
  22. data/lib/sparrow/strategies/ignore.rb +33 -5
  23. data/lib/sparrow/strategies/json_format_strategies.rb +4 -0
  24. data/lib/sparrow/strategies/json_format_strategies/array_json_format_strategy.rb +23 -0
  25. data/lib/sparrow/strategies/json_format_strategies/default_json_format_strategy.rb +11 -2
  26. data/lib/sparrow/strategies/json_format_strategies/json_format_strategy.rb +21 -6
  27. data/lib/sparrow/strategies/json_format_strategies/rack_body_json_format_strategy.rb +22 -0
  28. data/lib/sparrow/strategies/key_transformation.rb +2 -0
  29. data/lib/sparrow/strategies/key_transformation/camelize_key.rb +45 -17
  30. data/lib/sparrow/strategies/key_transformation/underscore_key.rb +17 -6
  31. data/lib/sparrow/strategies/raw_input.rb +29 -7
  32. data/lib/sparrow/strategies/transform_params.rb +46 -21
  33. data/lib/sparrow/transformable.rb +28 -20
  34. data/lib/sparrow/version.rb +1 -1
  35. data/sparrow.gemspec +1 -1
  36. data/spec/integration/apps/rails_app/config/boot.rb +1 -1
  37. data/spec/integration/rack/camel_caser_spec.rb +2 -2
  38. data/spec/unit/camelize_key_spec.rb +9 -7
  39. data/spec/unit/configuration_spec.rb +3 -0
  40. data/spec/unit/http_message.rb +66 -0
  41. data/spec/unit/logger_spec.rb +6 -0
  42. data/spec/unit/{camel_caser_spec.rb → sparrow_spec.rb} +8 -2
  43. data/spec/unit/steward_spec.rb +31 -0
  44. metadata +24 -13
  45. data/lib/sparrow/path_normalizer.rb +0 -10
  46. data/lib/sparrow/strategies/json_format_strategies/array_strategy.rb +0 -17
  47. data/lib/sparrow/strategies/json_format_strategies/rack_body.rb +0 -17
  48. data/lib/sparrow/strategies/key_transformation/key_normalizer.rb +0 -9
  49. 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['rack.input'].send(:read)
14
- @env['rack.input'].rewind
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['rack.input'].gets
46
+ input = @env[HttpMessage::RACK_INPUT_KEY].gets
25
47
 
26
48
  begin
27
- @env['rack.request.form_hash'] = MultiJson.load(input)
49
+ @env[HttpMessage::FORM_HASH_KEY] = MultiJson.load(input)
28
50
  rescue MultiJson::ParseError
29
51
  # ignore
30
52
  ensure
31
- @env['rack.input'].rewind
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.select do |strategy|
32
+ strategy = json_format_strategies.detect do |strategy|
18
33
  strategy.match?(body)
19
- end.first
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
@@ -0,0 +1,2 @@
1
+ require 'sparrow/strategies/key_transformation/underscore_key'
2
+ require 'sparrow/strategies/key_transformation/camelize_key'
@@ -1,25 +1,53 @@
1
- require File.dirname(__FILE__) + '/key_normalizer'
2
-
3
1
  module Sparrow
4
2
  module Strategies
5
- class CamelizeKey
6
- include KeyNormalizer
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
- attr_accessor :strategy
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
- def initialize(strategy = :lower)
11
- self.strategy = strategy
12
- end
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
- def transform_key(key)
15
- # dont touch all_Upper Keys (like "DE")
16
- # unless configuration.default_ignore_all_uppercase_keys
17
- # is set to false
18
- if Sparrow.configuration.camelize_ignore_uppercase_keys &&
19
- key.upcase == key
20
- normalize_key(key)
21
- else
22
- normalize_key(key).camelize(strategy)
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
- class UnderscoreKey
6
- include KeyNormalizer
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
- def transform_key(key)
9
- normalize_key(key).underscore
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
- attr_reader :env, :type
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['rack.input']
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 = json_body.force_encoding("BINARY")
41
- @env['rack.input'] = StringIO.new(new_raw_input)
42
- @env['rack.input'].rewind
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
- def initialize(key_transformation_strategy_buzzword)
15
- key_transformation_strategy = create_key_transformation_strategy(
16
- key_transformation_strategy_buzzword)
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
- when Array
23
- collection_or_hash.map { |element| transform(element) }
24
- when Hash
25
- collection_or_hash.deep_transform_keys do |key|
26
- key_transformation_strategy.transform_key(key)
27
- end
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
- def create_key_transformation_strategy(key_transformation_strategy_buzzword)
32
- class_name = "#{key_transformation_strategy_buzzword.to_s}_key".camelize
33
- strategy_class = "Sparrow::Strategies::#{class_name}".constantize
34
- strategy_class.new
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