Rails 5 - How to create custom scaffold generator? - ruby-on-rails

The goal is for command...
bin/rails generate custom_scaffold Thing
... to generate the following 6 files:
db/migrate/201812031331_create_things.rb
app/models/thing.rb
app/controllers/things_controller.rb
app/serializers/thing_serializer.rb
test/fixtures/things.yml
test/integration/requests/things_request_test.rb
... using Rails 5.
My current setup does generate app/models/thing.rb but does not populate it with Thing.
Expected:
class Thing < ApplicationRecord
end
Currently:
class <%= class_name %> < ApplicationRecord
end
I have read through these Rails guides but to little avail.
Does anyone have a working example?
My setup:
# lib/generators/custom_scaffold/custom_scaffold_generator.rb
class CustomScaffoldGenerator < Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
def create_files
copy_file 'migration.rb', "db/migrate/#{timestamp}_create_#{plural_name}.rb"
copy_file 'model.rb', "app/models/#{file_name}.rb"
copy_file 'controller.rb', "app/controllers/#{plural_name}_controller.rb"
copy_file 'serializer.rb', "app/serializers/#{file_name}_serializer.rb"
copy_file 'fixture.yml', "test/fixtures/#{plural_name}.yml"
copy_file 'request_test.rb', "test/integration/requests/#{plural_name}_request_test.rb"
end
private
def timestamp
Time.now.utc.strftime('%Y%m%d%H%M%S')
end
end
# lib/generators/custom_scaffold/templates/model.rb
class <%= class_name %> < ApplicationRecord
end
# lib/generators/custom_scaffold/templates/controller.rb
module V1
module Public
class <%= class_name.pluralize %>Controller < ApplicationController
end
end
end
# lib/generators/custom_scaffold/templates/migration.rb
# Ignore for now
# lib/generators/custom_scaffold/templates/serializer.rb
# Ignore for now
# lib/generators/custom_scaffold/templates/fixture.yml
# Ignore for now
# lib/generators/custom_scaffold/templates/request_test.rb
# Ignore for now
# Gemfile
source 'https://rubygems.org'
ruby '2.4.1'
gem 'rails', '~> 5.1.6'
gem 'puma', '~> 3.7'
gem 'pg'
gem 'rack-cors', require: 'rack/cors'
gem 'olive_branch'
gem 'fast_jsonapi'
gem 'awesome_print'
gem 'byebug', '~> 10.0', groups: %i[development test]
gem 'yaml_db'
group :development do
gem 'listen', '>= 3.0.5', '< 3.2'
gem 'mina', '~> 1.2', require: false
gem 'mina-puma', require: false
gem 'rubocop', require: false
gem 'annotate', require: false
end

You need to specify the file as a Thor template. Rails uses Thor templates for generating templates with ERB style code inside them.
Replace:
copy_file 'model.rb', "app/models/#{file_name}.rb"
With:
template 'model.rb.tt', "app/models/#{file_name}.rb"
By adding the .tt extension you're telling the generator to handle the file as a Thor template, which will interpret the Ruby code (ERB style) inside the file and then create a file with that same name minus the .tt extension. Any file you have without the .tt extension the generator will copy wholesale, without executing any of the code inside.
A useful tip: Sometimes you want to leave some ERB code inside a Thor template file without it being executed. By default any ERB style tags inside a .tt file will be process and in it's place a string will be written to the output file. You can avoid the processing of ERB tags but using a double percent sign in the tag.
For example, lets say you have a file named foo.erb.tt, which will create the file foo.erb when your generator runs. Let's also say we have a article_name variable and it's value is Breaking News
If you put <%= article_name %> in the file it will write Breaking News to the foo.erb.
If you put <%%= article_name %> (notice the %%) it will write <%= article_name %>to the foo.erb.
I found the following reference handy when learning this stuff.
Rails Application Templates article at Rails Guides.
Creating and Customizing Rails Generators & Templates article at Rails Guides.
Thor Actions Docs these are the commands used in our template.rb file.
Thor comes with several actions that help with script and generator tasks. You might be familiar with them since some came from Rails Templates. They are: say, ask, yes?, no?, add_file, remove_file, copy_file, template, directory, inside, run, inject_into_file and a couple more.

Related

Embedly-Ruby gem working in local but not in production

