Sprockets/Rails: Find all files Sprockets knows how to compile - ruby-on-rails

For Konacha, a Rails engine for testing Rails apps, we need a way to find all files that Sprockets can compile to JavaScript.
Right now we use something like
Dir['spec/javascripts/**/*_spec.*']
but this picks up .bak, .orig, and other backup files.
Can Sprockets tell us somehow whether it knows how to compile a file, so that backup files would be automatically excluded?
content_type_of doesn't help:
Rails.application.assets.content_type_of('test/javascripts/foo.js.bak')
=> "application/javascript"

You can iterate through all the files in a Sprockets::Environment's load path using the each_file method:
Rails.application.assets.each_file { |pathname| ... }
The block will be invoked with a Pathname instance for the fully expanded path of each file in the load path.
each_file returns an Enumerator, so you can skip the block and get an array with to_a, or call include? on it. For example, to check whether a file is in the load path:
assets = Rails.application.assets
pathname1 = Pathname.new("test/javascripts/foo.js").expand_path
pathname2 = Pathname.new("test/javascripts/foo.js.bak").expand_path
assets.each_file.include?(pathname1) # => true
assets.each_file.include?(pathname2) # => false

Related

Ruby double pipe usage

I have a file which have different paths for remote server and local server.
Remote server path:
"/app/public/front-end/public/JSONModels/IdPairing/Text.json"
Local server path:
"public/front-end/public/JSONModels/IdPairing/Text.json"
I basically wanna make sure my app finds correct path for the file regardless of which server I'm at.
So I found something like double pipe ( || ) in Ruby syntax like below:
File.read("/app/public/front-end/public/JSONModels/IdPairing/Text.json" || "public/front-end/public/JSONModels/IdPairing/Text.json")
But it seems like it only reads the first path. How do you make sure it reads the second path if the file is not being found in the first path?
Thanks
A lazy way is:
begin
File.read("/app/public/front-end/public/JSONModels/IdPairing/Text.json")
rescue
File.read("public/front-end/public/JSONModels/IdPairing/Text.json")
end
Why it doesn't work for you:
irb(main):001:0> File.read("some made up name")
Errno::ENOENT: No such file or directory - some made up name
from (irb):1:in `read'
from (irb):1
from /usr/bin/irb:12:in `<main>'
irb(main):002:0> File.read("some made up name" || "Gemfile")
Errno::ENOENT: No such file or directory - some made up name
from (irb):2:in `read'
from (irb):2
from /usr/bin/irb:12:in `<main>'
As you can see, when you try to read a file that doesn't exist, Ruby explodes.
That's what exist? is for.
What you should do
irb(main):003:0> filename = File.exist?("some made up name") ? "some made up name" : "Gemfile"
=> "Gemfile"
irb(main):004:0> File.read(filename)
That's one way to do it, at least.
I would suggest to put those paths into an environment specific config file. Take a look at railsconfig/config on GitHub.
After you have installed the gem, you can add a configuration to config/settings/<RAILS_ENV>.yml like
json_file: /app/public/front-end/public/JSONModels/IdPairing/Text.json
where <RAILS_ENV> can be development, test, production or a custom environment.
And in your code, just
File.read(Settings.json_file)
If you want to try these in sequence, define a list first:
FILES = [
"/app/public/front-end/public/JSONModels/IdPairing/Text.json",
"public/front-end/public/JSONModels/IdPairing/Text.json"
]
Then you can simply open the first one that exists:
path = FILES.find { |f| File.exists?(f) }
File.open(path)
The problem was that "string" || "string" will never work, the first string is logically true and the rest is irrelevant and ignored.

rails assets stylesheets config.rb

