praxis 2.0.pre.17 → 2.0.pre.21

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 (235) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/.simplecov +3 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +19 -0
  6. data/CONTRIBUTING.md +2 -79
  7. data/Gemfile +5 -1
  8. data/Guardfile +6 -4
  9. data/LICENSE +0 -2
  10. data/MAINTAINERS.md +1 -0
  11. data/README.md +15 -22
  12. data/Rakefile +4 -2
  13. data/bin/praxis +55 -58
  14. data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
  15. data/lib/praxis/action_definition.rb +65 -95
  16. data/lib/praxis/api_definition.rb +21 -29
  17. data/lib/praxis/api_general_info.rb +55 -66
  18. data/lib/praxis/application.rb +15 -32
  19. data/lib/praxis/blueprint.rb +80 -73
  20. data/lib/praxis/bootloader.rb +24 -33
  21. data/lib/praxis/bootloader_stages/environment.rb +5 -10
  22. data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
  23. data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
  24. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
  25. data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
  26. data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
  27. data/lib/praxis/bootloader_stages/routing.rb +5 -8
  28. data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
  29. data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
  30. data/lib/praxis/callbacks.rb +12 -11
  31. data/lib/praxis/collection.rb +11 -14
  32. data/lib/praxis/config.rb +17 -28
  33. data/lib/praxis/config_hash.rb +2 -1
  34. data/lib/praxis/controller.rb +7 -6
  35. data/lib/praxis/dispatcher.rb +34 -42
  36. data/lib/praxis/docs/open_api/info_object.rb +11 -8
  37. data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
  38. data/lib/praxis/docs/open_api/operation_object.rb +7 -4
  39. data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
  40. data/lib/praxis/docs/open_api/paths_object.rb +11 -9
  41. data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
  42. data/lib/praxis/docs/open_api/response_object.rb +24 -18
  43. data/lib/praxis/docs/open_api/responses_object.rb +3 -1
  44. data/lib/praxis/docs/open_api/schema_object.rb +61 -29
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +9 -6
  47. data/lib/praxis/docs/open_api_generator.rb +114 -150
  48. data/lib/praxis/endpoint_definition.rb +60 -77
  49. data/lib/praxis/error_handler.rb +2 -2
  50. data/lib/praxis/exception.rb +2 -0
  51. data/lib/praxis/exceptions/config.rb +3 -1
  52. data/lib/praxis/exceptions/config_load.rb +2 -0
  53. data/lib/praxis/exceptions/config_validation.rb +3 -1
  54. data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
  55. data/lib/praxis/exceptions/invalid_response.rb +3 -1
  56. data/lib/praxis/exceptions/invalid_trait.rb +3 -1
  57. data/lib/praxis/exceptions/stage_not_found.rb +3 -1
  58. data/lib/praxis/exceptions/validation.rb +4 -3
  59. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
  60. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
  61. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
  62. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
  63. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
  64. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
  65. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
  66. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
  67. data/lib/praxis/extensions/attribute_filtering.rb +3 -1
  68. data/lib/praxis/extensions/field_expansion.rb +6 -4
  69. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
  70. data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
  71. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
  72. data/lib/praxis/extensions/field_selection.rb +3 -1
  73. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
  74. data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
  75. data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
  76. data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
  77. data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
  78. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
  79. data/lib/praxis/extensions/pagination.rb +10 -15
  80. data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
  81. data/lib/praxis/extensions/rails_compat.rb +2 -0
  82. data/lib/praxis/extensions/rendering.rb +12 -12
  83. data/lib/praxis/field_expander.rb +8 -9
  84. data/lib/praxis/file_group.rb +8 -12
  85. data/lib/praxis/finalizable.rb +1 -0
  86. data/lib/praxis/handlers/json.rb +5 -2
  87. data/lib/praxis/handlers/plain.rb +2 -1
  88. data/lib/praxis/handlers/www_form.rb +6 -3
  89. data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
  90. data/lib/praxis/mapper/active_model_compat.rb +13 -10
  91. data/lib/praxis/mapper/resource.rb +196 -181
  92. data/lib/praxis/mapper/selector_generator.rb +106 -112
  93. data/lib/praxis/mapper/sequel_compat.rb +70 -67
  94. data/lib/praxis/media_type.rb +2 -2
  95. data/lib/praxis/media_type_identifier.rb +26 -22
  96. data/lib/praxis/middleware_app.rb +18 -15
  97. data/lib/praxis/multipart/parser.rb +46 -51
  98. data/lib/praxis/multipart/part.rb +78 -110
  99. data/lib/praxis/notifications.rb +2 -4
  100. data/lib/praxis/plugin.rb +11 -18
  101. data/lib/praxis/plugin_concern.rb +12 -15
  102. data/lib/praxis/plugins/mapper_plugin.rb +15 -13
  103. data/lib/praxis/plugins/pagination_plugin.rb +8 -6
  104. data/lib/praxis/plugins/rails_plugin.rb +33 -28
  105. data/lib/praxis/renderer.rb +11 -15
  106. data/lib/praxis/request.rb +48 -44
  107. data/lib/praxis/request_stages/action.rb +4 -6
  108. data/lib/praxis/request_stages/load_request.rb +2 -4
  109. data/lib/praxis/request_stages/request_stage.rb +19 -23
  110. data/lib/praxis/request_stages/response.rb +4 -6
  111. data/lib/praxis/request_stages/validate.rb +3 -5
  112. data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -28
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +24 -26
  117. data/lib/praxis/response_definition.rb +77 -122
  118. data/lib/praxis/response_template.rb +11 -15
  119. data/lib/praxis/responses/http.rb +23 -44
  120. data/lib/praxis/responses/internal_server_error.rb +18 -21
  121. data/lib/praxis/responses/multipart_ok.rb +4 -9
  122. data/lib/praxis/responses/validation_error.rb +8 -15
  123. data/lib/praxis/route.rb +8 -10
  124. data/lib/praxis/router/rack.rb +13 -7
  125. data/lib/praxis/router/simple.rb +10 -5
  126. data/lib/praxis/router.rb +27 -34
  127. data/lib/praxis/routing_config.rb +52 -29
  128. data/lib/praxis/simple_media_type.rb +5 -8
  129. data/lib/praxis/stage.rb +17 -25
  130. data/lib/praxis/tasks/api_docs.rb +17 -16
  131. data/lib/praxis/tasks/console.rb +3 -1
  132. data/lib/praxis/tasks/environment.rb +2 -0
  133. data/lib/praxis/tasks/routes.rb +26 -24
  134. data/lib/praxis/tasks.rb +3 -1
  135. data/lib/praxis/trait.rb +37 -46
  136. data/lib/praxis/types/fuzzy_hash.rb +13 -14
  137. data/lib/praxis/types/media_type_common.rb +11 -10
  138. data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
  139. data/lib/praxis/types/multipart_array.rb +100 -115
  140. data/lib/praxis/validation_handler.rb +5 -3
  141. data/lib/praxis/version.rb +3 -1
  142. data/lib/praxis.rb +4 -5
  143. data/praxis.gemspec +22 -21
  144. data/spec/functional_spec.rb +44 -56
  145. data/spec/praxis/action_definition_spec.rb +39 -48
  146. data/spec/praxis/api_definition_spec.rb +45 -47
  147. data/spec/praxis/api_general_info_spec.rb +28 -29
  148. data/spec/praxis/application_spec.rb +18 -14
  149. data/spec/praxis/blueprint_spec.rb +33 -34
  150. data/spec/praxis/bootloader_spec.rb +32 -30
  151. data/spec/praxis/callbacks_spec.rb +37 -37
  152. data/spec/praxis/collection_spec.rb +18 -25
  153. data/spec/praxis/config_hash_spec.rb +5 -4
  154. data/spec/praxis/config_spec.rb +27 -26
  155. data/spec/praxis/controller_spec.rb +8 -9
  156. data/spec/praxis/endpoint_definition_spec.rb +25 -32
  157. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
  158. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
  159. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
  160. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
  161. data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
  162. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
  163. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
  164. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
  165. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
  166. data/spec/praxis/extensions/rendering_spec.rb +9 -9
  167. data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
  168. data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
  169. data/spec/praxis/field_expander_spec.rb +6 -5
  170. data/spec/praxis/file_group_spec.rb +3 -1
  171. data/spec/praxis/handlers/json_spec.rb +6 -5
  172. data/spec/praxis/mapper/resource_spec.rb +39 -29
  173. data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
  174. data/spec/praxis/media_type_identifier_spec.rb +13 -10
  175. data/spec/praxis/media_type_spec.rb +12 -12
  176. data/spec/praxis/middleware_app_spec.rb +23 -22
  177. data/spec/praxis/multipart/parser_spec.rb +7 -9
  178. data/spec/praxis/notifications_spec.rb +4 -4
  179. data/spec/praxis/plugin_concern_spec.rb +5 -6
  180. data/spec/praxis/renderer_spec.rb +10 -9
  181. data/spec/praxis/request_spec.rb +38 -41
  182. data/spec/praxis/request_stages/action_spec.rb +14 -15
  183. data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
  184. data/spec/praxis/request_stages/validate_spec.rb +3 -1
  185. data/spec/praxis/response_definition_spec.rb +79 -92
  186. data/spec/praxis/response_spec.rb +35 -40
  187. data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
  188. data/spec/praxis/responses/validation_error_spec.rb +17 -18
  189. data/spec/praxis/route_spec.rb +4 -7
  190. data/spec/praxis/router_spec.rb +69 -79
  191. data/spec/praxis/routing_config_spec.rb +15 -14
  192. data/spec/praxis/stage_spec.rb +56 -53
  193. data/spec/praxis/trait_spec.rb +17 -17
  194. data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
  195. data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
  196. data/spec/praxis/types/multipart_array_spec.rb +33 -48
  197. data/spec/spec_app/app/concerns/authenticated.rb +5 -5
  198. data/spec/spec_app/app/concerns/basic_api.rb +3 -1
  199. data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
  200. data/spec/spec_app/app/controllers/base_class.rb +6 -5
  201. data/spec/spec_app/app/controllers/instances.rb +31 -34
  202. data/spec/spec_app/app/controllers/volumes.rb +6 -6
  203. data/spec/spec_app/app/responses/multipart.rb +1 -2
  204. data/spec/spec_app/app/responses/other_response.rb +2 -2
  205. data/spec/spec_app/config/environment.rb +19 -6
  206. data/spec/spec_app/config.ru +4 -3
  207. data/spec/spec_app/design/api.rb +13 -15
  208. data/spec/spec_app/design/media_types/instance.rb +6 -6
  209. data/spec/spec_app/design/media_types/volume.rb +2 -1
  210. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
  211. data/spec/spec_app/design/resources/instances.rb +11 -17
  212. data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
  213. data/spec/spec_app/design/resources/volumes.rb +4 -5
  214. data/spec/spec_helper.rb +11 -13
  215. data/spec/support/be_deep_equal_matcher.rb +5 -0
  216. data/spec/support/spec_authorization_plugin.rb +7 -12
  217. data/spec/support/spec_blueprints.rb +5 -4
  218. data/spec/support/spec_complex_authentication_plugin.rb +17 -34
  219. data/spec/support/spec_endpoint_definitions.rb +2 -3
  220. data/spec/support/spec_media_types.rb +28 -35
  221. data/spec/support/spec_resources.rb +22 -16
  222. data/spec/support/spec_simple_authentication_plugin.rb +5 -9
  223. data/tasks/loader.thor +4 -2
  224. data/tasks/thor/app.rb +7 -5
  225. data/tasks/thor/example.rb +23 -22
  226. data/tasks/thor/model.rb +7 -7
  227. data/tasks/thor/scaffold.rb +23 -23
  228. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
  229. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
  230. metadata +72 -84
  231. data/MAINTAINERS +0 -2
  232. data/TODO.md +0 -25
  233. data/spec/praxis/api_resource_spec.rb +0 -0
  234. data/spec/praxis/dispatcher_spec.rb +0 -0
  235. data/spec/spec_app/app/responses/bulk_response.rb +0 -0
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Praxis
4
- class Blueprint
5
-
4
+ class Blueprint
6
5
  # Simple helper class that can parse the `attribute :foobar` dsl into