I have added the emdedly-ruby gem (https://github.com/embedly/embedly-ruby) to my social bookmarking appliction. Embedly pulls in an image from a url and replaces the url link with the image in my application. It is working perfectly in local, but it doesn't work in production (Heroku). I am new to programming and I do not know how to capture an error in production. Any suggestions would be much appreciated!
Here is my bookmark.rb model file:
class Bookmark < ActiveRecord::Base
#attr_accessible :thumbnail_url
has_many :bookmark_topics
has_many :topics, through: :bookmark_topics
has_many :user_bookmarks
has_many :users, through: :user_bookmarks
has_many :likes, dependent: :destroy
default_scope { order('updated_at DESC') }
after_create :set_embedly_url
private
def set_embedly_url
embedly_api = Embedly::API.new :key => ENV['EMBEDLY_API_KEY'],
:user_agent => 'Mozilla/5.0 (compatible; mytestapp/1.0; my#email.com)'
Rails.logger.info ">>>> embedluy object: #{embedly_api.inspect}"
obj = embedly_api.extract :url => self.url
Rails.logger.info ">>>> embedly obj: #{obj.inspect}"
# get result and find the attribute for the thumbnail_url
# set the bookmark's thumbnail_url with that value -- update_attribute(thumbnail_url: val_of_thumbnail_url_from_response)
o = obj.first
Rails.logger.info ">>>> o: #{o.inspect}"
image = o.images.first
Rails.logger.info ">>>>> image: #{image.inspect}"
update_attribute(:thumbnail_url, image['url'])
Rails.logger.info ">>>> Now i am: #{self.inspect}"
true
end
end
Here is my bookmark.html.erb partial file in views:
<div class="btn-group">
<% show_remove_button ||= false %>
<li>
<div class="bookmark_container">
<div class="thumbnail">
<% link_text = bookmark.thumbnail_url.present? ? "<img src='#{bookmark.thumbnail_url}'/>".html_safe : bookmark.url %>
<%= link_to link_text, bookmark.url %>
</div>
</div>
<% if current_user %>
<div class="like">
<% if show_remove_button %>
<% user_bookmark = get_user_bookmark_for(bookmark) %>
<% if user_bookmark %>
<%= link_to "Remove", user_bookmark_path(user_bookmark), class: 'btn btn-danger', method: :delete %>
<% end %>
<% else %>
<% if like = current_user.liked(bookmark) %>
<%= link_to [bookmark, like], class: 'btn btn-info', method: :delete do %> Unlike <% end %>
<% else %>
<%= link_to [bookmark, Like.new], class: 'btn btn-info', method: :post do %> Like <% end %>
<% end %>
<% end %>
</div>
<% end %>
</li>
and here is my application.js file in my javascript folder:
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap
//= require_tree .
Here is my gemfile:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.0.5'
group :development, :test do
gem 'rspec-rails'
gem 'capybara'
end
group :production do
gem 'pg'
gem 'rails_12factor'
end
group :development do
gem 'sqlite3'
end
gem 'bootstrap-sass', '~> 3.1.1'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.2'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.0.0'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.2'
group :doc do
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false
end
gem 'devise'
gem 'faker'
gem 'figaro'
gem 'embedly'
Please let me know if there are any other files you would like to see. I have also read through embedly tutorials at http://embed.ly/docs/api/tutorials, but I am not using jquery in my code to make embedly work, as they are using in the tutorials. Do you think I am required to use jquery in my code to make it work? But then why would embedly work in local without jquery and not in production?
After a cursory look at your code, I can't tell for sure why it isn't working in Production, if it's working on local, but here are some debugging thoughts.
Firstly, try to be specific (in your own debugging and with SO), about what "not working" means. Is it throwing an error (where/when/how)? Is it printing a link with no embedded behavior? Is it printing the full Embedly link as an HTML string? Etc. This will help you/us narrow this down.
Once you have a sense for what is going wrong more precisely, here are some next steps for debugging:
If it's throwing an error, that's likely because a), you haven't included Embedly environment variables on Production that are necessary and included locally (such as the EMBEDLY_API_KEY), or some similar tool setup issue, or b), you haven't migrated the thumbnail_url column on Heroku, or something that causes a database issue.
In either case, you can probably best diagnose this issue by producing the error on production and then running heroku logs from your console to get a view of the most recent production server logs. You could also do heroku logs --tail to get a running log. These would help you see any errors, and also see, because of your puts statements, what is and isn't successfully run.
If it's not throwing an error, and not showing up, this might be a JS error.
To diagnose this, open up the JavaScript console in Web Inspector (CMD+Shift+J on a Mac, I believe) to see if there are any JS errors, either in loading files (your Embedly library, etc) or in your JS files themselves. This might have to do with how Heroku deals with the Asset Pipeline. If this is the case, check out the Rails Guide to the Asset Pipeline for some heavy/interesting reading.
The least likely of the cases that comes to mind is that it's just not HTML sanitizing or something, in which case you'd just see a blob of HTML on the page. In that case, I'd just Google the HTML sanitization method you're using and "production" or something, to see what else people have run into, or edit the question or start a new one, with the narrower error description.
Hope that helps!

