drupid 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.
@@ -0,0 +1,234 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2012 Lifepillar
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ module Drupid
24
+ class Platform
25
+ include Drupid::Utils
26
+
27
+ # The absolute path to this platform.
28
+ attr :local_path
29
+ # A Drupid::Platform::Project object with information about Drupal core,
30
+ # or nil if this platform does not contain Drupal core.
31
+ attr :drupal_project
32
+ # The path for contrib modules and themes (e.g., 'sites/all'),
33
+ # relative to #path.
34
+ attr_accessor :contrib_path
35
+ # The path to the sites directory (default: 'sites'), relative
36
+ # relative to #path.
37
+ attr :sites_dir
38
+
39
+ # Creates a new platform object for the Drupal installation at the
40
+ # specified path.
41
+ def initialize(pathname)
42
+ @local_path = Pathname.new(pathname).realpath # must exist
43
+ @sites_dir = Pathname.new('sites')
44
+ @contrib_path = @sites_dir + 'all'
45
+ @drupal_project = nil # Project
46
+ @projects = Hash.new # String -> PlatformProject
47
+ @libraries = Hash.new # String -> Library
48
+ end
49
+
50
+ # Returns the version of Drupal core in this platform, or
51
+ # nil if the version cannot be determined.
52
+ def version
53
+ if @drupal_project and @drupal_project.has_version?
54
+ return @drupal_project.version
55
+ end
56
+ return nil
57
+ end
58
+
59
+ # Returns the full path to the sites directory in this platform, e.g.,
60
+ # '/path/to/drupal/sites', as obtained by joining #local_path and #sites_dir.
61
+ def sites_path
62
+ @local_path + @sites_dir
63
+ end
64
+
65
+ # Returns the (possibly empty) list of sites in this platform.
66
+ def site_names
67
+ return [] unless sites_path.exist?
68
+ Pathname.glob(sites_path + '*/').map { |s| s.basename.to_s }.reject { |s| s =~ /^all$/ }
69
+ end
70
+
71
+ # Returns the full path to the libraries folder.
72
+ def libraries_path
73
+ @local_path + @contrib_path + 'libraries'
74
+ end
75
+
76
+
77
+ # Returns the relative path where the given component should be placed
78
+ # in this platform.
79
+ def dest_path(component)
80
+ if component.instance_of?(String) # assume it is the name of a platform project
81
+ c = get_project(component)
82
+ raise "No project called #{component} exists in this platform" if c.nil?
83
+ else
84
+ c = component
85
+ end
86
+ if c.core_project? or c.profile?
87
+ return c.target_path
88
+ else
89
+ return contrib_path + c.target_path
90
+ end
91
+ end
92
+
93
+ # Returns a list of the names of the profiles that exist in this platform.
94
+ # For profiles to be found, they must be located inside
95
+ # the subdirectory of #local_path named 'profiles'.
96
+ def profiles
97
+ Pathname.glob(local_path + 'profiles/*/*.profile').map { |p| p.basename('.profile').to_s }
98
+ end
99
+
100
+ # Returns the Drupid::PlatformProject object with the specified name,
101
+ # or nil if this platform does not contain a project with the given name.
102
+ def get_project(project_name)
103
+ return @drupal_project if @drupal_project and project_name == @drupal_project.name
104
+ return @projects[project_name]
105
+ end
106
+
107
+ # Returns true if this platform contains a project with the specified name.
108
+ def has_project?(project_name)
109
+ @projects.has_key?(project_name) or (@drupal_project and project_name == @drupal_project.name)
110
+ end
111
+
112
+ # Returns true if the specified site in this platform is bootstrapped.
113
+ # If no site is specified, returns true if the platform contains at least
114
+ # one bootstrapped site. Returns false otherwise. Example:
115
+ # platform.bootstrapped?('default')
116
+ def bootstrapped?(site = nil)
117
+ sites_list = (site) ? [site] : site_names
118
+ sites_list.each do |s|
119
+ p = sites_path + s
120
+ next unless p.exist?
121
+ return true if Drupid::Drush.bootstrapped?(p)
122
+ end
123
+ return false
124
+ end
125
+
126
+ # Analyzes this platform.
127
+ def analyze
128
+ blah "Analyzing #{local_path}"
129
+ analyze_drupal_core
130
+ analyze_projects
131
+ analyze_libraries
132
+ return self
133
+ end
134
+
135
+ # Retrieves information about Drupal core in this platform.
136
+ def analyze_drupal_core
137
+ @drupal_project = nil
138
+ vers = Drupid::Drush.drupal_version(local_path)
139
+ if vers
140
+ core = vers.match(/(\d+)\./)[1].to_i
141
+ @drupal_project = Drupid::Project.new('drupal', core, vers)
142
+ @drupal_project.local_path = self.local_path
143
+ end
144
+ end
145
+
146
+ # Extracts information about the projects in this platform.
147
+ # This method is invoked automatically by Drupid::Platform.analyze.
148
+ # In general, it does not need to be called by the user.
149
+ def analyze_projects
150
+ @projects = Hash.new
151
+ count = 0
152
+ search_paths = Array.new
153
+ search_paths << local_path+'modules/**/*.info'
154
+ search_paths << local_path+'themes/**/*.info'
155
+ search_paths << local_path+'profiles/*/*.info'
156
+ search_paths << local_path+contrib_path+'modules/**/*.info'
157
+ search_paths << local_path+contrib_path+'themes/**/*.info'
158
+ search_paths.uniq! # contrib_path may be ''
159
+ search_paths.each do |sp|
160
+ Dir[sp].each do |p|
161
+ pp = Drupid::PlatformProject.new(self, p)
162
+ @projects[pp.name] = pp
163
+ count += 1
164
+ end
165
+ end
166
+ count
167
+ end
168
+
169
+ # TODO: implement method
170
+ def analyze_libraries
171
+ end
172
+
173
+ # TODO: implement or throw away?
174
+ def to_makefile()
175
+ end
176
+
177
+ # Returns a list of the names of all contrib projects in this platform.
178
+ def project_names
179
+ @projects.values.reject { |p| p.core_project? }.map { |p| p.name }
180
+ end
181
+
182
+ # Returns a list of the names of all core projects.
183
+ def core_project_names
184
+ @projects.values.select { |p| p.core_project? }.map { |p| p.name }
185
+ end
186
+
187
+ # Iterates over all contrib projects in this platform.
188
+ def each_project
189
+ @projects.values.reject { |p| p.core_project? }.each { |p| yield p }
190
+ end
191
+
192
+ # Iterates over all core projects in this platform.
193
+ def each_core_project
194
+ @projects.values.select { |p| p.core_project? }.each { |p| yield p }
195
+ end
196
+
197
+ # Creates a PNG image depicting the relationships
198
+ # among the projects in this platform.
199
+ def dependency_graph
200
+ silence_warnings do
201
+ begin
202
+ require 'rgl/adjacency'
203
+ require 'rgl/dot'
204
+ rescue LoadError
205
+ odie "Please install the RGL gem with 'gem install rgl'"
206
+ end
207
+ end
208
+ if `which dot 2>/dev/null`.chomp.empty?
209
+ owarn "The 'dot' program is required to get an SVG image."
210
+ owarn "Without it you will only get a .dot file."
211
+ end
212
+ analyze
213
+ # We use this instead of a dag, because there may be circular dependencies...
214
+ graph = ::RGL::DirectedAdjacencyGraph.new
215
+ each_project do |p|
216
+ graph.add_vertex(p.name)
217
+ p.dependencies(:subprojects => false).each do |depname|
218
+ graph.add_vertex(depname) # does nothing if depname already exists
219
+ graph.add_edge(p.name, depname)
220
+ end
221
+ end
222
+ each_core_project do |p|
223
+ next if p.name.match(/test/) # Skip test modules
224
+ graph.add_vertex('node' == p.name ? '"node"' : p.name) # 'node' is a Dot keyword
225
+ p.dependencies.each do |depname|
226
+ graph.add_vertex(depname)
227
+ graph.add_edge(p.name, depname)
228
+ end
229
+ end
230
+ graph.write_to_graphic_file('svg')
231
+ end
232
+
233
+ end # Platform
234
+ end # Drupid
@@ -0,0 +1,91 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2012 Lifepillar
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ module Drupid
24
+ class PlatformProject < Project
25
+ attr :platform
26
+
27
+ def initialize proj_platform, project_or_info_path
28
+ pi = ProjectInfo.new(project_or_info_path)
29
+ super(pi.project_name, pi.project_core)
30
+ @info_file = pi.info_file
31
+ @local_path = pi.project_dir
32
+ @version = pi.project_version if pi.project_version
33
+ @proj_type = pi.project_type
34
+ @core_project = pi.core_project?
35
+ @platform = proj_platform
36
+ end
37
+
38
+ # Returns the path of this project relative to the path of the containing
39
+ # platform.
40
+ def relative_path
41
+ @local_path.relative_path_from(@platform.local_path)
42
+ end
43
+
44
+ # A subdirectory where this project is installed.
45
+ # For example, for 'sites/all/modules/specialmodules/mymodule',
46
+ # this method returns 'specialmodules';
47
+ # for 'profiles/custom_profiles/myprofile', this method returns 'custom_profiles';
48
+ # for 'modules/node/tests/', returns 'node'.
49
+ def subdir
50
+ if core_project? or 'profile' == proj_type
51
+ return @local_path.parent.relative_path_from(@platform.local_path + (proj_type + 's'))
52
+ else
53
+ return @local_path.parent.relative_path_from(@platform.local_path + @platform.contrib_path + (proj_type + 's'))
54
+ end
55
+ end
56
+
57
+ # Returns true if this is an enabled theme
58
+ # or an installed (enabled or disabled) module in the specified site,
59
+ # or in at least one site in the platform if no site is specified;
60
+ # returns false otherwise.
61
+ # Note that the project's path must be defined, because this method uses
62
+ # the path to determine whether the project is installed in a site.
63
+ # Consider, for example, a Drupal platform that
64
+ # contains two distinct copies of a module called Foo, one
65
+ # in ./sites/www.mysite.org/modules/foo
66
+ # and another in ./profiles/fooprofile/modules/foo. Suppose that the former is used by
67
+ # the site www.mysite.org, and the latter is used by another site, say
68
+ # www.othersite.org, installed using the fooprofile installation profile.
69
+ # If p is a Drupid::PlatformProject object associated to ./sites/www.mysite.org/modules/foo
70
+ # and q is another Drupid::PlatformProject object associated to
71
+ # ./profiles/fooprofile/modules/foo and both modules are installed in their
72
+ # respective sites,
73
+ # then the result of invoking this method will be as follows:
74
+ # p.installed?('www.mysite.org') # true
75
+ # p.installed?('www.othersite.org') # false
76
+ # q.installed?('www.mysite.org') # false
77
+ # q.installed?('www.othersite.org') # true
78
+ # p.installed? # true
79
+ # q.installed? # true
80
+ def installed? site = nil
81
+ site_list = (site) ? [site] : platform.site_names
82
+ site_list.each do |s|
83
+ site_path = platform.sites_path + s
84
+ next unless site_path.exist?
85
+ return true if Drush.installed?(site_path, name, relative_path)
86
+ end
87
+ return false
88
+ end
89
+
90
+ end # Project
91
+ end # Drupid