praxis 0.22.pre.1 → 2.0.pre.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +5 -20
  3. data/CHANGELOG.md +328 -323
  4. data/lib/praxis.rb +13 -9
  5. data/lib/praxis/action_definition.rb +8 -10
  6. data/lib/praxis/action_definition/headers_dsl_compiler.rb +1 -1
  7. data/lib/praxis/api_definition.rb +27 -44
  8. data/lib/praxis/api_general_info.rb +2 -3
  9. data/lib/praxis/application.rb +15 -142
  10. data/lib/praxis/bootloader.rb +1 -2
  11. data/lib/praxis/bootloader_stages/environment.rb +13 -0
  12. data/lib/praxis/config.rb +1 -1
  13. data/lib/praxis/controller.rb +0 -2
  14. data/lib/praxis/dispatcher.rb +4 -6
  15. data/lib/praxis/docs/generator.rb +8 -18
  16. data/lib/praxis/docs/link_builder.rb +1 -1
  17. data/lib/praxis/error_handler.rb +5 -5
  18. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
  19. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +1 -1
  20. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +125 -0
  21. data/lib/praxis/extensions/field_selection.rb +1 -12
  22. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +28 -34
  23. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +35 -39
  24. data/lib/praxis/extensions/rendering.rb +1 -1
  25. data/lib/praxis/file_group.rb +1 -1
  26. data/lib/praxis/handlers/xml.rb +1 -1
  27. data/lib/praxis/mapper/active_model_compat.rb +98 -0
  28. data/lib/praxis/mapper/resource.rb +242 -0
  29. data/lib/praxis/mapper/selector_generator.rb +154 -0
  30. data/lib/praxis/mapper/sequel_compat.rb +76 -0
  31. data/lib/praxis/media_type_identifier.rb +2 -1
  32. data/lib/praxis/middleware_app.rb +13 -15
  33. data/lib/praxis/multipart/part.rb +3 -5
  34. data/lib/praxis/notifications.rb +1 -1
  35. data/lib/praxis/plugins/mapper_plugin.rb +64 -0
  36. data/lib/praxis/request.rb +14 -7
  37. data/lib/praxis/request_stages/response.rb +2 -3
  38. data/lib/praxis/resource_definition.rb +15 -19
  39. data/lib/praxis/response.rb +6 -5
  40. data/lib/praxis/response_definition.rb +5 -7
  41. data/lib/praxis/response_template.rb +3 -4
  42. data/lib/praxis/responses/http.rb +36 -0
  43. data/lib/praxis/responses/internal_server_error.rb +12 -3
  44. data/lib/praxis/responses/multipart_ok.rb +11 -4
  45. data/lib/praxis/responses/validation_error.rb +10 -1
  46. data/lib/praxis/route.rb +1 -1
  47. data/lib/praxis/router.rb +3 -3
  48. data/lib/praxis/routing_config.rb +1 -1
  49. data/lib/praxis/tasks/api_docs.rb +2 -10
  50. data/lib/praxis/tasks/routes.rb +0 -1
  51. data/lib/praxis/trait.rb +1 -1
  52. data/lib/praxis/types/media_type_common.rb +2 -2
  53. data/lib/praxis/types/multipart.rb +1 -1
  54. data/lib/praxis/types/multipart_array.rb +2 -2
  55. data/lib/praxis/types/multipart_array/part_definition.rb +1 -1
  56. data/lib/praxis/version.rb +1 -1
  57. data/praxis.gemspec +11 -9
  58. data/spec/functional_spec.rb +0 -1
  59. data/spec/praxis/action_definition_spec.rb +16 -27
  60. data/spec/praxis/api_definition_spec.rb +8 -13
  61. data/spec/praxis/api_general_info_spec.rb +8 -3
  62. data/spec/praxis/application_spec.rb +8 -14
  63. data/spec/praxis/collection_spec.rb +3 -2
  64. data/spec/praxis/config_spec.rb +2 -2
  65. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +106 -0
  66. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +147 -0
  67. data/spec/praxis/extensions/field_selection/support/spec_resources_active_model.rb +130 -0
  68. data/spec/praxis/extensions/field_selection/support/spec_resources_sequel.rb +106 -0
  69. data/spec/praxis/handlers/xml_spec.rb +2 -2
  70. data/spec/praxis/mapper/resource_spec.rb +169 -0
  71. data/spec/praxis/mapper/selector_generator_spec.rb +325 -0
  72. data/spec/praxis/media_type_spec.rb +0 -10
  73. data/spec/praxis/middleware_app_spec.rb +16 -10
  74. data/spec/praxis/request_spec.rb +7 -17
  75. data/spec/praxis/request_stages/action_spec.rb +8 -1
  76. data/spec/praxis/request_stages/validate_spec.rb +1 -1
  77. data/spec/praxis/resource_definition_spec.rb +10 -12
  78. data/spec/praxis/response_definition_spec.rb +12 -26
  79. data/spec/praxis/response_spec.rb +6 -13
  80. data/spec/praxis/responses/internal_server_error_spec.rb +5 -2
  81. data/spec/praxis/router_spec.rb +5 -9
  82. data/spec/spec_app/app/controllers/instances.rb +1 -1
  83. data/spec/spec_app/config.ru +6 -1
  84. data/spec/spec_app/config/environment.rb +3 -21
  85. data/spec/spec_helper.rb +13 -17
  86. data/spec/support/be_deep_equal_matcher.rb +39 -0
  87. data/spec/support/spec_resources.rb +124 -0
  88. metadata +74 -53
  89. data/lib/praxis/extensions/attribute_filtering.rb +0 -28
  90. data/lib/praxis/extensions/attribute_filtering/query_builder.rb +0 -39
  91. data/lib/praxis/extensions/mapper_selectors.rb +0 -16
  92. data/lib/praxis/media_type_collection.rb +0 -127
  93. data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
  94. data/spec/praxis/media_type_collection_spec.rb +0 -157
  95. data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
  96. data/spec/spec_app/app/models/person.rb +0 -3
