How to package application layout into a Ruby gem? - ruby-on-rails

I'm working on a project comprising three different applications. They are supposed to share some models and the outer layout. To avoid code duplication, I'm trying to extract the application layout (project_name/app/views/layouts/application.html.haml) into a gem.
I followed these steps:
create the base gem structure with bundle gem common
place the layout file inside common/app/views/layouts/application.html.haml
wrote the Gemspec descriptors
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/common/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["Arthur Alkmim"]
gem.email = ["myemail#here"]
gem.description = %q{Common project files}
gem.summary = %q{Common project files, including layout and migrations}
gem.homepage = ""
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "common"
gem.require_paths = ["lib"]
gem.version = Common::VERSION
end
commit the changes
gem build common.gemspec (successful)
rake install (successful)
Place gem 'common' in the project Gemfile
But still the project won't load the application layout. What should I do to tell my project it has to load the layout files through the gem?
Thanks in advance.

Have you added your gem to your Rails Gemfile to include it in your application?
You can use the path option to specify a relative path in development. e.g.
gem 'common', :path => "../path/to/gem"
Don't forget to then run bundle install

I sort of solved it. I changed application.html.haml to common.html.haml and placed the relevant layout call in the ApplicationController. Seems like Rails won't let me package the application.html layout in a gem, but other layouts are okay. If somebody comes up with a better solution (less workaround-ish), please post!

Related

Local gem class is not found in Rails application

I have written a generator which creates the following ruby file and folder:
app/tests/test.rb
in the test.rb file I have a Test class which looks like this:
class Test < MyCustomModule::MyCustomClass::Base
...
end
Now, I want to use its functionality in one of the show.html.erb files creating new instance like this:
Test.new(...).render(...).html_safe
but I am getting the following error:
uninitialized constant MyCustomModule::MyCustomClass::Base
I have use the following answer to link my gem and my rails application. It seems to work as I am able to use the generator, but the gem module and class are not seen in the rails application.
Could anyone tell how to fix this issue?
I have try to follow the tips posted here but still nothing changed:
Adding config.autoload_paths += Dir["#{config.root}/lib/**/"] in application.rb file
I have created my gem structure looking at CarrierWave gem, so the naming should be correct
I try to disable config.threadsafe! but it is already disabled since config.cache_classes and config.eager_load are set to false in development
DEPRECATION WARNING: config.threadsafe! is deprecated. Rails
applications behave by default as thread safe in production as long as
config.cache_classes and config.eager_load are set to true.
Also, looking at adding-asset-to-your-gems rails documentation, it is said that:
A good example of this is the jquery-rails gem which comes with Rails
as the standard JavaScript library gem. This gem contains an engine
class which inherits from Rails::Engine. By doing this, Rails is
informed that the directory for this gem may contain assets and the
app/assets, lib/assets and vendor/assets directories of this engine
are added to the search path of Sprockets.
So, I have done this, and put my model class file in assets folder, but the result is the same.
The following screenshots demonstrate my real case:
The screenshot below displays my gem file structure
Here you can see how I am loading the gem in my Rails application Gemfile:
gem 'thumbnail_hover_effect', '0.0.3', github: 'thumbnail_hover_effec/thumbnail_hover_effec', branch: 'master'
Then I am using the gem generator a ruby file with a cutstom name in app/thumbnails/test.rb folder with the following code:
class Test < ThumbnailHoverEffect::Image::Base
...
end
and trying to use the Test class gives me uninitialized constant ThumbnailHoverEffect::Image::Base error.
Back in the gem files, these are how the thumbnail_hover_effect file looks like
require 'thumbnail_hover_effect/version'
require 'thumbnail_hover_effect/engine'
require 'thumbnail_hover_effect/image'
module ThumbnailHoverEffect
# Your code goes here...
end
and hoe the image file looks like:
module ThumbnailHoverEffect
#
class Image
...
end
end
From what you've posted here there is no ThumbnailHoverEffect::Image::Base defined. Rails autoloading conventions (which you should not be depending on a gem btw, more on that later) would be looking for this file in thumbnail_hover_effect/image/base.rb, but the directory structure you printed does not have that. Of course you could define the class in thumbnail_hover_effect/image.rb and it would work, but the abridged snippet you posted does not show that. So where is ThumbnailHoverEffect::Image::Base defined?
If it's in thumbnail_hover_effect/image/base.rb then that would indicate the file is not being loaded. You can sanity check this by putting a puts 'loading this stupid file' at the top of thumbnail_hover_effect/image/base.rb. That will allow you to bisect the problem by seeing whether there is a problem with your definition of the class, or whether the problem is with loading the proper files. Debugging is all about bisecting the problem.