7
6
  # an equivalent structure hash. Example:
8
7
  # do
@@ -13,22 +12,25 @@ module Praxis
13
12
  # end
14
13
  # is parsed as: { one: true, complex: { sub1: true} }
15
14
  class FieldsetParser
16
- def initialize( &block)
15
+ def initialize(&block)
17
16
  @hash = nil
18
17
  @block = block
19
18
  end
20
-
19
+
21
20
  def attribute(name, **args, &block)
22
- raise "Default fieldset definitions do not accept parameters (got: #{args})" \
23
- "If you're upgrading from a previous version of Praxis and still using the view :default " \
24
- "block syntax, make sure you don't use any view: X parameters when you define the attributes " \
25
- "(expand them explicitly if you want deeper structure)" \
26
- "The offending view with parameters is defined in:\n#{Kernel.caller.first}" unless args.empty?
21
+ unless args.empty?
22
+ raise "Default fieldset definitions do not accept parameters (got: #{args})" \
23
+ "If you're upgrading from a previous version of Praxis and still using the view :default " \
24
+ "block syntax, make sure you don't use any view: X parameters when you define the attributes " \
25
+ '(expand them explicitly if you want deeper structure)' \
26
+ "The offending view with parameters is defined in:\n#{Kernel.caller.first}"
27
+ end
27
28
  @hash[name] = block_given? ? FieldsetParser.new(&block).fieldset : true
