strelka 0.0.1.pre148 → 0.0.1.pre177
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +1294 -0
- data/IDEAS.rdoc +6 -0
- data/Manifest.txt +20 -0
- data/README.rdoc +8 -2
- data/Rakefile +9 -7
- data/examples/auth-demo.rb +32 -0
- data/examples/auth-demo2.rb +37 -0
- data/examples/auth-form.tmpl +10 -0
- data/examples/auth-success.tmpl +3 -0
- data/examples/config.yml +12 -0
- data/examples/examples.css +4 -0
- data/examples/examples.html +31 -0
- data/examples/gen-config.rb +5 -2
- data/examples/layout.tmpl +31 -0
- data/lib/strelka/app/auth.rb +480 -0
- data/lib/strelka/app/sessions.rb +8 -6
- data/lib/strelka/app/templating.rb +78 -17
- data/lib/strelka/app.rb +13 -5
- data/lib/strelka/authprovider/basic.rb +134 -0
- data/lib/strelka/authprovider/hostaccess.rb +91 -0
- data/lib/strelka/authprovider.rb +122 -0
- data/lib/strelka/cookie.rb +1 -1
- data/lib/strelka/cookieset.rb +1 -1
- data/lib/strelka/httprequest/auth.rb +31 -0
- data/lib/strelka/logging.rb +69 -14
- data/lib/strelka/mixins.rb +35 -65
- data/lib/strelka/session/db.rb +115 -0
- data/lib/strelka/session/default.rb +38 -49
- data/lib/strelka/session.rb +1 -1
- data/lib/strelka.rb +4 -1
- data/spec/lib/helpers.rb +8 -3
- data/spec/strelka/app/auth_spec.rb +367 -0
- data/spec/strelka/authprovider/basic_spec.rb +192 -0
- data/spec/strelka/authprovider/hostaccess_spec.rb +70 -0
- data/spec/strelka/authprovider_spec.rb +99 -0
- data/spec/strelka/cookie_spec.rb +1 -1
- data/spec/strelka/httprequest/auth_spec.rb +55 -0
- data/spec/strelka/httprequest/session_spec.rb +63 -3
- data/spec/strelka/session/db_spec.rb +85 -0
- data/spec/strelka/session/default_spec.rb +5 -51
- data.tar.gz.sig +0 -0
- metadata +88 -57
- metadata.gz.sig +0 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
require 'configurability'
|
5
|
+
|
6
|
+
require 'strelka' unless defined?( Strelka )
|
7
|
+
require 'strelka/app' unless defined?( Strelka::App )
|
8
|
+
require 'strelka/authprovider'
|
9
|
+
require 'strelka/mixins'
|
10
|
+
|
11
|
+
# HTTP Basic AuthProvider class -- a base class for RFC2617 Basic HTTP Authentication
|
12
|
+
# providers for {the Streka :auth plugin}[rdoc-ref:Strelka::App::Auth].
|
13
|
+
#
|
14
|
+
# == Configuration
|
15
|
+
#
|
16
|
+
# The configuration for this provider is read from the 'auth' section of the config, and
|
17
|
+
# may contain the following keys:
|
18
|
+
#
|
19
|
+
# [realm]:: the HTTP Basic realm. Defaults to the app's application ID
|
20
|
+
# [users]:: a Hash of username: SHA1+Base64'ed passwords
|
21
|
+
#
|
22
|
+
# An example:
|
23
|
+
#
|
24
|
+
# --
|
25
|
+
# auth:
|
26
|
+
# realm: Acme Admin Console
|
27
|
+
# users:
|
28
|
+
# mgranger: "9d5lIumnMJXmVT/34QrMuyj+p0E="
|
29
|
+
# jblack: "1pAnQNSVtpL1z88QwXV4sG8NMP8="
|
30
|
+
# kmurgen: "MZj9+VhZ8C9+aJhmwp+kWBL76Vs="
|
31
|
+
#
|
32
|
+
class Strelka::AuthProvider::Basic < Strelka::AuthProvider
|
33
|
+
extend Configurability,
|
34
|
+
Strelka::MethodUtilities
|
35
|
+
include Strelka::Constants,
|
36
|
+
Strelka::Loggable
|
37
|
+
|
38
|
+
# Configurability API - set the section of the config
|
39
|
+
config_key :auth
|
40
|
+
|
41
|
+
|
42
|
+
@users = nil
|
43
|
+
@realm = nil
|
44
|
+
|
45
|
+
##
|
46
|
+
# The Hash of users and their SHA1+Base64'ed passwords
|
47
|
+
singleton_attr_accessor :users
|
48
|
+
|
49
|
+
##
|
50
|
+
# The authentication realm
|
51
|
+
singleton_attr_accessor :realm
|
52
|
+
|
53
|
+
|
54
|
+
### Configurability API -- configure the auth provider instance.
|
55
|
+
def self::configure( config=nil )
|
56
|
+
if config
|
57
|
+
Strelka.log.debug "Configuring Basic authprovider: %p" % [ config ]
|
58
|
+
self.realm = config['realm'] if config['realm']
|
59
|
+
self.users = config['users'] if config['users']
|
60
|
+
else
|
61
|
+
self.realm = nil
|
62
|
+
self.users = {}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
#################################################################
|
68
|
+
### I N S T A N C E M E T H O D S
|
69
|
+
#################################################################
|
70
|
+
|
71
|
+
### Create a new Default AuthProvider.
|
72
|
+
def initialize( * )
|
73
|
+
super
|
74
|
+
|
75
|
+
# Default the authentication realm to the application's ID
|
76
|
+
unless self.class.realm
|
77
|
+
self.log.warn "No realm configured -- using the app id"
|
78
|
+
self.class.realm = self.app.conn.app_id
|
79
|
+
end
|
80
|
+
|
81
|
+
unless self.class.users
|
82
|
+
self.log.warn "No users configured -- using an empty user list"
|
83
|
+
self.class.users = {}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
######
|
89
|
+
public
|
90
|
+
######
|
91
|
+
|
92
|
+
# Check the authentication present in +request+ (if any) for validity, returning the
|
93
|
+
# authenticating user's name if authentication succeeds.
|
94
|
+
def authenticate( request )
|
95
|
+
authheader = request.header.authorization or
|
96
|
+
self.log_failure "No authorization header in the request."
|
97
|
+
|
98
|
+
# Extract the credentials bit
|
99
|
+
base64_userpass = authheader[ /^\s*Basic\s+(\S+)$/i, 1 ] or
|
100
|
+
self.log_failure "Invalid Basic Authorization header (%p)" % [ authheader ]
|
101
|
+
|
102
|
+
# Unpack the username and password
|
103
|
+
credentials = base64_userpass.unpack( 'm' ).first
|
104
|
+
self.log_failure "Malformed credentials %p" % [ credentials ] unless
|
105
|
+
credentials.index(':')
|
106
|
+
|
107
|
+
# Split the credentials, check for valid user
|
108
|
+
username, password = credentials.split( ':', 2 )
|
109
|
+
digest = self.class.users[ username ] or
|
110
|
+
self.log_failure "No such user %p." % [ username ]
|
111
|
+
|
112
|
+
# Fail if the password's hash doesn't match
|
113
|
+
self.log_failure "Password mismatch." unless
|
114
|
+
digest == Digest::SHA1.base64digest( password )
|
115
|
+
|
116
|
+
# Success!
|
117
|
+
self.log.info "Authentication for %p succeeded." % [ username ]
|
118
|
+
return username
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
#########
|
123
|
+
protected
|
124
|
+
#########
|
125
|
+
|
126
|
+
### Syntax sugar to allow returning 'false' while logging a reason for doing so.
|
127
|
+
### Log a message at 'info' level and return false.
|
128
|
+
def log_failure( reason )
|
129
|
+
self.log.warn "Auth failure: %s" % [ reason ]
|
130
|
+
header = "Basic realm=%s" % [ self.class.realm ]
|
131
|
+
finish_with( HTTP::AUTH_REQUIRED, "Requires authentication.", www_authenticate: header )
|
132
|
+
end
|
133
|
+
|
134
|
+
end # class Strelka::AuthProvider::Basic
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
|
4
|
+
require 'ipaddr'
|
5
|
+
require 'configurability'
|
6
|
+
|
7
|
+
require 'strelka' unless defined?( Strelka )
|
8
|
+
require 'strelka/app' unless defined?( Strelka::App )
|
9
|
+
require 'strelka/authprovider'
|
10
|
+
require 'strelka/mixins'
|
11
|
+
|
12
|
+
# HostAccess AuthProvider class -- restricts access to requests coming from a list of
|
13
|
+
# netblocks.
|
14
|
+
#
|
15
|
+
# You can configure which ones from the +auth+ section of the config:
|
16
|
+
#
|
17
|
+
# auth:
|
18
|
+
# allowed_netblocks:
|
19
|
+
# - 127.0.0.0/8
|
20
|
+
# - 10.5.3.0/22
|
21
|
+
class Strelka::AuthProvider::HostAccess < Strelka::AuthProvider
|
22
|
+
include Configurability,
|
23
|
+
Strelka::Constants,
|
24
|
+
Strelka::Loggable,
|
25
|
+
Strelka::MethodUtilities
|
26
|
+
|
27
|
+
|
28
|
+
# The default list of netblocks to allow
|
29
|
+
DEFAULT_ALLOWED_NETBLOCKS = %w[127.0.0.0/8]
|
30
|
+
|
31
|
+
|
32
|
+
#################################################################
|
33
|
+
### I N S T A N C E M E T H O D S
|
34
|
+
#################################################################
|
35
|
+
|
36
|
+
### Create a new Default AuthProvider.
|
37
|
+
def initialize( * )
|
38
|
+
super
|
39
|
+
|
40
|
+
self.allowed_netblocks = DEFAULT_ALLOWED_NETBLOCKS
|
41
|
+
|
42
|
+
# Register this instance with Configurability
|
43
|
+
config_key :auth
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
######
|
48
|
+
public
|
49
|
+
######
|
50
|
+
|
51
|
+
# An Array of IPAddr objects that represent the netblocks that will be allowed
|
52
|
+
# access to the protected resources
|
53
|
+
attr_reader :allowed_netblocks
|
54
|
+
|
55
|
+
|
56
|
+
### Set the list of allowed netblocks to +newblocks+.
|
57
|
+
def allowed_netblocks=( newblocks )
|
58
|
+
@allowed_netblocks = Array( newblocks ).map {|addr| IPAddr.new(addr) }
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
### Configurability API -- configure the auth provider instance.
|
63
|
+
def configure( config=nil )
|
64
|
+
self.log.debug "Configuring %p with config: %p" % [ self, config ]
|
65
|
+
if config && config['allowed_netblocks']
|
66
|
+
self.allowed_netblocks = config['allowed_netblocks']
|
67
|
+
else
|
68
|
+
self.allowed_netblocks = DEFAULT_ALLOWED_NETBLOCKS
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
### Check authorization for the specified +request+ by testing its the IP in its
|
74
|
+
### X-forwarded-for header against the allowed_netblocks.
|
75
|
+
def authorize( _, request )
|
76
|
+
client_ip = request.header.x_forwarded_for or
|
77
|
+
raise "No X-Forwarded-For header?!"
|
78
|
+
addr = IPAddr.new( client_ip )
|
79
|
+
|
80
|
+
return true if self.in_allowed_netblocks?( addr )
|
81
|
+
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
### Returns +true+ if the given +ipaddr+ is in the #allowed_netblocks.
|
87
|
+
def in_allowed_netblocks?( ipaddr )
|
88
|
+
return self.allowed_netblocks.any? {|nb| nb.include?(ipaddr) }
|
89
|
+
end
|
90
|
+
|
91
|
+
end # class Strelka::AuthProvider::HostAccess
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# encoding: utf-8
|
4
|
+
|
5
|
+
require 'pluginfactory'
|
6
|
+
|
7
|
+
require 'strelka' unless defined?( Strelka )
|
8
|
+
require 'strelka/mixins'
|
9
|
+
|
10
|
+
|
11
|
+
# This is the abstract base class for authentication and/or authorization providers
|
12
|
+
# for the {:auth plugin}[Strelka::App::Auth].
|
13
|
+
#
|
14
|
+
# To define your own session type, you'll need to inherit this class (either
|
15
|
+
# directly or via a subclass), name it <tt>Strelka::AuthProvider::{Something}</tt>,
|
16
|
+
# save it in a file named <tt>strelka/authprovider/{something}.rb</tt>, and
|
17
|
+
# override the required methods.
|
18
|
+
#
|
19
|
+
# Which methods you'll need to provide implementations for depends on whether
|
20
|
+
# your provider provides *authentication*, *authorization*, or both.
|
21
|
+
#
|
22
|
+
# == Authentication Providers
|
23
|
+
#
|
24
|
+
# Authentication providers should override either one or both of the following methods,
|
25
|
+
# depending on whether they will provide
|
26
|
+
#
|
27
|
+
# * #[]
|
28
|
+
# * #[]=
|
29
|
+
# * #save
|
30
|
+
# * #delete
|
31
|
+
# * #key?
|
32
|
+
# * #namespace=
|
33
|
+
# * #namespace
|
34
|
+
#
|
35
|
+
# These methods provide basic functionality, but you might find it more efficient
|
36
|
+
# to override them:
|
37
|
+
#
|
38
|
+
# * self.load_or_create
|
39
|
+
# * self.load
|
40
|
+
#
|
41
|
+
#
|
42
|
+
class Strelka::AuthProvider
|
43
|
+
extend Strelka::Delegation
|
44
|
+
include PluginFactory,
|
45
|
+
Strelka::Loggable,
|
46
|
+
Strelka::Constants,
|
47
|
+
Strelka::AbstractClass
|
48
|
+
|
49
|
+
|
50
|
+
### PluginFactory API -- return the Array of directories to search for concrete
|
51
|
+
### AuthProvider classes.
|
52
|
+
def self::derivative_dirs
|
53
|
+
return ['strelka/authprovider']
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
#################################################################
|
58
|
+
### I N S T A N C E M E T H O D S
|
59
|
+
#################################################################
|
60
|
+
|
61
|
+
### Create a new AuthProvider for the given +app+.
|
62
|
+
def initialize( app )
|
63
|
+
@app = app
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
######
|
68
|
+
public
|
69
|
+
######
|
70
|
+
|
71
|
+
##
|
72
|
+
# The Strelka::App that the AuthProvider belongs to.
|
73
|
+
attr_reader :app
|
74
|
+
|
75
|
+
|
76
|
+
### You should override this method if you want to authenticate the +request+. It should
|
77
|
+
### return a credentials object if authentication is successful, or throw an auth_required
|
78
|
+
### response if it fails.
|
79
|
+
def authenticate( request )
|
80
|
+
self.log.debug "No authentication provided, returning anonymous credentials."
|
81
|
+
return 'anonymous'
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
### If the +callback+ is set, call it with the specified +credentials+, and +request. Override this in
|
86
|
+
### your own AuthProvider to provide +additional_arguments+ to the +callback+, and/or to provide
|
87
|
+
### additional generic authorization.
|
88
|
+
def authorize( credentials, request, *additional_arguments, &callback )
|
89
|
+
return true unless callback
|
90
|
+
return true if callback.call( credentials, request, *additional_arguments )
|
91
|
+
self.require_authorization
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
#########
|
96
|
+
protected
|
97
|
+
#########
|
98
|
+
|
99
|
+
### Throw a 401 (Unauthorized) response with the specified +challenge+ as the
|
100
|
+
### www-Authenticate header.
|
101
|
+
def require_authentication( challenge )
|
102
|
+
finish_with( HTTP::AUTH_REQUIRED, "Requires authentication.", www_authenticate: challenge )
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
### Throw a 403 (Forbidden) response with the specified +message+.
|
107
|
+
def require_authorization( message="You are not authorized to access this resource." )
|
108
|
+
finish_with( HTTP::FORBIDDEN, message )
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
### Abort the current execution and return a response with the specified
|
113
|
+
### http_status code immediately. The specified +message+ will be logged,
|
114
|
+
### and will be included in any message that is returned as part of the
|
115
|
+
### response. The +headers+ hash will be used to set response headers.
|
116
|
+
def finish_with( http_status, message, headers={} )
|
117
|
+
status_info = { :status => http_status, :message => message, :headers => headers }
|
118
|
+
throw :finish, status_info
|
119
|
+
end
|
120
|
+
|
121
|
+
end # class Strelka::AuthProvider
|
122
|
+
|
data/lib/strelka/cookie.rb
CHANGED
@@ -24,7 +24,7 @@ class Strelka::Cookie
|
|
24
24
|
include Strelka::Loggable
|
25
25
|
|
26
26
|
# The format of the date field
|
27
|
-
COOKIE_DATE_FORMAT = '%a, %d
|
27
|
+
COOKIE_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
|
28
28
|
|
29
29
|
### RFC 2109: HTTP State Management Mechanism
|
30
30
|
# When it sends a request to an origin server, the user agent sends a
|
data/lib/strelka/cookieset.rb
CHANGED
@@ -82,7 +82,7 @@ class Strelka::CookieSet
|
|
82
82
|
|
83
83
|
|
84
84
|
### Index set operator method: set the cookie that corresponds to the given +name+
|
85
|
-
### to +value+. If +value+ is not an Strelka::Cookie, one
|
85
|
+
### to +value+. If +value+ is not an Strelka::Cookie, one is created and its
|
86
86
|
### value set to +value+.
|
87
87
|
def []=( name, value )
|
88
88
|
value = Strelka::Cookie.new( name.to_s, value ) unless value.is_a?( Strelka::Cookie )
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'strelka/constants'
|
4
|
+
require 'strelka/httprequest' unless defined?( Strelka::HTTPRequest )
|
5
|
+
|
6
|
+
|
7
|
+
# The mixin that adds methods to Strelka::HTTPRequest for
|
8
|
+
# authentication/authorization.
|
9
|
+
module Strelka::HTTPRequest::Auth
|
10
|
+
include Strelka::Constants
|
11
|
+
|
12
|
+
|
13
|
+
### Extension callback -- add instance variables to extended objects.
|
14
|
+
def initialize( * )
|
15
|
+
super
|
16
|
+
@authenticated_user = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
######
|
21
|
+
public
|
22
|
+
######
|
23
|
+
|
24
|
+
# The current session namespace
|
25
|
+
attr_accessor :authenticated_user
|
26
|
+
alias_method :authenticated?, :authenticated_user
|
27
|
+
|
28
|
+
|
29
|
+
end # module Strelka::HTTPRequest::Auth
|
30
|
+
|
31
|
+
|
data/lib/strelka/logging.rb
CHANGED
@@ -117,12 +117,68 @@ module Strelka::Logging
|
|
117
117
|
return self.format % args
|
118
118
|
end
|
119
119
|
end
|
120
|
-
end # class
|
120
|
+
end # class Formatter
|
121
121
|
|
122
122
|
|
123
123
|
# A ANSI-colorized formatter for Logger instances.
|
124
124
|
class ColorFormatter < Logger::Formatter
|
125
|
-
|
125
|
+
|
126
|
+
# Set some ANSI escape code constants (Shamelessly stolen from Perl's
|
127
|
+
# Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
|
128
|
+
ANSI_ATTRIBUTES = {
|
129
|
+
'clear' => 0,
|
130
|
+
'reset' => 0,
|
131
|
+
'bold' => 1,
|
132
|
+
'dark' => 2,
|
133
|
+
'underline' => 4,
|
134
|
+
'underscore' => 4,
|
135
|
+
'blink' => 5,
|
136
|
+
'reverse' => 7,
|
137
|
+
'concealed' => 8,
|
138
|
+
|
139
|
+
'black' => 30, 'on_black' => 40,
|
140
|
+
'red' => 31, 'on_red' => 41,
|
141
|
+
'green' => 32, 'on_green' => 42,
|
142
|
+
'yellow' => 33, 'on_yellow' => 43,
|
143
|
+
'blue' => 34, 'on_blue' => 44,
|
144
|
+
'magenta' => 35, 'on_magenta' => 45,
|
145
|
+
'cyan' => 36, 'on_cyan' => 46,
|
146
|
+
'white' => 37, 'on_white' => 47
|
147
|
+
}
|
148
|
+
|
149
|
+
|
150
|
+
### Create a string that contains the ANSI codes specified and return it
|
151
|
+
def self::ansi_code( *attributes )
|
152
|
+
attributes.flatten!
|
153
|
+
attributes.collect! {|at| at.to_s }
|
154
|
+
return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
|
155
|
+
attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
|
156
|
+
|
157
|
+
if attributes.empty?
|
158
|
+
return ''
|
159
|
+
else
|
160
|
+
return "\e[%sm" % attributes
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
### Colorize the given +string+ with the specified +attributes+ and
|
166
|
+
### return it, handling line-endings, color reset, etc.
|
167
|
+
def self::colorize( *args )
|
168
|
+
string = ''
|
169
|
+
|
170
|
+
if block_given?
|
171
|
+
string = yield
|
172
|
+
else
|
173
|
+
string = args.shift
|
174
|
+
end
|
175
|
+
|
176
|
+
ending = string[/(\s)$/] || ''
|
177
|
+
string = string.rstrip
|
178
|
+
|
179
|
+
return self.ansi_code( args.flatten ) + string + self.ansi_code( 'reset' ) + ending
|
180
|
+
end
|
181
|
+
|
126
182
|
|
127
183
|
# Color settings
|
128
184
|
LEVEL_FORMATS = {
|
@@ -169,7 +225,8 @@ module Strelka::Logging
|
|
169
225
|
|
170
226
|
return self.settings[ severity.downcase.to_sym ] % args
|
171
227
|
end
|
172
|
-
|
228
|
+
|
229
|
+
end # class Formatter
|
173
230
|
|
174
231
|
|
175
232
|
# An alternate formatter for Logger instances that outputs +div+ HTML
|
@@ -216,9 +273,9 @@ module Strelka::Logging
|
|
216
273
|
time.usec, # %2$d
|
217
274
|
Process.pid, # %3$d
|
218
275
|
Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
|
219
|
-
severity.downcase,
|
276
|
+
severity.downcase, # %5$s
|
220
277
|
progname, # %6$s
|
221
|
-
|
278
|
+
escape_html( msg ).gsub(/\n/, '<br />') # %7$s
|
222
279
|
]
|
223
280
|
|
224
281
|
return self.format % args
|
@@ -229,18 +286,16 @@ module Strelka::Logging
|
|
229
286
|
private
|
230
287
|
#######
|
231
288
|
|
232
|
-
###
|
233
|
-
|
234
|
-
def html_escape( string )
|
289
|
+
### Escape any HTML special characters in +string+.
|
290
|
+
def escape_html( string )
|
235
291
|
return string.
|
236
|
-
gsub(
|
237
|
-
gsub(
|
238
|
-
gsub(
|
292
|
+
gsub( '&', '&' ).
|
293
|
+
gsub( '<', '<' ).
|
294
|
+
gsub( '>', '>' )
|
239
295
|
end
|
240
296
|
|
241
|
-
end # class
|
297
|
+
end # class HtmlFormatter
|
242
298
|
|
243
|
-
end # module Strelka
|
244
299
|
|
245
|
-
#
|
300
|
+
end # module Strelka
|
246
301
|
|
data/lib/strelka/mixins.rb
CHANGED
@@ -10,7 +10,17 @@ require 'strelka/constants'
|
|
10
10
|
|
11
11
|
module Strelka
|
12
12
|
|
13
|
-
# Add logging to a Strelka class. Including classes get #log and
|
13
|
+
# Add logging to a Strelka class. Including classes get #log and
|
14
|
+
# #log_debug methods.
|
15
|
+
#
|
16
|
+
# class MyClass
|
17
|
+
# include Inversion::Loggable
|
18
|
+
#
|
19
|
+
# def a_method
|
20
|
+
# self.log.debug "Doing a_method stuff..."
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
14
24
|
module Loggable
|
15
25
|
|
16
26
|
# A logging proxy class that wraps calls to the logger into calls that include
|
@@ -75,70 +85,6 @@ module Strelka
|
|
75
85
|
|
76
86
|
end # module Loggable
|
77
87
|
|
78
|
-
# A collection of ANSI color utility functions
|
79
|
-
module ANSIColorUtilities
|
80
|
-
|
81
|
-
# Set some ANSI escape code constants (Shamelessly stolen from Perl's
|
82
|
-
# Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
|
83
|
-
ANSI_ATTRIBUTES = {
|
84
|
-
'clear' => 0,
|
85
|
-
'reset' => 0,
|
86
|
-
'bold' => 1,
|
87
|
-
'dark' => 2,
|
88
|
-
'underline' => 4,
|
89
|
-
'underscore' => 4,
|
90
|
-
'blink' => 5,
|
91
|
-
'reverse' => 7,
|
92
|
-
'concealed' => 8,
|
93
|
-
|
94
|
-
'black' => 30, 'on_black' => 40,
|
95
|
-
'red' => 31, 'on_red' => 41,
|
96
|
-
'green' => 32, 'on_green' => 42,
|
97
|
-
'yellow' => 33, 'on_yellow' => 43,
|
98
|
-
'blue' => 34, 'on_blue' => 44,
|
99
|
-
'magenta' => 35, 'on_magenta' => 45,
|
100
|
-
'cyan' => 36, 'on_cyan' => 46,
|
101
|
-
'white' => 37, 'on_white' => 47
|
102
|
-
}
|
103
|
-
|
104
|
-
###############
|
105
|
-
module_function
|
106
|
-
###############
|
107
|
-
|
108
|
-
### Create a string that contains the ANSI codes specified and return it
|
109
|
-
def ansi_code( *attributes )
|
110
|
-
attributes.flatten!
|
111
|
-
attributes.collect! {|at| at.to_s }
|
112
|
-
return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
|
113
|
-
attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
|
114
|
-
|
115
|
-
if attributes.empty?
|
116
|
-
return ''
|
117
|
-
else
|
118
|
-
return "\e[%sm" % attributes
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
### Colorize the given +string+ with the specified +attributes+ and return it, handling
|
124
|
-
### line-endings, color reset, etc.
|
125
|
-
def colorize( *args )
|
126
|
-
string = ''
|
127
|
-
|
128
|
-
if block_given?
|
129
|
-
string = yield
|
130
|
-
else
|
131
|
-
string = args.shift
|
132
|
-
end
|
133
|
-
|
134
|
-
ending = string[/(\s)$/] || ''
|
135
|
-
string = string.rstrip
|
136
|
-
|
137
|
-
return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
|
138
|
-
end
|
139
|
-
|
140
|
-
end # module ANSIColorUtilities
|
141
|
-
|
142
88
|
|
143
89
|
# Hides your class's ::new method and adds a +pure_virtual+ method generator for
|
144
90
|
# defining API methods. If subclasses of your class don't provide implementations of
|
@@ -244,6 +190,17 @@ module Strelka
|
|
244
190
|
end
|
245
191
|
|
246
192
|
|
193
|
+
### Define the given +delegated_methods+ as delegators to the like-named class
|
194
|
+
### method.
|
195
|
+
def def_class_delegators( *delegated_methods )
|
196
|
+
delegated_methods.each do |name|
|
197
|
+
define_method( name ) do |*args|
|
198
|
+
self.class.__send__( name, *args )
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
|
247
204
|
#######
|
248
205
|
private
|
249
206
|
#######
|
@@ -296,6 +253,10 @@ module Strelka
|
|
296
253
|
|
297
254
|
# A collection of miscellaneous functions that are useful for manipulating
|
298
255
|
# complex data structures.
|
256
|
+
#
|
257
|
+
# include Strelka::DataUtilities
|
258
|
+
# newhash = deep_copy( oldhash )
|
259
|
+
#
|
299
260
|
module DataUtilities
|
300
261
|
|
301
262
|
###############
|
@@ -331,6 +292,15 @@ module Strelka
|
|
331
292
|
|
332
293
|
|
333
294
|
# A collection of methods for declaring other methods.
|
295
|
+
#
|
296
|
+
# class MyClass
|
297
|
+
# include Strelka::MethodUtilities
|
298
|
+
#
|
299
|
+
# singleton_attr_accessor :types
|
300
|
+
# end
|
301
|
+
#
|
302
|
+
# MyClass.types = [ :pheno, :proto, :stereo ]
|
303
|
+
#
|
334
304
|
module MethodUtilities
|
335
305
|
|
336
306
|
### Creates instance variables and corresponding methods that return their
|