Rails 4 Use Factory Girl factories from Engine

I've created a rails engine (full, not mountable) to provide models to a number of different rails apps. I use Factory Girl Rails to test this engine and the tests all run fine for the engine itself.
I now want to be able to use these factories in other apps that include this engine.
The dependencies for the Gemspec look like this:
s.add_dependency "rails", "~> 4.0.3"
s.add_dependency "mysql2", "~> 0.3.15"
s.add_development_dependency "rspec-rails", "~> 3.0.0.beta"
s.add_development_dependency "factory_girl_rails", "~> 4.4.1"
s.add_development_dependency "shoulda-matchers", "~> 2.5.0"
And i have defined my factories in /spec/factories.rb:
factory :user do
...
end
To add the factories.rb to the definition paths in factory girl, I added the following to my /lib/engine_name/engine.rb file:
class Engine < ::Rails::Engine
initializer "model_core.factories", :after => "factory_girl.set_factory_paths" do
FactoryGirl.definition_file_paths << File.expand_path('../../../spec/factories.rb', __FILE__) if defined?(FactoryGirl)
end
end
In my rails apps I include the engine by adding the following to the Gemfile:
gem 'engine_name', git: "<GIT_LOCATION>"
I also add factory_girl_rails to the app (is there a way I can expose this from the engine? rather than having to specify it in the apps Gemfile too?).
And require factory girl rails in spec_helper.rb:
require 'factory_girl_rails'
Now when I write, say, a controller test like the following:
it "saves the user to the database" do
expect{post :create, user: attributes_for(:user)}.to change{User.count}.by(1)
end
I get the error: "Factory not registered: user"
I've double checked the factory girl definition file paths by opening the ruby console and running FactoryGirl.definition_file_paths and i can see the factories.rb from the engine in the output: "/home/ ... /gems/engine-name-abc123/spec/factories.rb"
Is there anything else i need to do to make these factories available?
(I have found a few similar questions on stackoverflow and beyond that all seem to point to adding those lines in engine.rb, or specifying namespaces in the factories.rb but I am not using namespaces with this engine.)
I found the easiest route to take with this was to add an install generator that simply copies the factories over. I also have the generator run the install migrations rake task as I will need these in any apps that use the engine.
So, in lib/generators/my_engine/install/install_generator.rb:
module MyEngine
module Generators
class InstallGenerator < Rails::Generators::Base
source_root File.expand_path('../templates', __FILE__)
def copy_migrations
rake("my_engine:install:migrations")
end
def copy_factories
copy_file "../path/to/spec/factories.rb", "spec/factories.rb"
end
end
end
end
Now in projects that use this engine, I simply run rails generate my_engine:install and the factories (and migrations) are ready for me to use.

ruby on rails roo gem cannot load zip/zipfilesystem