28
29
  end
29
30
 
30
31
  def fieldset
31
32
  return @hash if @hash
33
+
32
34
  # Lazy eval
33
35
  @hash = {}
34
36
  instance_eval(&@block)
@@ -36,18 +38,18 @@ module Praxis
36
38
  end
37
39
  end
38
40
  include Attributor::Type
41
+ include Attributor::Container
39
42
  include Attributor::Dumpable
40
43
 
41
44
  extend Finalizable
42
45
 
43
- @@caching_enabled = false
46
+ @@caching_enabled = false # rubocop:disable Style/ClassVars
44
47
 
45
48
  attr_reader :validating
46
49
  attr_accessor :object
47
50
 
48
51
  class << self
49
- attr_reader :attribute
50
- attr_reader :options
52
+ attr_reader :attribute, :options
51
53
  attr_accessor :reference
52
54
  end
53
55
 
@@ -65,14 +67,14 @@ module Praxis
65
67
  def self.new(object)
66
68
  # TODO: do we want to allow the identity map thing in the object?...maybe not.
67
69
  if @@caching_enabled
68
- return self.cache[object] ||= begin
69
- blueprint = self.allocate
70
+ return cache[object] ||= begin
71
+ blueprint = allocate
70
72
  blueprint.send(:initialize, object)
