axel 0.0.1

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 (128) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +21 -0
  3. data/.octopolo.yml +2 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +23 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +271 -0
  11. data/Rakefile +13 -0
  12. data/app/models/axel/api_proxy.rb +86 -0
  13. data/app/models/axel/associations/base.rb +64 -0
  14. data/app/models/axel/associations/belongs_to.rb +43 -0
  15. data/app/models/axel/associations/has_many.rb +29 -0
  16. data/app/models/axel/associations/has_one.rb +30 -0
  17. data/app/models/axel/payload.rb +4 -0
  18. data/app/models/axel/payload/base.rb +107 -0
  19. data/app/models/axel/payload/errors.rb +62 -0
  20. data/app/models/axel/payload/metadata.rb +57 -0
  21. data/app/models/axel/querier.rb +166 -0
  22. data/app/models/axel/router.rb +119 -0
  23. data/app/models/axel/service_resource.rb +4 -0
  24. data/app/models/axel/service_resource/associations.rb +80 -0
  25. data/app/models/axel/service_resource/attributes.rb +23 -0
  26. data/app/models/axel/service_resource/automatic_resource.rb +23 -0
  27. data/app/models/axel/service_resource/base.rb +47 -0
  28. data/app/models/axel/service_resource/builder.rb +40 -0
  29. data/app/models/axel/service_resource/inspects.rb +17 -0
  30. data/app/models/axel/service_resource/payload_parser.rb +46 -0
  31. data/app/models/axel/service_resource/queries.rb +25 -0
  32. data/app/models/axel/service_resource/requesters.rb +49 -0
  33. data/app/models/axel/service_resource/routes.rb +19 -0
  34. data/app/models/axel/service_resource/typhoid_extensions.rb +134 -0
  35. data/app/views/axel/base/empty.json.erb +0 -0
  36. data/app/views/axel/base/empty.xml.builder +0 -0
  37. data/app/views/layouts/axel.json.jbuilder +7 -0
  38. data/app/views/layouts/axel.xml.builder +12 -0
  39. data/axel.gemspec +42 -0
  40. data/lib/axel.rb +56 -0
  41. data/lib/axel/application_extensions.rb +13 -0
  42. data/lib/axel/application_helper.rb +27 -0
  43. data/lib/axel/base_controller.rb +6 -0
  44. data/lib/axel/cascadable_attribute.rb +33 -0
  45. data/lib/axel/configurations/resource.rb +29 -0
  46. data/lib/axel/configurations/service.rb +28 -0
  47. data/lib/axel/configurator.rb +54 -0
  48. data/lib/axel/configurators/services.rb +29 -0
  49. data/lib/axel/controller_base.rb +27 -0
  50. data/lib/axel/controller_helpers.rb +209 -0
  51. data/lib/axel/controller_parameters.rb +32 -0
  52. data/lib/axel/engine.rb +14 -0
  53. data/lib/axel/inspector.rb +91 -0
  54. data/lib/axel/payload/remote_error.rb +14 -0
  55. data/lib/axel/request_options.rb +26 -0
  56. data/lib/axel/uri.rb +47 -0
  57. data/lib/axel/version.rb +3 -0
  58. data/lib/generators/axel/install_generator.rb +16 -0
  59. data/lib/generators/templates/README.md +22 -0
  60. data/lib/generators/templates/axel.rb +81 -0
  61. data/script/rails +5 -0
  62. data/spec/controllers/pages_controller_spec.rb +217 -0
  63. data/spec/dummy/README.rdoc +261 -0
  64. data/spec/dummy/Rakefile +7 -0
  65. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  66. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  67. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  68. data/spec/dummy/app/controllers/pages_controller.rb +6 -0
  69. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  70. data/spec/dummy/app/mailers/.gitkeep +0 -0
  71. data/spec/dummy/app/models/.gitkeep +0 -0
  72. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  73. data/spec/dummy/config.ru +4 -0
  74. data/spec/dummy/config/application.rb +62 -0
  75. data/spec/dummy/config/boot.rb +10 -0
  76. data/spec/dummy/config/database.yml +25 -0
  77. data/spec/dummy/config/environment.rb +5 -0
  78. data/spec/dummy/config/environments/development.rb +37 -0
  79. data/spec/dummy/config/environments/production.rb +67 -0
  80. data/spec/dummy/config/environments/test.rb +37 -0
  81. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  82. data/spec/dummy/config/initializers/inflections.rb +15 -0
  83. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  84. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  85. data/spec/dummy/config/initializers/session_store.rb +8 -0
  86. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  87. data/spec/dummy/config/locales/en.yml +5 -0
  88. data/spec/dummy/config/routes.rb +3 -0
  89. data/spec/dummy/db/development.sqlite3 +0 -0
  90. data/spec/dummy/db/test.sqlite3 +0 -0
  91. data/spec/dummy/lib/assets/.gitkeep +0 -0
  92. data/spec/dummy/log/.gitignore +1 -0
  93. data/spec/dummy/log/.gitkeep +0 -0
  94. data/spec/dummy/public/404.html +26 -0
  95. data/spec/dummy/public/422.html +26 -0
  96. data/spec/dummy/public/500.html +25 -0
  97. data/spec/dummy/public/favicon.ico +0 -0
  98. data/spec/dummy/script/rails +6 -0
  99. data/spec/envelope_integration_check.rb +96 -0
  100. data/spec/helpers/axel/application_helper_spec.rb +31 -0
  101. data/spec/lib/axel/associations/base_spec.rb +28 -0
  102. data/spec/lib/axel/associations/belongs_to_spec.rb +62 -0
  103. data/spec/lib/axel/associations/has_many_spec.rb +23 -0
  104. data/spec/lib/axel/associations/has_one_spec.rb +23 -0
  105. data/spec/lib/axel/configurations/resource_spec.rb +15 -0
  106. data/spec/lib/axel/configurations/service_spec.rb +31 -0
  107. data/spec/lib/axel/configurator_spec.rb +26 -0
  108. data/spec/lib/axel/configurators/services_spec.rb +37 -0
  109. data/spec/lib/axel/controller_base_spec.rb +16 -0
  110. data/spec/lib/axel/controller_parameters_spec.rb +31 -0
  111. data/spec/lib/axel/inspector_spec.rb +45 -0
  112. data/spec/lib/axel/request_options_spec.rb +50 -0
  113. data/spec/lib/axel/uri_spec.rb +42 -0
  114. data/spec/lib/axel_spec.rb +64 -0
  115. data/spec/models/axel/api_proxy_spec.rb +66 -0
  116. data/spec/models/axel/payload/errors_spec.rb +165 -0
  117. data/spec/models/axel/payload/metadata_spec.rb +141 -0
  118. data/spec/models/axel/querier_spec.rb +158 -0
  119. data/spec/models/axel/router_spec.rb +115 -0
  120. data/spec/models/axel/service_resource/base_spec.rb +244 -0
  121. data/spec/models/axel/service_resource/builder_spec.rb +64 -0
  122. data/spec/models/axel/service_resource/payload_parser_spec.rb +60 -0
  123. data/spec/spec_helper.rb +39 -0
  124. data/spec/support/address.rb +5 -0
  125. data/spec/support/persona.rb +15 -0
  126. data/spec/support/user.rb +6 -0
  127. data/spec/views/axel/base/empty_spec.rb +34 -0
  128. metadata +508 -0
