/[Frey]/trunk/lib/Frey/Introspect.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/Frey/Introspect.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1125 - (show annotations)
Tue Jun 30 14:03:17 2009 UTC (14 years, 10 months ago) by dpavlin
File size: 9378 byte(s)
correctly support multiple roles (with role1,role2;)
1 package Frey::Introspect;
2
3 use Moose;
4 use Carp;
5 #use Moose::Meta::Role;
6 #use Moose::Meta::Class;
7 use Data::Dump qw/dump/;
8 use File::Slurp;
9 use List::Util;
10
11 use PPI;
12 use PPI::HTML;
13
14 use lib 'lib';
15 use Frey::Pod;
16
17 extends 'Frey::PPI';
18 with 'Frey::Web';
19 with 'Frey::Storage';
20
21 =head1 DESCRIPTION
22
23 Provide introspection on any perl class installed on system
24
25 =cut
26
27 has 'class' => (
28 is => 'rw',
29 isa => 'Str',
30 required => 1,
31 );
32
33 =head2 joose
34
35 my $js = $o->joose;
36
37 =cut
38
39 sub joose {
40 my ($self) = @_;
41
42 my ( $meta, $is_role ) = $self->class_meta;
43
44 if ( ! $is_role ) {
45 my @superclasses = map{ $_->meta->name }
46 grep { $_ ne 'Moose::Object' } $meta->superclasses;
47 warn "superclasses ",dump( @superclasses ) if $self->debug;
48 }
49
50 my $out;
51
52 my ( $m, $c ) = split(/::/, $self->class, 2);
53 my $filename = $m . '.' . ( $c ? "$c." : '' ) . 'js';
54
55 $c ||= '';
56
57 $out .= "Module(\"$m\", function (m) {\n\tClass(\"$c\", {\n\t\thas: {\n";
58
59 foreach ( $meta->get_attribute_list ) {
60 $out .= "\t\t\t$_: {\n";
61
62 my $attr = $meta->get_attribute($_);
63 my $is = eval { $attr->_is_metadata; };
64 return if $@;
65
66 $out .= "\t\t\t\tis: \"$is\",\n" if defined $is;
67 $out .= "\t\t\t\tlazy: true,\n" if $attr->is_lazy;
68 $out .= "\t\t\t\trequired: true,\n" if $attr->is_required;
69 $out .= "\t\t\t\tinit: \"" . $attr->init_arg . "\",\n" if $attr->init_arg; # FIXME
70
71 if( defined(my $isa = $attr->_isa_metadata) ){
72 if( blessed $isa ){
73 while( blessed $isa ){
74 $isa = $isa->name;
75 }
76 }
77 $isa =~ s/\s+\|\s+undef//gi;
78 $out .= "\t\t\t\tisa: Moose.$isa,\n";
79 }
80
81
82 $out .= "\t\t\t},\n";
83
84 }
85
86 $out .= "\t\t},\n\t\tmeta: Frey.HTML,
87 classMethods: {
88 renderHTML: function () {
89 return new Joose.SimpleRequest().getText(\"/" . $self->class . "\")
90 },\n";
91
92 $out .= "\t\t},\n";
93
94 $out .= "\t}),\n";
95
96 $out =~ s/,\n$/\n/;
97 $out .= "});\n";
98
99 $out .= "\nconsole.log( 'loaded " . $self->class . " from $filename' );\n";
100
101 warn "method_list = ",dump( $meta->get_method_list ) if $self->debug;
102
103 # print $out;
104 my $path = "static/blib/$filename";
105 write_file( $path, $out );
106 warn "# created $path\n";
107
108 return $out;
109 }
110
111 sub as_markup {
112 my ( $self ) = @_;
113
114 my ( $meta, $is_role ) = $self->class_meta;
115
116 my $class = $self->class;
117 $self->title( $class );
118
119 my $introspect_path = "var/introspect/$class.yaml";
120 $self->mkbasepath( $introspect_path );
121 my $introspect; # FIXME update with = $self->load( $introspect_path );
122
123 my ( $superclasses, $roles ) = ( '<b>Role</b>', '' );
124 if ( ! $is_role ) {
125 if ( $meta->superclasses ) {
126 $superclasses = 'Superclasses: ' .
127 join(', ',
128 map {
129 my $name = $_->meta->name;
130 $introspect->{superclass}->{$name} = {};
131
132 qq|<a target="$name" href="/$name" title="introspect $name">$name</a>| .
133 $self->dropdown( qq|<sup>?</sup>|, $_->meta )
134 }
135 #grep { $_ ne 'Moose::Object' }
136 $meta->superclasses
137 );
138 }
139 }
140
141 my $method_from_role;
142 my $attribute_from_role;
143
144 if ( $meta->can('roles') ) {
145 my $role_nr = 1;
146 $roles = join(' ',
147 grep { ! m/\Q$class\E/ } # skip me
148 map {
149 my $r = '';
150 foreach my $name ( split(/\|/, $_->name) ) {
151 $introspect->{roles}->{$name} = {};
152 $method_from_role->{ $_ }->{$name} = $role_nr foreach $_->get_method_list;
153 $attribute_from_role->{ $_ }->{$name} = $role_nr foreach $_->get_attribute_list;
154
155 $r .=
156 qq|<a target="$name" href="/$name" title="introspect $name">$name</a>|
157 # . qq|<sup>| . $self->dropdown( $role_nr++, $name->meta ) . qq|</sup>|
158 . $self->dropdown(
159 qq|<sup>| . $role_nr++ . qq|</sup>|,
160 $name->meta
161 )
162 ;
163 }
164 $r;
165 }
166 $meta->calculate_all_roles
167 );
168 $roles = qq|with roles: $roles| if $roles;
169 }
170 warn "# method_from_role ",dump( $method_from_role );
171
172 my @methods;
173 @methods = map {
174 my $name = $_;
175 if ( $method_from_role->{$name} ) {
176 my ( $role_name, $nr ) = each %{ $method_from_role->{$name} };
177 $introspect->{methods}->{$name}->{role} = $role_name;
178 $name .= qq|<sup title="$role_name">$nr</sup>|;
179 } else {
180 $introspect->{methods}->{$name} = {};
181 }
182 qq|<td class="m">$name</td>|
183 } sort {
184 lc($a) cmp lc($b)
185 } $self->class_methods( $class );
186
187 my @attributes;
188 if ( $meta->get_attribute_list ) {
189 @attributes = map {
190 my $name = $_;
191 $introspect->{attribute}->{$name} = {};
192 my $html_name = $name;
193 my $attr = $meta->get_attribute($name);
194 confess "$class attribute $name isn't blessed ",dump( $attr ) unless blessed $attr;
195 warn "## attr $name ref ",ref( $attr ) if $self->debug;
196
197 my ( $title, $properties ) = ( '', '' );
198 if ( $attr->can('is_required') && $attr->is_required ) {
199 ( $html_name, $title ) = ( "<b>$name</b>", ' title="required"' );
200 $introspect->{attribute}->{$name}->{required} = 1;
201 }
202
203 foreach my $check ( qw/has_type_constraint has_handles is_weak_ref is_required is_lazy should_coerce should_auto_deref has_default has_trigger has_applied_traits/ ) {
204 my $getter;
205
206 $getter = $check;
207 $getter =~ s/^has_//;
208
209 if ( $attr->can($check) && $attr->$check ) {
210 $properties .= $check;
211 # we need our dump here instead of $attr->$getter->dump because default can return scalar
212 my $v = $attr->$getter;
213 $properties .= ref($v)
214 ? $self->dropdown( qq|<sup>?</sup>|, $attr->$getter )
215 : qq| <code>$v</code>|
216 ;
217 }
218 $properties .= ' ';
219 }
220 my $type = $attr->can('has_type_constraint') && $attr->has_type_constraint ? $attr->type_constraint->name : '';
221
222 if ( $attribute_from_role->{$name} ) {
223 my ( $role_name, $nr ) = each %{ $attribute_from_role->{$name} };
224 $name .= qq|<sup title="$role_name">$nr</sup>|;
225 }
226
227 if ( my $doc = eval { $attr->documentation } ) {
228 $properties = qq|
229 $properties
230 <span class="documentation">$doc</span>
231 |;
232 $self->add_css(qq|
233 span.documentation {
234 background: #eee;
235 padding: 0.25em;
236 float: left;
237 clear: left;
238 }
239 |);
240 $introspect->{action}->{$name}->{documentation} = $doc;
241 }
242
243 qq|<td class="a">$html_name</td><td class="t">$type</td><td class="p">$properties</td>|
244 } sort $meta->get_attribute_list
245 }
246
247 my $table = qq|<table class="frey-introspect"><tr><th class="m">Methods</th><th class="a">Attributes</th><th>Type</th><th class="p">Properties</th></tr>|;
248 while ( @methods || @attributes ) {
249 my ($m,$a) = ( shift @methods, shift @attributes );
250 $m ||= '<td></td>';
251 $a ||= '<td></td>';
252 $table .= qq|<tr>$m$a</tr>|;
253 }
254 $table .= qq|</table>|;
255
256 my $path = $self->class_path( $class );
257
258 my ( $pod_toc, $pod ) = Frey::Pod->new( class => $class, request_url => $self->request_url )->as_markup;
259 return $pod_toc . $pod if $path =~ m{\.pod};
260
261 warn "# ", $pod_toc ? 'toc' : '', ' ', $pod ? 'pod' : '';
262
263 my $Document = PPI::Document->new( $path );
264
265 # Create a reusable syntax highlighter
266 my $Highlight = PPI::HTML->new(
267 line_numbers => 1,
268 # page => 1,
269 # colors => {
270 # line_number => '#CCCCCC',
271 # number => '#990000',
272 # },
273 );
274
275 # Spit out the HTML
276 my $source = $Highlight->html( $Document );
277
278 $source =~ s{(<span.*?line_number.*>\s*)(\d+)(:\s*</span>)}{$1<a target="editor" href="/editor+$path+$2">$2</a>$3}g;
279
280 # strip page html
281 # $source =~ s{^.*<body[^>]+>}{}s;
282 # $source =~ s{</body.*$}{}s;
283
284 my $runnable = join("</dd><dd>",
285 map {
286 $introspect->{runnable}->{$_} = {};
287 my $short = $_;
288 $short =~ s{_as_(?:markup|data|sponge)$}{};
289 qq|<a target="$class" href="/$class/$_" title="/$class/$_">$short</a>|
290 } $self->class_runnable( $class )
291 );
292 $runnable = "<dt>runnable</dt><dd>$runnable</dd>" if $runnable;
293
294 my $has_tests = '';
295 my @tests = sort { lc($a) cmp lc($b) } grep { defined $_ } $self->has_tests;
296 if ( @tests ) {
297 $has_tests
298 = qq|<dt>test|
299 . ( $#tests > 0 ? 's' : '' )
300 . qq|<dt><dd>|
301 . join("</dd><dd>", map {
302 qq|<a target="$class" href="/Frey::Test::Runner/as_markup?test=$_">$_</a>|
303 } @tests )
304 . qq|</dd>|
305 ;
306 $introspect->{tests} = [ @tests ],
307 }
308
309 my $includes = '';
310 if ( my $inc = $self->includes ) {
311 $introspect->{includes} = $inc;
312 foreach my $type ( keys %$inc ) {
313 $includes
314 .= ucfirst($type)
315 . qq|: |
316 . join("\n",
317 map {
318 qq|<a target="$_" href="/$_">$_</a>|
319 } @{
320 $inc->{$type}
321 }
322 )
323 ;
324 }
325 }
326
327 $self->store( $introspect_path, $introspect );
328
329 $self->add_css(qq|
330 .frey-introspect-right {
331 position: fixed;
332 top: 1em;
333 right: 1em;
334 z-index: 10;
335 background: #ffc;
336 padding: 0.5em;
337 width: 20%;
338 font-size: 80%;
339 }
340 .frey-introspect-right dl dd {
341 margin-left: 1em;
342 }
343
344 /* fix pod */
345 .frey-introspect-right dd ul {
346 padding-left: 0;
347 }
348 .frey-introspect-right dl ul > li {
349 list-style: none;
350 }
351 |);
352
353 my $has_pod = qq|
354 <dt><a href="#___top" title="Skip to POD">pod</a></dt>
355 <dd>$pod_toc</dd>
356 | if $pod_toc;
357
358 my $has_source = qq|
359 <dt><a href="#source" title="Skip to source">source</a></dt>
360 | if $source;
361
362 my $right = qq|
363 <dl>
364 $runnable
365 $has_tests
366 $has_pod
367 $has_source
368 </dl>
369 |;
370
371
372 return
373 qq|
374 <h1>$class</h1>
375 <div class="frey-introspect">
376 $superclasses $roles
377 <br>$includes
378 </div>
379 <div class="frey-introspect-right">
380 $right
381 </div>
382 $table
383 $pod
384 </div>
385
386 <h1>Source</h1><a name="source"></a>
387 <div class="frey-source">$source</div>
388 |
389 ;
390 }
391
392 =head1 SEE ALSO
393
394 L<MooseX::AutoDoc> on which this code is based
395
396 =cut
397
398 1;

  ViewVC Help
Powered by ViewVC 1.1.26