71
73
  blueprint
72
74
  end
73
75
  end
74
76
 
75
- blueprint = self.allocate
77
+ blueprint = allocate
76
78
  blueprint.send(:initialize, object)
77
79
  blueprint
78
80
  end
@@ -83,15 +85,11 @@ module Praxis
83
85
 
84
86
  def self.attributes(opts = {}, &block)
85
87
  if block_given?
86
- raise 'Redefining Blueprint attributes is not currently supported' if self.const_defined?(:Struct, false)
88
+ raise 'Redefining Blueprint attributes is not currently supported' if const_defined?(:Struct, false)
87
89
 
88
- if opts.key?(:reference) && opts[:reference] != self.reference
89
- raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
90
- elsif self.reference
91
- opts[:reference] = self.reference # pass the reference Class down
92
- else
93
- opts[:reference] = self
94
- end
90
+ raise "Reference mismatch in #{inspect}. Given :reference option #{opts[:reference].inspect}, while using #{reference.inspect}" if opts.key?(:reference) && opts[:reference] != reference
91
+
92
+ opts[:reference] = (reference || self)
95
93
 
96
94
  @options.merge!(opts)
97
95
  @block = block
@@ -99,13 +97,14 @@ module Praxis
99
97
  return @attribute
100
98
  end
101
99
 
102
- raise "@attribute not defined yet for #{self.name}" unless @attribute
100
+ raise "@attribute not defined yet for #{name}" unless @attribute
103
101
 
104
102
  @attribute.attributes
105
103
  end
106
104
 
107
105
  def self.domain_model(klass = nil)
108
106
  return @domain_model if klass.nil?
107
+
109
108
  @domain_model = klass
110
109
  end
111
110
 
@@ -118,16 +117,16 @@ module Praxis
118
117
  when self
119
118
  value
120
119
  when nil, Hash, String
121
- if (value = self.attribute.load(value, context, **options))
122
- self.new(value)
120
+ if (value = attribute.load(value, context, **options))
121
+ new(value)
123
122
  end
124
123
  else
125
- if value.is_a?(self.domain_model) || value.is_a?(self::Struct)
124
+ if value.is_a?(domain_model) || value.is_a?(self::Struct)
126
125
  # Wrap the value directly
127
- self.new(value)
126
+ new(value)
128
127
  else
129
128
  # Wrap the object inside the domain_model
130
- self.new(domain_model.new(value))
129
+ new(domain_model.new(value))
131
130
  end
132
131
  end
133
132
  end
@@ -141,7 +140,7 @@ module Praxis
141
140
  end
142
141
 
143
142
  def self.caching_enabled=(caching_enabled)
144
- @@caching_enabled = caching_enabled
143
+ @@caching_enabled = caching_enabled # rubocop:disable Style/ClassVars
145
144
  end
146
145
 
147
146
  # Fetch current blueprint cache, scoped by this class
@@ -154,29 +153,28 @@ module Praxis
154
153
  end
155
154
 