Where do I require the songkickr gem file?

I am attempting to use the songkickr gem in a rails app. I have installed the gem using the
gem install songkickr
The next step of the instruction is to:
require 'songkickr'
remote = Songkickr::Remote.new API_KEY
Where do I do this? In what file?
The github page for the gem is https://github.com/jrmehle/songkickr
and my github for this is on https://github.com/jeremybelcher/travel
Sorry for the beginner question, trying to lean al this. Any help is very much appreciated.
In your gemfile at your application root you need to add the line
gem 'songkickr'
Then run
bundle
There are several places where you can put ruby code in a Ruby on Rails app and you should just choose one of them, depending which makes the most sense for you application. For a start, how about you just try putting that code in one of your controllers?
In your Gemfile, add:
gem 'songkickr'
Then run the following command: bundle
At the top of the controller put:
require 'songkickr'
API_KEY = ""  # edit this line
Then in some action you can do this to test it out:
remote = Songkickr::Remote.new API_KEY
Later you might decide to make the "remote" object persist between requests and then you would need to do something more complicated.
Add require 'songkickr' to your application controller
Example:
class ApplicationController < ActionController::Base
protect_from_forgery
require 'songkickr'
end

Does gem spec. name have to equal the gem constant name?

tl;dr
I'm trying to break out a part of a larger application as a mountable engine. The engine will exist in different flavors, each contained in their own gem. I can't get the gem name be different from the engine constant name.
Details
The extracted part contains logic for registration, authentication and session handling. The application is used by clients in different parts of the world with different requirements and regulations regarding the end customers using the product. This prompted us to create separate modules for these needs for each regulatory region. They currently live in the lib directory and the different implementations are loaded depending on config.
The goal with the engines is that you mount the appropriate engine and the routes file routes all the concerned calls to the engine.
Since we have several such registration modules, and more to come, we need to maintain several gems for the variants. I'm trying to make it so that the gems have different names (auth_A, auth_B, etc) but the contained engine has the same contant name, Auth::Engine.
That way we can include the correct gem in the Gemfile and the rest will just work since the endpoint it should rout to is always the same regardless of what version is running.
Problem
The problem I run in to is that I can't seam to get the gem to have one name and the engine constant another...
If I generate a new engine names auth it works fine to mount it in the main app. If I then change the gem name and containing folder to auth_a and update the host apps Gemfile it stops working, I can bundle fine but when I try to start the host app it fails when it ties to mount the engine, complaining that Auth::Engine is an undefined constant.
My, slightly redacted, gemspec looks like this:
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "auth/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "auth_a"
s.version = Auth::VERSION
s.authors = ["Jonas Schubert Erlandsson"]
s.email = ["jonas.schubert.erlandsson#xxxxxx.com"]
s.homepage = "http://some.page.on/the/internet.html"
s.summary = "Authentication module for A"
s.description = "This engine, when mounted in Host app, handles registration, account update, login and logout of users."
s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails", "~> 3.2.13"
end
The only thing I have changed from the generated scaffold is s.name = "auth_a". The relevant line from the host apps Gemfile: gem 'auth_a', path: "../auth_a"...
I have looked through the entire source tree trying to find where it infers a name from the gem name but I can't see it. I don't know what I'm missing and the gem spec docs weren't much help for this either ... I didn't think that the gem name was bound to the constant names of the gem, but maybe I'm wrong? Can you override it? Or am I missing something else?
The answer to this was to simply add this to the line in the host apps Gemfile: gem 'auth_a', path: "../auth_a", require: 'auth'.
So it looks like it defaults to requiring, that is auto loading, a constant based on the gem name, but the require call tells it what to require instead.
Hope it helps someone else as well :)

