akephalos-nerian 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.md +56 -0
  3. data/bin/akephalos +88 -0
  4. data/lib/akephalos.rb +19 -0
  5. data/lib/akephalos/capybara.rb +316 -0
  6. data/lib/akephalos/client.rb +112 -0
  7. data/lib/akephalos/client/cookies.rb +73 -0
  8. data/lib/akephalos/client/filter.rb +120 -0
  9. data/lib/akephalos/configuration.rb +49 -0
  10. data/lib/akephalos/console.rb +32 -0
  11. data/lib/akephalos/cucumber.rb +6 -0
  12. data/lib/akephalos/htmlunit.rb +38 -0
  13. data/lib/akephalos/htmlunit/ext/http_method.rb +30 -0
  14. data/lib/akephalos/node.rb +172 -0
  15. data/lib/akephalos/page.rb +113 -0
  16. data/lib/akephalos/remote_client.rb +84 -0
  17. data/lib/akephalos/server.rb +56 -0
  18. data/lib/akephalos/version.rb +3 -0
  19. data/src/htmlunit/apache-mime4j-0.6.jar +0 -0
  20. data/src/htmlunit/commons-codec-1.4.jar +0 -0
  21. data/src/htmlunit/commons-collections-3.2.1.jar +0 -0
  22. data/src/htmlunit/commons-io-1.4.jar +0 -0
  23. data/src/htmlunit/commons-lang-2.4.jar +0 -0
  24. data/src/htmlunit/commons-logging-1.1.1.jar +0 -0
  25. data/src/htmlunit/cssparser-0.9.5.jar +0 -0
  26. data/src/htmlunit/htmlunit-2.8.jar +0 -0
  27. data/src/htmlunit/htmlunit-core-js-2.8.jar +0 -0
  28. data/src/htmlunit/httpclient-4.0.1.jar +0 -0
  29. data/src/htmlunit/httpcore-4.0.1.jar +0 -0
  30. data/src/htmlunit/httpmime-4.0.1.jar +0 -0
  31. data/src/htmlunit/nekohtml-1.9.14.jar +0 -0
  32. data/src/htmlunit/sac-1.3.jar +0 -0
  33. data/src/htmlunit/serializer-2.7.1.jar +0 -0
  34. data/src/htmlunit/xalan-2.7.1.jar +0 -0
  35. data/src/htmlunit/xercesImpl-2.9.1.jar +0 -0
  36. data/src/htmlunit/xml-apis-1.3.04.jar +0 -0
  37. metadata +164 -0