156
155
  def self.valid_type?(value)
157
- value.is_a?(self) || value.is_a?(self.attribute.type)
156
+ value.is_a?(self) || value.is_a?(attribute.type)
158
157
  end
159
158
 
160
159
  def self.example(context = nil, **values)
161
160
  context = case context
162
161
  when nil
163
- ["#{self.name}-#{values.object_id}"]
162
+ ["#{name}-#{values.object_id}"]
164
163
  when ::String
165
164
  [context]
166
165
  else
167
166
  context
168
167
  end
169
168
 
170
- self.new(self.attribute.example(context, values: values))
169
+ new(attribute.example(context, values: values))
171
170
  end
172
171
 
173
172
  def self.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil)
174
- raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
173
+ raise ArgumentError, "Invalid context received (nil) while validating value of type #{name}" if context.nil?
174
+
175
175
  context = [context] if context.is_a? ::String
176
176
 
177
- unless value.is_a?(self)
178
- raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{self.name} for an object of type #{value.class.name}."
179
- end
177
+ raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{name} for an object of type #{value.class.name}." unless value.is_a?(self)
180
178
 
181
179
  value.validate(context)
182
180
  end
@@ -187,13 +185,14 @@ module Praxis
187
185
  @block_for_default_fieldset = block
188
186
  end
189
187
 
190
- def self.view(name, **options, &block)
188
+ def self.view(name, **_options, &block)
191
189
  unless name == :default
192
190
  raise "[ERROR] Views are no longer supported. Please use fully expanded fields when rendering.\n" \
193
191
  "NOTE that defining the :default view is deprecated, but still temporarily allowed, as an alias to define the default_fieldset.\n" \
194
192
  "A view for name #{name} is attempted to be defined in:\n#{Kernel.caller.first}"
195
193
  end
196
- raise "Cannot define the default fieldset through the default view unless a block is passed" unless block_given?
194
+ raise 'Cannot define the default fieldset through the default view unless a block is passed' unless block_given?
195
+
197
196
  puts "[DEPRECATED] default fieldsets should be defined through `default_fieldset` instead of using the view :default block.\n" \
198
197
  "A default view is attempted to be defined in:\n#{Kernel.caller.first}"
199
198
  default_fieldset(&block)
@@ -219,45 +218,45 @@ module Praxis
219
218
  # Internal finalize! logic
220
219
  def self._finalize!
221
220
  if @block
222
- self.define_attribute!
223
- self.define_readers!
221
+ define_attribute!
222
+ define_readers!
224
223
  # Don't blindly override a the default fieldset if the MediaType wants to define it on its own
225
224
  if @block_for_default_fieldset
226
- parse_default_fieldset(@block_for_default_fieldset)
225
+ parse_default_fieldset(@block_for_default_fieldset)
227
226
  else
228
- self.generate_default_fieldset!
227
+ generate_default_fieldset!
229
228
  end
230
- self.resolve_domain_model!
229
+ resolve_domain_model!
231
230
  end
232
231
  super
233
232
  end
234
233
 
235
234
  def self.resolve_domain_model!
236
- return unless self.domain_model.is_a?(String)
235
+ return unless domain_model.is_a?(String)
237
236
 
238
- @domain_model = self.domain_model.constantize
237
+ @domain_model = domain_model.constantize
239
238
  end
240
239
 
241
240
  def self.define_attribute!
242
241
  @attribute = Attributor::Attribute.new(Attributor::Struct, @options, &@block)
243
242
  @block = nil
244
243
  @attribute.type.anonymous_type true
245
- self.const_set(:Struct, @attribute.type)
244
+ const_set(:Struct, @attribute.type)
246
245
  end
247
246
 
248
247
  def self.define_readers!
249
- self.attributes.each do |name, _attribute|
248
+ attributes.each do |name, _attribute|
250
249
  name = name.to_sym
251
250
 
252
251
  # Don't redefine existing methods
253
- next if self.instance_methods.include? name
252
+ next if instance_methods.include? name
254
253
 
255
254
  define_reader! name
256
255
  end
257
256
  end
258
257
 
259
258
  def self.define_reader!(name)
260
- attribute = self.attributes[name]
259
+ attribute = attributes[name]
261
260
  # TODO: profile and optimize
262
261
  # because we use the attribute in the reader,
263
262
  # it's likely faster to use define_method here
@@ -265,6 +264,7 @@ module Praxis
265
264
  define_method(name) do
266
265
  value = @object.__send__(name)
267
266
  return value if value.nil? || value.is_a?(attribute.type)