@@ -4,9 +4,8 @@ module Praxis
4
4
 
5
5
  class ResponseDefinition
6
6
  attr_reader :name
7
- attr_reader :application
8
-
9
- def initialize(response_name, application, **spec, &block)
7
+
8
+ def initialize(response_name, **spec, &block)
10
9
  unless response_name
11
10
  raise Exceptions::InvalidConfiguration.new(
12
11
  "Response name is required for a response specification"
@@ -14,7 +13,6 @@ module Praxis
14
13
  end
15
14
  @spec = { headers:{} }
16
15
  @name = response_name
17
- @application = application
18
16
  self.instance_exec(**spec, &block) if block_given?
19
17
 
20
18
  if self.status.nil?
@@ -143,9 +141,9 @@ module Praxis
143
141
  # FIXME: remove load when when MediaTypeCommon.identifier returns a MediaTypeIdentifier
144
142
  identifier = MediaTypeIdentifier.load(self.media_type.identifier)
145
143
 
146
- default_handlers = application.api_definition.info.produces
144
+ default_handlers = ApiDefinition.instance.info.produces
147
145
 
148
- handlers = application.handlers.select do |k,v|
146
+ handlers = Praxis::Application.instance.handlers.select do |k,v|
149
147
  default_handlers.include?(k)
150
148
  end
151
149
 
@@ -202,7 +200,7 @@ module Praxis
202
200
  raise ArgumentError, "Parts definition for response #{name} does not allow :like and a block simultaneously"
203
201
  end
204
202
  if like
205
- template = application.api_definition.response(like)
203
+ template = ApiDefinition.instance.response(like)
206
204
  @parts = template.compile(nil, **args)
207
205
  else # block
208
206
  @parts = Praxis::ResponseDefinition.new('anonymous', **args, &a_proc)
@@ -1,12 +1,11 @@
1
1
  module Praxis
2
2
 
3
3
  class ResponseTemplate
4
- attr_reader :name, :block, :application
4
+ attr_reader :name, :block
5
5
 
6
- def initialize(response_name, application, &block)
6
+ def initialize(response_name, &block)
7
7
  @name = response_name
8
8
  @block = block
9
- @application = application
10
9
  end
11
10
 
12
11
  def compile(action=nil, **args)
@@ -24,7 +23,7 @@ module Praxis
24
23
  args[:media_type] = media_type
25
24
  end
26
25
  end
27
- Praxis::ResponseDefinition.new(name, application, **args, &block)
26
+ Praxis::ResponseDefinition.new(name, **args, &block)
28
27
  end
29
28
 
30
29
  def describe
@@ -131,5 +131,41 @@ module Praxis
131
131
  self.status = 422
132
132
  end
133
133
 
134
+ ApiDefinition.define do |api|
135
+
136
+
137
+ [
138
+ [ :accepted, 202, "The request has been accepted for processing, but the processing has not been completed." ],
139
+ [ :no_content, 204,"The server successfully processed the request, but is not returning any content."],
140
+ [ :multiple_choices, 300,"Indicates multiple options for the resource that the client may follow."],
141
+ [ :moved_permanently, 301,"This and all future requests should be directed to the given URI."],
142
+ [ :found, 302,"The requested resource resides temporarily under a different URI."],
143
+ [ :see_other, 303,"The response to the request can be found under another URI using a GET method"],
144
+ [ :not_modified, 304,"Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-Match."],
145
+ [ :temporary_redirect, 307,"In this case, the request should be repeated with another URI; however, future requests should still use the original URI."],
146
+ [ :bad_request, 400,"The request cannot be fulfilled due to bad syntax."],
147
+ [ :unauthorized, 401,"Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided."],
148
+ [ :forbidden, 403,"The request was a valid request, but the server is refusing to respond to it."],
149
+ [ :not_found, 404,"The requested resource could not be found but may be available again in the future."],
150
+ [ :method_not_allowed, 405,"A request was made of a resource using a request method not supported by that resource."],
151
+ [ :not_acceptable, 406,"The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request."],
152
+ [ :request_timeout, 408,"The server timed out waiting for the request."],
153
+ [ :conflict, 409, "Indicates that the request could not be processed because of conflict in the request, such as an edit conflict in the case of multiple updates."],
154
+ [ :precondition_failed, 412,"The server does not meet one of the preconditions that the requester put on the request."],
155
+ [ :unprocessable_entity, 422,"The request was well-formed but was unable to be followed due to semantic errors."],
156
+ ].each do |name, code, base_description|
157
+ api.response_template name do |media_type: nil, location: nil, headers: nil, description: nil|
158
+ status code
159
+ description( description || base_description ) # description can "potentially" be overriden in an individual action.
160
+
161
+ media_type media_type if media_type
162
+ location location if location
163
+ headers headers if headers
164
+ end
165
+ end
166
+
167
+ end
168
+
169
+
134
170
  end
135
171
  end
@@ -13,15 +13,16 @@ module Praxis
13
13
  @error = error
14
14
  end
15
15
 
16
- def format!(exception = @error, config:)
16
+ def format!(exception = @error)
17
17
  if @error
18
- if config.praxis.show_exceptions == true
18
+
19
+ if Application.instance.config.praxis.show_exceptions == true
19
20
  msg = {
20
21
  name: exception.class.name,
21
22
  message: exception.message,
22
23
  backtrace: exception.backtrace
23
24
  }
24
- msg[:cause] = format!(exception.cause, config: config) if exception.cause
25
+ msg[:cause] = format!(exception.cause) if exception.cause
25
26
  else
26
27
  msg = {name: 'InternalServerError', message: "Something bad happened."}
27
28
  end
@@ -33,4 +34,12 @@ module Praxis
33
34
 
34
35
  end
35
36
 
37
+ ApiDefinition.define do |api|
38
+ api.response_template :internal_server_error do
39
+ description "A generic error message, given when an unexpected condition was encountered and no more specific message is suitable."
40
+ status 500
41
+ media_type "application/json"
42
+ end
43
+ end
44
+
36
45
  end
@@ -19,7 +19,7 @@ module Praxis
19
19
  end
20
20
 
21
21
 
22
- def encode!(handlers:)
22
+ def encode!
23
23
  case @body
24
24
  when Praxis::Types::MultipartArray
25
25
  @body = @body.dump
@@ -28,9 +28,9 @@ module Praxis
28
28
  end
29
29
  end
30
30
 
31
- def finish(application:)
32
- format!(config: application.config)
33
- encode!(handlers: application.handlers)
31
+ def finish
32
+ format!
33
+ encode!
34
34
 
35
35
  @body = Array(@body)
36
36
 
@@ -41,4 +41,11 @@ module Praxis
41
41
 
42
42
  end
43
43
 
44
+ ApiDefinition.define do |api|
45
+ api.response_template :multipart_ok do |media_type: Praxis::Types::MultipartArray|
46
+ status 200
47
+ media_type media_type
48
+ end
49
+ end
50
+
44
51
  end
@@ -15,7 +15,7 @@ module Praxis
15
15
  @documentation = documentation
16
16
  end
17
17
 
18
- def format!(**_args)
18
+ def format!
19
19
  @body = {name: 'ValidationError', summary: @summary }
20
20
  @body[:errors] = @errors if @errors
21
21
 
@@ -31,4 +31,13 @@ module Praxis
31
31
 
32
32
  end
33
33
 
34
+
35
+ ApiDefinition.define do |api|
36
+ api.response_template :validation_error do
37
+ description "An error message indicating that one or more elements of the request did not match the API specification for the action"
38
+ status 400
39
+ media_type "application/json"
40
+ end
41
+ end
42
+
34
43
  end
@@ -24,7 +24,7 @@ module Praxis
24
24
  path_params = example_hash.select{|k,v| path_param_keys.include? k }
25
25
  # Let's generate the example only using required params, to avoid mixing incompatible parameters
26
26
  query_params = example_hash.select{|k,v| required_query_param_keys.include? k }
27
- example = { verb: self.verb, url: self.path.expand(path_params), query_params: query_params }
27
+ example = { verb: self.verb, url: self.path.expand(path_params.transform_values(&:to_s)), query_params: query_params }
28
28
 
29
29
  end
30
30
 
@@ -50,7 +50,7 @@ module Praxis
50
50
  end
51
51
 
52
52
  def add_route(target, route)
53
- path_versioning = (application.versioning_scheme == :path)
53
+ path_versioning = (Application.instance.versioning_scheme == :path)
54
54
 
55
55
  # DEPRECATED: remove with ResourceDefinition.version using: :path
56
56
  path_versioning ||= (target.action.resource_definition.version_options[:using] == :path)
@@ -65,7 +65,7 @@ module Praxis
65
65
  def call(env_or_request)
66
66
  request = case env_or_request
67
67
  when Hash
68
- request_class.new(env_or_request, application: application)
68
+ request_class.new(env_or_request)
69
69
  when request_class
70
70
  env_or_request
71
71
  else
@@ -101,7 +101,7 @@ module Praxis
101
101
  body += " Available versions = #{pretty_versions}."
102
102
  end
103
103
  headers = {"Content-Type" => "text/plain"}
104
- if application.config.praxis.x_cascade
104
+ if Praxis::Application.instance.config.praxis.x_cascade
105
105
  headers['X-Cascade'] = 'pass'
106
106
  end
107
107
  result = [404, headers, [body]]
@@ -55,7 +55,7 @@ module Praxis
55
55
  path = (base + path).gsub('//','/')
56
56
  # Reject our own options
57
57
  route_name = options.delete(:name);
58
- pattern = Mustermann.new(path, {ignore_unknown_options: true}.merge( options ))
58
+ pattern = Mustermann.new(path, **{ignore_unknown_options: true}.merge( options ))
59
59
  route = Route.new(verb, pattern, version, name: route_name, prefixed_path: prefixed_path, **options)
60
60
  @routes << route
61
61
  route
@@ -58,16 +58,8 @@ namespace :praxis do
58
58
  require 'fileutils'
59
59
 
60
60
  Praxis::Blueprint.caching_enabled = false
61
-
62
- apps = Praxis::Application.registered_apps
63
- if apps.size == 1
64
- # Backwards compatible directory generation when there's only 1 app
65
- Praxis::Docs::Generator.generate(Dir.pwd, name: apps.first[0], skip_sub_directory: true)
66
- else
67
- apps.each do|name,instance|
68
- Praxis::Docs::Generator.generate(Dir.pwd, name: name)
69
- end
70
- end
61
+ generator = Praxis::Docs::Generator.new(Dir.pwd)
62
+ generator.save!
71
63
  end
72
64
 
73
65
  end
@@ -11,7 +11,6 @@ namespace :praxis do
11
11
  ]
