conduit 0.2.6

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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +22 -0
  3. data/app/controllers/conduit/responses_controller.rb +14 -0
  4. data/app/models/conduit/concerns/storage.rb +41 -0
  5. data/app/models/conduit/request.rb +109 -0
  6. data/app/models/conduit/response.rb +50 -0
  7. data/app/models/conduit/subscription.rb +6 -0
  8. data/config/routes.rb +3 -0
  9. data/db/migrate/20131209223122_create_conduit_requests.rb +13 -0
  10. data/db/migrate/20131209223734_create_conduit_responses.rb +10 -0
  11. data/db/migrate/20140305202729_create_conduit_subscriptions.rb +12 -0
  12. data/lib/conduit.rb +60 -0
  13. data/lib/conduit/acts_as_conduit_subscriber.rb +41 -0
  14. data/lib/conduit/configuration.rb +43 -0
  15. data/lib/conduit/core/action.rb +136 -0
  16. data/lib/conduit/core/connection.rb +85 -0
  17. data/lib/conduit/core/driver.rb +83 -0
  18. data/lib/conduit/core/parser.rb +55 -0
  19. data/lib/conduit/core/render.rb +69 -0
  20. data/lib/conduit/drivers/keep +0 -0
  21. data/lib/conduit/engine.rb +20 -0
  22. data/lib/conduit/storage.rb +37 -0
  23. data/lib/conduit/storage/aws.rb +94 -0
  24. data/lib/conduit/storage/file.rb +74 -0
  25. data/lib/conduit/util.rb +17 -0
  26. data/lib/conduit/version.rb +3 -0
  27. data/lib/tasks/conduit_tasks.rake +4 -0
  28. data/spec/classes/acts_as_conduit_subscriber_spec.rb +61 -0
  29. data/spec/classes/core/action_spec.rb +53 -0
  30. data/spec/classes/core/driver_spec.rb +24 -0
  31. data/spec/controllers/responses_controller_spec.rb +29 -0
  32. data/spec/dummy/README.rdoc +28 -0
  33. data/spec/dummy/Rakefile +6 -0
  34. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  35. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  36. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  37. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  38. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  39. data/spec/dummy/bin/bundle +3 -0
  40. data/spec/dummy/bin/rails +4 -0
  41. data/spec/dummy/bin/rake +4 -0
  42. data/spec/dummy/config.ru +4 -0
  43. data/spec/dummy/config/application.rb +29 -0
  44. data/spec/dummy/config/boot.rb +5 -0
  45. data/spec/dummy/config/database.yml +20 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +29 -0
  48. data/spec/dummy/config/environments/production.rb +80 -0
  49. data/spec/dummy/config/environments/test.rb +36 -0
  50. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  51. data/spec/dummy/config/initializers/conduit.rb +5 -0
  52. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  53. data/spec/dummy/config/initializers/inflections.rb +16 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  56. data/spec/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +23 -0
  59. data/spec/dummy/config/routes.rb +3 -0
  60. data/spec/dummy/db/schema.rb +46 -0
  61. data/spec/dummy/lib/conduit/drivers/my_driver/actions/foo.rb +10 -0
  62. data/spec/dummy/lib/conduit/drivers/my_driver/driver.rb +7 -0
  63. data/spec/dummy/lib/conduit/drivers/my_driver/parsers/foo.rb +13 -0
  64. data/spec/dummy/lib/conduit/drivers/my_driver/views/foo.erb +5 -0
  65. data/spec/dummy/lib/conduit/drivers/my_driver/views/layout.erb +3 -0
  66. data/spec/dummy/log/development.log +0 -0
  67. data/spec/dummy/log/test.log +9707 -0
  68. data/spec/dummy/public/404.html +58 -0
  69. data/spec/dummy/public/422.html +58 -0
  70. data/spec/dummy/public/500.html +57 -0
  71. data/spec/dummy/tmp/conduit/1/my_driver/foo/1-response.xml +3 -0
  72. data/spec/dummy/tmp/conduit/1/my_driver/foo/request.xml +7 -0
  73. data/spec/models/conduit/request_spec.rb +50 -0
  74. data/spec/models/conduit/response_spec.rb +52 -0
  75. data/spec/spec_helper.rb +36 -0
  76. data/spec/support/helper.rb +15 -0
  77. data/spec/support/xml/xml_request.xml +7 -0
  78. data/spec/support/xml/xml_response.xml +3 -0
  79. metadata +311 -0
