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.
@@ -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
+