I am attempting to use the roo gem to process .xlsx spreadsheets that are uploaded by an outside party. I'm getting the following error:
LoadError (cannot load such file -- zip/zipfilesystem):
I've found a lot of questions similar to this one (such as cannot load such file -- zip/zip) and I've tried to follow their solutions. So far, to no avail.
I originally required 'roo' in the controller, and after getting this error tried requiring 'zip/zip', 'zip/zipfilesystem', and just 'zip'. None of these seem to fix anything. I've also tried adding :require => 'zip', :require => 'zip/zipfilesystem', :require => 'zip/zip' to the Gemfile, and none of that seemed to change anything. Here is some pertinent code:
in Gemfile:
# for spreadsheet upload management
gem 'roo'
gem 'rubyzip'
gem 'spreadsheet'
gem 'nokogiri'
installed versions:
nokogiri (1.6.0)
roo (1.12.1)
rubyzip (1.0.0)
spreadsheet (0.8.9)
in Controller:
require 'roo'
module BatchOrderProcessing
class DataFilesController < ApplicationController
def create
# some code here ...
when ".xlsx"
spreadsheet = Roo::Excelx.new(uploaded_io.path, nil, :ignore)
header = spreadsheet.row(1)
if # some validation stuff...
puts "spreadsheet format inappropriate"
redirect_to # some place
end
process_datafile(fname, spreadsheet)
# more code ...
end
private
def process_datafile(fname, spreadsheet)
#df = DataFile.new
#df[:filename] = ActiveRecord::Base.connection.quote(fname)
if #df.save
begin
# parse asynchronously
datafile_scheduler = Rufus::Scheduler.new
datafile_scheduler.in '3s' do
#df.process_spreadsheet(spreadsheet)
end
redirect_to #df
rescue => e
# more code ...
end
else
# more code ...
end
end
I think this thing is crapping out before it gets to the model (where the process_spreadsheet() code is), but just in case, here's some model code:
def process_spreadsheet(spreadsheet)
# do some stuff
puts "parsing spreadsheet"
(2..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
row_array << row
invoice << row.to_s
# some more code....
dfi = DataFileItem.new()
dfi.attributes = row.to_hash.slice(*accessible_attributes)
dfi.data_file_id = self.id
dfi.save
self.data_file_items << dfi
# Update stuff in our DB based on rows in row_array...
end
I'm using rails 3.2.13 and ruby 2.0.0p195.
Am I requiring the wrong thing (or in the wrong way) somewhere? Let me know if any other code snippets would be helpful. Thaaaaanks.
rubyzip v1.0.0 was released 29 August 2013: https://github.com/rubyzip/rubyzip/releases
This is a new major version number, and more than one gem or project that depends on this has been caught out by the break with backwards-compatibility.
The quickest "get my code working like before" fix is to alter Gemfile reference to rubyzip:
gem 'rubyzip', '< 1.0.0'
In the longer-term, this may not be the best fix, it depends on how and/or why you are using rubyzip. I expect some gem publishers such as roo's authors will need to figure out how to transition nicely so that their own users don't end up with simultaneous requirements for incompatible versions of rubyzip.
Just opinion:
Seeing this in action has actually made me much less a fan of Ruby gems semantic versioning for major versions. If I ever break with backwards compatibility on any of my own projects, I think I'll just start a new gem, and put a notice on the old gem.
Try adding the zip-zip gem to your project. It provides a simple adapter for your dependencies using the RubyZip v0.9.9 interface allowing you to upgrade to RubyZip v1.0.0.
This has been fixed in the roo gem. You need to update to version 1.12.2 or higher to fix. See the issue here: https://github.com/Empact/roo/pull/65

observers for mongoid with rails-observers rails4

I am interested in any solution the title describes.
My gemfile is:
gem "rails", "~> 4.0.0"
gem "mongoid", "~> 3.1.3"
gem 'rails-observers'
I want to use observers on my mongoid models but I receive this error:
https://github.com/mongoid/mongoid/issues/3108
Any ideas are welcome
I just implemented this gem, https://github.com/chamnap/mongoid-observers/, because I often need it. Please have a look and give me feedback.
It looks like you need to include the module in each model and set the observers.
class ORM
include ActiveModel::Observing
end
# Calls PersonObserver.instance
ORM.observers = :person_observer
# Calls Cacher.instance and GarbageCollector.instance
ORM.observers = :cacher, :garbage_collector
# Same as above, just using explicit class references
ORM.observers = Cacher, GarbageCollector
https://github.com/rails/rails-observers/blob/master/lib/rails/observers/active_model/observing.rb#L19

Cannot find file or constant when using a gem locally in a Rails app

I have a gem that I am making and it is located at the same level as a rails app.
I am trying to use the gem in the rails app, but am not able to make it work - the constant I am trying to use, which is defined in the gem, is not accessible to the rails app for some reason.
What am I doing wrong here? What steps can I take to begin debugging the cause of the problem?
In the rails console, $:.grep /mygem/ shows me ["/Users/zabba/mygem/lib"]
Directory structure, with the contents of certain files:
~/mygem/
lib/
mygem/
some_class.rb
module Mygem
class SomeClass
end
end
mygem.rb
require 'mygem/some_class'
~/railsapp/
Gemfile:
gem 'mygem', path: '../mygem', require: 'mygem'
app/
models/
a_model.rb:
# require 'mygem' cannot find file
class AModel
def hello_world
SomeClass.new # Cannot find constant
Mygem::SomeClass.new # Cannot find constant
end
end
Can you create a folder vendor/gems and copy your gem in there. Then in you Gemfile
Gemfile
gem 'mygem', :path => "vendor/gems/mygem-0.0.1"
Then bundle install

Resources