How do I use a markdown file as the main file for rdoc (in a rails project)?

Is it possible to use a readme.md file with a Rails project and have rdoc use it a the main page? I tried setting up my rake task as follows, but the index.html page that gets generated has a placeholder sentence. If I change to README.rdoc it works as expected as includes the file's contents.
I'm also trying to use tomdoc which I guess might be complicating matters, not sure.
RDoc::Task.new :rdoc do |rdoc|
rdoc.main = "README.md"
rdoc.rdoc_files.include("README.md", "doc/*.rdoc", "app/**/*.rb", "lib/**/*.rb", "config/**/*.rb")
rdoc.title = "My Documentation"
rdoc.options << "--all"
rdoc.options << "--line-numbers"
rdoc.markup = "tomdoc"
rdoc.rdoc_dir = "rdoc"
end
It's not really an answer to your problem, but if you want a good alternative :
use YARD
and use this tips :
https://github.com/lsegal/yard/issues/404
I presume you want to use README.md to be Github compliant, i have often the same problem.
so this is a good alternative.
Personnaly i prefere YARD for documenting my ruby code, it's more powerfull, and actually completely compatible with rubygems, it's the prefered way.
Give that to enlarge your Rakefile :
require "github/markup"
require "redcarpet"
require "yard"
require "yard/rake/yardoc_task"
YARD::Rake::YardocTask.new do |t|
OTHER_PATHS = %w()
t.files = ['lib/**/*.rb', OTHER_PATHS]
t.options = %w(--markup-provider=redcarpet --markup=markdown --main=README.md)
end
Don't forget to bundle (update your Gemfile)

How to gemify a Rails (engine) plugin?

I have a engine style Rails plugin from which I can create a gem using Jeweler. But when I require it in my Rails environment (or erb) the models within the plugin are not loaded. I have followed a number of tutorials and read just about everything on the subject.
# environment.rb
config.gem 'myengine'
# in irb
require 'myengine'
I have unpacked the gem and verified that all files are present. My init.rb has been moved to a new folder called 'rails' as per. All files in 'lib' are automatically added to the $LOAD_PATH, so require 'myengine' runs lib/myengine.rb. I verified this by putting a puts 'hello' within.
Is it because of the physical presence of plugins in a known place that Rails can add all the models, controller etc. to the relevant load_paths? Do I need to replicate this manually when using a gem?
Would gemspec require_paths be a way of adding additional paths other than lib? I assume however that Rails does not just require every single file, but loads them on demand hence the need for the filename and class name to match?
%w{ models controllers helpers }.each do |dir|
path = File.join(File.dirname(__FILE__), 'app', dir) + '/'
$LOAD_PATH << path
puts 'requiring'
Dir.new(path).entries.each do |file|
if file =~ /\.rb/
puts file
require file
end
end
end
By adding the above to lib/myengine.rb all the models/controllers are required. But like I said in my question this is unlikely to be a good way forward.
Offhand I'd say the part about adding those directories to the search path is right on. What you shouldn't need to do is require each file manually (as you allude to in your last sentence). What Rails does when you reference a non-existent constant is to search for a file with the same name (underscored of course) in the load path.
If for some reason you can not abide by the constraint (think about it long and hard) then you are going to need to dig deeper into Rails and see how the reloading mechanism works so you can tie into it properly in development mode.
The problem was the files (in app) where not being added to the gem because when using Jeweler it only automatically adds files to required_paths which are committed to git.

Resources