reflexive 0.0.1

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