12
12
 
13
13
  rows = []
14
- # TODO SINGLETON: ... what do do here?...
15
14
  Praxis::Application.instance.resource_definitions.each do |resource_definition|
16
15
  resource_definition.actions.each do |name, action|
17
16
  method = begin
@@ -76,7 +76,7 @@ module Praxis
76
76
  dsl_compiler: ActionDefinition::HeadersDSLCompiler,
77
77
  case_insensitive_load: true
78
78
  }
79
- Attributor::Hash.of(key: String).construct(Proc.new {}, hash_opts)
79
+ Attributor::Hash.of(key: String).construct(Proc.new {}, **hash_opts)
80
80
  else
81
81
  Attributor::Hash.construct(Proc.new {})
82
82
  end
@@ -32,8 +32,8 @@ module Praxis
32
32
  #
33
33
  # @return [String] the string-representation of this type's identifier
34
34
  def identifier(identifier=nil)
35
- return @identifier.to_s unless identifier
36
- (@identifier = MediaTypeIdentifier.load(identifier)).to_s
35
+ return @identifier unless identifier
36
+ @identifier = MediaTypeIdentifier.load(identifier)
37
37
  end
38
38
  end
39
39
 
@@ -12,7 +12,7 @@ module Praxis
12
12
  return value if value.kind_of?(self) || value.nil?
