This code is working correctly. The problem is everything is getting printed to the console. I want to show it on the browser. How to do that? Do I need a template called create.html.erb. How to access the variables and basically the whole controller code in the view? Please help!!
require File.join(Rails.root, 'config/myconfig')
puts Rails.root
class UsersController < ApplicationController
layout 'admin'
#require File.expand_path('././myconfig') #=> C:/ruby/require/expand_path/ok.rb loaded
def list
#users = User.all
end
def new
#user = User.new
end
def create
if EntityList::ENTITIES.include?(params[:user][:entity_name])
puts " Entered entity is: "
#entity = params[:user][:entity_name]
#var = EntityList::RELATION_SHIPS[#entity.to_sym]
puts "Entered entity is related to"
if(#var.nil?)
#do nothing
else
puts #var
checking(#var)
end
##var.split(" ")
##len = #var.length
#puts #var[0]
#puts #var[1]
#puts #var[1]
##var.each {|#var| puts #var}
#for index in 0 ... #var.size
# puts EntityList::RELATION_SHIPS[#var[index].to_sym]
# end
# #var.each_with_index {|val, index| puts "#{val} => #{index}" }
##var2= EntityList::RELATION_SHIPS[#var.to_sym]
#puts "Entity2 is related to"
#puts #var2
flash[:notice] = "The entity you entered is valid!!"
puts "Before Redirection"
redirect_to(:action => "helloworld")
puts "After redirection"
puts "done"
else
redirect_to(:action => "SorryPage")
end
end
def checking(array)
array.split(" ")
for index2 in 0 ... array.size
if EntityList::RELATION_SHIPS[array[index2].to_sym].nil?
# do nothing
else
puts EntityList::RELATION_SHIPS[array[index2].to_sym]
some = EntityList::RELATION_SHIPS[array[index2].to_sym]
checking(some)
end
end
end
end
Yes, you would create an create.html.erb and can access all Controller Instance variables like #entity or #var from there.
I'd recommend looking at the output from a generate scaffold call to get an example how this works, e.g. by calling it in a new rails app:
rails new tryout
cd tryout
rails generate scaffold User name:string email:string
and then look at the generated controller and view templates.
Related
I'm making an export to csv file functionality in a Ruby on Rails repo and I'm almost done. However, when I press the "Export all" button, I get the undefined method `export' for nil:NilClass error. The log shows that format.csv { send_data #foos.export, filename: "foos-#{Date.today}.csv" } went wrong. What am I missing please?
This is model
class Foo < ApplicationRecord
has_many :bars
def export
[id, name, foos.map(&:name).join(' ')]
end
end
This is part of controller
def index
#foos = Foo.all
end
def export
all = Foo.all
attributes = %w{name}
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |foo|
csv << attributes.map{ |attr| foo.send(attr) }
end
respond_to do |format|
format.csv { send_data #foos.export, filename: "foos-#{Date.today}.csv" }
end
end
end
def name
"#{foo_id} #{name}"
end
This is View
<button class="btn btn-success">export all</button>
This is Routes
Rails.application.routes.draw do
resources :foos
get :export, controller: :foos
root "foos#index"
end
This is Rake (lib/tasks/export.rb)
namespace :export do
task foo: :environment do
file_name = 'exported_foo.csv'
csv_data = Foo.to_csv
File.write(file_name, csv_data)
end
end
Start by creating a service object that takes a collection of records and returns CSV so that you can test the CSV generation in isolation:
# app/services/foo_export_service.rb
# Just a Plain Old Ruby Object that converts a collection of foos into CSV
class FooExportService
# The initializer gives us a good place to setup our service
# #param [Enumerable] foo - an array or collection of records
def initialize(foos)
#headers = %w{name} # the attributes you want to use
#foos = foos
end
# performs the actual work
# #return [String]
def perform
CSV.generate do |csv|
#foos.each do |foo|
csv << foo.serializable_hash.slice(#headers).values
end
end
end
# A convenient factory method which makes stubbing the
# service easier
# #param [Enumerable] foos - an array or collection of records
# #return [String]
def self.perform(foos)
new(foos).perform
end
end
# example usage
FooExportService.perform(Foo.all)
Not everything in a Rails application needs to be jammed into a model, view or controller. They already have enough responsiblities. This also lets you resuse the code for example in your rake task if you actually need it.
This simply iterates over the collection and uses Rails built in serialization features to turn the model instances into hashes that can be serialized as CSV. It also uses the fact that Hash#slice also reorders the hash keys.
In your controller you then just use the service object:
class FoosController
def export
#foos = Foo.all
respond_to do |format|
format.csv do
send_data FooExportService.perform(#foos),
filename: "foos-#{Date.today}.csv"
end
end
end
end
You don't even really need a separate export action in the first place. Just use MimeResponds to add CSV as an availble response format to the index:
class FoosController
def index
# GET /foos
# GET /foos.csv
#foos = Foo.all
respond_to do |format|
format.html
format.csv do
send_data FooExportService.perform(#foos),
filename: "foos-#{Date.today}.csv"
end
end
end
end
<%= link_to("Export as CSV", foos_path(format: :csv)) %>
using ruby 2.6.5, Rails 6.0.3.7
There is before_action filter which are working fine when running the project in the development server. But while running the integration tests of the rails application.
The call back do not execute and the request goes directly to the called function rather than going to the before action first.
Here attaching my controller and integration test case and error output.
Controller
class TvSeriesController < ApplicationController
before_action :check_file, only: [:create, :tv_seriel_comments]
require 'roo'
def index
#tv_series = TvSeriel.includes(:comments).all
end
def show
#series = TvSeriel.includes({ comments: [:user] }).find(params[:id])
end
def create
spreadsheet = Roo::Spreadsheet.open(params[:file])
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |line|
row = HashWithIndifferentAccess[[header, spreadsheet.row(line)].transpose]
tv_serial = TvSeriel.new
tv_serial.name = row['TV Series']
tv_serial.genre = row["Genre"]
tv_serial.seasons = row["No of seasons"]
tv_serial.release_date = row["Date of First Release"]
tv_serial.director = row["Director"]
tv_serial.actor = row["Actor"]
tv_serial.shoot_location = row["Shoot location"]
tv_serial.country = row["Country"]
tv_serial.save
end
flash[:notice] = 'TV series uploaded!'
redirect_back(fallback_location: tv_series_index_path)
end
def tv_seriel_comments
spreadsheet = Roo::Spreadsheet.open(params[:file])
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |line|
row = HashWithIndifferentAccess[[header, spreadsheet.row(line)].transpose]
comment = Comment.new
user = User.find_by_name(row["User"])
comment.user = user
tv_serial = TvSeriel.find_by_name(row['TV Series'])
comment.tv_seriel = tv_serial
comment.stars = row['Stars']
comment.review = row['Review']
comment.save
end
flash[:notice] = 'TV series comment uploaded!'
redirect_back(fallback_location: tv_series_index_path)
end
def destroy
TvSeriel.find(params[:id]).destroy
flash[:notice] = 'TV series removed success!'
redirect_back(fallback_location: tv_series_index_path)
end
def search_actor
#tv_series = TvSeriel.includes(:comments).where("actor like ?", "%#{params[:actor]}%")
render 'tv_series/index'
end
## check if uploaded file is a cvs
def check_file
unless params[:file].blank?
if params[:file].content_type.eql?("text/csv")
else
flash[:alert] = "file format Invalid, expected: text/csv Got #{params[:file].content_type}"
redirect_back(fallback_location: :tv_series_index_path)
end
else
flash[:alert] = 'Please provide a file to be uploaded !'
end
end
end
Integration test:
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
fixtures :all
include Devise::Test::IntegrationHelpers
include Warden::Test::Helpers
test 'Get list of all users' do
#user = users(:vidur)
sign_in(#user)
get "/users"
assert_response :success
assert_select "h2", "User List"
end
test 'Update Series' do
#user = users(:vidur)
sign_in(#user)
post '/tv_series'
assert_response :success
end
end
Error:
# Running:
E
Error:
UserFlowsTest#test_Update_Series:
TypeError: no implicit conversion of nil into String
app/controllers/tv_series_controller.rb:15:in `create'
test/integration/user_flows_test.rb:19:in `block in <class:UserFlowsTest>'
Here the before_action filter do not execute and the request goes directly to the defined action can any body give the reason why only running test, and how to correct it, Thanks in advance.
Here is the git repo for complete code: https://github.com/vidurpunj/sopra_test.
It's because you don't have a file parameter.
So your check_file is using this part:
flash[:alert] = 'Please provide a file to be uploaded !'
And since you don't render or redirect, the controller action is called normally. See the Rails documentation on filters for more details:
If a "before" filter renders or redirects, the action will not run
I have got a AccountController class, this controller class is in the application kernel. I dont want to make changes into kernel, therefore Im going to Monkey Patch it.
Controller has method named successful_authentication, which I've rewritten. Inside the new method (in my module) this code calls new method named load_favourite_or_index.
I've read that alias_method_chain is deprecated now and should not be used. I'm trying to prepend my module before AccountController. But nothing happens, I guess my prepending code is not correct, please could you help me? Here's my code.
# frozen_string_literal: true
module RedmineKapDesign
module Patches
module AccountControllerPatch
def self.prepended(base) # :nodoc:
class << base
prepend InstanceMethods
end
end
module InstanceMethods
def successful_authentication(user)
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
logger.info "Setting.autologin? = #{Setting.autologin?}, params[:autologin] = #{params[:autologin]}"
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
set_autologin_cookie(user)
end
call_hook(:controller_account_success_authentication_after, {:user => user})
load_favourite_page_or_index
#redirect_back_or_default my_page_path
end
def load_favourite_page_or_index
user = User.current
favourite_page_field = CustomField.where(name: ["Favourite page", "favourite page", "favorite page", "Favourite page", "Любимая страница", "любимая страница", "Избранная страница", "избранная страница"]).first
page_url = user.custom_values.where(custom_field_id: favourite_page_field.id).first.value
if page_url.empty?
redirect_back_or_default my_page_path
else
redirect_to(page_url)
end
end
def self.hello
puts "Hello"
end
end
end
end
end
#unless AccountController.included_modules.include?(RedmineKapDesign::Patches::AccountControllerPatch)
# AccountController.send(:prepend, RedmineKapDesign::Patches::AccountControllerPatch)
#end
AccountController.singleton_class.prepend(RedmineKapDesign::Patches::AccountControllerPatch)
You don't need to separate out the instance methods into a seperate module. You can just place them in AccountControllerPatch and prepend whatever class you are monkeypatching with it.
module RedmineKapDesign
module Patches
module AccountControllerPatch
def successful_authentication(user)
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
logger.info "Setting.autologin? = #{Setting.autologin?}, params[:autologin] = #{params[:autologin]}"
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
set_autologin_cookie(user)
end
call_hook(:controller_account_success_authentication_after, {:user => user})
load_favourite_page_or_index
#redirect_back_or_default my_page_path
end
def load_favourite_page_or_index
user = User.current
favourite_page_field = CustomField.where(name: ["Favourite page", "favourite page", "favorite page", "Favourite page", "Любимая страница", "любимая страница", "Избранная страница", "избранная страница"]).first
page_url = user.custom_values.where(custom_field_id: favourite_page_field.id).first.value
if page_url.empty?
redirect_back_or_default my_page_path
else
redirect_to(page_url)
end
end
def self.hello
puts "Hello"
end
end
end
end
Using a seperate module is only really needed for class methods when using the module mixin pattern. In framework code using a seperate InstanceMethods module can be used for organizational purposes. But in a simple monkeypatch it just makes more of a mess.
# config/initializers/my_monkey_patch.rb
# You should explicity require classes in initializers
require Rails.root.join('path', 'to', 'monkeypatch')
require Rails.root.join('path', 'to', 'target')
::AccountController.prepend RedmineKapDesign::Patches::AccountControllerPatch
So I have the controller who scrapes the entire html of a page and stores it into mysql database. Before I store the data I want to encode it using the htmlentities gem. My issue is that with some websites it works ok e.g https://www.lookagain.co.uk/ but with others I get invalid byte sequence in UTF-8 such as https://www.google.co.uk/ and I do not know why. At first I though it might be something wrong with the database so I have changed all the fields to LONGTEXT but the problem still persists
Controller:
class PageScraperController < ApplicationController
require 'nokogiri'
require 'open-uri'
require 'diffy'
require 'htmlentities'
def scrape
#url = watched_link_params[:url].to_s
puts "LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOG#{#url}"
#page = Nokogiri::HTML(open(#url))
coder = HTMLEntities.new
#encodedHTML = coder.encode(#page)
create
end
def index
#savedHTML = ScrapedPage.all
end
def show
#savedHTML = ScrapedPage.find(id)
end
def new
#savedHTML = ScrapedPage.new
end
def create
#savedHTML = ScrapedPage.create(domain: #url, html: #encodedHTML, css: '', javascript: '')
if #savedHTML.save
puts "ADDED TO THE DATABASE"
redirect_to(root_path)
else
puts "FAILED TO ADD TO THE DATABASE"
end
end
def edit
end
def upadate
end
def delete
#watched_links = ScrapedPage.find(params[:id])
end
def destroy
#watched_links = ScrapedPage.find(params[:id])
#watched_links.destroy
redirect_to(root_path)
end
def watched_link_params
params.require(:default).permit(:url)
end
end
Lets say I have some data that remanis the same throughout all of my tests, for forever and eternity. I create this data in setup. I store the data to #instance_var. But when I call #instance_var.attribute in any test, I get the following error:
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I know my instance variable isn't null, cause after it is set, I can do a puts #instance_var.inspect on it...
Any ideas?
EDIT:
setup do
user = Factory(:user)
account = Factory(:account)
set_user(user)
set_account(account)
puts "||||||||||||||||||||||||||||||||||||||||||||||" #this proves that the instance vars are not null
puts "| object_test.rb |"
puts "| #{#user.name} "
puts "| #{#account.name} "
puts "||||||||||||||||||||||||||||||||||||||||||||||"
end
A failing test (with the error above)
test "for detection of first content with multiple contents" do
object = Factory(:object, :user_id => #user.id, :account_id => #account.id)
... #the rest of this test isn't important, as it is the above line, on #user, where the nil.id error occers
in test_helper.rb
def set_user(user)
#user = user
end
def set_account(account)
#account = account
end
I don't really think I need these two methods, as when I define the #instance variable in setup, I get teh same result
in test_helper.rb there are some constants set fore ActiveSupport::TestCase:
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
fixtures :all
disabling these did nothing. =(
Have you tried
setup do
#user = Factory(:user)
#account = Factory(:account)
end
Normally, if you set the instance variables in the setup block, they should be available to all your tests. (You might be having an issue with scopes.)
My solution was to make a shared class, shared_test.rb
require 'test_helper'
class SharedTest
def self.initialize_testing_data
self.reset_the_database
self.set_up_user_and_account
# make sure our user and account got created
puts "|||||||||||||||||||||||||||||||||||||||||||||"
puts "| The user and account "
puts "| we'll be testing with:"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "|||||||||||||||||||||||||||||||||||||||||||||"
end
def self.reset_the_database
#clear the database and reset it
call_rake("db:test:prepare")
call_rake("db:bootstrap RAILS_ENV=test")
end
def self.set_up_user_and_account
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
end
end
so then at the top of every test file that needs user and account to stay the same between all the tests, you just do
require 'shared_test.rb'
and methods are called like
SharedTest.initialize_testing_data