267
+
268
268
  attribute.load(value)
269
269
  end
270
270
  end
@@ -274,15 +274,16 @@ module Praxis
274
274
 
275
275
  @default_fieldset = {}
276
276
  attributes.each do |name, attr|
277
- the_type = (attr.type < Attributor::Collection) ? attr.type.member_type : attr.type
277
+ the_type = attr.type < Attributor::Collection ? attr.type.member_type : attr.type
278
278
  next if the_type < Blueprint
279
- # Note: we won't try to expand fields here, as we want to be lazy (and we're expanding)
279
+
280
+ # NOTE: we won't try to expand fields here, as we want to be lazy (and we're expanding)
280
281
  # every time a request comes in anyway. This could be an optimization we do at some point
281
282
  # or we can 'memoize it' to avoid trying to expand it over an over...
282
283
  @default_fieldset[name] = true
283
284
  end
284
285
  end
285
-
286
+
286
287
  def initialize(object)
287
288
  @object = object
288
289
  @validating = false
@@ -291,18 +292,14 @@ module Praxis
291
292
  # By default we'll use the object identity, to avoid rendering the same object twice
292
293
  # Override, if there is a better way cache things up
293
294
  def _cache_key
294
- self.object
295
+ object
295
296
  end
296
297
 
297
298
  # Render the wrapped data with the given fields (or using the default fieldset otherwise)
298
- def render(fields: self.class.default_fieldset, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **opts)
299
-
299
+ def render(fields: self.class.default_fieldset, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **_opts)
300
300
  # Accept a simple array of fields, and transform it to a 1-level hash with true values
301
- if fields.is_a? Array
302
- fields = fields.each_with_object({}) { |field, hash| hash[field] = true }
303
- end
301
+ fields = fields.each_with_object({}) { |field, hash| hash[field] = true } if fields.is_a? Array
304
302
 
305
- expanded = Praxis::FieldExpander.new.expand(self, fields)
306
303
  renderer.render(self, fields, context: context)
307
304
  end
308
305
 
@@ -313,26 +310,36 @@ module Praxis
313
310
  end
314
311
 
315
312
  def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
316
- raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
313
+ raise ArgumentError, "Invalid context received (nil) while validating value of type #{name}" if context.nil?
314
+
317
315
  context = [context] if context.is_a? ::String
318
- keys_with_values = []
319
316
 
320
317
  raise 'validation conflict' if @validating
318
+
321
319
  @validating = true
322
320
 
323
321
  errors = []
324
- self.class.attributes.each do |sub_attribute_name, sub_attribute|
325
- sub_context = self.class.generate_subcontext(context, sub_attribute_name)
326
- value = self.send(sub_attribute_name)
327
- keys_with_values << sub_attribute_name unless value.nil?
322
+ keys_provided = []
323
+
324
+ self.class.attributes.each do |key, attribute|
325
+ sub_context = self.class.generate_subcontext(context, key)
326
+ value = _get_attr(key)
327
+ keys_provided << key if @object.key?(key)
328
+
329
+ next if value.respond_to?(:validating) && value.validating # really, it's a thing with sub-attributes
328
330
 
329
- if value.respond_to?(:validating) # really, it's a thing with sub-attributes
330
- next if value.validating
331
+ # Isn't this handled by the requirements validation? NO! we might want to combine
332
+ errors.concat ["Attribute #{Attributor.humanize_context(sub_context)} is required."] if attribute.options[:required] && !@object.key?(key)
333
+ if @object[key].nil?
334
+ errors.concat ["Attribute #{Attributor.humanize_context(sub_context)} is not nullable."] if !Attributor::Attribute.nullable_attribute?(attribute.options) && @object.key?(key) # It is only nullable if there's an explicite null: true (undefined defaults to false)
335
+ # No need to validate the attribute further if the key wasn't passed...(or we would get nullable errors etc..cause the attribute has no
336
+ # context if its containing key was even passed (and there might not be a containing key for a top level attribute anyways))
337
+ else
338
+ errors.concat attribute.validate(value, sub_context)
331
339
  end
332
- errors.concat(sub_attribute.validate(value, sub_context))
333
340
  end
334
- self.class.attribute.type.requirements.each do |req|
335
- validation_errors = req.validate(keys_with_values, context)
341
+ self.class.attribute.type.requirements.each do |requirement|
342
+ validation_errors = requirement.validate(keys_provided, context)
336
343
  errors.concat(validation_errors) unless validation_errors.empty?
337
344
  end
338
345
  errors