13
13
 
14
14
  unless (value.kind_of?(::String) && ! content_type.nil?)
15
- raise Attributor::CoercionError, context: context, from: value.class, to: self.name, value: value
15
+ raise Attributor::CoercionError.new(context: context, from: value.class, to: self.name, value: value)
16
16
  end
17
17
 
18
18
  headers = {'Content-Type' => content_type}
@@ -83,7 +83,7 @@ module Praxis
83
83
 
84
84
  self.multiple << name if multiple
85
85
 
86
- compiler = Attributor::DSLCompiler.new(self, opts)
86
+ compiler = Attributor::DSLCompiler.new(self, **opts)
87
87
 
88
88
  if filename
89
89
  filename_attribute = compiler.define('filename', String, required: true)
@@ -218,7 +218,7 @@ module Praxis
218
218
  attr_accessor :preamble
219
219
  attr_reader :content_type
220
220
 
221
- def initialize(content_type: self.class.identifier)
221
+ def initialize(content_type: self.class.identifier.to_s)
222
222
  self.content_type = content_type
223
223
  end
224
224
 
@@ -14,7 +14,7 @@ module Praxis
14
14
 
15
15
  def update_attribute(attribute, options, block)
16
16
  attribute.options.merge!(options)
17
- attribute.type.attributes(options, &block)
17
+ attribute.type.attributes(**options, &block)
18
18
  end