@@ -0,0 +1,112 @@
1
+ require 'akephalos/configuration'
2
+
3
+ if RUBY_PLATFORM != "java"
4
+ require 'akephalos/remote_client'
5
+ Akephalos::Client = Akephalos::RemoteClient
6
+ else
7
+ require 'akephalos/htmlunit'
8
+ require 'akephalos/htmlunit/ext/http_method'
9
+
10
+ require 'akephalos/page'
11
+ require 'akephalos/node'
12
+
13
+ require 'akephalos/client/cookies'
14
+ require 'akephalos/client/filter'
15
+
16
+ module Akephalos
17
+
18
+ # Akephalos::Client wraps HtmlUnit's WebClient class. It is the main entry
19
+ # point for all interaction with the browser, exposing its current page and
20
+ # allowing navigation.
21
+ class Client
22
+ attr_reader :page
23
+
24
+ def initialize
25
+ @_client = java.util.concurrent.FutureTask.new do
26
+ client = HtmlUnit::WebClient.new
27
+
28
+ Filter.new(client)
29
+ client.setThrowExceptionOnFailingStatusCode(false)
30
+ client.setAjaxController(HtmlUnit::NicelyResynchronizingAjaxController.new)
31
+ client.setCssErrorHandler(HtmlUnit::SilentCssErrorHandler.new)
32
+ client.setThrowExceptionOnScriptError(false);
33
+ client
34
+ end
35
+ Thread.new { @_client.run }
36
+ end
37
+
38
+ # Set the global configuration settings for Akephalos.
39
+ #
40
+ # @note This is only used when communicating over DRb, since just a
41
+ # single client instance is exposed.
42
+ # @param [Hash] config the configuration settings
43
+ # @return [Hash] the configuration
44
+ def configuration=(config)
45
+ Akephalos.configuration = config
46
+ end
47
+
48
+ # Visit the requested URL and return the page.
49
+ #
50
+ # @param [String] url the URL to load
51
+ # @return [Page] the loaded page
52
+ def visit(url)
53
+ client.getPage(url)
54
+ page
55
+ end
56
+
57
+ # @return [Cookies] the cookies for this session
58
+ def cookies
59
+ @cookies ||= Cookies.new(client.getCookieManager)
60
+ end
61
+
62
+ # @return [String] the current user agent string
63
+ def user_agent
64
+ @user_agent || client.getBrowserVersion.getUserAgent
65
+ end
66
+
67
+ # Set the User-Agent header for this session. If :default is given, the
68
+ # User-Agent header will be reset to the default browser's user agent.
69
+ #
70
+ # @param [:default] user_agent the default user agent
71
+ # @param [String] user_agent the user agent string to use
72
+ def user_agent=(user_agent)
73
+ if user_agent == :default
74
+ @user_agent = nil
75
+ client.removeRequestHeader("User-Agent")
76
+ else
77
+ @user_agent = user_agent
78
+ client.addRequestHeader("User-Agent", user_agent)
79
+ end
80
+ end
81
+
82
+ # @return [Page] the current page
83
+ def page
84
+ self.page = client.getCurrentWindow.getTopWindow.getEnclosedPage
85
+ @page
86
+ end
87
+
88
+ # Update the current page.
89
+ #
90
+ # @param [HtmlUnit::HtmlPage] _page the new page
91
+ # @return [Page] the new page
92
+ def page=(_page)
93
+ if @page != _page
94
+ @page = Page.new(_page)
95
+ end
96
+ @page
97
+ end
98
+
99
+ private
100
+
101
+ # Call the future set up in #initialize and return the WebCLient
102
+ # instance.
103
+ #
104
+ # @return [HtmlUnit::WebClient] the WebClient instance
105
+ def client
106
+ @client ||= @_client.get.tap do |client|
107
+ client.getCurrentWindow.getHistory.ignoreNewPages_.set(true)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,73 @@
1
+ module Akephalos
2
+ class Client
3
+ # Interface for working with HtmlUnit's CookieManager, providing a basic
4
+ # API for manipulating the cookies in a session.
5
+ class Cookies
6
+ include Enumerable
7
+
8
+ # @param [HtmlUnit::CookieManager] cookie manager
9
+ def initialize(cookie_manager)
10
+ @cookie_manager = cookie_manager
11
+ end
12
+
13
+ # @param [name] the cookie name
14
+ # @return [Cookie] the cookie with the given name
15
+ # @return [nil] when no cookie is found
16
+ def [](name)
17
+ cookie = @cookie_manager.getCookie(name)
18
+ Cookie.new(cookie) if cookie
19
+ end
20
+
21
+ # Clears all cookies for this session.
22
+ def clear
23
+ @cookie_manager.clearCookies
24
+ end
25
+
26
+ # Iterator for all cookies in the current session.
27
+ def each
28
+ @cookie_manager.getCookies.each do |cookie|
29
+ yield Cookie.new(cookie)
30
+ end
31
+ end
32
+
33
+ # Remove the cookie from the session.
34
+ #
35
+ # @param [Cookie] the cookie to remove
36
+ def delete(cookie)
37
+ @cookie_manager.removeCookie(cookie.native)
38
+ end
39
+
40
+ # @return [true, false] whether there are any cookies
41
+ def empty?
42
+ !any?
43
+ end
44
+
45
+ class Cookie
46
+
47
+ attr_reader :domain, :expires, :name, :path, :value
48
+
49
+ # @param [HtmlUnit::Cookie] the cookie
50
+ def initialize(cookie)
51
+ @_cookie = cookie
52
+ @domain = cookie.getDomain
53
+ @expires = cookie.getExpires
54
+ @name = cookie.getName
55
+ @path = cookie.getPath
56
+ @value = cookie.getValue
57
+ @secure = cookie.isSecure
58
+ end
59
+
60
+ def secure?
61
+ !!@secure
62
+ end
63
+
64
+ # @return [HtmlUnit::Cookie] the native cookie object
65
+ # @api private
66
+ def native
67
+ @_cookie
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,120 @@
1
+ module Akephalos
2
+ class Client
3
+
4
+ # Akephalos::Client::Filter extends HtmlUnit's WebConnectionWrapper to
5
+ # enable filtering outgoing requests generated interally by HtmlUnit.
6
+ #
7
+ # When a request comes through, it will be tested against the filters
8
+ # defined in Akephalos.filters and return a mock response if a match is
9
+ # found. If no filters are defined, or no filters match the request, then
10
+ # the response will bubble up to HtmlUnit for the normal request/response
11
+ # cycle.
12
+ class Filter < HtmlUnit::Util::WebConnectionWrapper
13
+ # Filters an outgoing request, and if a match is found, returns the mock
14
+ # response.
15
+ #
16
+ # @param [WebRequest] request the pending HTTP request
17
+ # @return [WebResponseImpl] when the request matches a defined filter
18
+ # @return [nil] when no filters match the request
19
+ def filter(request)
20
+ if filter = find_filter(request)
21
+ start_time = Time.now
22
+ headers = filter[:headers].map do |name, value|
23
+ HtmlUnit::Util::NameValuePair.new(name.to_s, value.to_s)
24
+ end
25
+ response = HtmlUnit::WebResponseData.new(
26
+ filter[:body].to_s.to_java_bytes,
27
+ filter[:status],
28
+ HTTP_STATUS_CODES.fetch(filter[:status], "Unknown"),
29
+ headers
30
+ )
31
+ HtmlUnit::WebResponseImpl.new(response, request, Time.now - start_time)
32
+ end
33
+ end
34
+
35
+ # Searches for a filter which matches the request's HTTP method and url.
36
+ #
37
+ # @param [WebRequest] request the pending HTTP request
38
+ # @return [Hash] when a filter matches the request
39
+ # @return [nil] when no filters match the request
40
+ def find_filter(request)
41
+ Akephalos.filters.find do |filter|
42
+ request.http_method === filter[:method] && request.url.to_s =~ filter[:filter]
43
+ end
44
+ end
45
+
46
+ # This method is called by WebClient when a page is requested, and will
47
+ # return a mock response if the request matches a defined filter or else
48
+ # return the actual response.
49
+ #
50
+ # @api htmlunit
51
+ # @param [WebRequest] request the pending HTTP request
52
+ # @return [WebResponseImpl]
53
+ def getResponse(request)
54
+ filter(request) || super
55
+ end
56
+
57
+ # Map of status codes to their English descriptions.
58
+ HTTP_STATUS_CODES = {
59
+ 100 => "Continue",
60
+ 101 => "Switching Protocols",
61
+ 102 => "Processing",
62
+ 200 => "OK",
63
+ 201 => "Created",
64
+ 202 => "Accepted",
65
+ 203 => "Non-Authoritative Information",
66
+ 204 => "No Content",
67
+ 205 => "Reset Content",
68
+ 206 => "Partial Content",
69
+ 207 => "Multi-Status",
70
+ 300 => "Multiple Choices",
71
+ 301 => "Moved Permanently",
72
+ 302 => "Found",
73
+ 303 => "See Other",
74
+ 304 => "Not Modified",
75
+ 305 => "Use Proxy",
76
+ 306 => "Switch Proxy",
77
+ 307 => "Temporary Redirect",
78
+ 400 => "Bad Request",
79
+ 401 => "Unauthorized",
80
+ 402 => "Payment Required",
81
+ 403 => "Forbidden",
82
+ 404 => "Not Found",
83
+ 405 => "Method Not Allowed",
84
+ 406 => "Not Acceptable",
85
+ 407 => "Proxy Authentication Required",
86
+ 408 => "Request Timeout",
87
+ 409 => "Conflict",
88
+ 410 => "Gone",
89
+ 411 => "Length Required",
90
+ 412 => "Precondition Failed",
91
+ 413 => "Request Entity Too Large",
92
+ 414 => "Request-URI Too Long",
93
+ 415 => "Unsupported Media Type",
94
+ 416 => "Requested Range Not Satisfiable",
95
+ 417 => "Expectation Failed",
96
+ 418 => "I'm a teapot",
97
+ 421 => "There are too many connections from your internet address",
98
+ 422 => "Unprocessable Entity",
99
+ 423 => "Locked",
100
+ 424 => "Failed Dependency",
101
+ 425 => "Unordered Collection",
102
+ 426 => "Upgrade Required",
103
+ 449 => "Retry With",
104
+ 450 => "Blocked by Windows Parental Controls",
105
+ 500 => "Internal Server Error",
106
+ 501 => "Not Implemented",
107
+ 502 => "Bad Gateway",
108
+ 503 => "Service Unavailable",
109
+ 504 => "Gateway Timeout",
110
+ 505 => "HTTP Version Not Supported",
111
+ 506 => "Variant Also Negotiates",
112
+ 507 => "Insufficient Storage",
113
+ 509 => "Bandwidth Limit Exceeded",
114
+ 510 => "Not Extended",
115
+ 530 => "User access denied"
116
+ }.freeze
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,49 @@
1
+ module Akephalos
2
+
3
+ @configuration = {}
4
+
5
+ class << self
6
+ # @return [Hash] the configuration
7
+ attr_accessor :configuration
8
+ end
9
+
10
+ module Filters
11
+ # @return [Array] all defined filters
12
+ def filters
13
+ configuration[:filters] ||= []
14
+ end
15
+
16
+ # Defines a new filter to be tested by Akephalos::Filter when executing
17
+ # page requests. An HTTP method and a regex or string to match against the
18
+ # URL are required for defining a filter.
19
+ #
20
+ # You can additionally pass the following options to define how the
21
+ # filtered request should respond:
22
+ #
23
+ # :status (defaults to 200)
24
+ # :body (defaults to "")
25
+ # :headers (defaults to {})
26
+ #
27
+ # If we define a filter with no additional options, then, we will get an
28
+ # empty HTML response:
29
+ #
30
+ # Akephalos.filter :post, "http://example.com"
31
+ # Akephalos.filter :any, %r{http://.*\.com}
32
+ #
33
+ # If you instead, say, wanted to simulate a failure in an external system,
34
+ # you could do this:
35
+ #
36
+ # Akephalos.filter :post, "http://example.com",
37
+ # :status => 500, :body => "Something went wrong"
38
+ #
39
+ # @param [Symbol] method the HTTP method to match
40
+ # @param [RegExp, String] regex URL matcher
41
+ # @param [Hash] options response values
42
+ def filter(method, regex, options = {})
43
+ regex = Regexp.new(Regexp.escape(regex)) if regex.is_a?(String)
44
+ filters << {:method => method, :filter => regex, :status => 200, :body => "", :headers => {}}.merge!(options)
45
+ end
46
+ end
47
+
48
+ extend Filters
49
+ end
@@ -0,0 +1,32 @@
1
+ # Begin a new Capybara session, by default connecting to localhost on port
2
+ # 3000.
3
+ def session
4
+ Capybara.app_host ||= "http://localhost:3000"
5
+ @session ||= Capybara::Session.new(:Akephalos)
6
+ end
7
+ alias page session
8
+
9
+ module Akephalos
10
+ # Simple class for starting an IRB session.
11
+ class Console
12
+
13
+ # Start an IRB session. Tries to load irb/completion, and also loads a
14
+ # .irbrc file if it exists.
15
+ def self.start
16
+ require 'irb'
17
+
18
+ begin
19
+ require 'irb/completion'
20
+ rescue Exception
21
+ # No readline available, proceed anyway.
22
+ end
23
+
24
+ if ::File.exists? ".irbrc"
25
+ ENV['IRBRC'] = ".irbrc"
26
+ end
27
+
28
+ IRB.start
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ require 'capybara/cucumber'
2
+
3
+ Before('@akephalos') do
4
+ Capybara.current_driver = :akephalos
5
+ end
6
+
@@ -0,0 +1,38 @@
1
+ require "pathname"
2
+ require "java"
3
+
4
+ dependency_directory = $:.detect { |path| Dir[File.join(path, 'htmlunit/htmlunit-*.jar')].any? }
5
+
6
+ raise "Could not find htmlunit/htmlunit-VERSION.jar in load path:\n [ #{$:.join(",\n ")}\n ]" unless dependency_directory
7
+
8
+ Dir[File.join(dependency_directory, "htmlunit/*.jar")].each do |jar|
9
+ require jar
10
+ end
11
+
12
+ java.lang.System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog")
13
+ java.lang.System.setProperty("org.apache.commons.logging.simplelog.defaultlog", "fatal")
14
+ java.lang.System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true")
15
+
16
+ # Container module for com.gargoylesoftware.htmlunit namespace.
17
+ module HtmlUnit
18
+ java_import "com.gargoylesoftware.htmlunit.BrowserVersion"
19
+ java_import "com.gargoylesoftware.htmlunit.History"
20
+ java_import "com.gargoylesoftware.htmlunit.HttpMethod"
21
+ java_import "com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController"
22
+ java_import "com.gargoylesoftware.htmlunit.SilentCssErrorHandler"
23
+ java_import "com.gargoylesoftware.htmlunit.WebClient"
24
+ java_import "com.gargoylesoftware.htmlunit.WebResponseData"
25
+ java_import "com.gargoylesoftware.htmlunit.WebResponseImpl"
26
+
27
+ # Container module for com.gargoylesoftware.htmlunit.util namespace.
28
+ module Util
29
+ java_import "com.gargoylesoftware.htmlunit.util.NameValuePair"
30
+ java_import "com.gargoylesoftware.htmlunit.util.WebConnectionWrapper"
31
+ end
32
+
33
+ # Disable history tracking
34
+ History.field_reader :ignoreNewPages_
35
+
36
+ # Run in Firefox compatibility mode
37
+ BrowserVersion.setDefault(BrowserVersion::FIREFOX_3)
38
+ end
@@ -0,0 +1,30 @@
1
+ module HtmlUnit
2
+ # Reopen HtmlUnit's HttpMethod class to add convenience methods.
3
+ class HttpMethod
4
+
5
+ # Loosely compare HttpMethod with another object, accepting either an
6
+ # HttpMethod instance or a symbol describing the method. Note that :any is a
7
+ # special symbol which will always return true.
8
+ #
9
+ # @param [HttpMethod] other an HtmlUnit HttpMethod object
10
+ # @param [Symbol] other a symbolized representation of an http method
11
+ # @return [true/false]
12
+ def ===(other)
13
+ case other
14
+ when HttpMethod
15
+ super
16
+ when :any
17
+ true
18
+ when :get
19
+ self == self.class::GET
20
+ when :post
21
+ self == self.class::POST
22
+ when :put
23
+ self == self.class::PUT
24
+ when :delete
25
+ self == self.class::DELETE
26
+ end
27
+ end
28
+
29
+ end
30
+ end