Rails sprockets 3.0 find_asset - ruby-on-rails

I used to use rails-sprockets 2.x and was using the following in an email layout template to include the css
Rails.application.assets.find_asset('file').to_s.html_safe
However, this no longer works in rails-sprockets 3.x and it is suggested to use
Rails.application.assets_manifest.assets['file.css']
This only returns the string name of the file (if it exists). How would I get the body of the file so I can output it in the view?

I just ran into this issue too, although I upgraded to sprockets 3.x about 7 months.
I threw this together as quick as I could (there is likely a better solution out there...) - this will get you the path name - just use File.read()
def find_asset_path(asset_name)
if Rails.application.assets
Rails.application.assets.find_asset(asset_name).pathname
else
name = Rails.application.assets_manifest.assets[asset_name]
File.join(Rails.public_path, 'assets', name)
end
end

Related

Extend Date Class in Rails 4.0

I would like to have a new method for Date class in my Ruby 2.0 Rails 4 application. Adding a new like 'date_extensions.rb' in /lib used to work in another Ruby 1.9 Rails 3.2 app but not here. The extension is pretty simple now:
class Date
def week_day
self.wday == 0 ? 7 : self.wday
end
end
I do not like to put it in initializer as it keeps growing. Is there a good workaround?
At some point, Rails stopped adding the lib directory to the autoload paths. The other app had probably set a setting to autoload the lib directory and your new app doesn't have that (yet?). So that's why you're having to require the file directly via an initializer. For what it's worth: I think this is the better approach -- only load code when you need it. I usually add a config/initializers/application.rb and then add requires such as require "date_extensions" in there. Then the date_extensions.rb file goes in the lib folder as you suggest.

Rails 4 engine with nested namespace