19
19
 
20
20
  def create_attribute(type=Attributor::Struct, **opts, &block)
@@ -1,3 +1,3 @@
1
1
  module Praxis
2
- VERSION = '0.22.pre.1'
2
+ VERSION = '2.0.pre.4'
3
3
  end
@@ -21,22 +21,21 @@ Gem::Specification.new do |spec|
21
21
  spec.executables << 'praxis'
22
22
 
23
23
  spec.add_dependency 'rack', '>= 1'
24
- spec.add_dependency 'mustermann', '>=0', '<=1'
24
+ spec.add_dependency 'mustermann', '>=1.1', '<=2'
25
25
  spec.add_dependency 'activesupport', '>= 3'
26
26
  spec.add_dependency 'mime', '~> 0'
27
- spec.add_dependency 'praxis-mapper', '~> 4.3'
28
- spec.add_dependency 'praxis-blueprints', '~> 3.3'
29
- spec.add_dependency 'attributor', '~> 5.1'
30
- spec.add_dependency 'thor', '~> 0.18'
27
+ spec.add_dependency 'praxis-blueprints', '>= 3.4'
28
+ spec.add_dependency 'attributor', '>= 5.4'
29
+ spec.add_dependency 'thor'
31
30
  spec.add_dependency 'terminal-table', '~> 1.4'
