# -*- encoding: utf-8 -*- module WatchTower class Project # The module contains Path specific project methods, methods like name, path # The module can be included into another module/class or used on it's own, # it does extend itself so any methods defined here is available to both class # and instance level module PathBased include AnyBased extend self # Cache for working_directory by path # The key is the path to a file, the value is the working directory of # this path @@working_cache = Hash.new # Cache for project_name by path # The key is the path to a file, the value is the project's name @@project_name_cache = Hash.new # Cache for project path parts # The key is the path to a file, the value is the project's parts @@project_path_part_cache = Hash.new # Return the working directory (the project's path if you will) from a path # to any file inside the project # # @param [String] path The path to look the project path from # @param [Hash] options A hash of options # @return [String] the project's folder def working_directory(path, options = {}) return @@working_cache[path] if @@working_cache.key?(path) @@working_cache[path] = project_path_from_nested_path(code_path(options), path, nested_project_layers(options)) @@working_cache[path] end # Return the project's name from a path to any file inside the project # # @param path The path to look the project path from # @param [Hash] options A hash of options # @return [String] the project's name def project_name(path, options = {}) return @@project_name_cache[path] if @@project_name_cache.key?(path) @@project_name_cache[path] = project_name_from_nested_path(code_path(options), path, nested_project_layers(options)) @@project_name_cache[path] end protected # Get the code path from the options, if not found use the one from the # configurations # # @param [Hash] options # @return [String] The Code path def code_path(options = {}) options[:code_path] || Config[:code_path] end # Get the nested_project_layers from the options, if not found use the # one from the configurations # # @param [Hash] options # @return [String] The nested_project_layers def nested_project_layers(options = {}) options[:nested_project_layers] || Config[:nested_project_layers] end # Taken from timetap # https://github.com/elia/timetap/blob/master/lib/time_tap/project.rb#L40 # # Find out the path parts of the project that's currently being worked on, # under the code path, it uses the param nested_project_layers to determine # the project name from the entire expanded path to any file under the # project # # nested project layers works "how many folders inside your code folder # do you keep projects. # # For example, if your directory structure looks like: # ~/Code/ # Clients/ # AcmeCorp/ # website/ # intranet # BetaCorp/ # skunkworks/ # OpenSource/ # project_one/ # timetap/ # # A nested_project_layers setting of 2 would mean we track "AcmeCorp", "BetaCorp", and everything # under OpenSource, as their own projects # # @param code The path you store all the projects under # @param path The path to look the project name from # @param nested_project_layers How many folders, defaults to 2 # @return [Array] The project path's parts # @raise [WatchTower::PathNotUnderCodePath] if the path is not nested under code def project_path_part(code, path, nested_project_layers = 2) return @@project_path_part_cache[path] if @@project_path_part_cache.key?(path) # Expand pathes code = expand_path code path = expand_path path regex_suffix = "([^/]+)" regex_suffix = [regex_suffix] * nested_project_layers regex_suffix = regex_suffix.join("/") path.scan(%r{(#{code})/#{regex_suffix}}).flatten.collect(&:chomp). tap { |r| raise PathNotUnderCodePath unless r.any? }. tap { |ppp| @@project_path_part_cache[path] = ppp } end # Find out the project's name # See #project_path_part # # @param code The path you store all the projects under # @param path The path to look the project name from # @param nested_project_layers How many folders, defaults to 2 # @return [String] The project's name # @raise [WatchTower::PathNotUnderCodePath] if the path is not nested under code def project_name_from_nested_path(code, path, nested_project_layers = 2) project_path_part(code, path, nested_project_layers)[nested_project_layers] end # Find out the project's path # See #project_path_part # # @param code The path you store all the projects under # @param path The path to look the project name from # @param nested_project_layers How many folders, defaults to 2 # @return [String] The project's path # @raise [WatchTower::PathNotUnderCodePath] if the path is not nested under code def project_path_from_nested_path(code, path, nested_project_layers = 2) project_path_part(code, path, nested_project_layers)[0..nested_project_layers].join('/') end end end end