class Drupid::ProjectInfo
A convenience class that encapsulates methods for detecting some project’s properties from a local copy of a project.
Attributes
The full path to the main .info file of this project
The project’s core compatibility number. See Drupid::VersionCore
.
The full path to the project’s directory.
The project’s name.
The project’s type (‘core’, ‘module’, ‘theme’, ‘profile’).
The project’s version. See Drupid::Version
.
Public Class Methods
The argument must be the path to a .info file or a project directory. If the path to a directory is passed, then this object will try automatically detect the .info file inside the directory, which is not a trivial task because, among the rest,
-
the .info file may be located in some sub-directory;
-
there may be more than one .info file;
-
the .info file name may be unrelated to the name of its containing directories.
Faithful to its name, Drupid
won’t try to be too keen, and it will raise an exception if it cannot reliably detect a .info file.
# File lib/drupid/project.rb 56 def initialize(project_or_info_path) 57 p = Pathname.new(project_or_info_path).realpath # must exist 58 @project_core = nil 59 @project_type = nil 60 @project_version = nil 61 if '.info' == p.extname 62 @project_name = p.basename('.info').to_s 63 @info_file = p 64 grandparent = @info_file.parent.parent 65 if grandparent.basename.to_s == @project_name 66 @project_dir = grandparent 67 else 68 @project_dir = @info_file.parent 69 end 70 else 71 @project_name = p.basename.to_s 72 @project_dir = p 73 @info_file = _identify_main_info_file 74 end 75 debug "Parsing project info from #{@info_file}" 76 _parse_info_file 77 end
Public Instance Methods
# File lib/drupid/project.rb 79 def core_project? 80 @is_core_project 81 end
Private Instance Methods
If the .info file name differs from the name of the containing directory and the .info file contains a ‘project’ field, do the following:
-
if <project name>.info does not exist and if the ‘project’ field is the same as the .info file name or the directory name, update the project’s name accordingly.
This check will fix, for example, the project’s name for a project like Google Analytics, whose project’s name is ‘google_analytics’ but the .info file is called ‘googleanalytics.info’. It will also fix the project’s name when the directory name has been changed and this object has been passed the path to the project’s directory rather than the path to the .info file.
Testing that <project name>.info does not exist is necessary to avoid renaming projects when more than one .info file exists in the same directory (see for example the Entity module).
# File lib/drupid/project.rb 176 def _check_project_name 177 dirname = @project_dir.basename.to_s 178 if @project_name != dirname and # E.g., 'featured_news' != 'featured_news_feature' 179 !(@info_file.dirname+(dirname+'.info')).exist? and # E.g., '.../featured_news_feature/featured_news_feature.info' does not exist 180 @info_data.has_key?('project') 181 pn = @info_data['project'] 182 if pn == @info_file.basename('.info').to_s or pn == dirname 183 @project_name = pn 184 end 185 end 186 end
Returns the absolute path of the “main” .info file of this project. The first file satisfying one of the following heuristics is returned:
-
‘./#name.info’ exists.
-
‘./#name/#name.info’ exists.
-
‘./<any name>.info’ exists and no other .info file exists at the top-level.
-
‘./#name/<any name>.info’ exists and no other .info file exists inside ‘./#name’.
-
There is a unique .info file, anywhere inside the project’s folder.
If none of the above is satisfied, pick any .info file and set the project’s name after the .info file’s ‘project’ field (if any). Then return that .info file.
Finally, if the .info file has no ‘project’ field, give up hoping that one day Drupal will have better specifications and that people will eventually follow the specifications—but complain fiercely by raising an exception.
# File lib/drupid/project.rb 102 def _identify_main_info_file 103 attempts = [ 104 @project_dir + (@project_name + '.info'), 105 @project_dir + @project_name + (@project_name + '.info'), 106 @project_dir + '*.info', 107 @project_dir + @project_name + '*.info', 108 @project_dir+'**/*.info' 109 ] 110 attempts.each do |p| 111 list = Pathname.glob(p.to_s) 112 if 1 == list.size and list.first.exist? 113 # Set the project's name after the .info file name 114 @project_name = list.first.basename('.info').to_s 115 return list.first 116 end 117 end 118 # We get here if all the above has failed. 119 Pathname.glob(@project_dir.to_s+'/**/*.info').each do |p| 120 data = p.open("r").read 121 match = data.match(/project\s*=\s*["']?(.+)["']?/) 122 unless match.nil? 123 @project_name = match[1].strip 124 return p 125 end 126 end 127 # Give up :/ 128 raise "The .info file for #{@project_name} cannot be reliably detected" 129 end
Extracts the relevant information from the .info file.
# File lib/drupid/project.rb 132 def _parse_info_file 133 _read_info_file 134 _check_project_name 135 _set_project_version 136 _set_project_type 137 end
Reads the content of the .info file into a hash. Parses only ‘simple’ key-value pairs (of the form X = v). Then, check for some other keys useful to determine the project’s type (e.g, ‘stylesheets’)
# File lib/drupid/project.rb 143 def _read_info_file 144 @info_data = Hash.new 145 data = @info_file.open("r").read 146 data.each_line do |l| 147 next if l =~ /^\s*$/ 148 next if l =~ /^\s*;/ 149 if l.match(/^(.+)=(.+)$/) 150 key = $~[1].strip 151 value = $~[2].strip.gsub(/\A["']|["']\Z/, '') 152 @info_data[key] = value 153 end 154 end 155 @info_data['stylesheets'] = true if data.match(/^\s*stylesheets */) 156 @info_data['regions'] = true if data.match(/^\s*regions */) 157 end
# File lib/drupid/project.rb 188 def _set_project_core 189 @info_data['core'].match(/^(\d+)\.x$/) 190 raise "Missing mandatory core compatibility for #{@project_name}" unless $1 191 @project_core = VersionCore.new($1) 192 end
Requires: @info_data must not be nil
# File lib/drupid/project.rb 206 def _set_project_type 207 # Determine whether this is a core project 208 if (@info_data.has_key?('package') and @info_data['package'] =~ /Core/i) or 209 (@info_data.has_key?('project') and @info_data['project'] =~ /drupal/i) 210 @is_core_project = true 211 else 212 @is_core_project = false 213 end 214 # Determine the project's type (module, profile or theme) 215 if @info_file.sub_ext('.profile').exist? 216 @project_type = 'profile' 217 elsif @info_file.sub_ext('.module').exist? 218 @project_type = 'module' 219 elsif @info_data.has_key?('engine') or 220 @info_data.has_key?('Base theme') or 221 @info_data.has_key?('base theme') or 222 @info_data.has_key?('stylesheets') or 223 @info_data.has_key?('regions') 224 @project_type = 'theme' 225 end 226 # If the above didn't work, examine the path the project is in as a last resort. 227 # This is needed, at least, to avoid "type cannot be determined" errors 228 # for some test directories in Drupal Core, which contain an .info file 229 # but no other file :/ 230 unless project_type 231 @project_dir.each_filename do |p| 232 case p 233 when 'modules' 234 @project_type = 'module' 235 when 'themes' 236 @project_type = 'theme' 237 when 'profiles' 238 @project_type = 'profile' 239 end 240 end 241 end 242 raise "The project's type for #{@project_name} cannot be determined" unless @project_type 243 end
# File lib/drupid/project.rb 194 def _set_project_version 195 _set_project_core 196 if @info_data.has_key?('version') 197 v = @info_data['version'] 198 v = @project_core.to_s + '-' + v if v !~ /^#{@project_core}-/ 199 @project_version = Version.from_s(v) 200 else 201 @project_version = nil 202 end 203 end