32
31
 
33
32
  spec.add_development_dependency 'bundler'
34
33
  spec.add_development_dependency 'rake', '~> 0.9'
35
34
  spec.add_development_dependency 'rake-notes', '~> 0'
36
35
  if RUBY_PLATFORM !~ /java/
37
- spec.add_development_dependency 'pry', '~> 0'
38
- spec.add_development_dependency 'pry-byebug', '~> 1'
39
- spec.add_development_dependency 'pry-stack_explorer', '~> 0'
36
+ spec.add_development_dependency 'pry'
37
+ spec.add_development_dependency 'pry-byebug'
38
+ spec.add_development_dependency 'pry-stack_explorer'
40
39
  spec.add_development_dependency 'sqlite3', '~> 1'
41
40
  else
42
41
  spec.add_development_dependency 'jdbc-sqlite3'
@@ -50,6 +49,9 @@ Gem::Specification.new do |spec|
50
49
  spec.add_development_dependency 'rack-test', '~> 0'
51
50
  spec.add_development_dependency 'simplecov', '~> 0'
52
51
  spec.add_development_dependency 'fuubar', '~> 2'
53
- spec.add_development_dependency 'yard', '~> 0'
52
+ spec.add_development_dependency 'yard', ">= 0.9.20"
54
53
  spec.add_development_dependency 'coveralls'
54
+ # Just for the query selector extensions etc...
55
+ spec.add_development_dependency 'sequel', '~> 5'
56
+ spec.add_development_dependency 'activerecord', '> 4'
55
57
  end
@@ -136,7 +136,6 @@ describe 'Functional specs' do
136
136
  headers = last_response.headers
137
137
  expect(headers['Content-Type']).to eq('application/json')
138
138
  expect(headers['Spec-Middleware']).to eq('used')
139
- expect(headers['Content-Length']).to eq(last_response.body.size.to_s)
140
139
  end
141
140
 
142
141
  it 'returns early when making the before filter break' do
@@ -1,15 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Praxis::ActionDefinition do
4
-
5
- before(:context) do
6
- Thread.current[:praxis_instance] = Praxis::Application.new(name: 'action_definition_spec', skip_registration: true)
7
- end
8
-
9
- let(:praxis_instance) do
10
- Thread.current[:praxis_instance]
11
- end
12
-
13
4
  class SpecMediaType < Praxis::MediaType
