| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package UR::Namespace; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 266 |  |  | 266 |  | 1028 | use strict; | 
|  | 266 |  |  |  |  | 357 |  | 
|  | 266 |  |  |  |  | 6761 |  | 
| 4 | 266 |  |  | 266 |  | 914 | use warnings; | 
|  | 266 |  |  |  |  | 337 |  | 
|  | 266 |  |  |  |  | 5407 |  | 
| 5 | 266 |  |  | 266 |  | 921 | use File::Find; | 
|  | 266 |  |  |  |  | 313 |  | 
|  | 266 |  |  |  |  | 14663 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | require UR; | 
| 8 | 266 |  |  | 266 |  | 78522 | use UR::AttributeHandlers; | 
|  | 266 |  |  |  |  | 474 |  | 
|  | 266 |  |  |  |  | 3070 |  | 
| 9 |  |  |  |  |  |  | our $VERSION = "0.46"; # UR $VERSION; | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | UR::Object::Type->define( | 
| 12 |  |  |  |  |  |  | class_name => 'UR::Namespace', | 
| 13 |  |  |  |  |  |  | is => ['UR::Singleton'], | 
| 14 |  |  |  |  |  |  | is_abstract => 1, | 
| 15 |  |  |  |  |  |  | has => [ | 
| 16 |  |  |  |  |  |  | domain => { | 
| 17 |  |  |  |  |  |  | is => 'Text', | 
| 18 |  |  |  |  |  |  | is_optional => 1, | 
| 19 |  |  |  |  |  |  | len => undef, | 
| 20 |  |  |  |  |  |  | doc => "DNS domain name associated with the namespace in question", | 
| 21 |  |  |  |  |  |  | }, | 
| 22 |  |  |  |  |  |  | allow_sloppy_primitives => { | 
| 23 |  |  |  |  |  |  | is => 'Boolean', | 
| 24 |  |  |  |  |  |  | default_value => 1, | 
| 25 |  |  |  |  |  |  | doc => 'when true, unrecognized data types will function as UR::Value::SloppyPrimitive' | 
| 26 |  |  |  |  |  |  | }, | 
| 27 |  |  |  |  |  |  | method_resolution_order => { | 
| 28 |  |  |  |  |  |  | is => 'Text', | 
| 29 |  |  |  |  |  |  | value => ($^V lt v5.9.5 ? 'dfs' : 'c3'), | 
| 30 |  |  |  |  |  |  | valid_values => ($^V lt v5.9.5 ? ['dfs'] : ['dfs', 'c3']), | 
| 31 |  |  |  |  |  |  | doc => 'Method Resolution Order to use for this namespace. C3 is only supported in Perl >= 5.9.5.', | 
| 32 |  |  |  |  |  |  | }, | 
| 33 |  |  |  |  |  |  | ], | 
| 34 |  |  |  |  |  |  | doc => 'The class for a singleton module representing a UR namespace.', | 
| 35 |  |  |  |  |  |  | ); | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | sub import { | 
| 38 | 2930 |  |  | 2930 |  | 1578945 | my $class = shift; | 
| 39 | 2930 | 100 | 100 |  |  | 1761354 | return if ($class eq 'UR' or $class eq __PACKAGE__); | 
| 40 |  |  |  |  |  |  |  | 
| 41 | 462 |  |  |  |  | 836 | my $calling_package; | 
| 42 | 462 |  |  |  |  | 4472 | for(my $i = 0; ($calling_package) = caller($i); $i++) { | 
| 43 | 490 | 100 |  |  |  | 2860 | last unless (substr($calling_package, 0, 4) eq 'UR::'); | 
| 44 |  |  |  |  |  |  | } | 
| 45 | 462 | 50 |  |  |  | 1566 | return unless $calling_package; | 
| 46 | 462 |  |  |  |  | 2338 | UR::AttributeHandlers::import_support_functions_to_package($calling_package); | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | sub get_member_class { | 
| 50 | 3435 |  |  | 3435 | 0 | 5130 | my $self = shift; | 
| 51 | 3435 |  |  |  |  | 12155 | return UR::Object::Type->get(@_); | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | # FIXME  These should change to using the namespace metadata DB when | 
| 55 |  |  |  |  |  |  | # that's in place, rather than trolling through the directory tree | 
| 56 |  |  |  |  |  |  | sub get_material_classes { | 
| 57 | 6 |  |  | 6 | 1 | 128 | my $self = shift->_singleton_object; | 
| 58 | 6 |  |  |  |  | 8 | my @classes; | 
| 59 | 6 | 100 |  |  |  | 29 | if (my $cached = $self->{material_classes}) { | 
| 60 | 2 |  |  |  |  | 5 | @classes = map { UR::Object::Type->get($_) } @$cached; | 
|  | 21 |  |  |  |  | 39 |  | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  | else { | 
| 63 | 4 |  |  |  |  | 6 | my @names; | 
| 64 | 4 |  |  |  |  | 32 | for my $class_name ($self->_get_class_names_under_namespace()) { | 
| 65 | 53 |  |  |  |  | 65 | my $class = eval { UR::Object::Type->get($class_name) }; | 
|  | 53 |  |  |  |  | 176 |  | 
| 66 | 53 | 100 |  |  |  | 160 | next unless $class; | 
| 67 | 43 |  |  |  |  | 88 | push @classes, $class; | 
| 68 | 43 |  |  |  |  | 119 | push @names, $class_name; | 
| 69 |  |  |  |  |  |  | } | 
| 70 | 4 |  |  |  |  | 28 | $self->{material_classes} = \@names; | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 6 |  |  |  |  | 48 | return @classes; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # Subclasses can override this method to tell the dynamic module loader | 
| 76 |  |  |  |  |  |  | # whether it should go ahead and load the given module name or not. | 
| 77 |  |  |  |  |  |  | # The default behavior is to go ahead and try for them all | 
| 78 |  |  |  |  |  |  | sub should_dynamically_load_class { | 
| 79 |  |  |  |  |  |  | # my($self,$class_name) = @_; | 
| 80 | 4213 |  |  | 4213 | 0 | 11365 | return 1; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | sub get_material_class_names | 
| 85 |  |  |  |  |  |  | { | 
| 86 | 0 |  |  | 0 | 1 | 0 | return map {$_->class_name} $_[0]->get_material_classes(); | 
|  | 0 |  |  |  |  | 0 |  | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | # Returns data source objects for all the data sources of the namespace | 
| 90 |  |  |  |  |  |  | sub get_data_sources | 
| 91 |  |  |  |  |  |  | { | 
| 92 | 2 |  |  | 2 | 1 | 1013 | my $class = shift; | 
| 93 | 2 | 50 | 33 |  |  | 19 | if ($class eq 'UR' or (ref($class) and $class->id eq 'UR')) { | 
|  |  |  | 33 |  |  |  |  | 
| 94 | 0 |  |  |  |  | 0 | return 'UR::DataSource::Meta';  # UR only has 1 "real" data source, the other stuff in that dir are base classes | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | } else { | 
| 97 | 2 |  |  |  |  | 3 | my %found; | 
| 98 | 2 |  |  |  |  | 18 | my $namespace_name = $class->class; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 2 |  |  |  |  | 5 | foreach my $inc ( @main::INC ) { | 
| 101 | 27 |  |  |  |  | 35 | my $path = join('/', $inc,$namespace_name,'DataSource'); | 
| 102 | 27 | 100 |  |  |  | 395 | if (-d $path) { | 
| 103 | 2 |  |  |  |  | 315 | foreach ( glob($path . '/*.pm') ) { | 
| 104 | 10 |  |  |  |  | 48 | my($module_name) = m/DataSource\/([^\/]+)\.pm$/; | 
| 105 | 10 |  |  |  |  | 16 | my $ds_class_name = $namespace_name . '::DataSource::' . $module_name; | 
| 106 | 10 |  |  |  |  | 20 | $found{$ds_class_name} = 1; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  | } | 
| 110 | 2 |  |  |  |  | 9 | my @data_sources = map { $_->get() } keys(%found); | 
|  | 10 |  |  |  |  | 241 |  | 
| 111 | 2 |  |  |  |  | 37 | return @data_sources; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | sub get_base_contexts | 
| 116 |  |  |  |  |  |  | { | 
| 117 | 0 |  |  | 0 | 0 | 0 | return shift->_get_class_names_under_namespace("Context"); | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub get_vocabulary | 
| 121 |  |  |  |  |  |  | { | 
| 122 | 201 |  |  | 201 | 0 | 4589 | my $class = shift->_singleton_class_name; | 
| 123 | 201 |  |  |  |  | 414 | return $class . "::Vocabulary"; | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | sub get_base_directory_name | 
| 127 |  |  |  |  |  |  | { | 
| 128 | 11 |  |  | 11 | 1 | 316 | my $class = shift->_singleton_class_name; | 
| 129 | 11 |  |  |  |  | 53 | my $dir = $class->__meta__->module_path; | 
| 130 | 11 |  |  |  |  | 50 | $dir =~ s/\.pm$//; | 
| 131 | 11 |  |  |  |  | 50 | return $dir; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub get_deleted_module_directory_name | 
| 135 |  |  |  |  |  |  | { | 
| 136 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 137 | 0 |  |  |  |  | 0 | my $meta = $self->__meta__; | 
| 138 | 0 |  |  |  |  | 0 | my $path = $meta->module_path; | 
| 139 | 0 |  |  |  |  | 0 | $path =~ s/.pm$//g; | 
| 140 | 0 |  |  |  |  | 0 | $path .= "/.deleted"; | 
| 141 | 0 |  |  |  |  | 0 | return $path; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | # FIXME This is misnamed... | 
| 145 |  |  |  |  |  |  | # It really returns all the package names under the specified directory | 
| 146 |  |  |  |  |  |  | # (assumming the packages defined in the found files are named like the | 
| 147 |  |  |  |  |  |  | # pathname of the file), not just those that implement classes | 
| 148 |  |  |  |  |  |  | sub _get_class_names_under_namespace | 
| 149 |  |  |  |  |  |  | { | 
| 150 | 4 |  |  | 4 |  | 63 | my $class = shift->_singleton_class_name; | 
| 151 | 4 |  |  |  |  | 5 | my $subdir = shift; | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 4 | 50 |  |  |  | 16 | Carp::confess if ref($class); | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 4 |  |  |  |  | 25 | my $dir = $class->get_base_directory_name; | 
| 156 |  |  |  |  |  |  |  | 
| 157 | 4 |  |  |  |  | 6 | my $namespace_dir; | 
| 158 | 4 | 50 | 33 |  |  | 23 | if (defined($subdir) and length($subdir)) { | 
| 159 | 0 |  |  |  |  | 0 | $namespace_dir = join("/",$dir, $subdir); | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  | else { | 
| 162 | 4 |  |  |  |  | 7 | $namespace_dir = $dir; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 4 |  |  |  |  | 7 | my $namespace = $class; | 
| 166 | 4 |  |  |  |  | 5 | my %class_names; | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | my $preprocess = sub { | 
| 169 | 26 | 100 |  | 26 |  | 340 | if ($File::Find::dir =~ m/\/t$/) { | 
|  |  | 50 |  |  |  |  |  | 
| 170 | 4 |  |  |  |  | 101 | return (); | 
| 171 |  |  |  |  |  |  | } elsif (-e ($File::Find::dir . "/UR_IGNORE")) { | 
| 172 | 0 |  |  |  |  | 0 | return (); | 
| 173 |  |  |  |  |  |  | } else { | 
| 174 | 22 |  |  |  |  | 404 | return @_; | 
| 175 |  |  |  |  |  |  | } | 
| 176 | 4 |  |  |  |  | 18 | }; | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | my $wanted = sub { | 
| 179 | 141 | 100 |  | 141 |  | 3766 | return if -d $File::Find::name;                   # not interested in directories | 
| 180 | 115 | 50 |  |  |  | 199 | return if $File::Find::name =~ /\/\.deleted\//;   # .deleted directories are created by ur update classes | 
| 181 | 115 | 50 |  |  |  | 780 | return if -e $File::Find::name . '/UR_IGNORE';    # ignore a whole directory? | 
| 182 | 115 | 100 |  |  |  | 267 | return unless $File::Find::name =~ m/\.pm$/;      # must be a perl module | 
| 183 | 105 | 50 |  |  |  | 406 | return unless $File::Find::name =~ m/.*($namespace\/.*)\.pm/; | 
| 184 |  |  |  |  |  |  |  | 
| 185 | 105 |  |  |  |  | 127 | my $try_class = $1; | 
| 186 | 105 | 50 |  |  |  | 187 | return if $try_class =~ m([^\w/]);  # Skip names that make for illegal package names.  Must be word chars or a / | 
| 187 | 105 |  |  |  |  | 190 | $try_class =~ s/\//::/g; | 
| 188 | 105 | 50 |  |  |  | 977 | $class_names{$try_class} = 1 if $try_class; | 
| 189 | 4 |  |  |  |  | 15 | }; | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 4 |  |  |  |  | 23 | my @dirs_to_search = @INC; | 
| 192 | 4 |  |  |  |  | 8 | my $path_to_check = $namespace; | 
| 193 | 4 | 50 |  |  |  | 14 | $path_to_check .= "/$subdir" if $subdir; | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 4 |  |  |  |  | 51 | @dirs_to_search = map($_ . '/' . $path_to_check, @dirs_to_search); # only look in places with namespace_name as a subdir | 
| 196 | 4 | 100 |  |  |  | 87 | unshift(@dirs_to_search, $namespace_dir) if (-d $namespace_dir); | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 4 | 50 |  |  |  | 9 | @dirs_to_search = grep { $_ =~ m/\/$path_to_check/ and -d $_ } | 
|  | 56 |  |  |  |  | 1029 |  | 
| 199 |  |  |  |  |  |  | @dirs_to_search; | 
| 200 | 4 | 50 |  |  |  | 16 | return unless @dirs_to_search; | 
| 201 | 4 |  |  |  |  | 493 | find({ wanted => $wanted, preprocess => $preprocess }, @dirs_to_search); | 
| 202 | 4 |  |  |  |  | 73 | return sort keys %class_names; | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | 1; | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | =pod | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | =head1 NAME | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | UR::Namespace - Manage collections of packages and classes | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | In a file called MyApp.pm: | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | use UR; | 
| 219 |  |  |  |  |  |  | UR::Object::Type->define( | 
| 220 |  |  |  |  |  |  | class_name => 'MyApp', | 
| 221 |  |  |  |  |  |  | is => 'UR::Namespace', | 
| 222 |  |  |  |  |  |  | ); | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | Other programs, as well as modules in the MyApp subdirectory can now put | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | use MyApp; | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | in their code, and they will have access to all the classes and data under | 
| 229 |  |  |  |  |  |  | the MyApp tree. | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | A UR namespace is the top-level object that represents your data's class | 
| 234 |  |  |  |  |  |  | structure in the most general way.  After use-ing a namespace module, the | 
| 235 |  |  |  |  |  |  | program gets access to the module autoloader, which will automatically use | 
| 236 |  |  |  |  |  |  | modules on your behalf if you attempt to interact with their packages in | 
| 237 |  |  |  |  |  |  | a UR-y way, such as calling get(). | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | Most programs will not interact with the Namespace, except to C | 
| 240 |  |  |  |  |  |  | package. | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | =head1 Methods | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | =over 4 | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | =item get_material_classes | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | my @class_metas = $namespace->get_material_classes(); | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | Return a list of L class metadata object that exist in | 
| 251 |  |  |  |  |  |  | the given Namespace.  Note that this uses File::Find to find C<*.pm> files | 
| 252 |  |  |  |  |  |  | under the Namespace directory and calls Cget($name)> | 
| 253 |  |  |  |  |  |  | for each package name to get the autoloader to use the package.  It's likely | 
| 254 |  |  |  |  |  |  | to be pretty slow. | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | =item get_material_class_names | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | my @class_names = $namespace->get_material_class_names() | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | Return just the names of the classes produced by C. | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | =item get_data_sources | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | my @data_sources = $namespace->get_data_sources() | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | Return the data source objects it finds defined under the DataSource | 
| 267 |  |  |  |  |  |  | subdirectory of the namespace. | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | =item get_base_directory_name | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | my $path = $namespace->get_base_directory_name() | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | Returns the directory path where the Namespace module was loaded from. | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | =back | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | L, L, L | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | =cut |