@@ -342,14 +349,14 @@ module Praxis
342
349
 
343
350
  # generic semi-private getter used by Renderer
344
351
  def _get_attr(name)
345
- self.send(name)
352
+ send(name)
346
353
  end
347
354
 
348
355
  # Delegates the json-schema methods to the underlying attribute/member_type
349
356
  def self.as_json_schema(**args)
350
357
  @attribute.type.as_json_schema(args)
351
358
  end
352
-
359
+
353
360
  def self.json_schema_type
354
361
  @attribute.type.json_schema_type
355
362
  end
@@ -1,13 +1,12 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  class Bootloader
4
-
5
- attr_reader :application
6
- attr_reader :stages
5
+ attr_reader :application, :stages
7
6
 
8
7
  def initialize(application)
9
8
  @application = application
10
- @stages = Array.new
9
+ @stages = []
11
10
 
12
11
  setup_stages!
13
12
  end
@@ -26,7 +25,7 @@ module Praxis
26
25
 
27
26
  # then setup plugins
28
27
  stages << BootloaderStages::PluginLoader.new(:plugins, application)
29
-
28
+
30
29
  # then the initializers. as it is their job to ensure monkey patches and other
31
30
  # config is in place first.
32
31
  stages << BootloaderStages::FileLoader.new(:initializers, application)
@@ -50,24 +49,18 @@ module Praxis
50
49
  Praxis::Blueprint.finalize!
51
50
  Praxis::EndpointDefinition.finalize!
52
51
  end
53
-
54
52
  end
55
53
 
56
54
  def delete_stage(stage_name)
57
- if (stage = stages.find { |stage| stage.name == stage_name })
58
- stages.delete(stage)
59
- else
60
- raise Exceptions::StageNotFound.new(
61
- "Cannot remove stage with name #{stage_name}, stage does not exist."
62
- )
63
- end
64
- end
55
+ raise Exceptions::StageNotFound, "Cannot remove stage with name #{stage_name}, stage does not exist." unless (stage = stages.find { |s| s.name == stage_name })
65
56
 
57
+ stages.delete(stage)
58
+ end
66
59
 
67
60
  def before(*stage_path, &block)
68
61
  stage_name = stage_path.shift
69
62
  the_stage = stages.find { |stage| stage.name == stage_name }
70
- raise Exceptions::StageNotFound.new("Error running a before block for stage #{stage_name}") unless the_stage
63
+ raise Exceptions::StageNotFound, "Error running a before block for stage #{stage_name}" unless the_stage
71
64
 
72
65
  the_stage.before(*stage_path, &block)
73
66
  end
@@ -75,36 +68,37 @@ module Praxis
75
68
  def after(*stage_path, &block)
76
69
  stage_name = stage_path.shift
77
70
  the_stage = stages.find { |stage| stage.name == stage_name }
78
- raise Exceptions::StageNotFound.new("Error running an after block for stage #{stage_name}") unless the_stage
71
+ raise Exceptions::StageNotFound, "Error running an after block for stage #{stage_name}" unless the_stage
79
72
 
80
73
  the_stage.after(*stage_path, &block)
81
74
  end
82
75
 
83
- def use(plugin,**options, &block)
76
+ def use(plugin, **options, &block)
84
77
  if plugin.ancestors.include?(PluginConcern)
85
78
  plugin.setup!
86
79
  plugin = plugin::Plugin
87
80
  end
88
81
 
89
82
  instance = if plugin.ancestors.include?(Singleton)
90
- plugin.instance
91
- elsif plugin.kind_of?(Class)
92
- plugin.new
93
- else
94
- plugin
95
- end
83
+ plugin.instance
84
+ elsif plugin.is_a?(Class)
85
+ plugin.new
86
+ else
87
+ plugin
88
+ end
96
89
 
97
90
  instance.application = application
98
91
  instance.options.merge!(options)
99
92
  instance.block = block if block_given?
100
93
 
101
94
  config_key = if instance.config_key.nil?
102
- raise "Cannot use plugin: #{plugin}. It does not have a config_key defined, and its class does not have a name" unless instance.class.name
103
- # Default the config key based on the full class name transformed to snake case (and joining modules with '_')
104
- instance.class.name.to_s.split('::').collect{|n| n.underscore }.join('_').to_sym
105
- else
106
- instance.config_key
107
- end
95
+ raise "Cannot use plugin: #{plugin}. It does not have a config_key defined, and its class does not have a name" unless instance.class.name
96
+
97
+ # Default the config key based on the full class name transformed to snake case (and joining modules with '_')
98
+ instance.class.name.to_s.split('::').collect(&:underscore).join('_').to_sym
99
+ else
100
+ instance.config_key
101
+ end
108
102
 