I have this file app/assets/stylesheets/config.rb with the following content:
http_path = "/"
css_dir = "."
sass_dir = "."
images_dir = "img"
javascripts_dir = "js"
output_style = :compressed
relative_assets=true
line_comments = false
what is it for?
To answer your question "what is it for", it's configuration for the Compass scss compiler. The reference in the other answer is handy, but for up to date details without speculation check out the details of the Compass configuration file here:
http://compass-style.org/help/tutorials/configuration-reference/
I had originally posted to also provide an answer for where the file came from (if you didn't create it yourself. I answer this because it was 'magially' appearing in my Rails project, and a Google search for generation of config.rb in Rails ends up here. I first supsected sass-rails, but someone from that project confirmed they don't generate that file. Then I figured out that auto-generation of this file was being caused by Sublime Text with the LiveReload package. See the related github issue.
I'm guessing that it defines how to find assets from the stylesheets folder, and some output options on what happens to the assets before being sent to the client browser.
http_path = "/" <= probably the root path to assets, so if you had
application.css, the URL path to it would be
http://example.com/application.css
css_dir = "." <= the directory to look for css files. "." in linux
means "the current directory", so this would set you
CSS path to app/assets/stylesheets
sass_dir = "." <= same as above, except for .sass/.scss files
images_dir = "img" <= images go in app/assets/stylesheets/img
javascripts_dir = "js" <= javascript go in app/assets/stylesheets/js
(seems like a strange place to put them)
output_style = :compressed <= either to produce .gzip'ed versions of files
or to minify them, maybe both - consult docs on this
relative_assets=true <= relative, as opposed to absolute paths to assets, I think
line_comments = false <= I think this turns on/off the ability to include comments,
or maybe whether the comments are included in the
compressed versions of the file. turn it to "true" and
compare the assets, as served to the browser, and see
what the difference is
You can fix it by removing Live Reload plugin from Sublime Text.
Sublime Text > Preferences > Package Control > Remove Package > Live Reload
For me it's good solution because I didn't using Live Reload.
Who using live reload please follow issue: github.

Dynamic CSS using Sprockets

I'm working on a site builder in rails and I would like to render the sites css using Sprockets SCSS processors. Since the user can change colors and logos, I can't use Sprockets precompilation so I've started working on a Rails SCSS template handler to handle dynamic views.
The goal is to compile 'app/views/sites/show.css.scss' any time /sites/43543.css is requested. Here's what I have so far. You'll notice I first run the template through the ERB processor and then attempt to run it through Sprockets.
https://gist.github.com/3870095
Manuel Meurer came up with an alternative solution that writes the ERB output to a path and then triggers the Asset Pipeline to compile it. I was able to get his solution to work locally but it wont work on heroku because the asset path is not writable. Files can only be written to the tmp directory and those files are only guaranteed for a single request.
http://www.krautcomputing.com/blog/2012/03/27/how-to-compile-custom-sass-stylesheets-dynamically-during-runtime/
After a long day I was able to solve my problem thanks to John Feminella and his post on google. The challenging part for me was figuring out how to create a new Sprockets::Context. Luckily John's solution doesn't require a Context.
Updated gist here
Attempt #1
This code does not allow importing css files from the asset pipeline.
#import "foundation"; works because foundation is loaded as a compass module
#import "custom_css"; results in an error message saying the file could not be found
def call(template)
erb = ActionView::Template.registered_template_handler(:erb).call(template)
%{
options = Compass.configuration.to_sass_engine_options.merge(
:syntax => :scss,
:custom => {:resolver => ::Sass::Rails::Resolver.new(CompassRails.context)},
)
Sass::Engine.new((begin;#{erb};end), options).render
}
end
Attempt #2
This code fails to embed base64 urls using asset-data-url
def call(template)
erb = ActionView::Template.registered_template_handler(:erb).call(template)
%{
compiler = Compass::Compiler.new *Compass.configuration.to_compiler_arguments
options = compiler.options.merge({
:syntax => :scss,
:custom => {:resolver => ::Sass::Rails::Resolver.new(CompassRails.context)},
})
Sass::Engine.new((begin;#{erb};end), options).render
}
end
Attempt 3
Turns out you can use empty values while creating the context. Code below works in development but throws an error in production.
ActionView::Template::Error (can't modify immutable index)
It appears the error occurs in Sprockets::Index which is used instead of Sprockets::Environment in production. Switching to Sprockets::Environment doesn't solve the problem either.
def call(template)
erb = ActionView::Template.registered_template_handler(:erb).call(template)
%{
context = CompassRails.context.new(::Rails.application.assets, '', Pathname.new(''))
resolver = ::Sass::Rails::Resolver.new(context)
compiler = Compass::Compiler.new *Compass.configuration.to_compiler_arguments
options = compiler.options.merge({
:syntax => :scss,
:custom => {:resolver => resolver}
})
Sass::Engine.new((begin;#{erb};end), options).render
}
end

JRuby with acts_as_flying_saucer on windows

I've been using Jruby with Rails and acts_as_flying_saucer to successfully generate PDF files for almost a year now. I'm having to move some apps off of our Linux server to a Windows2003 box. This shouldn't cause too much problem as Tomcat/JRuby run just fine on Windows.
The problem I'm having is when acts_as_flying_saucer deposits an HTML file in my temp directory (c:\tmp) it never converts to a PDF. Then Actionpack never sees it and throws an error:
ActionController::MissingFile in ProjectsController#pdf
Cannot read file c:/tmp/b5b79e6ef202dbaa112232220e86a010.pdf
Indeed the file is not there. I added these lines to my production.rb:
ActsAsFlyingSaucer::Config.options = {
:classpath_separator => ';', # classpath separator. unixes system use ':' and windows ';'
:tmp_path => 'c:/tmp', # path where temporary files will be stored
:nailgun =>false
}
but it still won't produce a PDF. I've also tried reversing the directory separator (and properly escaping it of course).
Is anyone else doing this successfully?

ruby reading files from S3 with open-URI

I'm having some problems reading a file from S3. I want to be able to load the ID3 tags remotely, but using open-URI doesn't work, it gives me the following error:
ruby-1.8.7-p302 > c=TagLib2::File.new(open(URI.parse("http://recordtemple.com.s3.amazonaws.com/music/745/original/The%20Stranger.mp3?1292096514")))
TypeError: can't convert Tempfile into String
from (irb):8:in `initialize'
from (irb):8:in `new'
from (irb):8
However, if i download the same file and put it on my desktop (ie no need for open-URI), it works just fine.
c=TagLib2::File.new("/Users/momofwombie/Desktop/blah.mp3")
is there something else I should be doing to read a remote file?
UPDATE: I just found this link, which may explain a little bit, but surely there must be some way to do this...
Read header data from files on remote server
Might want to check out AWS::S3, a Ruby Library for Amazon's Simple Storage Service
Do an AWS::S3:S3Object.find for the file and then an use about to retrieve the metadata
This solution assumes you have the AWS credentials and permission to access the S3 bucket that contains the files in question.
TagLib2::File.new doesn't take a file handle, which is what you are passing to it when you use open without a read.
Add on read and you'll get the contents of the URL, but TagLib2::File doesn't know what to do with that either, so you are forced to read the contents of the URL, and save it.
I also noticed you are unnecessarily complicating your use of OpenURI. You don't have to parse the URL using URI before passing it to open. Just pass the URL string.
require 'open-uri'
fname = File.basename($0) << '.' << $$.to_s
File.open(fname, 'wb') do |fo|
fo.print open("http://recordtemple.com.s3.amazonaws.com/music/745/original/The%20Stranger.mp3?1292096514").read
end
c = TagLib2::File.new(fname)
# do more processing...
File.delete(fname)
I don't have TagLib2 installed but I ran the rest of the code and the mp3 file downloaded to my disk and is playable. The File.delete would clean up afterwards, which should put you in the state you want to be in.
This solution isn't going to work much longer. Paperclip > 3.0.0 has removed to_file. I'm using S3 & Heroku. What I ended up doing was copying the file to a temporary location and parsing it from there. Here is my code:
dest = Tempfile.new(upload.spreadsheet_file_name)
dest.binmode
upload.spreadsheet.copy_to_local_file(:default_style, dest.path)
file_loc = dest.path
...
CSV.foreach(file_loc, :headers => true, :skip_blanks => true) do |row|}
This seems to work instead of open-URI:
Mp3Info.open(mp3.to_file.path) do |mp3info|
puts mp3info.tag.artist
end
Paperclip has a to_file method that downloads the file from S3.

Resources