proxy_rb 0.4.0 → 0.5.0

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