proxy_rb 0.4.0 → 0.5.0

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -4
  3. data/Gemfile +2 -2
  4. data/History.md +17 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +15 -4
  7. data/Rakefile +8 -2
  8. data/exe/proxy_rb +8 -0
  9. data/fixtures/empty/README.md +18 -0
  10. data/fixtures/empty/Rakefile +10 -0
  11. data/lib/proxy_rb/announcer.rb +162 -0
  12. data/lib/proxy_rb/{rspec/helpers → api}/.keep +0 -0
  13. data/lib/proxy_rb/api/core.rb +25 -0
  14. data/lib/proxy_rb/api/http_proxy.rb +94 -0
  15. data/lib/proxy_rb/api/passwords.rb +26 -0
  16. data/lib/proxy_rb/api.rb +19 -0
  17. data/lib/proxy_rb/basic_configuration/{in_config_wrapper.rb → in_configuration_wrapper.rb} +1 -1
  18. data/lib/proxy_rb/basic_configuration.rb +2 -2
  19. data/lib/proxy_rb/cli.rb +27 -0
  20. data/lib/proxy_rb/colorizer.rb +113 -0
  21. data/lib/proxy_rb/configuration.rb +1 -1
  22. data/lib/proxy_rb/configuration_wrapper.rb +59 -0
  23. data/lib/proxy_rb/console/help.rb +29 -0
  24. data/lib/proxy_rb/console.rb +62 -0
  25. data/lib/proxy_rb/credentials.rb +11 -0
  26. data/lib/proxy_rb/drivers/poltergeist_driver.rb +8 -2
  27. data/lib/proxy_rb/drivers/selenium_driver.rb +7 -1
  28. data/lib/proxy_rb/drivers/webkit_driver.rb +10 -1
  29. data/lib/proxy_rb/errors.rb +12 -0
  30. data/lib/proxy_rb/event_bus/name_resolver.rb +163 -0
  31. data/lib/proxy_rb/event_bus.rb +60 -0
  32. data/lib/proxy_rb/events.rb +34 -0
  33. data/lib/proxy_rb/http_proxy.rb +8 -0
  34. data/lib/proxy_rb/initializer.rb +132 -0
  35. data/lib/proxy_rb/{rspec/matchers → matchers}/.keep +0 -0
  36. data/lib/proxy_rb/matchers/be_forbidden.rb +14 -0
  37. data/lib/proxy_rb/matchers/be_successful.rb +14 -0
  38. data/lib/proxy_rb/matchers/have_mime_type.rb +9 -0
  39. data/lib/proxy_rb/password_fetchers/basic_password_fetcher.rb +25 -0
  40. data/lib/proxy_rb/password_fetchers/chaining_password_fetcher.rb +54 -0
  41. data/lib/proxy_rb/password_fetchers/environment_password_fetcher.rb +7 -1
  42. data/lib/proxy_rb/password_fetchers/vault_password_fetcher.rb +7 -1
  43. data/lib/proxy_rb/proxy_url.rb +8 -0
  44. data/lib/proxy_rb/request.rb +5 -21
  45. data/lib/proxy_rb/resource.rb +11 -7
  46. data/lib/proxy_rb/response.rb +5 -13
  47. data/lib/proxy_rb/rspec.rb +32 -13
  48. data/lib/proxy_rb/runtime.rb +49 -0
  49. data/lib/proxy_rb/setup.rb +60 -0
  50. data/lib/proxy_rb/version.rb +1 -1
  51. data/proxy_rb.gemspec +2 -2
  52. metadata +38 -29
  53. data/lib/proxy_rb/rspec/helpers/http_proxy.rb +0 -83
  54. data/lib/proxy_rb/rspec/helpers/passwords.rb +0 -29
  55. data/lib/proxy_rb/rspec/shared_contexts/.keep +0 -0
  56. data/lib/proxy_rb/rspec/shared_examples/.keep +0 -0
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # The ANSIColor module can be used for namespacing and mixed into your own
5
+ # classes.
6
+ module AnsiColor
7
+ # :stopdoc:
8
+ ATTRIBUTES = [
9
+ [:clear, 0],
10
+ [:reset, 0], # synonym for :clear
11
+ [:bold, 1],
12
+ [:dark, 2],
13
+ [:italic, 3], # not widely implemented
14
+ [:underline, 4],
15
+ [:underscore, 4], # synonym for :underline
16
+ [:blink, 5],
17
+ [:rapid_blink, 6], # not widely implemented
18
+ [:negative, 7], # no reverse because of String#reverse
19
+ [:concealed, 8],
20
+ [:strikethrough, 9], # not widely implemented
21
+ [:black, 30],
22
+ [:red, 31],
23
+ [:green, 32],
24
+ [:yellow, 33],
25
+ [:blue, 34],
26
+ [:magenta, 35],
27
+ [:cyan, 36],
28
+ [:white, 37],
29
+ [:on_black, 40],
30
+ [:on_red, 41],
31
+ [:on_green, 42],
32
+ [:on_yellow, 43],
33
+ [:on_blue, 44],
34
+ [:on_magenta, 45],
35
+ [:on_cyan, 46],
36
+ [:on_white, 47]
37
+ ].freeze
38
+
39
+ ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
40
+ # :startdoc:
41
+
42
+ # Returns true, if the coloring function of this module
43
+ # is switched on, false otherwise.
44
+ def self.coloring?
45
+ @coloring
46
+ end
47
+
48
+ # Turns the coloring on or off globally, so you can easily do
49
+ # this for example:
50
+ # ProxyRb::Colorizer.coloring = STDOUT.isatty
51
+ def self.coloring=(val)
52
+ @coloring = val
53
+ end
54
+ self.coloring = true
55
+
56
+ ATTRIBUTES.each do |c, v|
57
+ define_method(c) do |string|
58
+ result = []
59
+ result << "\e[#{v}m" if ProxyRb::AnsiColor.coloring?
60
+ if block_given?
61
+ result << yield
62
+ elsif string
63
+ result << string
64
+ elsif respond_to?(:to_str)
65
+ result << to_str
66
+ else
67
+ return result # only switch on
68
+ end
69
+ result << "\e[0m" if ProxyRb::AnsiColor.coloring?
70
+
71
+ result.join
72
+ end
73
+ end
74
+
75
+ # Regular expression that is used to scan for ANSI-sequences while
76
+ # uncoloring strings.
77
+ COLORED_REGEXP = /\e\[(?:[34][0-7]|[0-9])?m/
78
+
79
+ def self.included(klass)
80
+ if klass == String
81
+ ATTRIBUTES.delete(:clear)
82
+ ATTRIBUTE_NAMES.delete(:clear)
83
+ end
84
+ end
85
+
86
+ # Returns an uncolored version of the string, that is all
87
+ # ANSI-sequences are stripped from the string.
88
+ def uncolored(string = nil) # :yields:
89
+ if block_given?
90
+ yield.gsub(COLORED_REGEXP, '')
91
+ elsif string
92
+ string.gsub(COLORED_REGEXP, '')
93
+ elsif respond_to?(:to_str)
94
+ to_str.gsub(COLORED_REGEXP, '')
95
+ else
96
+ ''
97
+ end
98
+ end
99
+
100
+ # Returns an array of all ProxyRb::Platforms::AnsiColor attributes as symbols.
101
+ def attributes
102
+ ATTRIBUTE_NAMES
103
+ end
104
+ end
105
+ end
106
+
107
+ # ProxyRb
108
+ module ProxyRb
109
+ # Colorize output
110
+ class Colorizer
111
+ include ProxyRb::AnsiColor
112
+ end
113
+ end
@@ -3,7 +3,6 @@ require 'contracts'
3
3
 
4
4
  require 'proxy_rb/version'
5
5
  require 'proxy_rb/basic_configuration'
6
- require 'proxy_rb/basic_configuration/in_config_wrapper'
7
6
 
8
7
  require 'proxy_rb/password_fetchers/basic_password_fetcher'
9
8
  require 'proxy_rb/password_fetchers/environment_password_fetcher'
@@ -18,6 +17,7 @@ module ProxyRb
18
17
  class Configuration < BasicConfiguration
19
18
  option_accessor :password_fetcher, contract: { PasswordFetchers::BasicPasswordFetcher => PasswordFetchers::BasicPasswordFetcher }, default: ProxyRb::PasswordFetchers::EnvironmentPasswordFetcher.new(prefix: 'SECRET')
20
19
  option_accessor :driver, contract: { Drivers::BasicDriver => Drivers::BasicDriver }, default: ProxyRb::Drivers::WebkitDriver.new
20
+ option_accessor :console_history_file, contract: { String => String }, default: '~/.proxy_rb_history'
21
21
  end
22
22
  end
23
23
 
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ module ProxyRb
3
+ # This wraps the current runtime configuration of proxy_rb.
4
+ # If an option is changed, it notifies the event queue.
5
+ #
6
+ # This class is not meant for direct use - ConfigWrapper.new - by normal
7
+ # users.
8
+ #
9
+ # @private
10
+ class ConfigurationWrapper
11
+ private
12
+
13
+ attr_reader :config, :event_bus
14
+
15
+ public
16
+
17
+ # Create proxy
18
+ #
19
+ # @param [Config] config
20
+ # An proxy_rb config object.
21
+ #
22
+ # @param [#notify] event_bus
23
+ # The event queue which should be notified.
24
+ def initialize(config, event_bus)
25
+ @config = config
26
+ @event_bus = event_bus
27
+ end
28
+
29
+ # Proxy all methods
30
+ #
31
+ # If one method ends with "=", e.g. ":option1=", then notify the event
32
+ # queue, that the user changes the value of "option1"
33
+ def method_missing(name, *args, &block)
34
+ event_bus.notify Events::ChangedConfiguration.new(changed: { name: name.to_s.gsub(/=$/, ''), value: args.first }) if name.to_s.end_with? '='
35
+
36
+ config.send(name, *args, &block)
37
+ end
38
+
39
+ # Pass on respond_to?-calls
40
+ def respond_to_missing?(name, _include_private)
41
+ config.respond_to? name
42
+ end
43
+
44
+ # Compare two configs
45
+ #
46
+ # The comparism is done based on their values. No hooks are compared.
47
+ #
48
+ # Somehow `#respond_to_missing?`, `method_missing?` and `respond_to?` don't
49
+ # help here.
50
+ def ==(other)
51
+ config == other
52
+ end
53
+
54
+ # Pass on respond_to?-calls
55
+ def respond_to?(m)
56
+ config.respond_to? m
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'proxy_rb/api'
3
+
4
+ # ProxyRb
5
+ module ProxyRb
6
+ # Consule
7
+ class Console
8
+ # Helpers for ProxyRb::Console
9
+ module Help
10
+ # Output help information
11
+ def proxy_rb_help
12
+ puts 'ProxyRb Version: ' + ProxyRb::VERSION
13
+ puts 'Issue Tracker: ' + 'https://github.com/fedux-org/proxy_rb/issues'
14
+ puts "Documentation:\n" + %w(http://www.rubydoc.info/gems/proxy_rb).map { |d| format('* %s', d) }.join("\n")
15
+ puts
16
+
17
+ nil
18
+ end
19
+
20
+ # List available methods in proxy_rb
21
+ def proxy_rb_methods
22
+ ms = (ProxyRb::Api.instance_methods - Module.instance_methods).each_with_object([]) { |e, a| a << format('* %s', e) }.sort
23
+ puts "Available Methods:\n" + ms.join("\n")
24
+
25
+ nil
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'irb'
3
+
4
+ require 'proxy_rb/console/help'
5
+ require 'proxy_rb/api'
6
+
7
+ # ProxyRb
8
+ module ProxyRb
9
+ # Consule
10
+ class Console
11
+ # Start the proxy_rb console
12
+ def start
13
+ # Start IRB with current context:
14
+ # http://stackoverflow.com/questions/4189818/how-to-run-irb-start-in-context-of-current-class
15
+ ARGV.clear
16
+ IRB.setup nil
17
+
18
+ IRB.conf[:IRB_NAME] = 'proxy_rb'
19
+
20
+ IRB.conf[:PROMPT] = {}
21
+ IRB.conf[:PROMPT][:PROXY_RB] = {
22
+ PROMPT_I: '%N:%03n:%i> ',
23
+ PROMPT_S: '%N:%03n:%i%l ',
24
+ PROMPT_C: '%N:%03n:%i* ',
25
+ RETURN: "# => %s\n"
26
+ }
27
+ IRB.conf[:PROMPT_MODE] = :PROXY_RB
28
+
29
+ IRB.conf[:RC] = false
30
+
31
+ require 'irb/completion'
32
+ require 'irb/ext/save-history'
33
+ IRB.conf[:READLINE] = true
34
+ IRB.conf[:SAVE_HISTORY] = 1000
35
+ IRB.conf[:HISTORY_FILE] = ProxyRb.config.console_history_file
36
+
37
+ context = Class.new do
38
+ include ProxyRb::Console::Help
39
+ include ProxyRb::Api
40
+
41
+ def inspect
42
+ 'nil'
43
+ end
44
+ end
45
+
46
+ irb = IRB::Irb.new(IRB::WorkSpace.new(context.new))
47
+ IRB.conf[:MAIN_CONTEXT] = irb.context
48
+
49
+ trap('SIGINT') do
50
+ irb.signal_handle
51
+ end
52
+
53
+ begin
54
+ catch(:IRB_EXIT) do
55
+ irb.eval_input
56
+ end
57
+ ensure
58
+ IRB.irb_at_exit
59
+ end
60
+ end
61
+ end
62
+ end
@@ -33,6 +33,17 @@ module ProxyRb
33
33
  !(user_name? && password?)
34
34
  end
35
35
 
36
+ # Convert to hash
37
+ #
38
+ # @return [Hash]
39
+ # The credentials as hash
40
+ def to_hash
41
+ {
42
+ user_name: user_name,
43
+ password: password
44
+ }
45
+ end
46
+
36
47
  private
37
48
 
38
49
  def user_name?
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
- require 'proxy_rb/drivers/basic_driver'
3
2
  require 'capybara'
4
3
 
4
+ require 'proxy_rb/drivers/basic_driver'
5
+ require 'proxy_rb/errors'
6
+
5
7
  begin
6
8
  require 'capybara/poltergeist'
7
9
  rescue LoadError => e
@@ -44,9 +46,13 @@ module ProxyRb
44
46
  ::Capybara.current_driver = proxy.to_ref
45
47
  end
46
48
 
47
- def rescuable_errors
49
+ def timeout_errors
48
50
  [::Capybara::Poltergeist::TimeoutError]
49
51
  end
52
+
53
+ def failure_errors
54
+ [::Capybara::Poltergeist::StatusFailError]
55
+ end
50
56
  end
51
57
  end
52
58
  end
@@ -2,6 +2,7 @@
2
2
  require 'proxy_rb/drivers/basic_driver'
3
3
  require 'capybara'
4
4
  require 'selenium-webdriver'
5
+ require 'proxy_rb/errors'
5
6
 
6
7
  # ProxyRb
7
8
  module ProxyRb
@@ -20,6 +21,7 @@ module ProxyRb
20
21
  end
21
22
 
22
23
  profile = Selenium::WebDriver::Firefox::Profile.new
24
+ # profile.proxy = Selenium::WebDriver::Proxy.new(http: proxy.full_url)
23
25
  profile.proxy = Selenium::WebDriver::Proxy.new(http: format('%s:%s', proxy.host, proxy.port))
24
26
 
25
27
  unless ::Capybara.drivers.key? proxy.to_ref
@@ -31,7 +33,11 @@ module ProxyRb
31
33
  ::Capybara.current_driver = proxy.to_ref
32
34
  end
33
35
 
34
- def rescuable_errors
36
+ def timeout_errors
37
+ []
38
+ end
39
+
40
+ def failure_errors
35
41
  []
36
42
  end
37
43
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'proxy_rb/drivers/basic_driver'
3
+ require 'proxy_rb/errors'
3
4
 
4
5
  begin
5
6
  require 'capybara/webkit'
@@ -48,9 +49,17 @@ module ProxyRb
48
49
  ::Capybara.current_driver = proxy.to_ref
49
50
  end
50
51
 
51
- def rescuable_errors
52
+ def timeout_errors
52
53
  [::Capybara::Webkit::TimeoutError]
53
54
  end
55
+
56
+ def failure_errors
57
+ [
58
+ Capybara::Webkit::InvalidResponseError,
59
+ Capybara::Webkit::NoResponseError,
60
+ Capybara::Webkit::ConnectionError
61
+ ]
62
+ end
54
63
  end
55
64
  end
56
65
  end
@@ -2,4 +2,16 @@
2
2
  module ProxyRb
3
3
  # Raised if time out occured while fetch resource
4
4
  class UrlTimeoutError < StandardError; end
5
+
6
+ # Raised when resource cannot be downloaded
7
+ class ResourceNotDownloadableError < StandardError; end
8
+
9
+ # Raised if one tries to use an unknown configuration option
10
+ class UnknownOptionError < ArgumentError; end
11
+
12
+ # Raised if an event name cannot be resolved
13
+ class EventNameResolveError < StandardError; end
14
+
15
+ # Raised if given object is not an event
16
+ class NoEventError < StandardError; end
5
17
  end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+ require 'proxy_rb/errors'
3
+
4
+ # Event notification library
5
+ module ProxyRb
6
+ # EventBus
7
+ class EventBus
8
+ # Resolve name to Event name
9
+ class NameResolver
10
+ # @private
11
+ # Helpers for Resolvers
12
+ module ResolveHelpers
13
+ def camel_case(underscored_name)
14
+ if RUBY_VERSION < '1.9.3'
15
+ underscored_name.to_s.split('_').map { |word| word.upcase.chars.to_a[0] + word.chars.to_a[1..-1].join }.join
16
+ else
17
+ underscored_name.to_s.split('_').map { |word| word.upcase[0] + word[1..-1] }.join
18
+ end
19
+ end
20
+
21
+ # Thanks ActiveSupport
22
+ # (Only needed to support Ruby 1.9.3 and JRuby)
23
+ # rubocop:disable Metrics/CyclomaticComplexity
24
+ # rubocop:disable Metrics/PerceivedComplexity
25
+ def constantize(camel_cased_word)
26
+ names = camel_cased_word.split('::')
27
+
28
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
29
+ Object.const_get(camel_cased_word) if names.empty?
30
+
31
+ # Remove the first blank element in case of '::ClassName' notation.
32
+ names.shift if names.size > 1 && names.first.empty?
33
+
34
+ names.inject(Object) do |constant, name|
35
+ if constant == Object
36
+ constant.const_get(name)
37
+ else
38
+ candidate = constant.const_get(name)
39
+
40
+ next candidate if constant.const_defined?(name, false)
41
+
42
+ next candidate unless Object.const_defined?(name)
43
+
44
+ # Go down the ancestors to check if it is owned directly. The check
45
+ # stops when we reach Object or the end of ancestors tree.
46
+ # rubocop:disable Style/EachWithObject
47
+ constant = constant.ancestors.inject do |const, ancestor|
48
+ break const if ancestor == Object
49
+ break ancestor if ancestor.const_defined?(name, false)
50
+ const
51
+ end
52
+ # rubocop:enable Style/EachWithObject
53
+
54
+ # owner is in Object, so raise
55
+ constant.const_get(name, false)
56
+ end
57
+ end
58
+ end
59
+ # rubocop:enable Metrics/PerceivedComplexity
60
+ # rubocop:enable Metrics/CyclomaticComplexity
61
+ end
62
+
63
+ # @private
64
+ # Convert a class in to an event class
65
+ class ClassResolver
66
+ class << self
67
+ def match?(event_id)
68
+ event_id.is_a? Class
69
+ end
70
+
71
+ # Which types are supported
72
+ def supports
73
+ [Class]
74
+ end
75
+ end
76
+
77
+ def transform(_, event_id)
78
+ event_id
79
+ end
80
+ end
81
+
82
+ # @private
83
+ # Convert a string in to an event class
84
+ class StringResolver
85
+ include ResolveHelpers
86
+
87
+ class << self
88
+ def match?(event_id)
89
+ event_id.is_a? String
90
+ end
91
+
92
+ # Which types are supported
93
+ def supports
94
+ [String]
95
+ end
96
+ end
97
+
98
+ def transform(_, event_id)
99
+ constantize(event_id)
100
+ end
101
+ end
102
+
103
+ # @private
104
+ # Convert a symbol in to an event class
105
+ class SymbolResolver
106
+ include ResolveHelpers
107
+
108
+ class << self
109
+ def match?(event_id)
110
+ event_id.is_a? Symbol
111
+ end
112
+
113
+ # Which types are supported
114
+ def supports
115
+ [Symbol]
116
+ end
117
+ end
118
+
119
+ def transform(default_namespace, event_id)
120
+ constantize("#{default_namespace}::#{camel_case(event_id)}")
121
+ end
122
+ end
123
+
124
+ # @private
125
+ # Default failing resolver
126
+ #
127
+ # This comes into play if the user passes an invalid event type
128
+ class FailingResolver
129
+ class << self
130
+ def match?(event_id)
131
+ raise ArgumentError, %(Input type "#{event_id.class}" of event_id "#{event_id}" is invalid)
132
+ end
133
+
134
+ def supports
135
+ []
136
+ end
137
+ end
138
+ end
139
+
140
+ protected
141
+
142
+ attr_reader :resolvers, :default_namespace
143
+
144
+ public
145
+
146
+ def initialize(default_namespace)
147
+ @default_namespace = default_namespace
148
+
149
+ @resolvers = []
150
+ @resolvers << ClassResolver
151
+ @resolvers << StringResolver
152
+ @resolvers << SymbolResolver
153
+ @resolvers << FailingResolver
154
+ end
155
+
156
+ def transform(event_id)
157
+ resolvers.find { |r| r.match? event_id }.new.transform(default_namespace, event_id)
158
+ rescue => e
159
+ raise EventNameResolveError, %(Transforming "#{event_id}" into an event class failed. Supported types are: #{@resolvers.map(&:supports).flatten.join(', ')}. #{e.message}.\n\n#{e.backtrace.join("\n")})
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ require 'proxy_rb/event_bus/name_resolver'
3
+ require 'proxy_rb/errors'
4
+
5
+ module ProxyRb
6
+ # Event bus
7
+ #
8
+ # Implements and in-process pub-sub events broadcaster allowing multiple observers
9
+ # to subscribe to different events that fire as your tests are executed.
10
+ #
11
+ class EventBus
12
+ # Create EventBus
13
+ #
14
+ # @param [#transform] resolver
15
+ # A resolver which transforms Symbol, String, Class into an event Class.
16
+ def initialize(resolver)
17
+ @resolver = resolver
18
+ @handlers = Hash.new { |h, k| h[k] = [] }
19
+ end
20
+
21
+ # Register for an event
22
+ #
23
+ # @param [String, Symbol, Class, Array] event_ids
24
+ # If Array, register multiple events witht the same handler. If String,
25
+ # Symbol, Class register handler for given event.
26
+ #
27
+ # @param [#call] handler_object
28
+ # The handler object, needs to have method `#call`. Either
29
+ # `handler_object` or `block` can be defined. The handler object gets the
30
+ # event passed to `#call`.
31
+ #
32
+ # @yield
33
+ # Handler block which gets the event passed as parameter.
34
+ def register(event_ids, handler_object = nil, &handler_proc)
35
+ handler = handler_proc || handler_object
36
+
37
+ raise ArgumentError, 'Please pass either an object#call or a handler block' if handler.nil? || !handler.respond_to?(:call)
38
+
39
+ Array(event_ids).flatten.each do |id|
40
+ @handlers[
41
+ @resolver.transform(id).to_s
42
+ ] << handler
43
+ end
44
+
45
+ nil
46
+ end
47
+
48
+ # Broadcast an event
49
+ #
50
+ # @param [Object] event
51
+ # An object of registered event class. This object is passed to the event
52
+ # handler.
53
+ #
54
+ def notify(event)
55
+ raise NoEventError, 'Please pass an event object, not a class' if event.is_a?(Class)
56
+
57
+ @handlers[event.class.to_s].each { |handler| handler.call(event) }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # Events
5
+ module Events
6
+ # Basic event
7
+ #
8
+ # This is not meant for direct use - BasicEvent.new - by users. It is inherited by normal events
9
+ #
10
+ # @private
11
+ class BasicEvent
12
+ attr_reader :entity
13
+
14
+ def initialize(entity)
15
+ @entity = entity
16
+ end
17
+ end
18
+
19
+ # Proxy was set
20
+ class ProxySet < BasicEvent; end
21
+
22
+ # User was set
23
+ class ProxyUserSet < BasicEvent; end
24
+
25
+ # User was set
26
+ class ResourceUserSet < BasicEvent; end
27
+
28
+ # User was set
29
+ class ResourceSet < BasicEvent; end
30
+
31
+ # The configuration was changed
32
+ class ChangedConfiguration < BasicEvent; end
33
+ end
34
+ end
@@ -42,5 +42,13 @@ module ProxyRb
42
42
  def to_ref
43
43
  Shellwords.escape(*[host, port, user].compact.join('_')).to_sym
44
44
  end
45
+
46
+ # Return proxy as full url
47
+ #
48
+ # @return [ProxyUrl]
49
+ # The proxy as url
50
+ def full_url
51
+ ProxyUrl.build url.to_hash.merge(credentials.to_hash)
52
+ end
45
53
  end
46
54
  end