angus 0.0.6 → 0.0.7

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.
@@ -9,6 +9,7 @@ require_relative 'responses'
9
9
  require_relative 'renders/base'
10
10
  require_relative 'marshallings/base'
11
11
  require_relative 'base_actions'
12
+ require_relative 'proxy_actions'
12
13
  require_relative 'definition_reader'
13
14
 
14
15
  require 'angus/sdoc'
@@ -16,16 +17,20 @@ require 'angus/sdoc'
16
17
  module Angus
17
18
  class Base < RequestHandler
18
19
  include BaseActions
20
+ include ProxyActions
19
21
 
20
22
  FIRST_VERSION = '0.1'
21
23
 
22
- PRODUCTION_ENV = :production
23
- DEVELOPMENT_ENV = :development
24
- TEST_ENV = :test
25
- DEFAULT_ENV = DEVELOPMENT_ENV
24
+ PRODUCTION_ENV = :production
25
+ DEVELOPMENT_ENV = :development
26
+ TEST_ENV = :test
27
+ DEFAULT_ENV = DEVELOPMENT_ENV
28
+ DEFAULT_DOC_LANGUAGE = :en
26
29
 
27
30
  attr_reader :definitions
28
31
 
32
+ attr_accessor :default_doc_language
33
+
29
34
  def initialize
30
35
  super
31
36
 
@@ -35,9 +40,16 @@ module Angus
35
40
  @configured = false
36
41
  @definitions = nil
37
42
  @logger = Logger.new(STDOUT)
43
+ @default_doc_language = DEFAULT_DOC_LANGUAGE
38
44
 
39
45
  configure!
40
46
 
47
+ after_configure
48
+ end
49
+
50
+ def after_configure
51
+ super
52
+
41
53
  register_base_routes
42
54
  register_resources_routes
43
55
  end
@@ -113,7 +125,6 @@ module Angus
113
125
  response = Response.new
114
126
 
115
127
  resource = resource_definition.resource_class.new(request, params)
116
- response['Content-Type'] = 'application/json'
117
128
 
118
129
  op_response = resource.send(operation.code_name)
119
130
  op_response = {} unless op_response.is_a?(Hash)
@@ -27,7 +27,9 @@ module Angus
27
27
  if params[:format] == 'json'
28
28
  render(response, Angus::SDoc::JsonFormatter.format_service(@definitions), format: :json)
29
29
  else
30
- render(response, Angus::SDoc::HtmlFormatter.format_service(@definitions), format: :html)
30
+ language = params[:lang] || self.default_doc_language
31
+ render(response, Angus::SDoc::HtmlFormatter.format_service(@definitions, language),
32
+ format: :html)
31
33
  end
32
34
  end
33
35
  end
@@ -3,8 +3,8 @@ module Angus
3
3
 
4
4
  attr_reader :definitions
5
5
 
6
- def initialize
7
- @definitions = SDoc::DefinitionsReader.service_definition('definitions')
6
+ def initialize(definitions = nil)
7
+ @definitions = definitions || SDoc::DefinitionsReader.service_definition('definitions')
8
8
  end
9
9
 
10
10
  def message_definition(key, level)