I could not find a way to generate engines with nested namespaces under rails. Every time I do that, I basically have to edit and move around the generated files by hand.
Is there really no support for nested namespaces in rails? Seems unlikely.
At the company we namespace everything like this:
CompanyName::SerivceName::Module
So when I'm working on Service1, and making the engine which will be integrated into the app that customer support uses to play around with the users and data of that service on customer requests, I'd like to create that engine under
CompanyName::Serive1::CustomerSupport
However rails seems to be unable to do that.
Using rails plugin new a::b::blah is not accepted:
akovanm0:test avandra$ rails plugin new a::b::blah -T --dummy-path=spec/dummy --mountable --full --mountable
Invalid plugin name a::b::blah. Please give a name which use only alphabetic or numeric or "_" characters.
Specifying rails plugin new a/b/blah generates an engine, but has the same output as rails plugin new blah
Specifying rails plugin new a_b_blah generates an engine with the literal name a_b_blah, not namespaced. (and the actual name is camelcased to ABBlah)
What I'd like to achieve is an engine whose controllers, models and views are generated under the a::b::blah namespace, and it is mountable the same way.
I want all generated controllers to go under app/controllers/a/b/blah, the models to go under app/models/a/b/blah, and so on...
Is there a way to achieve this?
For anyone reading this in 2022, I believe later versions of Rails handle engine names with dashes as separate namespaces.
rails plugin new parent_engine-sub_engine --mountable
Will create the following engine.rb:
module ParentEngine
module SubEngine
class Engine < ::Rails::Engine
isolate_namespace ParentEngine::SubEngine
end
end
end
You need to create engine with mountable option enabled like this,
rails plugin new engine_name --mountable.
It will add isolate_namespace EngineName method call in lib/engine_name/engine.rb to isolate engine namespace.
I think you can't do that :(
EDIT: Look at the bottom of the answer, I have modified the rails plugin generator just to do it :)
If you look carefully to the source (https://github.com/rails/rails/blob/5f07366bed77116dbfbb5b98d1cdf6c61b3dfc9b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb#L299) you will see that the plugin name is just the basename of the destination folder.
def original_name
#original_name ||= File.basename(destination_root)
end
So if you write rails plugin new a/b/c then the plugin will be created at the a/b/c subfolder in your current folder but the name will be just c :(
If you override that original_name method to return a/b/c as desired then you will need to fight both the valid_const? method (https://github.com/rails/rails/blob/5f07366bed77116dbfbb5b98d1cdf6c61b3dfc9b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb#L307) that validates the format name and accepts "only alphabetic or numeric or _ characters." and the templates that creates the modules.
def valid_const?
if original_name =~ /[^0-9a-zA-Z_]+/
raise Error, "Invalid plugin name #{original_name}. Please give a name which use only alphabetic or numeric or \"_\" characters."
elsif camelized =~ /^\d/
raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(name)
raise Error, "Invalid plugin name #{original_name}. Please give a name which does not match one of the reserved rails words."
elsif Object.const_defined?(camelized)
raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name."
end
end
I'm thinking on using a plugin template (http://edgeguides.rubyonrails.org/rails_application_templates.html) for my namespaced plugins instead :(
EDIT: I lied about what methods you would have to fight. It's not the name method, it's the templates
EDIT (II): I have modified the plugin_new folder so nested namespaces are allowed. You have it here: https://github.com/brenes/nested-plugin-generator
I would appreciate any feedback :)
Try https://github.com/T-Dnzt/Modular-Engine or create your own generator

How to implement generators for a plugin located at the `lib/<plugin_name>` directory?

I am using Ruby on Rails 3.2.2. I have implemented a Something plugin (it is almost a gem, but is not a gem) and all related files are in the lib/something directory. Since I would like to automate code generation related to that plugin, I came up with Ruby on Rails Generators. So, for the Something plugin, I am looking for implementing my own generators in the lib/something directory.
How should I make that and what are prescriptions? That is, for example, what rails generate command line should be invoked to properly generate all needed files in the lib/something directory? generators would still work with plugins (not gem)? what are advices about this matter?
I would make it a gem. I've made generators using gems, but I don't know if the generators would still work with plugins.
If you are having difficulty with the command line, I am guessing that you don't need any argument. (If you need an argument, I could copy the provided templates, and if I needed some other argument I'd be lost, so my advise is limited to non-argument.)
I have a generator gem which generates migration files needed for another gem. It checks if the migration with a given root name (w/o the timestamp prefix) is in db/migrate, and otherwise creates it.
Here is my code. I think this example is the help you need.
class ItrcClientFilesGenerator < Rails::Generators::Base
source_root(File.dirname(__FILE__) + "/../src")
desc "Generator to create migrations for needed db tables"
def create_itrc_client_files
prefix = DateTime.now.strftime("%Y%m%d%H%M")
existing_migrations =
Dir.glob("db/migrate/*itrc*").map do |path|
File.basename(path).gsub(/^\d*_/, '')
end
Dir.glob(File.dirname(__FILE__) + "/../src/*").sort.each_with_index do |src_filepath, index|
src_filename = File.basename(src_filepath)
unless existing_migrations.include?(src_filename.gsub(/^\d*_/, '')) then
this_prefix = "#{prefix}#{'%02i' % index}_"
dst_filename = src_filename.gsub(/^\d*_/, this_prefix)
copy_file(src_filename, "db/migrate/" + dst_filename)
end
end
end
end

Always preprocess a specific Javascript file with Rail 3.1 asset pipeline

Is there a way to always run the ERB preprocessor on a Javascript file?
I'm using Mustache to use the same templates on the client and server. I'd like to include these templates in my application.js files so they're available on the client. So I'm preprocessing my Javascript file (templates.js.erb, which then gets required in application.js) with erb:
App.templates.productShow = <%= MustacheController.read("product/show").to_json %>;
This works great but when I edit the "product/show.html.mustache" template I need to also edit "templates.js.erb" so Rails knows to recompile this file which then picks up the latest changes from the mustache template.
There's no issue running this in production since the assets get compiled when I deploy, but it's annoying when developing. Ideally I could set the preprocessor to run on "templates.js.erb" every time I reload. My current solution is to inline the Javascript in the application layout but it would be nice to keep it separate.
I ended up writing a guardfile for this that adds a timestamp to the end of the file. Just touching the file is enough for sprockets to recompile but if you're using git you need to actually alter the file. Otherwise anyone else who pulls in the code won't get the latest preprocessed files. Here's the code...
#!/usr/bin/ruby
guard 'mustache' do
watch(%r{app/templates/.+\.mustache})
end
require 'guard/guard'
module ::Guard
class Mustache < ::Guard::Guard
def run_on_change(paths)
# file to be updated when any mustache template is updated
file_name = "./app/assets/javascripts/templates.js.erb"
# get array of lines in file
lines = File.readlines(file_name)
# if last line is a comment delete
lines.delete_at(-1) if lines[-1].match /\/\//
# add timestamp
lines << "// #{Time.now}"
# rewrite file
File.open(file_name, "w") do |f|
lines.each{|line| f.puts(line)}
end
end
end
end
This seems to be a common complaint with the pipeline - that you have to touch a file that references variables for those changes to be reflected in development mode. A few people have asked similar questions, and I do not think there is a way around it.
Forcing Sprockets to recompile for every single request is not really a viable solution because it takes so long to do the compilation.
Maybe you could set up guard to watch your mustache directory and recompile templates.js.erb when you make changes to it. Similar to how guard-bundler watches your Gemfile and rebundles on change.

Accessing the app name from inside a rails template when generating rails app

I'm messing around with rails 2.3 templates and want to be able to use the app name as a variable inside my template, so when I use...
rails appname -m path/to/template.rb
...I want to be able to access appname inside template.rb. Anyone know how to do this?
Thanks
I was looking for an answer to this question. unfortunately the answer above (#root) doesn't seem to work in Rails 3.
Here's the variables you can access in Rails 3 app templates (even easier):
#app_name
#app_path
Thanks for the answers. Mike Woodhouse, you were so close. Turns out, all you need to do to access the appname from inside your rails template is...
#root.split('/').last
The #root variable is the first thing created when initializing templates and is available inside your rails templates. RAILS_ROOT does not work.
In Rails 3, use the app_name attribute.
See the documentation for the Rails::Generators::AppGenerator.
I ran into a similar problem, none of the variables listed above were available to me in Rails 4. I found that #name was available while running
rails plugin new engines/dummy -m my_template.rb
There are other useful variables available from within the template. You can see for yourself and play around by utilizing pry. Inside my template I added
require 'pry'; binding.pry
and then ran ls to show a list of available instance variables
ls -i
instance variables:
#_initializer #app_path #behavior #destination_stack #extra_entries #name #output_buffer #shell
#_invocations #args #builder #dummy_path #gem_filter #options #rails_template #source_paths
#after_bundle_callbacks #author #camelized #email #in_group #original_name #shebang
There's probably a more straightforward way, but this seems to work:
RAILS_ROOT.split('/').last
EDIT: Bleah - this got voted down once, and the voter was right. If I'd read the question more carefully, I'd have noticed the 2.3 and template.rb elements. Apologies.
I suspect that RAILS_ROOT won't have been created at the point that you need the app name. Looking at ruby\lib\ruby\gems\1.8\gems\rails-2.2.2\bin\rails, however, almost the first thing that happens is this:
app_path = ARGV.first
It's used at the end of the script to allow a chdir and freeze to be done if needed - I didn't know I could insta-freeze at creation, so I learned something new at least. ARGV then gets used here:
Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app')
which quickly gets us to the place where ARGV is really handled:
rails-2.3.1\lib\rails_generator\scripts.rb
where I see
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
Somewhere below here is probably where the templating gets handled. I'm afraid I'm at a very early stage with 2.3 and templating is an area that I haven't looked at yet.
Does that help any better than my first effort?
RAILS_ROOT will give you the absolute path to your root directory. Your app name will be the portion of the string after the final '/' which you can grab in any number of ways.
EDIT: Not quite enough to get the job done. Mike and Dan iron it out below.
I believe the preferred way now is to call Rails.root and no longer RAILS_ROOT. Apparently someone on planet rails has an aversion to uppercase or some similar important reason. As of 2.3.5 they both appear to work.
I was getting error
`template': undefined local variable or method `app_name'
ruby 1.9.2p290, rails 3.2.11, thor 0.18.0, Windows
but with rails 2.3 generator:
class DynanavGenerator < Rails::Generators::Base
(can't be sure whether this error happened under rails 3.0.9 or earlier)
changed class definition to be:
class DynanavGenerator < Rails::Generators::NamedBase
which then gave:
No value provided for required arguments 'name'
I then added a 'name' ("something" below):
rails generate dynanav something --force
which gave the original error, so I then added:
def app_name
#name.titleize
end
to the class and all was well.
As of Rails 4 (maybe earlier versions?), use Rails.application.class to get the application name. For example, if your app is named Fizzbuzz, here are a few ways you might access it:
rails(development)> Rails.application.class
=> Fizzbuzz::Application
rails(development)> Rails.application.class.name
=> "Fizzbuzz::Application"
rails(development)> Rails.application.class.parent
=> Fizzbuzz
rails(development)> Rails.application.class.parent.to_s
=> "Fizzbuzz"

Resources