109
103
  if application.plugins.key?(instance.config_key)
110
104
  used_in = application.plugins[config_key].class
@@ -128,8 +122,5 @@ module Praxis
128
122
  stage.run
129
123
  end
130
124
  end
131
-
132
125
  end
133
-
134
-
135
126
  end
@@ -1,21 +1,18 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  module BootloaderStages
4
-
5
5
  class Environment < Stage
6
-
7
6
  # require environment files. we will require 2 files:
8
7
  # 1) the environment.rb file - generic stuff for all environments
9
8
  # 2) "Deployer.environment".rb - environment specific stuff
10
9
  def execute
11
10
  setup_initial_config!
12
11
 
13
- env_file = application.root + "config/environment.rb"
14
- require env_file if File.exists? env_file
12
+ env_file = application.root / 'config/environment.rb'
13
+ require env_file if File.exist? env_file
15
14
 
16
- unless application.file_layout
17
- setup_default_layout!
18
- end
15
+ setup_default_layout! unless application.file_layout
19
16
  end
20
17
 
21
18
  def setup_default_layout!
@@ -52,8 +49,6 @@ module Praxis
52
49
  end
53
50
  end
54
51
  end
55
-
56
52
  end
57
-
58
53
  end
59
54
  end
@@ -1,10 +1,8 @@
1
- module Praxis
2
-
1
+ # frozen_string_literal: true
3
2
 
3
+ module Praxis
4
4
  module BootloaderStages
5
-
6
5
  class FileLoader < Stage
7
-
8
6
  attr_reader :path
9
7
 
10
8
  def initialize(name, application, path: nil)
@@ -16,6 +14,7 @@ module Praxis
16
14
  application.file_layout[*path].each do |file|
17
15
  next if application.loaded_files.include?(file)
18
16
  next unless file.extname == '.rb'
17
+
19
18
  require file
20
19
  application.loaded_files << file
21
20
  end
@@ -24,8 +23,6 @@ module Praxis
24
23
  def callback_args
25
24
  application.file_layout[*path]
26
25
  end
27
-
28
26
  end
29
27
  end
30
-
31
28
  end
@@ -1,21 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module BootloaderStages
3
5
  class PluginConfigLoad < Stage
4
-
5
6
  def execute
6
7
  application.plugins.each do |config_key, plugin|
7
8
  context = [plugin.class.name]
8
9
  value = plugin.load_config!
9
10
  object = plugin.config_attribute.load(value, context)
10
11
 
11
- if object
12
- application.config.send("#{config_key}=", object)
13
- end
12
+ application.config.send("#{config_key}=", object) if object
14
13
 
15
- plugin.config = application.config.send("#{config_key}")
14
+ plugin.config = application.config.send(config_key.to_s)
16
15
  end
17
16
  end
18
-
19
17
  end
20
18
  end
21
19
  end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module BootloaderStages
3
5
  class PluginConfigPrepare < Stage
4
-
5
6
  def execute
6
7
  application.plugins.each do |config_key, plugin|
7
8
  attribute = Attributor::Attribute.new(Attributor::Struct) {}
@@ -12,7 +13,6 @@ module Praxis
12
13
  application.config.class.attributes[config_key] = plugin.config_attribute
13
14
  end
14
15
  end
15
-
16
16
  end
17
17
  end
18
18
  end
@@ -1,19 +1,15 @@
1
- module Praxis
2
-
1
+ # frozen_string_literal: true
3
2
 
3
+ module Praxis
4
4
  module BootloaderStages
5
-
6
5
  class PluginLoader < Stage
7
-
8
- def initialize(name, context,**opts)
6
+ def initialize(name, context, **opts)
9
7
  super
10
8
 
11
9
  stages << PluginConfigPrepare.new(:prepare, context, parent: self)
12
10
  stages << PluginConfigLoad.new(:load, context, parent: self)
13
11
  stages << PluginSetup.new(:setup, context, parent: self)
14
12
  end
15
-
16
13
  end
17
-
18
14
  end
19
15
  end
@@ -1,13 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module BootloaderStages
3
5
  class PluginSetup < Stage
4
-
5
6
  def execute
6
- application.plugins.each do |config_key, plugin|
7
+ application.plugins.each do |_config_key, plugin|
7
8
  plugin.setup!
8
9
  end
9
10
  end
10
-
11
11
  end
12
12
  end
13
13
  end