@@ -13,7 +13,7 @@ module Angus
13
13
  NEW_APP_DIRECTORIES = %W(#{DEFINITIONS_DIR} #{RESOURCES_DIR} #{SERVICES_DIR})
14
14
 
15
15
  NEW_APP_FILES = %w(
16
- Gemfile
16
+ Gemfile.erb
17
17
  config.ru.erb
18
18
  services/service.rb.erb
19
19
  definitions/messages.yml
@@ -2,4 +2,4 @@ source 'http://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
4
 
5
- gem 'angus', '~> 0.0'
5
+ gem 'angus', '<%= Angus::VERSION %>'
@@ -7,4 +7,4 @@ Bundler.setup
7
7
  require 'angus'
8
8
  require 'services/<%= app_name %>'
9
9
 
10
- run <%= classify(name) %>.build
10
+ run <%= classify(name) %>.new
@@ -1,2 +1,2 @@
1
1
  require_relative 'marshalling'
2
- require_relative 'unmarshalling'
2
+ require_relative 'exceptions'
@@ -0,0 +1,17 @@
1
+ module Angus
2
+ module Marshalling
3
+
4
+ class InvalidGetterError < StandardError
5
+
6
+ def initialize(getter, bc_first_line = nil)
7
+ @getter = getter
8
+ @bc_first_line = bc_first_line
9
+ end
10
+
11
+ def message
12
+ "The requested getter (#@getter) does not exist. #@bc_first_line"
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -4,26 +4,6 @@ require 'date'
4
4
  module Angus
5
5
  module Marshalling
6
6
 
7
- # Marshal an object
8
- #
9
- # This method is intended for scalar objects and arrays of scalar objects.
10
- #
11
- # For more complex objects:
12
- # @see Angus::Marshalling.marshal_object
13
- #
14
- # @param object The object to be marshalled
15
- # @return [Array] An object suitable for be converted easily to JSON notation
16
- #
17
- # If {object} is an array, this method returns an array with all the elements marshalled
18
- # Else returns a marshalled representation of the object.
19
- def self.marshal(object)
20
- if object.is_a?(Array)
21
- object.map { |element| marshal(element) }
22
- else
23
- marshal_scalar(object)
24
- end
25
- end
26
-
27
7
  # Marshal a complex object.
28
8
  #
29
9
  # @param object The object to be marshalled
@@ -36,8 +16,11 @@ module Angus
36
16
  key = getter.keys[0]
37
17
  value = get_value(object, key)
38
18
 
39
- #TODO Consider adding ActiveRecord::Relation support
40
- #ex: "if value.is_a?(Array) || value.is_a?(ActiveRecord::Relation)"
19
+ #HACK to support ActiveRecord::Relation
20
+ if defined?(ActiveRecord::Relation) && value.is_a?(ActiveRecord::Relation)
21
+ value = value.to_a
22
+ end
23
+
41
24
  if value.is_a?(Array)
42
25
  result[key] = value.map { |object| marshal_object(object, getter.values[0]) }
43
26
  else
@@ -57,8 +40,8 @@ module Angus
57
40
  #
58
41
  # @param [Object] object The object to get the value from
59
42
  # @param [Symbol, String] getter to request from the object
60
- # @raise [InvalidGetterError] when getter is not present
61
- # in the object
43
+ # @raise [InvalidGetterError] when getter is not present in the object
44
+ #
62
45
  # @return [Object] the requested value
63
46
  def self.get_value(object, getter)
64
47
  if object.is_a?(Hash)
@@ -72,8 +55,9 @@ module Angus
72
55
  #
73
56
  # @param [Object] object The object to get the value from
74
57
  # @param [Symbol, String] method the method to invoke in the object
75
- # @raise [InvalidGetterError] when the object does not responds
76
- # to method as public.
58
+ #
59
+ # @raise [InvalidGetterError] when the object does not responds to method as public.
60
+ #
77
61
  # @return [Object] the requested value
78
62
  def self.get_value_from_object(object, method)
79
63
  value = object.public_send method.to_sym
@@ -85,9 +69,8 @@ module Angus
85
69
  else
86
70
  value
87
71
  end
88
- # TODO ver si tirar una exception especifica o que hacer?
89
- #rescue NoMethodError => error
90
- # raise InvalidGetterError.new(method, error.backtrace.first)
72
+ rescue NoMethodError => error
73
+ raise Marshalling::InvalidGetterError.new(method, error.backtrace.first)
91
74
  end
92
75
 
93
76
  # Gets a value from a hash for a given key.
@@ -106,8 +89,7 @@ module Angus
106
89
  elsif hash.has_key?(key.to_s)
107
90
  hash[key.to_s]
108
91
  else
109
- #raise InvalidGetterError.new(key)
110
- raise NoMethodError.new(key.to_s)
92
+ raise Marshalling::InvalidGetterError.new(key)
111
93
  end
112
94
  end
113
95
 
@@ -8,9 +8,9 @@ module Angus
8
8
  class ExceptionHandler
9
9
  include Angus::StatusCodes
10
10
 
11
- def initialize(app)
11
+ def initialize(app, definitions = nil)
12
12
  @app = app
13
- @definition_reader = Angus::DefinitionReader.new
13
+ @definition_reader = Angus::DefinitionReader.new(definitions)
14
14
  end
15
15
 
16
16
  def call(env)
@@ -29,7 +29,6 @@ module Angus
29
29
 
30
30
  private
31
31
 
32
-
33
32
  # Builds a service error response
34
33
  def build_error_response(error)
35
34
  error_messages = messages_from_error(error)
@@ -37,7 +36,6 @@ module Angus
37
36
  JsonRender.convert(:status => :error, :messages => error_messages)
38
37
  end
39
38
 
40
-
41
39
  # Returns an array of messages errors to be sent in an operation response
42
40
  #
43
41
  # If {error} respond_to? :errors then the method returns one error message
@@ -59,16 +57,15 @@ module Angus
59
57
  messages << {:level => level, :key => key, :dsc => description}
60
58
  end
61
59
  elsif error.respond_to?(:error_key)
62
- messages << {:level => level, :key => error.error_key,
63
- :dsc => error_message(error)}
60
+ messages << { :level => level, :key => error.error_key,
61
+ :dsc => error_message(error) }
64
62
  else
65
- messages << {:level => level, :key => error.class.name, :dsc => error.message}
63
+ messages << { :level => level, :key => error.class.name, :dsc => error.message }
66
64
  end
67
65
 
68
66
  messages
69
67
  end
70
68
 
71
-
72
69
  # Returns the message for an error.
73
70
  #
74
71
  # It first tries to get the message from text attribute of the error definition
@@ -103,7 +100,8 @@ module Angus
103
100
 
104
101
  # Returns a suitable HTTP status code for the given error
105
102
  #
106
- # If error param responds to #errors, then #{HTTP_STATUS_CODE_CONFLICT} will be returned.
103
+ # If error param responds to #errors, then #{StatusCodes::HTTP_STATUS_CODE_CONFLICT} will
104
+ # be returned.
107
105
  #
108
106
  # If error param responds to #error_key, then the status_code associated
109
107
  # with the message will be returned.
@@ -0,0 +1,75 @@
1
+ module Angus
2
+ module ProxyActions
3
+
4
+ def after_configure
5
+ register_proxy_actions
6
+ register_proxy_routes
7
+ end
8
+
9
+ def register_proxy_routes
10
+ @definitions.proxy_operations.each do |proxy_operation|
11
+ register_proxy_route(proxy_operation)
12
+ end
13
+ end
14
+
15
+ def register_proxy_actions
16
+ router.on(:get, File.join(doc_path, '/proxy/:service')) do |env, params|
17
+ require 'picasso-remote'
18
+ response = Response.new
19
+
20
+ service = params[:service]
21
+
22
+ remote_definition = Picasso::Remote::ServiceDirectory.service_definition(service)
23
+
24
+ render(response, Picasso::SDoc::JsonFormatter.format_service(remote_definition),
25
+ format: :json)
26
+ end
27
+ end
28
+
29
+ def register_proxy_route(proxy_operation)
30
+ require 'picasso-remote'
31
+
32
+ remote_api_uri = Picasso::Remote::ServiceDirectory.api_url(proxy_operation.service_name)
33
+
34
+ proxy_client = get_proxy_client(remote_api_uri)
35
+
36
+ op_path = "#{api_path}#{proxy_operation.path}"
37
+
38
+ router.on(proxy_operation.http_method.to_sym, op_path) do |env, params|
39
+ request = Rack::Request.new(env)
40
+ request.body.rewind
41
+ raw_body = request.body.read
42
+
43
+ proxy_path = env['PATH_INFO'].gsub(api_path, '')
44
+
45
+ status, headers, body = proxy_client.make_request(
46
+ proxy_operation.http_method,
47
+ proxy_path,
48
+ env['QUERY_STRING'],
49
+ {},
50
+ raw_body
51
+ )
52
+
53
+ Response.new(body, status, headers)
54
+ end
55
+ end
56
+
57
+ def self.remote_operation(service_code_name, operation_code_name)
58
+ remote_service = Picasso::Remote::ServiceDirectory.service_definition(service_code_name)
59
+ remote_service.operation_definition(operation_code_name)
60
+ end
61
+
62
+ def get_proxy_client(url)
63
+ proxy_clients[url] ||= build_proxy_client(url)
64
+ end
65
+
66
+ def build_proxy_client(url)
67
+ Angus::Remote::ProxyClient.new(url)
68
+ end
69
+
70
+ def proxy_clients
71
+ @proxy_clients ||= {}
72
+ end
73
+
74
+ end
75
+ end
@@ -1,7 +1,7 @@
1
1
  module HtmlRender
2
2
 
3
3
  def self.render(response, html)
4
- response['Content-Type'] = 'text/html'
4
+ response['Content-Type'] = 'text/html;charset=utf-8'
5
5
 
6
6
  response.write(html)
7
7
  end
@@ -31,10 +31,16 @@ module Angus
31
31
 
32
32
  def to_app
33
33
  inner_app = lambda { |env| self.dup.call!(env) }
34
- @middleware.reverse.inject(inner_app) do |app, middleware|
34
+ @app ||= @middleware.reverse.inject(inner_app) do |app, middleware|
35
35
  klass, args, block = middleware
36
36
 
37
- klass.new(app, *args, &block)
37
+ # HACK to improve performance for now, in reality Middleware::ExceptionHandler should get
38
+ # the doc from a know place or the documentation should be available to all middleware.
39
+ if klass == Middleware::ExceptionHandler
40
+ klass.new(app, @definitions)
41
+ else
42
+ klass.new(app, *args, &block)
43
+ end
38
44
  end
39
45
  end
40
46
 
@@ -54,7 +60,7 @@ module Angus
54
60
  response.finish
55
61
  end
56
62
 
57
- # TODO ver multiples formatos en el futuro
63
+ # TODO add more formats in the future.
58
64
  def render(response, content, options = {})
59
65
  format = options[:format] || DEFAULT_RENDER
60
66
  case(format)
@@ -96,4 +102,4 @@ module Angus
96
102
  end
97
103
 
98
104
  end
99
- end
105
+ end
@@ -50,10 +50,9 @@ module Angus
50
50
  result
51
51
  else
52
52
  field_name = response_representation.name
53
- field_type = response_representation.type || response_representation.elements_type
53
+ field_type_name = response_representation.type || response_representation.elements_type
54
54
 
55
- # TODO fix this
56
- representation = representation_by_name(field_type)
55
+ representation = representation_by_name(field_type_name)
57
56
 
58
57
  if representation.nil?
59
58
  field_name.to_sym
@@ -2,7 +2,7 @@ module Angus
2
2
  class Response < Rack::Response
3
3
  def initialize(*)
4
4
  super
5
- headers['Content-Type'] ||= 'text/html'
5
+ headers['Content-Type'] ||= 'application/json'
6
6
  end
7
7
 
8
8
  def body=(value)
@@ -18,8 +18,8 @@ module Angus
18
18
  result = body
19
19
 
20
20
  if drop_content_info?
21
- headers.delete "Content-Length"
22
- headers.delete "Content-Type"
21
+ headers.delete('Content-Length')
22
+ headers.delete'Content-Type'
23
23
  end
24
24
 
25
25
  if drop_body?
@@ -6,27 +6,6 @@ module Angus
6
6
  module Responses
7
7
  include Angus::StatusCodes
8
8
 
9
- # Returns the error definition.
10
- #
11
- # If the error does not responds to error_key nil will be returned, see EvolutionError.
12
- #
13
- # @param [#error_key] error An error object
14
- #
15
- # @return [Hash]
16
- def get_error_definition(error)
17
- error_key = error.class.name
18
-
19
- get_message_definition(error_key, Angus::SDoc::Definitions::Message::ERROR_LEVEL)
20
- end
21
-
22
- def get_message_definition(key, level)
23
- message = @definitions.messages.find { |name, definition|
24
- name == key.to_s && definition.level.downcase == level.downcase
25
- }
26
-
27
- message.last if message
28
- end
29
-
30
9
  # Builds a service success response
31
10
  #
32
11
  # @param [Hash<Symbol, Object>] messages Elements to be sent in the response
@@ -45,44 +24,6 @@ module Angus
45
24
  json(elements)
46
25
  end
47
26
 
48
- # Builds a ResponseMessage object
49
- #
50
- # @param [#to_s] key Message key
51
- # @param [#to_s] level Message level
52
- # @param [*Object] params Objects to be used when formatting the message description
53
- #
54
- # @raise [NameError] when there's no message for the given key and level
55
- #
56
- # @return [ResponseMessage]
57
- def build_message(key, level, *params)
58
- message_definition = get_message_definition(key, level)
59
-
60
- unless message_definition
61
- raise NameError.new("Could not found message with key: #{key}, level: #{level}")
62
- end
63
-
64
- description = if message_definition.text
65
- message_definition.text % params
66
- else
67
- message_definition.description
68
- end
69
-
70
- Angus::SDoc::Definitions::Message
71
-
72
- message = Angus::SDoc::Definitions::Message.new
73
- message.key = key
74
- message.level = level
75
- message.description = description
76
-
77
- message
78
- end
79
-
80
- # Builds a service success response
81
- def build_warning_response(error)
82
- error_messages = messages_from_error(error, :warning)
83
- build_response(:success, *error_messages)
84
- end
85
-
86
27
  # Builds a success response with the received data
87
28
  #
88
29
  # @param [Hash] data the hash to be returned in the response
@@ -96,21 +37,6 @@ module Angus
96
37
  build_success_response(marshalled_data, messages)
97
38
  end
98
39
 
99
- # Builds a success response with no elements
100
- #
101
- # The response would include the following:
102
- # - status
103
- # - messages
104
- #
105
- # @param [Array<ResponseMessage>, Array<Symbol>] messages Message to be included in the response
106
- #
107
- # @return [String] JSON response
108
- def build_no_data_response(messages = [])
109
- messages = build_messages(Angus::SDoc::Definitions::Message::INFO_LEVEL, messages)
110
-
111
- build_success_response({}, messages)
112
- end
113
-
114
40
  # Builds a list of messages with the following level
115
41
  #
116
42
  # ResponseMessage objects contained in messages param won't be modified, this method
@@ -124,72 +50,46 @@ module Angus
124
50
  # @return [Array<ResponseMessage>]
125
51
  def build_messages(level, messages)
126
52
  (messages || []).map do |message|
127
- if message.kind_of?(Angus::SDoc::Definitions::Message)
128
- message
129
- else
130
- build_message(message, level)
131
- end
53
+ build_message(message, level)
132
54
  end
133
55
  end
134
56
 
135
- # Sets the content_type to json and serialize +element+ as json
136
- def json(element)
137
- #content_type :json
138
- JSON(element, :ascii_only => true)
139
- end
140
-
141
- private
142
- # Returns an array of messages errors to be sent in an operation response
143
- #
144
- # If {error} respond_to? :errors then the method returns one error message
145
- # for each one.
57
+ # Builds a ResponseMessage object
146
58
  #
147
- # Each message returned is a hash with:
148
- # - level
149
- # - key
150
- # - description
59
+ # @param [#to_s] key Message key
60
+ # @param [#to_s] level Message level
61
+ # @param [*Object] params Objects to be used when formatting the message description
151
62
  #
152
- # @param [Exception] error The error to be returned
63
+ # @raise [NameError] when there's no message for the given key and level
153
64
  #
154
- # @return [Array] an array of messages
155
- def messages_from_error(error, level = :error)
156
- messages = []
157
-
158
- if error.respond_to?(:errors)
159
- error.errors.each do |key, description|
160
- messages << {:level => level, :key => key, :dsc => description}
161
- end
162
- elsif error.respond_to?(:error_key)
163
- messages << {:level => level, :key => error.error_key,
164
- :dsc => error_message(error)}
165
- else
166
- messages << {:level => level, :key => error.class.name, :dsc => error.message}
65
+ # @return [ResponseMessage]
66
+ def build_message(key, level, *params)
67
+ message_definition = get_message_definition(key, level)
68
+
69
+ unless message_definition
70
+ raise NameError.new("Could not found message with key: #{key}, level: #{level}")
167
71
  end
168
72
 
169
- messages
73
+ description = if message_definition.text
74
+ message_definition.text % params
75
+ else
76
+ message_definition.description
77
+ end
78
+
79
+ { :level => level, :key => key, :dsc => description }
170
80
  end
171
81
 
172
- # Returns the message for an error.
173
- #
174
- # It first tries to get the message from text attribute of the error definition
175
- # if no definition is found or if the text attribute is blank it the returns the error
176
- # message attribute.
177
- #
178
- # @param [Exception] error The error to get the message for.
179
- #
180
- # @return [String] the error message.
181
- def error_message(error)
182
- error_definition = get_error_definition(error)
183
-
184
- if error_definition && !error_definition.text.blank?
185
- error_definition.text
186
- else
187
- error.message
188
- end
82
+ def get_message_definition(key, level)
83
+ message = @definitions.messages.find { |name, definition|
84
+ name == key.to_s && definition.level.downcase == level.downcase
85
+ }
86
+
87
+ message.last if message
189
88
  end
190
89
 
191
- def build_response(status, *messages)
192
- json(:status => status, :messages => messages)
90
+ # Serializes +element+ as json
91
+ def json(element)
92
+ JSON(element, :ascii_only => true)
193
93
  end
194
94
 
195
95
  end
@@ -2,14 +2,14 @@ module Angus
2
2
  module StatusCodes
3
3
 
4
4
  # TODO remove HTTP_STATUS from all constants
5
- HTTP_STATUS_CODE_OK = 200
5
+ HTTP_STATUS_CODE_OK = 200
6
6
 
7
- HTTP_STATUS_CODE_FORBIDDEN = 403
8
- HTTP_STATUS_CODE_NOT_FOUND = 404
9
- HTTP_STATUS_CODE_CONFLICT = 409
10
- HTTP_STATUS_CODE_UNPROCESSABLE_ENTITY = 422
7
+ HTTP_STATUS_CODE_FORBIDDEN = 403
8
+ HTTP_STATUS_CODE_NOT_FOUND = 404
9
+ HTTP_STATUS_CODE_CONFLICT = 409
10
+ HTTP_STATUS_CODE_UNPROCESSABLE_ENTITY = 422
11
11
 
12
- HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR = 500
12
+ HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR = 500
13
13
 
14
14
  def self.included(base)
15
15
  self.constants.each do |const|
@@ -1,3 +1,3 @@
1
1
  module Angus
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -36,7 +36,7 @@ describe Angus::Middleware::ExceptionHandler, { :work_dir => work_dir } do
36
36
  app.stub(:call).with(any_args).and_raise(error)
37
37
  end
38
38
 
39
- it 'returns CONFLICT ERROR' do
39
+ it 'returns HTTP_STATUS_CODE_CONFLICT ERROR' do
40
40
  response = middleware.call(env)
41
41
 
42
42
  response.first.should eq(Angus::StatusCodes::HTTP_STATUS_CODE_CONFLICT)
@@ -1,4 +1,15 @@
1
1
  UserNotFound:
2
2
  status_code: 404
3
3
  level: error
4
- description: User not found
4
+ description: User not found
5
+
6
+ UserCreatedSuccessfully:
7
+ status_code: 200
8
+ level: Info
9
+ description: The User has been created successfully.
10
+ text: The User has been created successfully.
11
+
12
+ UserDeletedSuccessfully:
13
+ status_code: 200
14
+ level: Info
15
+ description: The User has been deleted successfully.
@@ -1,9 +1,25 @@
1
- user_profile:
1
+ user:
2
2
  - field: id
3
- description: Idenfiticador del usuario
3
+ description: User identifier
4
4
  required: true
5
5
  type: integer
6
6
  - field: name
7
- description: Nombres del usuario
7
+ description: User name
8
8
  required: true
9
- type: string
9
+ type: string
10
+ - field: last_login
11
+ description: User last login time
12
+ required: true
13
+ type: datetime
14
+ - field: birth_date
15
+ description: User birth date
16
+ required: true
17
+ type: date
18
+ - field: gender
19
+ description: User gender
20
+ required: true
21
+ type: string
22
+ - field: roles
23
+ description: User roles
24
+ required: false
25
+ elements_type: integer
@@ -13,7 +13,7 @@ get_user:
13
13
  - element: profile
14
14
  description: Perfil del usuario
15
15
  required: true
16
- type: user_profile
16
+ type: user
17
17
 
18
18
  messages:
19
19
  - key: UserNotFound
@@ -30,4 +30,29 @@ get_users:
30
30
  - element: users
31
31
  description: Perfil del usuario
32
32
  required: true
33
- elements_type: user_profile
33
+ elements_type: user
34
+
35
+ create_user:
36
+ name: Create a new user
37
+ description: >
38
+ Creates and returns a new user.
39
+
40
+ path: /users
41
+ method: post
42
+
43
+ messages:
44
+ - key: UserCreatedSuccessfully
45
+
46
+ delete_user:
47
+ name: Delete user
48
+ description: >
49
+ Deletes a user.
50
+
51
+ path: /users/:id
52
+ method: delete
53
+ uri:
54
+ - element: id
55
+ description: User identifier.
56
+
57
+ messages:
58
+ - key: UserDeletedSuccessfully
@@ -0,0 +1 @@
1
+ User = Struct.new(:id, :name, :last_login, :birth_date, :gender, :roles)
@@ -1,10 +1,18 @@
1
1
  require_relative '../exceptions/user_errors'
2
+ require_relative '../models/user'
3
+
2
4
 
3
5
  class Users < Angus::BaseResource
4
- USERS = [{ :id => 1, :name => 'ac/dc' }, { :id => 2, :name => 'madonna' }]
6
+ USERS = [{ :id => 1, :name => 'ac/dc', 'last_login' => DateTime.now, :birth_date => Date.today,
7
+ :gender => :male, :roles => [1, 2, 3] },
8
+ User.new(2, 'madonna', DateTime.now, Date.today, :female)]
5
9
 
6
10
  def get_user
7
- user = USERS.find { |user| user[:id] == params[:user_id].to_i }
11
+ user = if params[:user_id] == '3'
12
+ { :id => 3 }
13
+ else
14
+ USERS.find { |user| user[:id] == params[:user_id].to_i }
15
+ end
8
16
 
9
17
  raise UserNotFound.new(params[:user_id]) unless user
10
18
 
@@ -15,4 +23,15 @@ class Users < Angus::BaseResource
15
23
  { :users => USERS }
16
24
  end
17
25
 
26
+ def create_user
27
+ { :messages => [:UserCreatedSuccessfully] }
28
+ end
29
+
30
+ def delete_user
31
+ if params[:id] == '2'
32
+ { :messages => [:UserAlreadyDeleted] }
33
+ else
34
+ { :messages => [:UserDeletedSuccessfully] }
35
+ end
36
+ end
18
37
  end
@@ -7,14 +7,20 @@ require 'functional/basic/services/basic'
7
7
  describe Spec::Functional::Basic, { :work_dir => "#{File.dirname(__FILE__ )}/basic" } do
8
8
  include Rack::Test::Methods
9
9
 
10
- def app
11
- Spec::Functional::Basic.new
12
- end
10
+ subject(:app) { Rack::Lint.new(Spec::Functional::Basic.new) }
13
11
 
14
12
  it 'responds to /' do
15
13
  get '/'
16
14
 
17
15
  last_response.status.should eq(200)
16
+ last_response.header['Content-Type'].should eq('application/json')
17
+ end
18
+
19
+ it 'responds to /' do
20
+ get '/basic'
21
+
22
+ last_response.status.should eq(200)
23
+ last_response.header['Content-Type'].should eq('application/json')
18
24
  end
19
25
 
20
26
  describe 'the documentation url' do
@@ -29,7 +35,7 @@ describe Spec::Functional::Basic, { :work_dir => "#{File.dirname(__FILE__ )}/bas
29
35
  it 'sets a html content type' do
30
36
  get '/basic/doc/0.1'
31
37
 
32
- last_response.header['Content-Type'].should eq('text/html')
38
+ last_response.header['Content-Type'].should eq('text/html;charset=utf-8')
33
39
  end
34
40
  end
35
41
 
@@ -81,6 +87,109 @@ describe Spec::Functional::Basic, { :work_dir => "#{File.dirname(__FILE__ )}/bas
81
87
  'dsc' => 'User with id=-1 not found' })}
82
88
  end
83
89
  end
90
+
91
+ context 'when an attribute is missing from the response' do
92
+ it 'sets the correct status code' do
93
+ get '/basic/api/0.1/users/3'
94
+
95
+ last_response.status.should eq(500)
96
+ end
97
+
98
+ it 'sets a json content type' do
99
+ get '/basic/api/0.1/users/3'
100
+
101
+ last_response.header['Content-Type'].should eq('application/json')
102
+ end
103
+
104
+ describe 'the response body' do
105
+ subject(:body) {
106
+ get '/basic/api/0.1/users/3'
107
+ JSON(last_response.body)
108
+ }
109
+
110
+ its(['status']) { should eq('error')}
111
+ its(['messages']) { should include({ 'level' => 'error', 'key' => 'Angus::Marshalling::InvalidGetterError',
112
+ 'dsc' => 'The requested getter (name) does not exist. ' })}
113
+ end
114
+ end
115
+
116
+ context 'when a message is returned' do
117
+ context 'when the message has a given text' do
118
+ it 'sets the correct status code' do
119
+ post '/basic/api/0.1/users'
120
+
121
+ last_response.status.should eq(200)
122
+ end
123
+
124
+ it 'sets a json content type' do
125
+ post '/basic/api/0.1/users'
126
+
127
+ last_response.header['Content-Type'].should eq('application/json')
128
+ end
129
+
130
+ describe 'the response body' do
131
+ subject(:body) {
132
+ post'/basic/api/0.1/users'
133
+ JSON(last_response.body)
134
+ }
135
+
136
+ its(['status']) { should eq('success')}
137
+ its(['messages']) { should include({ 'level' => 'info', 'key' => 'UserCreatedSuccessfully',
138
+ 'dsc' => 'The User has been created successfully.' })}
139
+ end
140
+ end
141
+
142
+ context 'when a message does not have a given text' do
143
+ it 'sets the correct status code' do
144
+ delete '/basic/api/0.1/users/1'
145
+
146
+ last_response.status.should eq(200)
147
+ end
148
+
149
+ it 'sets a json content type' do
150
+ delete '/basic/api/0.1/users/1'
151
+
152
+ last_response.header['Content-Type'].should eq('application/json')
153
+ end
154
+
155
+ describe 'the response body' do
156
+ subject(:body) {
157
+ delete '/basic/api/0.1/users/1'
158
+ JSON(last_response.body)
159
+ }
160
+
161
+ its(['status']) { should eq('success')}
162
+ its(['messages']) { should include({ 'level' => 'info', 'key' => 'UserDeletedSuccessfully',
163
+ 'dsc' => 'The User has been deleted successfully.' })}
164
+ end
165
+ end
166
+
167
+ context 'when an inexistent message is returned' do
168
+ it 'sets the correct status code' do
169
+ delete '/basic/api/0.1/users/2'
170
+
171
+ last_response.status.should eq(500)
172
+ end
173
+
174
+ it 'sets a json content type' do
175
+ delete '/basic/api/0.1/users/2'
176
+
177
+ last_response.header['Content-Type'].should eq('application/json')
178
+ end
179
+
180
+ describe 'the response body' do
181
+ subject(:body) {
182
+ delete '/basic/api/0.1/users/2'
183
+ JSON(last_response.body)
184
+ }
185
+
186
+ its(['status']) { should eq('error')}
187
+ its(['messages']) { should include({ 'level' => 'error', 'key' => 'NameError',
188
+ 'dsc' => 'Could not found message with key: UserAlreadyDeleted, level: info' })}
189
+ end
190
+ end
191
+ end
192
+
84
193
  end
85
194
 
86
195
  end
@@ -8,9 +8,7 @@ describe Spec::Functional::EmptyResource,
8
8
  { :work_dir => "#{File.dirname(__FILE__ )}/empty_resource" } do
9
9
  include Rack::Test::Methods
10
10
 
11
- def app
12
- Spec::Functional::EmptyResource.new
13
- end
11
+ subject(:app) { Rack::Lint.new(Spec::Functional::EmptyResource.new) }
14
12
 
15
13
  it 'responds to /' do
16
14
  get '/'
@@ -50,7 +48,7 @@ describe Spec::Functional::EmptyResource,
50
48
  it 'sets a html content type' do
51
49
  get url
52
50
 
53
- last_response.header['Content-Type'].should eq('text/html')
51
+ last_response.header['Content-Type'].should eq('text/html;charset=utf-8')
54
52
  end
55
53
  end
56
54
 
@@ -8,9 +8,7 @@ describe Spec::Functional::NoResources,
8
8
  { :work_dir => "#{File.dirname(__FILE__ )}/no_resources" } do
9
9
  include Rack::Test::Methods
10
10
 
11
- def app
12
- Spec::Functional::NoResources.new
13
- end
11
+ subject(:app) { Rack::Lint.new(Spec::Functional::NoResources.new) }
14
12
 
15
13
  it 'responds to /' do
16
14
  get '/'
@@ -50,7 +48,7 @@ describe Spec::Functional::NoResources,
50
48
  it 'sets a html content type' do
51
49
  get url
52
50
 
53
- last_response.header['Content-Type'].should eq('text/html')
51
+ last_response.header['Content-Type'].should eq('text/html;charset=utf-8')
54
52
  end
55
53
  end
56
54
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: angus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-12-02 00:00:00.000000000 Z
14
+ date: 2013-12-30 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: thor
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: '0.0'
40
40
  - - ! '>='
41
41
  - !ruby/object:Gem::Version
42
- version: 0.0.4
42
+ version: 0.0.5
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -50,7 +50,7 @@ dependencies:
50
50
  version: '0.0'
51
51
  - - ! '>='
52
52
  - !ruby/object:Gem::Version
53
- version: 0.0.4
53
+ version: 0.0.5
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: angus-router
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -210,8 +210,8 @@ extensions: []
210
210
  extra_rdoc_files: []
211
211
  files:
212
212
  - lib/angus/responses.rb
213
- - lib/angus/marshallings/unmarshalling.rb
214
213
  - lib/angus/marshallings/marshalling.rb
214
+ - lib/angus/marshallings/exceptions.rb
215
215
  - lib/angus/marshallings/base.rb
216
216
  - lib/angus/generator.rb
217
217
  - lib/angus/status_codes.rb
@@ -221,8 +221,8 @@ files:
221
221
  - lib/angus/renders/html_render.rb
222
222
  - lib/angus/renders/base.rb
223
223
  - lib/angus/renders/json_render.rb
224
+ - lib/angus/generator/templates/Gemfile.erb
224
225
  - lib/angus/generator/templates/resources/resource.rb.erb
225
- - lib/angus/generator/templates/Gemfile
226
226
  - lib/angus/generator/templates/README.md
227
227
  - lib/angus/generator/templates/config.ru.erb
228
228
  - lib/angus/generator/templates/services/service.rb.erb
@@ -246,6 +246,7 @@ files:
246
246
  - lib/angus/utils.rb
247
247
  - lib/angus/definition_reader.rb
248
248
  - lib/angus/base.rb
249
+ - lib/angus/proxy_actions.rb
249
250
  - lib/angus.rb
250
251
  - spec/spec_helper.rb
251
252
  - spec/angus/request_handler_spec.rb
@@ -254,6 +255,7 @@ files:
254
255
  - spec/functional/no_resources_spec.rb
255
256
  - spec/functional/empty_resource_spec.rb
256
257
  - spec/functional/basic/exceptions/user_errors.rb
258
+ - spec/functional/basic/models/user.rb
257
259
  - spec/functional/basic/resources/users.rb
258
260
  - spec/functional/basic/services/basic.rb
259
261
  - spec/functional/basic/definitions/representations.yml
@@ -305,6 +307,7 @@ test_files:
305
307
  - spec/functional/no_resources_spec.rb
306
308
  - spec/functional/empty_resource_spec.rb
307
309
  - spec/functional/basic/exceptions/user_errors.rb
310
+ - spec/functional/basic/models/user.rb
308
311
  - spec/functional/basic/resources/users.rb
309
312
  - spec/functional/basic/services/basic.rb
310
313
  - spec/functional/basic/definitions/representations.yml
@@ -1,29 +0,0 @@
1
- require 'bigdecimal'
2
- require 'date'
3
-
4
- module Angus
5
- module Unmarshalling
6
-
7
- def self.unmarshal_scalar(scalar, type)
8
- return nil if scalar.nil?
9
-
10
- case type
11
- when :string
12
- scalar
13
- when :integer
14
- scalar
15
- when :boolean
16
- scalar
17
- when :date
18
- Date.iso8601(scalar)
19
- when :date_time
20
- DateTime.iso8601(scalar)
21
- when :decimal
22
- BigDecimal.new(scalar.to_s)
23
- else
24
- raise ArgumentError, "Unknown type: #{type}"
25
- end
26
- end
27
-
28
- end
29
- end