angus 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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