reflexive 0.0.1
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.
- data/lib/reflexive/application.rb +171 -0
- data/lib/reflexive/coderay_html_encoder.rb +46 -0
- data/lib/reflexive/coderay_ruby_scanner.rb +234 -0
- data/lib/reflexive/columnizer.rb +73 -0
- data/lib/reflexive/constantize.rb +71 -0
- data/lib/reflexive/descendants.rb +39 -0
- data/lib/reflexive/faster_open_struct.rb +116 -0
- data/lib/reflexive/helpers.rb +189 -0
- data/lib/reflexive/methods.rb +140 -0
- data/lib/reflexive/parse_tree_top_down_walker.rb +392 -0
- data/lib/reflexive/reflexive_ripper.rb +274 -0
- data/lib/reflexive/routing_helpers.rb +70 -0
- data/lib/reflexive/variables_scope.rb +16 -0
- data/lib/reflexive.rb +1 -0
- data/public/javascripts/reflexive/jquery-1.4.2.js +6240 -0
- data/public/javascripts/reflexive/reflexive.js +12 -0
- data/public/stylesheets/reflexive/coderay.css +130 -0
- data/public/stylesheets/reflexive/reflexive.css +111 -0
- data/reflexive.gemspec +25 -0
- data/views/constants_methods.html.erb +0 -0
- data/views/constants_show.erb +96 -0
- data/views/dashboard.erb +50 -0
- data/views/directories_show.erb +16 -0
- data/views/error.erb +5 -0
- data/views/files_show.erb +27 -0
- data/views/layout.erb +20 -0
- data/views/methods_apidock.erb +17 -0
- data/views/methods_definition.erb +37 -0
- data/views/methods_rubydoc.erb +16 -0
- metadata +231 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
require "sinatra/reloader" if ENV["SINATRA_RELOADER"]
|
3
|
+
require "sinatra_more/markup_plugin"
|
4
|
+
|
5
|
+
require "coderay"
|
6
|
+
require "andand"
|
7
|
+
require "ostruct"
|
8
|
+
require "open-uri"
|
9
|
+
|
10
|
+
require "looksee"
|
11
|
+
Looksee.styles.each { |k, _| Looksee.styles[k] = "%s" }
|
12
|
+
|
13
|
+
require "reflexive/faster_open_struct"
|
14
|
+
require "reflexive/helpers"
|
15
|
+
require "reflexive/columnizer"
|
16
|
+
require "reflexive/constantize"
|
17
|
+
require "reflexive/descendants"
|
18
|
+
require "reflexive/methods"
|
19
|
+
|
20
|
+
if ENV["SINATRA_RELOADER"]
|
21
|
+
require "rails/all"
|
22
|
+
require "arel"
|
23
|
+
|
24
|
+
module ::Kernel
|
25
|
+
def r(*args)
|
26
|
+
raise((args.size == 1 ? args[0] : args).inspect)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Reflexive
|
32
|
+
class Application < Sinatra::Base
|
33
|
+
register SinatraMore::MarkupPlugin
|
34
|
+
include Reflexive::Helpers
|
35
|
+
|
36
|
+
configure(:development) do
|
37
|
+
if ENV["SINATRA_RELOADER"]
|
38
|
+
register Sinatra::Reloader
|
39
|
+
also_reload "lib/**/*.rb"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def root
|
45
|
+
require "pathname"
|
46
|
+
Pathname("../../../").expand_path(__FILE__)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
set :public, self.root + "public"
|
51
|
+
set :views, self.root + "views"
|
52
|
+
|
53
|
+
def self.action(path, &block)
|
54
|
+
get("/reflexive/#{ path }", &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def e(message)
|
58
|
+
@message = message
|
59
|
+
erb :error
|
60
|
+
end
|
61
|
+
|
62
|
+
action "dashboard" do
|
63
|
+
erb :dashboard
|
64
|
+
end
|
65
|
+
|
66
|
+
action "constant_lookup" do
|
67
|
+
if klass = Reflexive.constant_lookup(*params.values_at(:name, :scope))
|
68
|
+
redirect(constant_path(klass.to_s))
|
69
|
+
else
|
70
|
+
e "failed to lookup constant `#{ params[:name] }' in scope #{ params[:scope] }"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
action "files/*" do |path|
|
75
|
+
@path = "/" + path
|
76
|
+
if File.stat(@path).directory?
|
77
|
+
erb :directories_show
|
78
|
+
else
|
79
|
+
@source = highlight_file(@path)
|
80
|
+
erb :files_show
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
get "/reflexive/load_path_lookup" do
|
85
|
+
path = params[:path]
|
86
|
+
feature = Reflexive.loaded_features_lookup(path) || Reflexive.load_path_lookup(path)
|
87
|
+
if feature
|
88
|
+
redirect(file_path(feature))
|
89
|
+
else
|
90
|
+
e "failed to find feature: #{ path }"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)/definition> do |klass, method|
|
95
|
+
find_klass(klass)
|
96
|
+
@method_name = method
|
97
|
+
@path, @line = @klass.method(@method_name).source_location
|
98
|
+
@source = highlight_file(@path, :highlight_lines => [@line])
|
99
|
+
erb :methods_definition
|
100
|
+
end
|
101
|
+
|
102
|
+
get %r</reflexive/constants/([^/&#]+)/instance_methods/([^/&#]+)/definition> do |klass, method|
|
103
|
+
find_klass(klass)
|
104
|
+
@method_name = method
|
105
|
+
@path, @line = @klass.instance_method(@method_name).source_location
|
106
|
+
@source = highlight_file(@path, :highlight_lines => [@line])
|
107
|
+
erb :methods_definition
|
108
|
+
end
|
109
|
+
|
110
|
+
get %r</reflexive/constants/([^/&#]+)/methods/([^/&#]+)/apidock> do |klass, method|
|
111
|
+
find_klass(klass)
|
112
|
+
@method_name = method
|
113
|
+
erb :methods_apidock
|
114
|
+
end
|
115
|
+
|
116
|
+
get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)> do |klass, method|
|
117
|
+
find_klass(klass)
|
118
|
+
begin
|
119
|
+
if @klass.method(method).source_location
|
120
|
+
redirect(class_method_definition_path(klass, method) +
|
121
|
+
"#highlighted")
|
122
|
+
else
|
123
|
+
redirect(method_documentation_path(klass, method))
|
124
|
+
end
|
125
|
+
rescue NameError
|
126
|
+
e "failed to find `#{ method }' class method for #{ klass }"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
get %r</reflexive/constants/([^/&#]+)/instance_methods/([^/&#]+)> do |klass, method|
|
131
|
+
find_klass(klass)
|
132
|
+
begin
|
133
|
+
if @klass.instance_method(method).source_location
|
134
|
+
redirect(instance_method_definition_path(klass, method) +
|
135
|
+
"#highlighted")
|
136
|
+
else
|
137
|
+
redirect(method_documentation_path(klass, method))
|
138
|
+
end
|
139
|
+
rescue NameError
|
140
|
+
e "failed to find `#{ method }' instance method for #{ klass }"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
get %r</reflexive/constants/([^/&#]+)> do |klass|
|
145
|
+
find_klass(klass)
|
146
|
+
|
147
|
+
exclude_trite = ![ BasicObject, Object ].include?(@klass)
|
148
|
+
@methods = Reflexive::Methods.new(@klass, :exclude_trite => exclude_trite)
|
149
|
+
|
150
|
+
ancestors_without_self_and_super = @klass.ancestors[2..-1] || []
|
151
|
+
class_ancestors = ancestors_without_self_and_super.select { |ancestor| ancestor.class == Class }
|
152
|
+
@class_ancestors = class_ancestors if class_ancestors.size > 0
|
153
|
+
|
154
|
+
if @klass.respond_to?(:superclass) &&
|
155
|
+
@klass.superclass != Object &&
|
156
|
+
@klass.superclass != nil
|
157
|
+
@superclass = @klass.superclass
|
158
|
+
end
|
159
|
+
|
160
|
+
erb :constants_show
|
161
|
+
end
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
error(404) { @app.call(env) if @app }
|
166
|
+
|
167
|
+
def find_klass(klass = params[:klass])
|
168
|
+
@klass = Reflexive.constantize(klass) if klass
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "reflexive/routing_helpers"
|
2
|
+
|
3
|
+
require "coderay"
|
4
|
+
require "coderay/encoder"
|
5
|
+
require "coderay/encoders/html"
|
6
|
+
|
7
|
+
module Reflexive
|
8
|
+
class CodeRayHtmlEncoder < ::CodeRay::Encoders::HTML
|
9
|
+
include RoutingHelpers
|
10
|
+
|
11
|
+
def token(text, type = :plain, tags = {})
|
12
|
+
if constant_access = tags[:constant_access]
|
13
|
+
name, scope = constant_access.values_at(:name, :scope)
|
14
|
+
@out << "<a href='#{ constant_lookup_path(name, scope) }'>"
|
15
|
+
super(text, type)
|
16
|
+
@out << "</a>"
|
17
|
+
elsif type == :meta_scope
|
18
|
+
# pass
|
19
|
+
elsif type == :content && tags[:load_path]
|
20
|
+
@out << "<a href='#{ load_path_lookup_path(text) }'>"
|
21
|
+
super(text, type)
|
22
|
+
@out << "</a>"
|
23
|
+
elsif method_call = tags[:method_call]
|
24
|
+
@out << "<a href='#{ method_call_path(method_call) }'>"
|
25
|
+
super(text, type)
|
26
|
+
@out << "</a>"
|
27
|
+
elsif local_variable_assignment = tags[:local_variable_assignment]
|
28
|
+
@out << "<span id='lv:#{ local_variable_assignment }'>"
|
29
|
+
super(text, type)
|
30
|
+
@out << "</span>"
|
31
|
+
elsif local_variable_access = tags[:local_variable_access]
|
32
|
+
@out << "<a href='#lv:#{ local_variable_access }' class='lva'>"
|
33
|
+
super(text, type)
|
34
|
+
@out << "</a>"
|
35
|
+
else
|
36
|
+
super(text, type) rescue raise([text, type].inspect)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def compile(tokens, options)
|
41
|
+
for token in tokens
|
42
|
+
token(*token)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require "coderay"
|
2
|
+
|
3
|
+
module Reflexive
|
4
|
+
require "reflexive/reflexive_ripper"
|
5
|
+
|
6
|
+
# TODO Heredocs in args are royally screwed
|
7
|
+
class CodeRayRubyScanner < ::CodeRay::Scanners::Scanner
|
8
|
+
SCANNER_EVENT_TO_CODERAY_TOKEN =
|
9
|
+
{
|
10
|
+
:kw => :reserved,
|
11
|
+
:nl => :space,
|
12
|
+
:sp => :space,
|
13
|
+
:ignored_nl => :space,
|
14
|
+
# :tstring_beg => :delimiter,
|
15
|
+
# :tstring_end => :delimiter,
|
16
|
+
:tstring_content => :content,
|
17
|
+
# Кipper reports rbrace always, CodeRay differentiates
|
18
|
+
# between blocks, and rbraces in string interpol
|
19
|
+
# :embexpr_beg => :inline_delimiter,
|
20
|
+
:lbrace => :operator,
|
21
|
+
:rbrace => :operator,
|
22
|
+
:lparen => :operator,
|
23
|
+
:rparen => :operator,
|
24
|
+
:lbracket => :operator,
|
25
|
+
:rbracket => :operator,
|
26
|
+
:comma => :operator,
|
27
|
+
:op => :operator,
|
28
|
+
:int => :integer,
|
29
|
+
:period => :operator,
|
30
|
+
:const => :constant,
|
31
|
+
:cvar => :class_variable,
|
32
|
+
:ivar => :instance_variable,
|
33
|
+
:gvar => :global_variable,
|
34
|
+
:embvar => :escape, # ?
|
35
|
+
:embdoc_beg => :comment,
|
36
|
+
:embdoc => :comment,
|
37
|
+
:embdoc_end => :comment,
|
38
|
+
:qwords_beg => :content,
|
39
|
+
:words_sep => :content,
|
40
|
+
:CHAR => :integer,
|
41
|
+
# * :constant => :class, CodeRay reports `class` token for
|
42
|
+
# class def, we report just `const` always
|
43
|
+
# * Ripper doesn't have `char` event
|
44
|
+
# * `42 if true` - CodeRay reports `pre_constant`
|
45
|
+
}
|
46
|
+
|
47
|
+
def coderay_tokens(scanner_events)
|
48
|
+
@coderay_tokens = []
|
49
|
+
in_backtick = false
|
50
|
+
in_symbol = false
|
51
|
+
in_embexpr_nesting = 0
|
52
|
+
scanner_events.each do |scanner_event|
|
53
|
+
token_val, event, tags =
|
54
|
+
ReflexiveRipper.destruct_scanner_event(scanner_event)
|
55
|
+
|
56
|
+
# if event == :meta_scope
|
57
|
+
# @coderay_tokens << [token_val, event]
|
58
|
+
# next
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# if token_val == :method_call
|
62
|
+
# @coderay_tokens << [token_val, event, tags]
|
63
|
+
# next
|
64
|
+
# end
|
65
|
+
|
66
|
+
ripper_token = SCANNER_EVENT_TO_CODERAY_TOKEN[event.to_sym] || event.to_sym
|
67
|
+
if in_backtick && event == :lparen
|
68
|
+
@coderay_tokens.pop # remove [:open, :shell], [token_val, :delimiter]
|
69
|
+
@coderay_tokens.pop # and replace with method declaration
|
70
|
+
@coderay_tokens << ["`", :method]
|
71
|
+
@coderay_tokens << ["(", :operator]
|
72
|
+
elsif in_embexpr_nesting > 0 && event == :rbrace
|
73
|
+
@coderay_tokens << [token_val, :inline_delimiter]
|
74
|
+
@coderay_tokens << [:close, :inline]
|
75
|
+
in_embexpr_nesting -= 1
|
76
|
+
elsif event == :embexpr_beg
|
77
|
+
@coderay_tokens << [:open, :inline]
|
78
|
+
@coderay_tokens << [token_val, :inline_delimiter]
|
79
|
+
in_embexpr_nesting += 1
|
80
|
+
elsif in_symbol && [:ident, :const, :ivar, :cvar, :gvar, :op, :kw].include?(event)
|
81
|
+
# parse.y
|
82
|
+
#
|
83
|
+
# symbol : tSYMBEG sym
|
84
|
+
#
|
85
|
+
# sym : fname
|
86
|
+
# | tIVAR
|
87
|
+
# | tGVAR
|
88
|
+
# | tCVAR
|
89
|
+
# ;
|
90
|
+
#
|
91
|
+
# fname : tIDENTIFIER
|
92
|
+
# | tCONSTANT
|
93
|
+
# | tFID
|
94
|
+
# | op
|
95
|
+
# {
|
96
|
+
# lex_state = EXPR_ENDFN;
|
97
|
+
# $$ = $1;
|
98
|
+
# }
|
99
|
+
# | reswords
|
100
|
+
# {
|
101
|
+
# lex_state = EXPR_ENDFN;
|
102
|
+
# /*%%%*/
|
103
|
+
# $$ = $<id>1;
|
104
|
+
# /*%
|
105
|
+
# $$ = $1;
|
106
|
+
# %*/
|
107
|
+
# }
|
108
|
+
# ;
|
109
|
+
@coderay_tokens << [token_val, :content]
|
110
|
+
@coderay_tokens << [:close, :symbol]
|
111
|
+
in_symbol = false
|
112
|
+
elsif ripper_token == :regexp_beg
|
113
|
+
@coderay_tokens << [:open, :regexp]
|
114
|
+
@coderay_tokens << [token_val, :delimiter]
|
115
|
+
elsif ripper_token == :regexp_end
|
116
|
+
@coderay_tokens << [token_val, :delimiter]
|
117
|
+
@coderay_tokens << [:close, :regexp]
|
118
|
+
elsif ripper_token == :tstring_beg
|
119
|
+
@coderay_tokens << [:open, :string]
|
120
|
+
@coderay_tokens << [token_val, :delimiter]
|
121
|
+
elsif ripper_token == :tstring_end
|
122
|
+
@coderay_tokens << [token_val, :delimiter]
|
123
|
+
if in_backtick
|
124
|
+
@coderay_tokens << [:close, :shell]
|
125
|
+
in_backtick = false
|
126
|
+
elsif in_symbol
|
127
|
+
@coderay_tokens << [:close, :symbol]
|
128
|
+
in_symbol = false
|
129
|
+
else
|
130
|
+
@coderay_tokens << [:close, :string]
|
131
|
+
end
|
132
|
+
elsif ripper_token == :symbeg
|
133
|
+
if in_symbol # nesting not supported
|
134
|
+
@coderay_tokens << [token_val, :symbol]
|
135
|
+
else
|
136
|
+
@coderay_tokens << [:open, :symbol]
|
137
|
+
@coderay_tokens << [token_val, :delimiter]
|
138
|
+
in_symbol = true
|
139
|
+
end
|
140
|
+
elsif ripper_token == :backtick
|
141
|
+
if in_backtick # nesting not supported
|
142
|
+
@coderay_tokens << [token_val, :operator]
|
143
|
+
else
|
144
|
+
@coderay_tokens << [:open, :shell]
|
145
|
+
@coderay_tokens << [token_val, :delimiter]
|
146
|
+
in_backtick = true
|
147
|
+
end
|
148
|
+
else
|
149
|
+
if @coderay_tokens.size > 0 && @coderay_tokens[-1][1] == ripper_token
|
150
|
+
@coderay_tokens.last[0] << token_val # same consequent tokens get squeezed
|
151
|
+
else
|
152
|
+
@coderay_tokens << [token_val, ripper_token]
|
153
|
+
@coderay_tokens.last << tags if tags
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
tokens = @coderay_tokens
|
159
|
+
# tokens = squeeze_constants(tokens)
|
160
|
+
# tokens = inject_constant_tags(tokens)
|
161
|
+
tokens = inject_load_path_tags(tokens)
|
162
|
+
tokens
|
163
|
+
end
|
164
|
+
|
165
|
+
def squeeze_constants(coderay_tokens)
|
166
|
+
tokens = []
|
167
|
+
coderay_tokens.reverse_each do |token|
|
168
|
+
if tokens.size > 0
|
169
|
+
if token[1] == :constant &&
|
170
|
+
tokens[-1][1] == :constant
|
171
|
+
tokens[-1][0] = "#{ token[0] }#{ tokens[-1][0] }"
|
172
|
+
elsif token == [ "::", :operator ] &&
|
173
|
+
tokens[-1][1] == :constant
|
174
|
+
tokens[-1][0] = "#{ token[0] }#{ tokens[-1][0] }"
|
175
|
+
else
|
176
|
+
tokens << token
|
177
|
+
end
|
178
|
+
else
|
179
|
+
tokens << token
|
180
|
+
end
|
181
|
+
end
|
182
|
+
tokens.reverse
|
183
|
+
end
|
184
|
+
|
185
|
+
def inject_constant_tags(coderay_tokens)
|
186
|
+
scope = nil
|
187
|
+
coderay_tokens.each do |token|
|
188
|
+
if token[1] == :constant
|
189
|
+
token << { :scope => scope }
|
190
|
+
elsif token[0] == :method_call &&
|
191
|
+
token[1] == :open
|
192
|
+
begin
|
193
|
+
token[2][:scope] = scope
|
194
|
+
rescue Exception
|
195
|
+
r(token)
|
196
|
+
end
|
197
|
+
|
198
|
+
elsif token[1] == :meta_scope
|
199
|
+
scope = token[0]
|
200
|
+
scope = nil if scope.empty?
|
201
|
+
end
|
202
|
+
end
|
203
|
+
coderay_tokens
|
204
|
+
end
|
205
|
+
|
206
|
+
def inject_load_path_tags(coderay_tokens)
|
207
|
+
state = nil
|
208
|
+
coderay_tokens.each do |token|
|
209
|
+
value, type = token
|
210
|
+
if state == nil && type == :ident && value =~ /\A(require|load)\z/
|
211
|
+
state = :ident
|
212
|
+
elsif state == :ident && [ :operator, :space ].include?(type)
|
213
|
+
# skip
|
214
|
+
elsif state == :ident && value == :open && type == :string
|
215
|
+
state = :open_string
|
216
|
+
elsif state == :open_string && type == :delimiter
|
217
|
+
# skip
|
218
|
+
elsif state == :open_string && type == :content
|
219
|
+
token << { :load_path => true }
|
220
|
+
else
|
221
|
+
state = nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
coderay_tokens
|
225
|
+
end
|
226
|
+
|
227
|
+
def scan_tokens(tokens, options)
|
228
|
+
parser = ReflexiveRipper.new(code)
|
229
|
+
parser.parse
|
230
|
+
tokens.replace(coderay_tokens(parser.scanner_events))
|
231
|
+
tokens
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module Reflexive
|
4
|
+
module Columnizer
|
5
|
+
class << self
|
6
|
+
#
|
7
|
+
# Arrange the given strings in columns, restricted to the given
|
8
|
+
# width. Smart enough to ignore content in terminal control
|
9
|
+
# sequences.
|
10
|
+
#
|
11
|
+
def columnize(strings, width)
|
12
|
+
return '' if strings.empty?
|
13
|
+
num_columns = 1
|
14
|
+
layout = [strings]
|
15
|
+
loop do
|
16
|
+
break if layout.first.length <= 1
|
17
|
+
next_layout = layout_in_columns(strings, num_columns + 1)
|
18
|
+
break if layout_width(next_layout) > width
|
19
|
+
layout = next_layout
|
20
|
+
num_columns += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
pad_strings(layout)
|
24
|
+
rectangularize_layout(layout)
|
25
|
+
layout.transpose.map do |row|
|
26
|
+
' ' + row.compact.join(' ')
|
27
|
+
end.join("\n") << "\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
private # -----------------------------------------------------
|
31
|
+
|
32
|
+
def layout_in_columns(strings, num_columns)
|
33
|
+
strings_per_column = (strings.length / num_columns.to_f).ceil
|
34
|
+
(0...num_columns).map { |i| strings[i*strings_per_column...(i+1)*strings_per_column] || [] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def layout_width(layout)
|
38
|
+
widths = layout_column_widths(layout)
|
39
|
+
widths.inject(0) { |sum, w| sum + w } + 2*layout.length
|
40
|
+
end
|
41
|
+
|
42
|
+
def layout_column_widths(layout)
|
43
|
+
layout.map do |column|
|
44
|
+
column.map { |string| display_width(string) }.max || 0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def display_width(string)
|
49
|
+
CGI.unescapeHTML(string.gsub(/<\/?.+?>/, '')).size
|
50
|
+
end
|
51
|
+
|
52
|
+
def pad_strings(layout)
|
53
|
+
widths = layout_column_widths(layout)
|
54
|
+
layout.each_with_index do |column, i|
|
55
|
+
column_width = widths[i]
|
56
|
+
column.each do |string|
|
57
|
+
padding = column_width - display_width(string)
|
58
|
+
string << ' '*padding
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def rectangularize_layout(layout)
|
64
|
+
return if layout.length == 1
|
65
|
+
height = layout[0].length
|
66
|
+
layout[1..-1].each do |column|
|
67
|
+
column.length == height or
|
68
|
+
column[height - 1] = nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Reflexive
|
2
|
+
module_function
|
3
|
+
|
4
|
+
def constant_lookup(name, scope)
|
5
|
+
if name =~ /^::/
|
6
|
+
begin
|
7
|
+
return Reflexive.constantize(name)
|
8
|
+
rescue NameError, ArgumentError
|
9
|
+
return nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
scope_parts = scope.split("::")
|
14
|
+
|
15
|
+
begin
|
16
|
+
name_with_scope = "#{ scope_parts.join("::") }::#{ name }"
|
17
|
+
return Reflexive.constantize(name_with_scope)
|
18
|
+
rescue NameError, ArgumentError
|
19
|
+
# For defined top-level module, when looked up from another class:
|
20
|
+
# ArgumentError: Object is not missing constant TopLevelConst!
|
21
|
+
# from .../activesupport-2.3.5/lib/active_support/dependencies.rb:417:in `load_missing_constant'
|
22
|
+
retry if scope_parts.pop
|
23
|
+
end
|
24
|
+
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# from C:\Users\work\Documents\ubuntu_shared\edge\vendor\rails\activesupport\lib\active_support\inflector\methods.rb
|
29
|
+
# Ruby 1.9 introduces an inherit argument for Module#const_get and
|
30
|
+
# #const_defined? and changes their default behavior.
|
31
|
+
if Module.method(:const_get).arity == 1
|
32
|
+
# Tries to find a constant with the name specified in the argument string:
|
33
|
+
#
|
34
|
+
# "Module".constantize # => Module
|
35
|
+
# "Test::Unit".constantize # => Test::Unit
|
36
|
+
#
|
37
|
+
# The name is assumed to be the one of a top-level constant, no matter whether
|
38
|
+
# it starts with "::" or not. No lexical context is taken into account:
|
39
|
+
#
|
40
|
+
# C = 'outside'
|
41
|
+
# module M
|
42
|
+
# C = 'inside'
|
43
|
+
# C # => 'inside'
|
44
|
+
# "C".constantize # => 'outside', same as ::C
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# NameError is raised when the name is not in CamelCase or the constant is
|
48
|
+
# unknown.
|
49
|
+
def constantize(camel_cased_word)
|
50
|
+
names = camel_cased_word.split('::')
|
51
|
+
names.shift if names.empty? || names.first.empty?
|
52
|
+
|
53
|
+
constant = Object
|
54
|
+
names.each do |name|
|
55
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
56
|
+
end
|
57
|
+
constant
|
58
|
+
end
|
59
|
+
else
|
60
|
+
def constantize(camel_cased_word) #:nodoc:
|
61
|
+
names = camel_cased_word.split('::')
|
62
|
+
names.shift if names.empty? || names.first.empty?
|
63
|
+
|
64
|
+
constant = Object
|
65
|
+
names.each do |name|
|
66
|
+
constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
|
67
|
+
end
|
68
|
+
constant
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Reflexive
|
2
|
+
module_function
|
3
|
+
|
4
|
+
# List all descedents of this class.
|
5
|
+
#
|
6
|
+
# class X ; end
|
7
|
+
# class A < X; end
|
8
|
+
# class B < X; end
|
9
|
+
# X.descendents #=> [A,B]
|
10
|
+
#
|
11
|
+
# You may also limit the generational distance
|
12
|
+
# the subclass may be from the parent class.
|
13
|
+
#
|
14
|
+
# class X ; end
|
15
|
+
# class A < X; end
|
16
|
+
# class B < A; end
|
17
|
+
# X.descendents #=> [A, B]
|
18
|
+
# X.descendents(1) #=> [A]
|
19
|
+
#
|
20
|
+
# NOTE: This is a intensive operation. Do not
|
21
|
+
# expect it to be super fast.
|
22
|
+
|
23
|
+
def descendants(klass, generations=nil)
|
24
|
+
subclass = []
|
25
|
+
|
26
|
+
ObjectSpace.each_object(Class) do |c|
|
27
|
+
next if c == klass
|
28
|
+
|
29
|
+
ancestors = c.ancestors[0 .. (generations || -1)]
|
30
|
+
subclass << c if ancestors.include?(klass)
|
31
|
+
|
32
|
+
ancestors = c.singleton_class.ancestors[0 .. (generations || -1)]
|
33
|
+
subclass << c if ancestors.include?(klass)
|
34
|
+
end
|
35
|
+
|
36
|
+
subclass
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|