axel 0.0.1

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