@@ -0,0 +1,136 @@
1
+ #
2
+ # The job of this class is to provide common
3
+ # functionality to a action class.
4
+ #
5
+ # e.g.
6
+ # => class MyAction < Conduit::Core::Action
7
+ # => required_attributes :foo, :bar
8
+ # => optional_attributes :baz
9
+ # => end
10
+ #
11
+ # => action = MyAction.new(foo: 'foo', bar: 'bar', baz: 'baz')
12
+ # => action.perform
13
+ #
14
+
15
+ require 'set'
16
+
17
+ module Conduit
18
+ module Core
19
+ class Action
20
+
21
+ def self.inherited(base)
22
+ base.send :include, Conduit::Core::Connection
23
+ base.send :include, Conduit::Core::Render
24
+ base.send :include, InstanceMethods
25
+ base.extend ClassMethods
26
+
27
+ # TODO: Move this to the driver scope
28
+ # which allows for setting this
29
+ # "globally" for the driver.
30
+ #
31
+ path = caller.first[/^[^:]+/]
32
+ define_method(:action_path) do
33
+ File.dirname(path)
34
+ end
35
+ end
36
+
37
+ module ClassMethods
38
+
39
+ attr_accessor :_action_path
40
+
41
+ # Set required attributes
42
+ #
43
+ # e.g.
44
+ # => required_attributes :foo, :bar, :baz
45
+ #
46
+ def required_attributes(*args)
47
+ requirements.merge(args)
48
+ attributes.merge(args)
49
+ end
50
+
51
+ # Set optional attributes
52
+ #
53
+ # e.g.
54
+ # => optional_attributes :foo, :bar, :baz
55
+ #
56
+ def optional_attributes(*args)
57
+ attributes.merge(args)
58
+ end
59
+
60
+ # Storage array for required attributes
61
+ #
62
+ def requirements
63
+ @requirements ||= Set.new
64
+ end
65
+
66
+ # Storage array for all attributes
67
+ #
68
+ def attributes
69
+ @attributes ||= Set.new
70
+ end
71
+
72
+ end
73
+
74
+ module InstanceMethods
75
+
76
+ delegate :requirements, :attributes, to: :class
77
+
78
+ def initialize(**options)
79
+ @options = options
80
+ validate!(options)
81
+ end
82
+
83
+ # Object used for passing data to the view
84
+ # Only keys listed in attributes will be
85
+ # used.
86
+ #
87
+ def view_context
88
+ OpenStruct.new(@options.select do |k,v|
89
+ attributes.include?(k)
90
+ end)
91
+ end
92
+
93
+ # Location where the view files can be found
94
+ # Default to lib/conduit/drivers/<drivername>/views
95
+ # Can be overriden per class.
96
+ #
97
+ def view_path
98
+ File.join(File.dirname(action_path), 'views')
99
+ end
100
+
101
+ # Return the rendered view
102
+ #
103
+ def view
104
+ tpl = self.class.name.demodulize
105
+ .underscore.downcase
106
+ render(tpl)
107
+ end
108
+
109
+ # Entry method. The class will use
110
+ # use this to trigger the request.
111
+ #
112
+ # Override to customize.
113
+ #
114
+ def perform
115
+ request(body: view, method: :post)
116
+ end
117
+
118
+ private
119
+
120
+ # Ensures that all required attributes are present
121
+ # If not all attributes are present, will raise
122
+ # an ArgumentError listing missing attributes
123
+ #
124
+ def validate!(options)
125
+ missing_keys = (requirements.to_a - options.keys)
126
+ if missing_keys.any?
127
+ raise ArgumentError,
128
+ "Missing keys: #{missing_keys.join(', ')}"
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,85 @@
1
+ #
2
+ # The job of this module is to handle network
3
+ # communication rom Conduit to a remote host.
4
+ #
5
+
6
+ require 'conduit/version'
7
+ require 'excon'
8
+
9
+ module Conduit
10
+ module Core
11
+ module Connection
12
+
13
+ def self.included(base)
14
+ base.extend ClassMethods
15
+ base.send :include, InstanceMethods
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ # Define a remote_url
21
+ #
22
+ # e.g.
23
+ # remote_url 'http://myapi.com/endpoint'
24
+ #
25
+ def remote_url(host=nil)
26
+ @remote_url ||= host
27
+ end
28
+
29
+ end
30
+
31
+ module InstanceMethods
32
+
33
+ delegate :remote_url, to: :class
34
+
35
+ # Make a request
36
+ #
37
+ # @param [Hash] params
38
+ # @option params [String] :body text to be sent over a socket
39
+ # @option params [Hash<Symbol, String>] :headers The default headers to supply in a request
40
+ # @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
41
+ # @option params [String] :path appears after 'scheme://host:port/'
42
+ # @option params [Fixnum] :port The port on which to connect, to the destination host
43
+ # @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
44
+ # @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
45
+ # @option params [Proc] :response_block
46
+ #
47
+ #
48
+ def request(params, &block)
49
+ params[:headers] ||= {}
50
+ params[:headers]['User-Agent'] ||= "conduit/#{Conduit::VERSION}"
51
+ connection.request(params, &block)
52
+ end
53
+
54
+ private
55
+
56
+ # Connection that will be used
57
+ #
58
+ # @param [Hash] params
59
+ # @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
60
+ # @option params [Hash<Symbol, String>] :headers The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request
61
+ # @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
62
+ # @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
63
+ # @option params [Fixnum] :port The port on which to connect, to the destination host
64
+ # @option params [Hash] :query Default query; appended to the 'scheme://host:port/path/' in the form of '?key=value'. Will only be used if params[:query] is not supplied to Connection#request
65
+ # @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
66
+ # @option params [String] :proxy Proxy server; e.g. 'http://myproxy.com:8888'
67
+ # @option params [Fixnum] :retry_limit Set how many times we'll retry a failed request. (Default 4)
68
+ # @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
69
+ # @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
70
+ #
71
+ # @return [Excon::Response]
72
+ #
73
+ # @raise [Excon::Errors::StubNotFound]
74
+ # @raise [Excon::Errors::Timeout]
75
+ # @raise [Excon::Errors::SocketError]
76
+ #
77
+ def connection(**params)
78
+ @excon ||= Excon.new(remote_url, params)
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,83 @@
1
+ #
2
+ # The job of this class is to require all
3
+ # actions belonging to this driver
4
+ #
5
+ # e.g.
6
+ # => module Conduit::Driver
7
+ # => class MyDriver < Conduit::Core::Driver
8
+ # => required_credentials :foo, :bar, :baz
9
+ # =>
10
+ # => action :purchase
11
+ # => action :activate
12
+ # => action :suspend
13
+ # =>
14
+ # => end
15
+ # => end
16
+ #
17
+
18
+ require 'set'
19
+
20
+ module Conduit
21
+ module Core
22
+ module Driver
23
+
24
+ def self.extended(base)
25
+ base.instance_variable_set("@_driver_path",
26
+ File.dirname(caller.first[/^[^:]+/]))
27
+ end
28
+
29
+ # Set required credentials
30
+ #
31
+ # e.g.
32
+ # => required_credentials :foo, :bar, :baz
33
+ #
34
+ def required_credentials(*args)
35
+ credentials.merge(args)
36
+ end
37
+
38
+ # Set available actions
39
+ #
40
+ # e.g.
41
+ # => action :purchase
42
+ #
43
+ def action(action_name)
44
+ require File.join(@_driver_path, 'actions', action_name.to_s)
45
+ require File.join(@_driver_path, 'parsers', action_name.to_s)
46
+ actions << action_name
47
+ end
48
+
49
+ # Storage array for required credentials
50
+ #
51
+ # e.g.
52
+ # Conduit::Driver::Fusion.credentials
53
+ # => [:foo, :bar, :baz]
54
+ #
55
+ def credentials
56
+ @credentials ||= Set.new
57
+ end
58
+
59
+ # Storage array for required credentials
60
+ #
61
+ # e.g.
62
+ # Conduit::Driver::Fusion.actions
63
+ # => [:purchase]
64
+ #
65
+ def actions
66
+ @actions ||= Set.new
67
+ end
68
+
69
+ private
70
+
71
+ # Return the name of the driver
72
+ #
73
+ # e.g.
74
+ # Conduit::Drivers::Fusion.name
75
+ # => "fusion"
76
+ #
77
+ def driver_name
78
+ self.name.demodulize.underscore.downcase
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,55 @@
1
+ #
2
+ # The job of this class is parse a raw response
3
+ # and provide parsed attributes that can be
4
+ # predictably consumed.
5
+ #
6
+ module Conduit
7
+ module Core
8
+ class Parser
9
+
10
+ class << self
11
+
12
+ # Define an attribute that will be publically exposed
13
+ # when dealing with conduit responses.
14
+ #
15
+ def attribute(attr_name, &block)
16
+ attributes << attr_name
17
+ define_method(attr_name, &block)
18
+ end
19
+
20
+ # Storage array for all attributes
21
+ #
22
+ def attributes
23
+ @attributes ||= []
24
+ end
25
+
26
+ end
27
+
28
+ delegate :attributes, to: :class
29
+
30
+ # Returns a hash representation of each attribute
31
+ # defined in a parser and its value.
32
+ #
33
+ def serializable_hash
34
+ attributes.inject({}) do |hash, attribute|
35
+ hash.tap { |h| h[attribute] = send(attribute) }
36
+ end
37
+ end
38
+
39
+ # Default response status.
40
+ # Should be overwritten by parser implementation.
41
+ #
42
+ def response_status
43
+ raise NoMethodError, "Please define response_status in your parser."
44
+ end
45
+
46
+ # Default response error container.
47
+ # Should be overwritten by parser implementation.
48
+ #
49
+ def response_errors
50
+ raise NoMethodError, "Please define response_errors in your parser."
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,69 @@
1
+ #
2
+ # The job of this module is to provide template
3
+ # rendering functionality. Included classes
4
+ # must provide a view_path, and view_context
5
+ # methods to be of any use.
6
+ #
7
+
8
+ require 'tilt'
9
+
10
+ module Conduit
11
+ module Core
12
+ module Render
13
+
14
+ # Create instance variables, any of these can
15
+ # be overriden within the including class.
16
+ #
17
+ # view_path: Location where the view files are stored
18
+ # view_context: Object that contains the variables used in the template
19
+ #
20
+ def self.included(base)
21
+ attr_accessor :view_path, :view_context
22
+ end
23
+
24
+ # Render a template file
25
+ #
26
+ # e.g. Without layout
27
+ # => render :purchase, layout: false
28
+ #
29
+ # e.g. With layout
30
+ # => render :purchase
31
+ #
32
+ def render(file, layout: true)
33
+ raise ViewPathNotDefined, "" unless view_path
34
+ layout ? render_with_layout(file) : render_template(file)
35
+ end
36
+
37
+ # Render a template file
38
+ #
39
+ # e.g. Without layout
40
+ # => render_template(:template)
41
+ #
42
+ # e.g. With layout
43
+ # => render_template(:layout) do
44
+ # => render_template(:template)
45
+ # => end
46
+ #
47
+ def render_template(file)
48
+ path = File.join(view_path, "#{file}.erb")
49
+ Tilt::ERBTemplate.new(path).render(view_context) do
50
+ yield if block_given?
51
+ end
52
+ end
53
+
54
+ # Render the file with a layout
55
+ #
56
+ # e.g.
57
+ # => render_layout(:template)
58
+ #
59
+ def render_with_layout(file)
60
+ render_template(:layout) do
61
+ render_template(file)
62
+ end
63
+ end
64
+
65
+ class ViewPathNotDefined < StandardError; end
66
+
67
+ end
68
+ end
69
+ end
File without changes
@@ -0,0 +1,20 @@
1
+ module Conduit
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Conduit
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, :fixture => false
7
+ g.assets false
8
+ g.helper false
9
+ end
10
+
11
+ initializer "conduit.load_app_root", before: :load_config_initializers do |app|
12
+ Conduit.app_root = app.root
13
+ end
14
+
15
+ initializer 'conduit.load_drivers', after: :load_config_initializers do |app|
16
+ Conduit::Driver.load_drivers
17
+ end
18
+
19
+ end
20
+ end