File Coverage

lib/UR/Namespace.pm
Criterion Covered Total %
statement 89 101 88.1
branch 31 44 70.4
condition 6 12 50.0
subroutine 14 17 82.3
pod 4 9 44.4
total 144 183 78.6


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 its
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