looksee 0.2.1 → 1.0.0
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/CHANGELOG +14 -0
- data/LICENSE +22 -0
- data/README.markdown +161 -0
- data/Rakefile +9 -36
- data/ext/extconf.rb +9 -0
- data/ext/mri/1.9.2/debug.h +36 -0
- data/ext/mri/1.9.2/id.h +170 -0
- data/ext/mri/1.9.2/method.h +103 -0
- data/ext/mri/1.9.2/node.h +483 -0
- data/ext/mri/1.9.2/thread_pthread.h +27 -0
- data/ext/mri/1.9.2/vm_core.h +707 -0
- data/ext/mri/1.9.2/vm_opts.h +51 -0
- data/ext/mri/env-1.8.h +27 -0
- data/ext/mri/eval_c-1.8.h +27 -0
- data/ext/mri/mri.c +269 -0
- data/ext/{looksee → mri}/node-1.9.h +0 -0
- data/ext/rbx/rbx.c +13 -0
- data/lib/looksee.rb +4 -385
- data/lib/looksee/adapter.rb +10 -0
- data/lib/looksee/adapter/base.rb +100 -0
- data/lib/looksee/adapter/rubinius.rb +73 -0
- data/lib/looksee/clean.rb +122 -0
- data/lib/looksee/columnizer.rb +73 -0
- data/lib/looksee/core_ext.rb +59 -0
- data/lib/looksee/editor.rb +58 -0
- data/lib/looksee/help.rb +54 -0
- data/lib/looksee/inspector.rb +55 -0
- data/lib/looksee/lookup_path.rb +95 -0
- data/lib/looksee/rbx.bundle +0 -0
- data/lib/looksee/shortcuts.rb +1 -105
- data/lib/looksee/version.rb +9 -1
- data/lib/looksee/wirble_compatibility.rb +2 -3
- data/spec/adapter_spec.rb +546 -0
- data/spec/columnizer_spec.rb +52 -0
- data/spec/core_ext_spec.rb +41 -0
- data/spec/editor_spec.rb +128 -0
- data/spec/inspector_spec.rb +178 -0
- data/spec/lookup_path_spec.rb +84 -0
- data/spec/spec_helper.rb +13 -127
- data/spec/support/core_ext.rb +25 -0
- data/spec/support/temporary_classes.rb +102 -0
- data/spec/support/test_adapter.rb +72 -0
- data/spec/wirble_compatibility_spec.rb +20 -23
- metadata +91 -57
- data/.autotest +0 -9
- data/History.txt +0 -22
- data/Manifest.txt +0 -21
- data/README.rdoc +0 -129
- data/ext/looksee/extconf.rb +0 -6
- data/ext/looksee/looksee.c +0 -144
- data/looksee.gemspec +0 -44
- data/script/console +0 -7
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/spec/looksee_spec.rb +0 -425
- data/tasks/extconf.rake +0 -13
- data/tasks/extconf/looksee.rake +0 -43
@@ -0,0 +1,51 @@
|
|
1
|
+
/*-*-c-*-*/
|
2
|
+
/**********************************************************************
|
3
|
+
|
4
|
+
vm_opts.h - VM optimize option
|
5
|
+
|
6
|
+
$Author: akr $
|
7
|
+
|
8
|
+
Copyright (C) 2004-2007 Koichi Sasada
|
9
|
+
|
10
|
+
**********************************************************************/
|
11
|
+
|
12
|
+
|
13
|
+
#ifndef RUBY_VM_OPTS_H
|
14
|
+
#define RUBY_VM_OPTS_H
|
15
|
+
|
16
|
+
/* Compile options.
|
17
|
+
* You can change these options at runtime by VM::CompileOption.
|
18
|
+
* Following definitions are default values.
|
19
|
+
*/
|
20
|
+
|
21
|
+
#define OPT_TRACE_INSTRUCTION 1
|
22
|
+
#define OPT_TAILCALL_OPTIMIZATION 0
|
23
|
+
#define OPT_PEEPHOLE_OPTIMIZATION 1
|
24
|
+
#define OPT_SPECIALISED_INSTRUCTION 1
|
25
|
+
#define OPT_INLINE_CONST_CACHE 1
|
26
|
+
|
27
|
+
|
28
|
+
/* Build Options.
|
29
|
+
* You can't change these options at runtime.
|
30
|
+
*/
|
31
|
+
|
32
|
+
/* C compiler depend */
|
33
|
+
#define OPT_DIRECT_THREADED_CODE 1
|
34
|
+
#define OPT_TOKEN_THREADED_CODE 0
|
35
|
+
#define OPT_CALL_THREADED_CODE 0
|
36
|
+
|
37
|
+
/* VM running option */
|
38
|
+
#define OPT_CHECKED_RUN 1
|
39
|
+
#define OPT_INLINE_METHOD_CACHE 1
|
40
|
+
#define OPT_BLOCKINLINING 0
|
41
|
+
|
42
|
+
/* architecture independent, affects generated code */
|
43
|
+
#define OPT_OPERANDS_UNIFICATION 0
|
44
|
+
#define OPT_INSTRUCTIONS_UNIFICATION 0
|
45
|
+
#define OPT_UNIFY_ALL_COMBINATION 0
|
46
|
+
#define OPT_STACK_CACHING 0
|
47
|
+
|
48
|
+
/* misc */
|
49
|
+
#define SUPPORT_JOKE 0
|
50
|
+
|
51
|
+
#endif /* RUBY_VM_OPTS_H */
|
data/ext/mri/env-1.8.h
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
struct FRAME {
|
2
|
+
VALUE self;
|
3
|
+
int argc;
|
4
|
+
ID last_func;
|
5
|
+
ID orig_func;
|
6
|
+
VALUE last_class;
|
7
|
+
struct FRAME *prev;
|
8
|
+
struct FRAME *tmp;
|
9
|
+
struct RNode *node;
|
10
|
+
int iter;
|
11
|
+
int flags;
|
12
|
+
unsigned long uniq;
|
13
|
+
};
|
14
|
+
|
15
|
+
struct SCOPE {
|
16
|
+
struct RBasic super;
|
17
|
+
ID *local_tbl;
|
18
|
+
VALUE *local_vars;
|
19
|
+
int flags;
|
20
|
+
};
|
21
|
+
|
22
|
+
struct RVarmap {
|
23
|
+
struct RBasic super;
|
24
|
+
ID id;
|
25
|
+
VALUE val;
|
26
|
+
struct RVarmap *next;
|
27
|
+
};
|
@@ -0,0 +1,27 @@
|
|
1
|
+
struct BLOCK {
|
2
|
+
NODE *var;
|
3
|
+
NODE *body;
|
4
|
+
VALUE self;
|
5
|
+
struct FRAME frame;
|
6
|
+
struct SCOPE *scope;
|
7
|
+
VALUE klass;
|
8
|
+
NODE *cref;
|
9
|
+
int iter;
|
10
|
+
int vmode;
|
11
|
+
int flags;
|
12
|
+
int uniq;
|
13
|
+
struct RVarmap *dyna_vars;
|
14
|
+
VALUE orig_thread;
|
15
|
+
VALUE wrapper;
|
16
|
+
VALUE block_obj;
|
17
|
+
struct BLOCK *outer;
|
18
|
+
struct BLOCK *prev;
|
19
|
+
};
|
20
|
+
|
21
|
+
struct METHOD {
|
22
|
+
VALUE klass, rklass;
|
23
|
+
VALUE recv;
|
24
|
+
ID id, oid;
|
25
|
+
int safe_level;
|
26
|
+
NODE *body;
|
27
|
+
};
|
data/ext/mri/mri.c
ADDED
@@ -0,0 +1,269 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
#if RUBY_VERSION >= 192
|
4
|
+
# include "vm_core.h"
|
5
|
+
# include "method.h"
|
6
|
+
# include "ruby/st.h"
|
7
|
+
#elif RUBY_VERSION >= 190
|
8
|
+
# include "node-1.9.h"
|
9
|
+
# include "ruby/st.h"
|
10
|
+
#else
|
11
|
+
# include "node.h"
|
12
|
+
# include "st.h"
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#if RUBY_VERSION < 187
|
16
|
+
# define RCLASS_IV_TBL(c) (RCLASS(c)->iv_tbl)
|
17
|
+
# define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
|
18
|
+
# define RCLASS_SUPER(c) (RCLASS(c)->super)
|
19
|
+
#endif
|
20
|
+
|
21
|
+
/*
|
22
|
+
* Return the internal superclass of this class.
|
23
|
+
*
|
24
|
+
* This is either a Class or "IClass." IClasses represent Modules
|
25
|
+
* included in the ancestry, and should be treated as opaque objects
|
26
|
+
* in ruby space. Convert the IClass to a Module using #iclass_to_module
|
27
|
+
* before using it in ruby.
|
28
|
+
*/
|
29
|
+
VALUE Looksee_internal_superclass(VALUE self, VALUE internal_class) {
|
30
|
+
VALUE super = RCLASS_SUPER(internal_class);
|
31
|
+
if (!super)
|
32
|
+
return Qnil;
|
33
|
+
return super;
|
34
|
+
}
|
35
|
+
|
36
|
+
/*
|
37
|
+
* Return the internal class of the given object.
|
38
|
+
*
|
39
|
+
* This is either the object's singleton class, if it exists, or the
|
40
|
+
* object's birth class.
|
41
|
+
*/
|
42
|
+
VALUE Looksee_internal_class(VALUE self, VALUE object) {
|
43
|
+
return CLASS_OF(object);
|
44
|
+
}
|
45
|
+
|
46
|
+
/*
|
47
|
+
* Return the class or module that the given internal class
|
48
|
+
* represents.
|
49
|
+
*
|
50
|
+
* If a class is given, this is the class. If an iclass is given,
|
51
|
+
* this is the module it represents in the lookup chain.
|
52
|
+
*/
|
53
|
+
VALUE Looksee_internal_class_to_module(VALUE self, VALUE internal_class) {
|
54
|
+
if (!SPECIAL_CONST_P(internal_class)) {
|
55
|
+
switch (BUILTIN_TYPE(internal_class)) {
|
56
|
+
case T_ICLASS:
|
57
|
+
return RBASIC(internal_class)->klass;
|
58
|
+
case T_CLASS:
|
59
|
+
return internal_class;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
rb_raise(rb_eArgError, "not an internal class: %s", RSTRING_PTR(rb_inspect(internal_class)));
|
63
|
+
}
|
64
|
+
|
65
|
+
#if RUBY_VERSION >= 192
|
66
|
+
|
67
|
+
# define VISIBILITY_TYPE rb_method_flag_t
|
68
|
+
|
69
|
+
typedef struct add_method_if_matching_arg {
|
70
|
+
VALUE names;
|
71
|
+
VISIBILITY_TYPE visibility;
|
72
|
+
} add_method_if_matching_arg_t;
|
73
|
+
|
74
|
+
static int add_method_if_matching(ID method_name, rb_method_entry_t *me, add_method_if_matching_arg_t *arg) {
|
75
|
+
if (method_name == ID_ALLOCATOR)
|
76
|
+
return ST_CONTINUE;
|
77
|
+
|
78
|
+
if (UNDEFINED_METHOD_ENTRY_P(me))
|
79
|
+
return ST_CONTINUE;
|
80
|
+
|
81
|
+
if ((me->flag & NOEX_MASK) == arg->visibility)
|
82
|
+
rb_ary_push(arg->names, ID2SYM(method_name));
|
83
|
+
|
84
|
+
return ST_CONTINUE;
|
85
|
+
}
|
86
|
+
|
87
|
+
static int add_method_if_undefined(ID method_name, rb_method_entry_t *me, VALUE *names) {
|
88
|
+
if (UNDEFINED_METHOD_ENTRY_P(me))
|
89
|
+
rb_ary_push(*names, ID2SYM(method_name));
|
90
|
+
return ST_CONTINUE;
|
91
|
+
}
|
92
|
+
|
93
|
+
#else
|
94
|
+
|
95
|
+
# if RUBY_VERSION >= 190
|
96
|
+
# define VISIBILITY(node) ((node)->nd_body->nd_noex & NOEX_MASK)
|
97
|
+
# else
|
98
|
+
# define VISIBILITY(node) ((node)->nd_noex & NOEX_MASK)
|
99
|
+
# endif
|
100
|
+
|
101
|
+
# define VISIBILITY_TYPE unsigned long
|
102
|
+
|
103
|
+
typedef struct add_method_if_matching_arg {
|
104
|
+
VALUE names;
|
105
|
+
VISIBILITY_TYPE visibility;
|
106
|
+
} add_method_if_matching_arg_t;
|
107
|
+
|
108
|
+
static int add_method_if_matching(ID method_name, NODE *body, add_method_if_matching_arg_t *arg) {
|
109
|
+
/* This entry is for the internal allocator function. */
|
110
|
+
if (method_name == ID_ALLOCATOR)
|
111
|
+
return ST_CONTINUE;
|
112
|
+
|
113
|
+
/* Module#undef_method:
|
114
|
+
* * sets body->nd_body to NULL in ruby <= 1.8
|
115
|
+
* * sets body to NULL in ruby >= 1.9
|
116
|
+
*/
|
117
|
+
if (!body || !body->nd_body)
|
118
|
+
return ST_CONTINUE;
|
119
|
+
|
120
|
+
if (VISIBILITY(body) == arg->visibility)
|
121
|
+
rb_ary_push(arg->names, ID2SYM(method_name));
|
122
|
+
return ST_CONTINUE;
|
123
|
+
}
|
124
|
+
|
125
|
+
static int add_method_if_undefined(ID method_name, NODE *body, VALUE *names) {
|
126
|
+
if (!body || !body->nd_body)
|
127
|
+
rb_ary_push(*names, ID2SYM(method_name));
|
128
|
+
return ST_CONTINUE;
|
129
|
+
}
|
130
|
+
|
131
|
+
#endif
|
132
|
+
|
133
|
+
static VALUE internal_instance_methods(VALUE klass, VISIBILITY_TYPE visibility) {
|
134
|
+
add_method_if_matching_arg_t arg;
|
135
|
+
arg.names = rb_ary_new();
|
136
|
+
arg.visibility = visibility;
|
137
|
+
st_foreach(RCLASS_M_TBL(klass), add_method_if_matching, (st_data_t)&arg);
|
138
|
+
return arg.names;
|
139
|
+
}
|
140
|
+
|
141
|
+
/*
|
142
|
+
* Return the list of public instance methods (as Symbols) of the
|
143
|
+
* given internal class.
|
144
|
+
*/
|
145
|
+
VALUE Looksee_internal_public_instance_methods(VALUE self, VALUE klass) {
|
146
|
+
return internal_instance_methods(klass, NOEX_PUBLIC);
|
147
|
+
}
|
148
|
+
|
149
|
+
/*
|
150
|
+
* Return the list of protected instance methods (as Symbols) of the
|
151
|
+
* given internal class.
|
152
|
+
*/
|
153
|
+
VALUE Looksee_internal_protected_instance_methods(VALUE self, VALUE klass) {
|
154
|
+
return internal_instance_methods(klass, NOEX_PROTECTED);
|
155
|
+
}
|
156
|
+
|
157
|
+
/*
|
158
|
+
* Return the list of private instance methods (as Symbols) of the
|
159
|
+
* given internal class.
|
160
|
+
*/
|
161
|
+
VALUE Looksee_internal_private_instance_methods(VALUE self, VALUE klass) {
|
162
|
+
return internal_instance_methods(klass, NOEX_PRIVATE);
|
163
|
+
}
|
164
|
+
|
165
|
+
/*
|
166
|
+
* Return the list of undefined instance methods (as Symbols) of the
|
167
|
+
* given internal class.
|
168
|
+
*/
|
169
|
+
VALUE Looksee_internal_undefined_instance_methods(VALUE self, VALUE klass) {
|
170
|
+
VALUE names = rb_ary_new();
|
171
|
+
st_foreach(RCLASS_M_TBL(klass), add_method_if_undefined, (st_data_t)&names);
|
172
|
+
return names;
|
173
|
+
}
|
174
|
+
|
175
|
+
VALUE Looksee_singleton_class_p(VALUE self, VALUE object) {
|
176
|
+
return BUILTIN_TYPE(object) == T_CLASS && FL_TEST(object, FL_SINGLETON) ? Qtrue : Qfalse;
|
177
|
+
}
|
178
|
+
|
179
|
+
VALUE Looksee_singleton_instance(VALUE self, VALUE singleton_class) {
|
180
|
+
if (BUILTIN_TYPE(singleton_class) == T_CLASS && FL_TEST(singleton_class, FL_SINGLETON)) {
|
181
|
+
VALUE object;
|
182
|
+
if (!st_lookup(RCLASS_IV_TBL(singleton_class), rb_intern("__attached__"), (st_data_t *)&object))
|
183
|
+
rb_raise(rb_eRuntimeError, "[looksee bug] can't find singleton object");
|
184
|
+
return object;
|
185
|
+
} else {
|
186
|
+
rb_raise(rb_eTypeError, "expected singleton class, got %s", rb_obj_classname(singleton_class));
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
VALUE Looksee_module_name(VALUE self, VALUE module) {
|
191
|
+
if (BUILTIN_TYPE(module) == T_CLASS || BUILTIN_TYPE(module) == T_MODULE) {
|
192
|
+
VALUE name = rb_mod_name(module);
|
193
|
+
return name == Qnil ? rb_str_new2("") : name;
|
194
|
+
} else {
|
195
|
+
rb_raise(rb_eTypeError, "expected module, got %s", rb_obj_classname(module));
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
#if RUBY_VERSION < 190
|
200
|
+
|
201
|
+
#include "env-1.8.h"
|
202
|
+
#include "eval_c-1.8.h"
|
203
|
+
|
204
|
+
/*
|
205
|
+
* Return the source file and line number of the given object and method.
|
206
|
+
*/
|
207
|
+
VALUE Looksee_source_location(VALUE self, VALUE unbound_method) {
|
208
|
+
if (!rb_obj_is_kind_of(unbound_method, rb_cUnboundMethod))
|
209
|
+
rb_raise(rb_eTypeError, "expected UnboundMethod, got %s", rb_obj_classname(unbound_method));
|
210
|
+
|
211
|
+
struct METHOD *method;
|
212
|
+
Data_Get_Struct(unbound_method, struct METHOD, method);
|
213
|
+
|
214
|
+
NODE *node;
|
215
|
+
switch (nd_type(method->body)) {
|
216
|
+
// Can't be a FBODY or ZSUPER.
|
217
|
+
case NODE_SCOPE:
|
218
|
+
node = method->body->nd_defn;
|
219
|
+
break;
|
220
|
+
case NODE_BMETHOD:
|
221
|
+
{
|
222
|
+
struct BLOCK *block;
|
223
|
+
Data_Get_Struct(method->body->nd_orig, struct BLOCK, block);
|
224
|
+
(node = block->frame.node) || (node = block->body);
|
225
|
+
// Proc#to_s suggests this may be NULL sometimes.
|
226
|
+
if (!node)
|
227
|
+
return Qnil;
|
228
|
+
}
|
229
|
+
break;
|
230
|
+
case NODE_DMETHOD:
|
231
|
+
{
|
232
|
+
struct METHOD *original_method;
|
233
|
+
NODE *body = method->body;
|
234
|
+
Data_Get_Struct(body->nd_orig, struct METHOD, original_method);
|
235
|
+
node = original_method->body->nd_defn;
|
236
|
+
}
|
237
|
+
break;
|
238
|
+
default:
|
239
|
+
rb_raise(rb_eRuntimeError, "[LOOKSEE BUG] unexpected NODE type: %d", nd_type(method->body));
|
240
|
+
}
|
241
|
+
VALUE file = rb_str_new2(node->nd_file);
|
242
|
+
VALUE line = INT2NUM(nd_line(node));
|
243
|
+
VALUE location = rb_ary_new2(2);
|
244
|
+
rb_ary_store(location, 0, file);
|
245
|
+
rb_ary_store(location, 1, line);
|
246
|
+
return location;
|
247
|
+
}
|
248
|
+
|
249
|
+
#endif
|
250
|
+
|
251
|
+
void Init_mri(void) {
|
252
|
+
VALUE mLooksee = rb_const_get(rb_cObject, rb_intern("Looksee"));
|
253
|
+
VALUE mAdapter = rb_const_get(mLooksee, rb_intern("Adapter"));
|
254
|
+
VALUE mBase = rb_const_get(mAdapter, rb_intern("Base"));
|
255
|
+
VALUE mMRI = rb_define_class_under(mAdapter, "MRI", mBase);
|
256
|
+
rb_define_method(mMRI, "internal_superclass", Looksee_internal_superclass, 1);
|
257
|
+
rb_define_method(mMRI, "internal_class", Looksee_internal_class, 1);
|
258
|
+
rb_define_method(mMRI, "internal_class_to_module", Looksee_internal_class_to_module, 1);
|
259
|
+
rb_define_method(mMRI, "internal_public_instance_methods", Looksee_internal_public_instance_methods, 1);
|
260
|
+
rb_define_method(mMRI, "internal_protected_instance_methods", Looksee_internal_protected_instance_methods, 1);
|
261
|
+
rb_define_method(mMRI, "internal_private_instance_methods", Looksee_internal_private_instance_methods, 1);
|
262
|
+
rb_define_method(mMRI, "internal_undefined_instance_methods", Looksee_internal_undefined_instance_methods, 1);
|
263
|
+
rb_define_method(mMRI, "singleton_class?", Looksee_singleton_class_p, 1);
|
264
|
+
rb_define_method(mMRI, "singleton_instance", Looksee_singleton_instance, 1);
|
265
|
+
rb_define_method(mMRI, "module_name", Looksee_module_name, 1);
|
266
|
+
#if RUBY_VERSION < 190
|
267
|
+
rb_define_method(mMRI, "source_location", Looksee_source_location, 1);
|
268
|
+
#endif
|
269
|
+
}
|
File without changes
|
data/ext/rbx/rbx.c
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
VALUE Looksee_internal_class(VALUE self, VALUE object) {
|
4
|
+
return CLASS_OF(object);
|
5
|
+
}
|
6
|
+
|
7
|
+
void Init_rbx(void) {
|
8
|
+
VALUE mLooksee = rb_const_get(rb_cObject, rb_intern("Looksee"));
|
9
|
+
VALUE mAdapter = rb_const_get(mLooksee, rb_intern("Adapter"));
|
10
|
+
VALUE mBase = rb_const_get(mAdapter, rb_intern("Base"));
|
11
|
+
VALUE mRubinius = rb_define_class_under(mAdapter, "Rubinius", mBase);
|
12
|
+
rb_define_method(mRubinius, "internal_class", Looksee_internal_class, 1);
|
13
|
+
}
|
data/lib/looksee.rb
CHANGED
@@ -1,386 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require "looksee/version"
|
1
|
+
require 'looksee/clean'
|
2
|
+
require 'looksee/core_ext'
|
4
3
|
|
5
|
-
#
|
6
|
-
|
7
|
-
# are two ways to use it:
|
8
|
-
#
|
9
|
-
# 1. Keep all methods contained in the Looksee namespace:
|
10
|
-
#
|
11
|
-
# require 'looksee'
|
12
|
-
#
|
13
|
-
# 2. Let it all hang out:
|
14
|
-
#
|
15
|
-
# require 'looksee/shortcuts'
|
16
|
-
#
|
17
|
-
# The latter adds the following shortcuts to the built-in classes:
|
18
|
-
#
|
19
|
-
# Object#lookup_path
|
20
|
-
# Object#dump_lookup_path
|
21
|
-
# Object#lp
|
22
|
-
# Object#lpi
|
23
|
-
#
|
24
|
-
# See their docs.
|
25
|
-
#
|
26
|
-
# == Usage
|
27
|
-
#
|
28
|
-
# In irb:
|
29
|
-
#
|
30
|
-
# require 'looksee/shortcuts'
|
31
|
-
# lp some_object
|
32
|
-
#
|
33
|
-
# +lp+ returns a LookupPath object, which has +inspect+ defined to
|
34
|
-
# print things out pretty. By default, it shows public, protected,
|
35
|
-
# undefined, and overridden methods. They're all colored, which makes
|
36
|
-
# showing overridden methods not such a strange idea.
|
37
|
-
#
|
38
|
-
# Some examples of the other shortcuts:
|
39
|
-
#
|
40
|
-
# lpi Array
|
41
|
-
# some_object.lookup_path
|
42
|
-
# foo.bar.baz.dump_lookup_path.and.more
|
43
|
-
#
|
44
|
-
# If you're being namespace-clean, you'll need to do:
|
45
|
-
#
|
46
|
-
# require 'looksee'
|
47
|
-
# Looksee.lookup_path(thing) # like "lp thing"
|
48
|
-
#
|
49
|
-
# For a quick reference:
|
50
|
-
#
|
51
|
-
# Looksee.help
|
52
|
-
#
|
53
|
-
# == Configuration
|
54
|
-
#
|
55
|
-
# Set these:
|
56
|
-
#
|
57
|
-
# Looksee.default_lookup_path_options
|
58
|
-
# Looksee.default_width
|
59
|
-
# Looksee.styles
|
60
|
-
#
|
61
|
-
# See their docs.
|
62
|
-
#
|
63
|
-
module Looksee
|
64
|
-
class << self
|
65
|
-
#
|
66
|
-
# Return a collection of methods that +object+ responds to,
|
67
|
-
# according to the options given. The following options are
|
68
|
-
# recognized:
|
69
|
-
#
|
70
|
-
# * +:public+ - include public methods
|
71
|
-
# * +:protected+ - include protected methods
|
72
|
-
# * +:private+ - include private methods
|
73
|
-
# * +:undefined+ - include undefined methods (see Module#undef_method)
|
74
|
-
# * +:overridden+ - include methods overridden by subclasses
|
75
|
-
#
|
76
|
-
# The default (if options is nil or omitted) is given by
|
77
|
-
# #default_lookup_path_options.
|
78
|
-
#
|
79
|
-
def lookup_path(object, *options)
|
80
|
-
normalized_options = Looksee.default_lookup_path_options.dup
|
81
|
-
hash_options = options.last.is_a?(Hash) ? options.pop : {}
|
82
|
-
options.each do |option|
|
83
|
-
normalized_options[option] = true
|
84
|
-
end
|
85
|
-
normalized_options.update(hash_options)
|
86
|
-
LookupPath.for(object, normalized_options)
|
87
|
-
end
|
88
|
-
|
89
|
-
#
|
90
|
-
# The default options passed to lookup_path.
|
91
|
-
#
|
92
|
-
# Default: <tt>{:public => true, :protected => true, :undefined =>
|
93
|
-
# true, :overridden => true}</tt>
|
94
|
-
#
|
95
|
-
attr_accessor :default_lookup_path_options
|
96
|
-
|
97
|
-
#
|
98
|
-
# The width to use for displaying output, when not available in
|
99
|
-
# the COLUMNS environment variable.
|
100
|
-
#
|
101
|
-
# Default: 80
|
102
|
-
#
|
103
|
-
attr_accessor :default_width
|
104
|
-
|
105
|
-
#
|
106
|
-
# The default styles to use for the +inspect+ strings.
|
107
|
-
#
|
108
|
-
# This is a hash with keys:
|
109
|
-
#
|
110
|
-
# * :module
|
111
|
-
# * :public
|
112
|
-
# * :protected
|
113
|
-
# * :private
|
114
|
-
# * :undefined
|
115
|
-
# * :overridden
|
116
|
-
#
|
117
|
-
# The values are format strings. They should all contain a single
|
118
|
-
# "%s", which is where the name is inserted.
|
119
|
-
#
|
120
|
-
# Default:
|
121
|
-
#
|
122
|
-
# {
|
123
|
-
# :module => "\e[1;37m%s\e[0m", # white
|
124
|
-
# :public => "\e[1;32m%s\e[0m", # green
|
125
|
-
# :protected => "\e[1;33m%s\e[0m", # yellow
|
126
|
-
# :private => "\e[1;31m%s\e[0m", # red
|
127
|
-
# :undefined => "\e[1;34m%s\e[0m", # blue
|
128
|
-
# :overridden => "\e[1;30m%s\e[0m", # black
|
129
|
-
# }
|
130
|
-
#
|
131
|
-
attr_accessor :styles
|
132
|
-
|
133
|
-
#
|
134
|
-
# Return the chain of classes and modules which comprise the
|
135
|
-
# object's method lookup path.
|
136
|
-
#
|
137
|
-
def lookup_modules(object)
|
138
|
-
modules = []
|
139
|
-
klass = Looksee.internal_class(object)
|
140
|
-
while klass
|
141
|
-
modules << Looksee.internal_class_to_module(klass)
|
142
|
-
klass = Looksee.internal_superclass(klass)
|
143
|
-
end
|
144
|
-
modules
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
self.default_lookup_path_options = {:public => true, :protected => true, :undefined => true, :overridden => true}
|
149
|
-
self.default_width = 80
|
150
|
-
self.styles = {
|
151
|
-
:module => "\e[1;37m%s\e[0m", # white
|
152
|
-
:public => "\e[1;32m%s\e[0m", # green
|
153
|
-
:protected => "\e[1;33m%s\e[0m", # yellow
|
154
|
-
:private => "\e[1;31m%s\e[0m", # red
|
155
|
-
:undefined => "\e[1;34m%s\e[0m", # blue
|
156
|
-
:overridden => "\e[1;30m%s\e[0m", # black
|
157
|
-
}
|
158
|
-
|
159
|
-
class LookupPath
|
160
|
-
attr_reader :entries
|
161
|
-
|
162
|
-
def initialize(entries)
|
163
|
-
@entries = entries
|
164
|
-
end
|
165
|
-
|
166
|
-
#
|
167
|
-
# Create a LookupPath for the given object.
|
168
|
-
#
|
169
|
-
# Options may be given to restrict which visibilities are
|
170
|
-
# included.
|
171
|
-
#
|
172
|
-
# :public
|
173
|
-
# :protected
|
174
|
-
# :private
|
175
|
-
# :undefined
|
176
|
-
# :overridden
|
177
|
-
#
|
178
|
-
def self.for(object, options={})
|
179
|
-
entries = entries_for(object, options)
|
180
|
-
new(entries)
|
181
|
-
end
|
182
|
-
|
183
|
-
#
|
184
|
-
# Return a new LookupPath which only contains names matching the
|
185
|
-
# given pattern.
|
186
|
-
#
|
187
|
-
def grep(pattern)
|
188
|
-
entries = self.entries.map do |entry|
|
189
|
-
entry.grep(pattern)
|
190
|
-
end
|
191
|
-
self.class.new(entries)
|
192
|
-
end
|
193
|
-
|
194
|
-
def inspect(options={})
|
195
|
-
options = normalize_inspect_options(options)
|
196
|
-
entries.map{|e| e.inspect(options)}.join("\n")
|
197
|
-
end
|
198
|
-
|
199
|
-
private # -------------------------------------------------------
|
200
|
-
|
201
|
-
def self.entries_for(object, options)
|
202
|
-
seen = {}
|
203
|
-
Looksee.lookup_modules(object).map do |mod|
|
204
|
-
entry = Entry.for(mod, seen, options)
|
205
|
-
entry.methods.each{|m| seen[m] = true}
|
206
|
-
entry
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def normalize_inspect_options(options)
|
211
|
-
options[:width] ||= ENV['COLUMNS'].to_i.nonzero? || Looksee.default_width
|
212
|
-
options
|
213
|
-
end
|
214
|
-
|
215
|
-
#
|
216
|
-
# An entry in the LookupPath.
|
217
|
-
#
|
218
|
-
# Contains a module and its methods, along with visibility
|
219
|
-
# information (public, private, etc.).
|
220
|
-
#
|
221
|
-
class Entry
|
222
|
-
def initialize(mod, methods=[], visibilities={})
|
223
|
-
@module = mod
|
224
|
-
@methods = methods
|
225
|
-
@visibilities = visibilities
|
226
|
-
end
|
227
|
-
|
228
|
-
def self.for(mod, seen, options)
|
229
|
-
entry = new(mod)
|
230
|
-
entry.initialize_for(seen, options)
|
231
|
-
entry
|
232
|
-
end
|
233
|
-
|
234
|
-
attr_reader :module, :methods
|
235
|
-
|
236
|
-
def initialize_for(seen, options)
|
237
|
-
add_methods(Looksee.internal_public_instance_methods(@module).map{|sym| sym.to_s} , :public , seen) if options[:public ]
|
238
|
-
add_methods(Looksee.internal_protected_instance_methods(@module).map{|sym| sym.to_s}, :protected, seen) if options[:protected]
|
239
|
-
add_methods(Looksee.internal_private_instance_methods(@module).map{|sym| sym.to_s} , :private , seen) if options[:private ]
|
240
|
-
add_methods(Looksee.internal_undefined_instance_methods(@module).map{|sym| sym.to_s}, :undefined, seen) if options[:undefined]
|
241
|
-
@methods.sort!
|
242
|
-
end
|
243
|
-
|
244
|
-
def grep(pattern)
|
245
|
-
methods = []
|
246
|
-
visibilities = {}
|
247
|
-
@methods.each do |name|
|
248
|
-
if name[pattern]
|
249
|
-
methods << name
|
250
|
-
visibilities[name] = @visibilities[name]
|
251
|
-
end
|
252
|
-
end
|
253
|
-
self.class.new(@module, methods, visibilities)
|
254
|
-
end
|
255
|
-
|
256
|
-
#
|
257
|
-
# Return the name of the class or module.
|
258
|
-
#
|
259
|
-
# Singleton classes are displayed in brackets. Singleton class
|
260
|
-
# of singleton classes are displayed in double brackets. But
|
261
|
-
# you'd never need that, would you?
|
262
|
-
#
|
263
|
-
def module_name
|
264
|
-
name = @module.to_s # #name doesn't do singleton classes right
|
265
|
-
nil while name.sub!(/#<Class:(.*)>/, '[\\1]')
|
266
|
-
name
|
267
|
-
end
|
268
|
-
|
269
|
-
#
|
270
|
-
# Yield each method along with its visibility (:public,
|
271
|
-
# :private, :protected, :undefined, or :overridden).
|
272
|
-
#
|
273
|
-
def each
|
274
|
-
@methods.each do |name|
|
275
|
-
yield name, @visibilities[name]
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
include Enumerable
|
280
|
-
|
281
|
-
#
|
282
|
-
# Return a nice, pretty string for inspection.
|
283
|
-
#
|
284
|
-
# Contains the module name, plus the method names laid out in
|
285
|
-
# columns. Pass a :width option to control the output width.
|
286
|
-
#
|
287
|
-
def inspect(options={})
|
288
|
-
string = styled_module_name << "\n" << Columnizer.columnize(styled_methods, options[:width])
|
289
|
-
string.chomp
|
290
|
-
end
|
291
|
-
|
292
|
-
private # -----------------------------------------------------
|
293
|
-
|
294
|
-
def add_methods(methods, visibility, seen)
|
295
|
-
methods.each do |method|
|
296
|
-
@methods << method
|
297
|
-
@visibilities[method] = seen[method] ? :overridden : visibility
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
def styled_module_name
|
302
|
-
Looksee.styles[:module] % module_name
|
303
|
-
end
|
304
|
-
|
305
|
-
def styled_methods
|
306
|
-
map do |name, visibility|
|
307
|
-
Looksee.styles[visibility] % name
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
module Columnizer
|
314
|
-
class << self
|
315
|
-
#
|
316
|
-
# Arrange the given strings in columns, restricted to the given
|
317
|
-
# width. Smart enough to ignore content in terminal control
|
318
|
-
# sequences.
|
319
|
-
#
|
320
|
-
def columnize(strings, width)
|
321
|
-
return '' if strings.empty?
|
322
|
-
|
323
|
-
num_columns = 1
|
324
|
-
layout = [strings]
|
325
|
-
loop do
|
326
|
-
break if layout.first.length <= 1
|
327
|
-
next_layout = layout_in_columns(strings, num_columns + 1)
|
328
|
-
break if layout_width(next_layout) > width
|
329
|
-
layout = next_layout
|
330
|
-
num_columns += 1
|
331
|
-
end
|
332
|
-
|
333
|
-
pad_strings(layout)
|
334
|
-
rectangularize_layout(layout)
|
335
|
-
layout.transpose.map do |row|
|
336
|
-
' ' + row.compact.join(' ')
|
337
|
-
end.join("\n") << "\n"
|
338
|
-
end
|
339
|
-
|
340
|
-
private # -----------------------------------------------------
|
341
|
-
|
342
|
-
def layout_in_columns(strings, num_columns)
|
343
|
-
strings_per_column = (strings.length / num_columns.to_f).ceil
|
344
|
-
(0...num_columns).map{|i| strings[i*strings_per_column...(i+1)*strings_per_column] || []}
|
345
|
-
end
|
346
|
-
|
347
|
-
def layout_width(layout)
|
348
|
-
widths = layout_column_widths(layout)
|
349
|
-
widths.inject(0){|sum, w| sum + w} + 2*layout.length
|
350
|
-
end
|
351
|
-
|
352
|
-
def layout_column_widths(layout)
|
353
|
-
layout.map do |column|
|
354
|
-
column.map{|string| display_width(string)}.max || 0
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
def display_width(string)
|
359
|
-
# remove terminal control sequences
|
360
|
-
string.gsub(/\e\[.*?m/, '').length
|
361
|
-
end
|
362
|
-
|
363
|
-
def pad_strings(layout)
|
364
|
-
widths = layout_column_widths(layout)
|
365
|
-
layout.each_with_index do |column, i|
|
366
|
-
column_width = widths[i]
|
367
|
-
column.each do |string|
|
368
|
-
padding = column_width - display_width(string)
|
369
|
-
string << ' '*padding
|
370
|
-
end
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
def rectangularize_layout(layout)
|
375
|
-
return if layout.length == 1
|
376
|
-
height = layout[0].length
|
377
|
-
layout[1..-1].each do |column|
|
378
|
-
column.length == height or
|
379
|
-
column[height - 1] = nil
|
380
|
-
end
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
require 'looksee/wirble_compatibility'
|
4
|
+
# Ugh.
|
5
|
+
Looksee::WirbleCompatibility.init
|