14
5
  identifier 'application/json'
15
6
 
@@ -40,8 +31,10 @@ describe Praxis::ActionDefinition do
40
31
  end
41
32
  end
42
33
  end
34
+
43
35
  subject(:action) do
44
- praxis_instance.api_definition.define do |api|
36
+
37
+ Praxis::ApiDefinition.define do |api|
45
38
  api.response_template :ok do |media_type: , location: nil, headers: nil, description: nil |
46
39
  status 200
47
40
 
@@ -49,10 +42,6 @@ describe Praxis::ActionDefinition do
49
42
  location location
50
43
  headers headers if headers
51
44
  end
52
-
53
- api.info do # applies to all API infos
54
- base_path "/pref"
55
- end
56
45
  end
57
46
  Praxis::ActionDefinition.new(:foo, resource_definition) do
58
47
  routing { get '/:one' }
@@ -122,11 +111,11 @@ describe Praxis::ActionDefinition do
122
111
  let(:traits) { {test: trait} }
123
112
 
124
113
  before do
125
- allow(praxis_instance.api_definition).to receive(:traits).and_return(traits)
114
+ allow(Praxis::ApiDefinition.instance).to receive(:traits).and_return(traits)
126
115
  end
127
116
 
128
117
  its('params.attributes.keys') { should eq [:inherited, :app_name, :name, :one]}
129
- its('routes.first.path.to_s') { should eq '/pref/foobars/hello_world/test_trait/:app_name/:one' }
118
+ its('routes.first.path.to_s') { should eq '/api/foobars/hello_world/test_trait/:app_name/:one' }
130
119
  its(:traits) { should eq [:test] }
131
120
 
132
121
  it 'is reflected in the describe output' do
@@ -292,7 +281,7 @@ describe Praxis::ActionDefinition do
292
281
  subject(:action) { resource_definition.actions[:show] }
293
282
 
294
283
  it 'works' do
295
- expansion = action.primary_route.path.expand(cloud_id:232, id: 2)
284
+ expansion = action.primary_route.path.expand(cloud_id:'232', id: '2')
296
285
  expect(expansion).to eq "/api/clouds/232/instances/2"
297
286
  end
298
287
 
@@ -326,9 +315,11 @@ describe Praxis::ActionDefinition do
326
315
  end
327
316
 
328
317
  context 'with a base_path and base_params on ApiDefinition' do
329
- subject(:action) do
330
- api_def=Praxis::ApiDefinition.new(praxis_instance)
331
- api_def.define do |api|
318
+ # Without getting a fresh new ApiDefinition it is very difficult to test stuff using the Singleton
319
+ # So for some tests we're gonna create a new instance and work with it to avoid the singleton issues
320
+ let(:non_singleton_api) do
321
+ api_def=Praxis::ApiDefinition.__send__(:new)
322
+ api_def.instance_eval do |api|
332
323
 
333
324
  api.info do
334
325
  base_path '/apps/:app_name'
@@ -341,13 +332,11 @@ describe Praxis::ActionDefinition do
341
332
  end
342
333
 
343
334
  end
344
- # No setter...and its fine to do it here as it would not be used in a runtime situation
345
- praxis_instance.instance_variable_set(:@api_definition, api_def)
346
- # Define the action after the api_definition is set (as it uses is config to setup the routes)
347
- Praxis::ActionDefinition.new(:bar, resource_definition) do
348
- routing { get '/:one' }
349
- params { attribute :one, String }
350
- end
335
+ api_def
336
+ end
337
+
338
+ before do
339
+ allow(Praxis::ApiDefinition).to receive(:instance).and_return(non_singleton_api)
351
340
  end
352
341
 
353
342
  its('routes.first.path.to_s') { should eq '/apps/:app_name/foobars/hello_world/:one' }