@@ -0,0 +1,29 @@
1
+ require 'axel/configurations/service'
2
+ module Axel
3
+ module Configurators
4
+ class Services
5
+ attr_reader :services
6
+ def initialize
7
+ @services = {}.with_indifferent_access
8
+ end
9
+
10
+ def resources
11
+ services.values.collect(&:resources).inject({}.with_indifferent_access) { |hash, pair| hash.merge pair }
12
+ end
13
+
14
+ def add_service(service_name, url)
15
+ if services[service_name]
16
+ services[service_name].url = url if url
17
+ services[service_name]
18
+ else
19
+ services[service_name] = Configurations::Service.new(service_name, url)
20
+ end
21
+ end
22
+
23
+ def add_resource(service_name, resource_name, options = {})
24
+ service = add_service service_name, (options.delete(:service) || {})[:url]
25
+ service.add_resource resource_name, options
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ module Axel
2
+ class ControllerBase
3
+ def configured
4
+ should_use_api? ? api_base : proper_base
5
+ end
6
+
7
+ def should_use_api?
8
+ config.uses_rails_api? && !!api_base
9
+ rescue NameError
10
+ false
11
+ end
12
+
13
+ private
14
+
15
+ def api_base
16
+ ::ActionController::API
17
+ end
18
+
19
+ def proper_base
20
+ ::ActionController::Base
21
+ end
22
+
23
+ def config
24
+ Axel._config
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,209 @@
1
+ module Axel
2
+ RecordNotFound = Class.new StandardError
3
+ module ControllerHelpers
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ if respond_to?(:helper_method)
7
+ helper_method :errors,
8
+ :metadata,
9
+ :header_status,
10
+ :drop_meta?,
11
+ :format,
12
+ :render_nil_format,
13
+ :safe_json_load,
14
+ :xml_clean
15
+ end
16
+
17
+ delegate :header_status,
18
+ to: :errors
19
+
20
+ if respond_to?(:layout) && !ControllerBase.new.should_use_api?
21
+ layout "axel"
22
+ end
23
+
24
+ if respond_to?(:rescue_from)
25
+ rescue_from ForceSSL do |e|
26
+ drop_meta!
27
+ rescue_error status: :forbidden, message: "SSL is required"
28
+ end
29
+
30
+ rescue_from NotAuthorized do |e|
31
+ rescue_error status: 401, message: "User not authorized"
32
+ end
33
+
34
+ rescue_from Axel::RecordNotFound do |e|
35
+ rescue_error status: 404, message: "Record not found"
36
+ end
37
+
38
+ if defined?(ActiveRecord::RecordNotFound)
39
+ rescue_from ActiveRecord::RecordNotFound do |e|
40
+ rescue_error status: 404, message: "Record not found"
41
+ end
42
+ end
43
+
44
+ if defined?(ActiveModel::MassAssignmentSecurity::Error)
45
+ rescue_from ActiveModel::MassAssignmentSecurity::Error do |e|
46
+ rescue_error status: 422, message: "Unacceptable parameter being used"
47
+ end
48
+ end
49
+
50
+ if defined?(ActionController::UnknownFormat)
51
+ rescue_from ActionController::UnknownFormat do |e|
52
+ render nothing: true, status: 406, message: "Unknown Format"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ module ClassMethods
59
+ def respond_to_json_xml
60
+ respond_to :json, :xml
61
+ end
62
+ end
63
+
64
+ # Public: Quick access to a set of errors we're tracking
65
+ # while dealing with a request
66
+ #
67
+ # Example:
68
+ #
69
+ # errors
70
+ # # => <# Errors ...>
71
+ #
72
+ # Return an object for recording errors over a request
73
+ def errors
74
+ @errors ||= Payload::Errors.new
75
+ end
76
+
77
+ def metadata
78
+ @metadata ||= Payload::Metadata.new
79
+ end
80
+
81
+ # Default API response when an error occurs
82
+ def rescue_error(options = {})
83
+ messages = ([options[:messages]] + [options[:message]]).flatten.compact
84
+ errors.new_error options[:status], *messages
85
+ respond_to_empty
86
+ end
87
+
88
+ # Public: Use for weird actions like #update where we don't want
89
+ # to create a whole rabl template for it. This will allow the format
90
+ # to fixed correctly and render a different action for you.
91
+ #
92
+ # action - used to pick the template we're now rendering
93
+ #
94
+ # Example:
95
+ #
96
+ # respond_with_action :show
97
+ #
98
+ def respond_with_action(action)
99
+ respond_with do |f|
100
+ f.json { render action: action }
101
+ f.xml { render action: action }
102
+ end
103
+ end
104
+ alias render_action respond_with_action
105
+
106
+ # Public: Use to respond with an empty template. Especially useful for
107
+ # manipulative controller actions where you don't want to have a new view,
108
+ # but you DO want to return our "envelope"
109
+ def respond_to_empty
110
+ respond_to do |f|
111
+ f.json { render nothing: true, layout: "axel", status: header_status }
112
+ f.xml { render nothing: true, layout: "axel", status: header_status }
113
+ end
114
+ end
115
+ alias render_empty respond_to_empty
116
+
117
+ # Public: Use as a before filter. Will find a resource with some automation.
118
+ # Find based on params.
119
+ #
120
+ # options - Hash of options for tweaking
121
+ # :finder - Column being used for the select (:id, :user_name)
122
+ # :value - The value the column should be (an ID or Name value)
123
+ #
124
+ # Example:
125
+ #
126
+ # PersonasController#find_resource # (with params[:id] => 1)
127
+ # # => @persona # => <# Persona id: 1 #>
128
+ #
129
+ # PersonasController#find_resource(finder: :user_id) # (with params[:user_id] => 1)
130
+ # # => @persona # => <# Persona user_id: 1 #>
131
+ #
132
+ # PersonasController#find_resource(finder: :user_id, value: 2) # (with params[:user_id] => 1)
133
+ # # => @persona # => <# Persona user_id: 2 #>
134
+ #
135
+ # Return the value of the instance variable we just set
136
+ def find_resource(options = {})
137
+ resource_name = controller_name.singularize
138
+ resource = controller_name.classify.constantize
139
+ finder_column = options[:finder] || :id
140
+ finder_value = options[:value] || params[:id]
141
+ resources = resource.where(finder_column => finder_value)
142
+ if resources.length == 0
143
+ raise RecordNotFound
144
+ else
145
+ instance_variable_set "@#{resource_name}", resources.first
146
+ end
147
+ end
148
+
149
+ def query_params
150
+ try_strong_params request.GET
151
+ end
152
+
153
+ def post_params
154
+ try_strong_params request.POST
155
+ end
156
+
157
+ def object_params
158
+ try_strong_params post_params.fetch(object_name, post_params)
159
+ end
160
+
161
+ def try_strong_params(regular_params)
162
+ ControllerParameters.new(regular_params).params_object
163
+ end
164
+
165
+ def object_name
166
+ controller_name.singularize
167
+ end
168
+
169
+ def force_ssl!
170
+ return true unless Rails::Application.productionish?
171
+ if !request.ssl?
172
+ raise ForceSSL
173
+ end
174
+ end
175
+
176
+ def drop_meta!
177
+ metadata.drop!
178
+ end
179
+
180
+ def drop_meta?
181
+ metadata.drop?
182
+ end
183
+
184
+ def format
185
+ params[:format] || :json
186
+ end
187
+
188
+ def render_nil_format
189
+ {
190
+ json: nil,
191
+ xml: ""
192
+ }.with_indifferent_access[format]
193
+ end
194
+
195
+ def rabl_render(object, view)
196
+ Rabl.render(object, view, view_path: 'app/views', scope: self)
197
+ end
198
+
199
+ def safe_json_load(json)
200
+ json.present? ? MultiJson.load(json, mode: :null) : nil
201
+ end
202
+
203
+ def xml_clean(payload)
204
+ payload.gsub(/\<(\/)*hash\>\s{1}/, '').
205
+ gsub(/<\?\W*([xX][mM][lL])\W*version.*encoding.*\?>/, "").
206
+ strip
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,32 @@
1
+ module Axel
2
+ class ControllerParameters
3
+ private
4
+ attr_accessor :controller_params
5
+
6
+ public
7
+
8
+ def initialize(controller_params)
9
+ self.controller_params = controller_params
10
+ end
11
+
12
+ def params_object
13
+ strong_params? ? params_class.new(controller_params) : controller_params
14
+ end
15
+
16
+ def params_class # testability
17
+ ActionController::Parameters
18
+ end
19
+ private :params_class
20
+
21
+ def strong_module
22
+ ActionController::StrongParameters
23
+ end
24
+
25
+ def strong_params?
26
+ !!(params_class && strong_module)
27
+ rescue
28
+ false
29
+ end
30
+ private :strong_params?
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Axel
2
+ class Engine < ::Rails::Engine
3
+ if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR > 0
4
+ isolate_namespace Axel
5
+ end
6
+ config.railties_order = [self, :main_app, :all]
7
+
8
+ initializer 'axel.application_helper' do |app|
9
+ ActiveSupport.on_load :action_controller do
10
+ helper Axel::ApplicationHelper
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,91 @@
1
+ module Axel
2
+ class Inspector
3
+ private
4
+ attr_writer :object
5
+ attr_writer :parens_params
6
+ attr_writer :attributes
7
+
8
+ public
9
+ attr_reader :object
10
+ attr_reader :parens_params
11
+ attr_reader :attributes
12
+
13
+ def initialize(object, parens_params = [], attributes = [])
14
+ self.object = object
15
+ self.parens_params = hasherize parens_params
16
+ self.attributes = hasherize attributes
17
+ end
18
+
19
+ def inspect
20
+ class_name.tap do |string|
21
+ string << "(#{display_parens})" unless parens_params.empty?
22
+ string << " #{display_attributes}" unless attributes.empty?
23
+ unless class?
24
+ string.prepend "#<"
25
+ string << ">"
26
+ end
27
+ end
28
+ end
29
+
30
+ def class_name
31
+ class? ? object.name : object.class.name
32
+ end
33
+ private :class_name
34
+
35
+ def class?
36
+ object.is_a?(Class)
37
+ end
38
+
39
+ def display_parens
40
+ display parens_params
41
+ end
42
+ private :display_parens
43
+
44
+ def display_attributes
45
+ display attributes, :show_keys
46
+ end
47
+ private :display_attributes
48
+
49
+ def display(array, show_keys = false)
50
+ array.collect { |key,value|
51
+ show_keys ? "#{key}: #{attribute_for_inspect(value)}" : attribute_for_inspect(value)
52
+ }.join(", ")
53
+ end
54
+ private :display
55
+
56
+ def attribute_for_inspect(value)
57
+ if value.is_a?(String) && value.length > 50
58
+ "#{value[0..50]}...".inspect
59
+ elsif value.is_a?(Date) || value.is_a?(Time)
60
+ %("#{value}")
61
+ else
62
+ value.inspect
63
+ end
64
+ end
65
+ private :attribute_for_inspect
66
+
67
+ def safe_send(key)
68
+ object.public_send(key)
69
+ rescue
70
+ nil
71
+ end
72
+ private :safe_send
73
+
74
+ def hasherize(params)
75
+ if params.is_a? Hash
76
+ params
77
+ elsif params.is_a? Array
78
+ Hash[params.map { |key|
79
+ begin
80
+ [key, safe_send(key)] if object.respond_to? key
81
+ rescue
82
+ [key, "<Error Collecting>"]
83
+ end
84
+ }.compact]
85
+ else
86
+ {}
87
+ end
88
+ end
89
+ private :hasherize
90
+ end
91
+ end
@@ -0,0 +1,14 @@
1
+ module Axel
2
+ module Payload
3
+ class RemoteError < StandardError
4
+ attr_reader :remote_errors
5
+ def initialize(remote_errors)
6
+ @remote_errors = remote_errors
7
+ end
8
+
9
+ def to_s
10
+ "Failed. HTTP Status: #{remote_errors.status_code}, Messages: #{@remote_errors.messages.join('. ')}"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'active_support/core_ext/hash/deep_merge'
3
+ module Axel
4
+ class RequestOptions
5
+ attr_accessor :given_options
6
+ attr_accessor :default_options
7
+
8
+ def initialize(default_options = {}, given_options)
9
+ self.default_options = (default_options || {})
10
+ self.given_options = (given_options || {}).with_indifferent_access
11
+ end
12
+
13
+ def compiled
14
+ default_request_options.
15
+ dup.
16
+ deep_merge!(default_options).
17
+ deep_merge!(given_options).
18
+ with_indifferent_access
19
+ end
20
+
21
+ def default_request_options
22
+ { headers: { 'Content-Type' => 'application/json' } }.with_indifferent_access
23
+ end
24
+ private :default_request_options
25
+ end
26
+ end