Class: BulletTrain::Resolver
- Inherits:
-
Object
- Object
- BulletTrain::Resolver
- Includes:
- I18n::Backend::Flatten
- Defined in:
- lib/bullet_train/resolver.rb
Instance Method Summary collapse
- #calculate_source_file_details ⇒ Object
- #class_path ⇒ Object
- #current_theme ⇒ Object
- #ejected_theme? ⇒ Boolean
- #file_path ⇒ Object
-
#initialize(needle) ⇒ Resolver
constructor
A new instance of Resolver.
-
#js_or_stylesheet_path ⇒ Object
In this search, we prioritize files in local themes and then look in theme gems if nothing is found.
- #locale_path ⇒ Object
- #partial_path ⇒ Object
- #run(eject: false, open: false, force: false, interactive: false) ⇒ Object
- #url? ⇒ Boolean
Constructor Details
#initialize(needle) ⇒ Resolver
Returns a new instance of Resolver.
7 8 9 |
# File 'lib/bullet_train/resolver.rb', line 7 def initialize(needle) @needle = needle end |
Instance Method Details
#calculate_source_file_details ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/bullet_train/resolver.rb', line 109 def calculate_source_file_details result = { absolute_path: nil, project_path: nil, package_name: nil, } result[:absolute_path] = file_path || class_path || locale_path || js_or_stylesheet_path || partial_path # If we get the partial resolver template itself, that means we couldn't find the file. if result[:absolute_path].match?("app/views/bullet_train/partial_resolver.html.erb") || result[:absolute_path].nil? puts "We could not resolve the value you're looking for: #{@needle}".red puts "" puts "If you're looking for a partial, please try passing the partial string in either of the following two ways:" puts "1. Without underscore and extention: ".blue + "bin/resolve shared/attributes/code" puts "2. Literal path with package name: ".blue + "bin/resolve bullet_train-themes/app/views/themes/base/attributes/_code.html.erb" puts "" puts "If you're looking for a locale, the key might not be implemented yet." puts "Try adding your own custom text for the key to your locale file and try again." exit end if result[:absolute_path] if result[:absolute_path].include?("/bullet_train") # This Regular Expression covers gem versions like bullet_train-1.2.26, # and hashed versions of branches on GitHub like bullet_train-core-b00a02bd513c. gem_version_regex = /[a-z|\-._0-9]*/ regex = /#{"bullet_train-core#{gem_version_regex}" if result[:absolute_path].include?("bullet_train-core")}\/bullet_train#{gem_version_regex}.*/ base_path = result[:absolute_path].scan(regex).pop # Try to calculate which package the file is from, and what it's path is within that project. ["app", "config", "lib"].each do |directory| regex = /\/#{directory}\// if base_path.match?(regex) project_path = "./#{directory}/#{base_path.rpartition(regex).last}" package_name = base_path.rpartition(regex).first.split("/").last # If the "package name" is actually just the local project directory. if package_name == `pwd`.chomp.split("/").last package_name = nil end result[:project_path] = project_path result[:package_name] = package_name end end # If we can't find the package name, check if it's because the file path is in the root of the gem # and not in a subdirectory like app, config or lib. if result[:package_name].nil? && base_path.split("/").size == 3 result[:project_path] = "./#{base_path.split("/").last}" result[:package_name] = base_path.split("/").second end end end result end |
#class_path ⇒ Object
170 171 172 173 174 175 |
# File 'lib/bullet_train/resolver.rb', line 170 def class_path @needle.constantize Object.const_source_location(@needle).first rescue NameError => _ false end |
#current_theme ⇒ Object
298 299 300 301 302 |
# File 'lib/bullet_train/resolver.rb', line 298 def current_theme msmn = Masamune::AbstractSyntaxTree.new(Rails.root.join("app/helpers/application_helper.rb").read) current_theme_def = msmn.method_definitions.find { |node| node.token_value == "current_theme" } msmn.symbols.find { |sym| sym.line_number == current_theme_def.line_number + 1 }.token_value end |
#ejected_theme? ⇒ Boolean
294 295 296 |
# File 'lib/bullet_train/resolver.rb', line 294 def ejected_theme? current_theme != "light" && Dir.exist?("#{Rails.root}/app/assets/stylesheets/#{current_theme}") end |
#file_path ⇒ Object
237 238 239 240 241 242 |
# File 'lib/bullet_train/resolver.rb', line 237 def file_path # We don't have to do anything here... the absolute path is what we're passed, and we just pass it back. if @needle[0] == "/" @needle end end |
#js_or_stylesheet_path ⇒ Object
In this search, we prioritize files in local themes and then look in theme gems if nothing is found.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/bullet_train/resolver.rb', line 263 def js_or_stylesheet_path file_name = @needle.split("/").last # Prioritize the current theme and fall back to # the default `light` theme if nothing is found locally. puts "Searching under current theme: #{current_theme.blue}" asset_globs = [ "*.js", # Include JavaScript files under the app's root directory. "app/assets/javascript/**/*.#{current_theme}.js", "app/assets/javascript/#{current_theme}/**/*.js", "app/assets/stylesheets/**/*.#{current_theme}.css", "app/assets/stylesheets/#{current_theme}/**/*.css", ] files = Dir.glob(asset_globs).reject { |file| file.match?("/builds/") } absolute_file_path = files.find { |file| file.end_with?(file_name) } if absolute_file_path absolute_file_path else # Search for the file in its respective gem. Fall back to the `light` theme if no gem is available. gem_path = [`bundle show bullet_train-themes-#{current_theme}`, `bundle show bullet_train-themes-light`].map(&:chomp).find(&:present?) return nil unless gem_path # At this point we can be more generic since we're inside the gem. files = Dir.glob(["#{gem_path}/**/*.js", "#{gem_path}/**/*.css"]) files.find { |file| file.end_with?(file_name) } end end |
#locale_path ⇒ Object
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/bullet_train/resolver.rb', line 244 def locale_path # This is a complete list of translation files provided by this app or any linked Bullet Train packages. (["#{Rails.root}/config/locales"] + `find ./tmp/gems/*`.lines.map(&:strip).map { |link| File.readlink(link) + "/config/locales" }).each do |locale_source| if File.exist?(locale_source) `find -L #{locale_source} | grep ".yml"`.lines.map(&:strip).each do |file_path| yaml = YAML.load_file(file_path, aliases: true) translations = flatten_translations(nil, yaml, nil, false) if translations[@needle.to_sym].present? return file_path end end end end nil end |
#partial_path ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/bullet_train/resolver.rb', line 177 def partial_path # Parse literal partial strings. if @needle.match?(/\.html\.erb$/) partial_parts = @needle.split("/") # TODO: We should probably just default to raising an error if the developer # provides a literal partial string without the name of the package it's coming from. if partial_parts.size <= 3 # If the string looks something like "shared/attributes/_code.html.erb", # all we need to do is change it to "shared/attributes/code" partial_parts.last.gsub!(/(_)|(\.html\.erb)/, "") @needle = partial_parts.join("/") elsif @needle.match?(/bullet_train/) # If it's a full path, we need to make sure we're getting it from the right package. _, partial_view_package, partial_path_without_package = @needle.partition(/(bullet_train-core\/)?bullet_train[a-z|\-._0-9]*/) # Pop off `bullet_train-core` and the gem's version so we can call `bundle show` correctly. partial_view_package.gsub!("bullet_train-core/", "") partial_view_package.gsub!(/[-|.0-9]*$/, "") if partial_view_package.match?(/[-|.0-9]*$/) local_package_path = `bundle show #{partial_view_package}`.chomp return local_package_path + partial_path_without_package else puts "You passed the absolute path for a partial literal, but we couldn't find the package name in the string:".red puts "`#{@needle}`".red puts "" puts "Check the string one more time to see if the package name is there." puts "i.e.: bullet_train-1.2.24/app/views/layouts/devise.html.erb".blue puts "" puts "If you're not sure what the package name is, run `bin/resolve --interactive`, follow the prompt, and pass the annotated path." puts "i.e.: <!-- BEGIN /your/local/path/bullet_train-base/app/views/layouts/devise.html.erb -->".blue exit end end begin annotated_path = ApplicationController.render(template: "bullet_train/partial_resolver", layout: nil, assigns: {needle: @needle}).lines[1].chomp rescue ActionView::Template::Error => e # This is a really hacky way to get the file name, but the reason we're getting an error in the first place is because # the partial requires locals that we aren't providing in the ApplicationController.render call above, # resulting in an undefined local variable error. We do however get the file name, which we can pass back to the developer. return e.file_name end if annotated_path =~ /<!-- BEGIN (\S*) -->/ # If the developer enters a partial that is in bullet_train-base like devise/shared/oauth or devise/shared/links, # it will return a string starting with app/ so we simply point them to the file in this repository. if annotated_path.match?(/^<!-- BEGIN app/) && !ejected_theme? gem_path = `bundle show bullet_train`.chomp "#{gem_path}/#{$1}" else $1 end else raise "It looks like `config.action_view.annotate_rendered_view_with_filenames` isn't enabled?" end rescue ActionView::Template::Error => _ nil end |
#run(eject: false, open: false, force: false, interactive: false) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/bullet_train/resolver.rb', line 11 def run(eject: false, open: false, force: false, interactive: false) # Try to figure out what kind of thing they're trying to look up. source_file = calculate_source_file_details source_file[:relative_path] = nil if source_file[:absolute_path] if File.exist?(source_file[:absolute_path]) puts "" # standard:disable Style/IdenticalConditionalBranches puts "Absolute path:".green puts " #{source_file[:absolute_path]}".green puts "" else puts "" # standard:disable Style/IdenticalConditionalBranches puts "Sorry, we could not find the file you're looking for.".red puts "Please check the path and try again.".red exit 1 end source_file[:relative_path] = source_file[:absolute_path].split(/(?=#{source_file[:package_name]})/).pop if source_file[:package_name].present? puts "Package name:".green puts " #{source_file[:package_name]}".green else puts "Note: If this file was previously ejected from a package, we can no longer see which package it came from. However, it should say at the top of the file where it was ejected from.".yellow end puts "" if interactive && !eject puts "\nWould you like to eject the file into the local project? (y/n)\n" input = $stdin.gets $stdin.getc while $stdin.ready? if input.first.downcase == "y" eject = true end end if eject if source_file[:package_name] if File.exist?(source_file[:project_path]) && !force return puts "Can't eject! `#{source_file[:project_path]}` already exists!\n".red else `mkdir -p #{source_file[:project_path].split("/")[0...-1].join("/")}` puts "Ejecting `#{source_file[:absolute_path]}` to `#{source_file[:project_path]}`".green File.open((source_file[:project_path]).to_s, "w+") do |file| case source_file[:project_path].split(".").last when "rb", "yml" file.puts "# Ejected from `#{source_file[:relative_path] || source_file[:package_name]}`.\n\n" when "erb" file.puts "<% # Ejected from `#{source_file[:relative_path] || source_file[:package_name]}`. %>\n\n" end end `cat #{source_file[:absolute_path]} >> #{source_file[:project_path]}`.strip # Look for showcase preview. file_name = source_file[:absolute_path].split("/").last showcase_partials = Dir.glob(`bundle show bullet_train-themes-light`.chomp + "/app/views/showcase/**/*.html.erb") showcase_preview = showcase_partials.find { |partial| partial.split("/").last == file_name } if showcase_preview puts "Ejecting showcase preview for #{source_file[:relative_path]}" partial_relative_path = showcase_preview.scan(/(?=app\/views\/showcase).*/).last directory = partial_relative_path.split("/")[0..-2].join("/") FileUtils.mkdir_p(directory) FileUtils.touch(partial_relative_path) `cp #{showcase_preview} #{partial_relative_path}` end end # Just in case they try to open the file, open it from the new location. source_file[:absolute_path] = source_file[:project_path] else puts "This file is already in the local project directory. Skipping ejection.".yellow puts "" end end if interactive && !open puts "\nWould you like to open `#{source_file[:absolute_path]}`? (y/n)\n" input = $stdin.gets $stdin.getc while $stdin.ready? if input.first.downcase == "y" open = true end end if open path = source_file[:package_name] ? source_file[:absolute_path] : (source_file[:project_path]).to_s puts "Opening `#{path}`.\n".green # TODO: Use TerminalCommands to open this file open_command = `which open`.present? ? "open" : "xdg-open" exec "#{open_command} #{source_file[:absolute_path]}" end else puts "Couldn't resolve `#{@needle}`.".red end end |
#url? ⇒ Boolean
166 167 168 |
# File 'lib/bullet_train/resolver.rb', line 166 def url? @needle.match?(/https?:\/\//) end |