Helper function use in Ruby on Rails (3) - ruby-on-rails

I'm trying to create a helper function that strips all stopwords from the content field of data. I've used the basic scaffolding, like so:
rails generate scaffold MyData pageId:integer content:text
I've added a private method in the controller as such:
private
STOP_WORDS = %w{a am an as at be by do go he i if in is it me my no of on or so to un up us we}
def remove_stop_words(lowercase_string)
lowercase_string.gsub(/\b(#{STOP_WORDS.join('|')})\b/mi, '')
end
and now I'm wondering, since the controller is
def index
#tube_data = TubeDatum.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #tube_data }
end
end
and the corresponding view is
<% #tube_data.each do |tube_datum| %>
<tr>
<td><%= tube_datum.pageId %></td>
<td><%= tube_datum.content %></td>
....
how to go about making each tube_data.content stripped?
Thanks!

Add the function in: app/helpers/application_helper.rb
STOP_WORDS = %w{a am an as at be by do go he i if in is it me my no of on or so to un up us we}
def remove_stop_words(lowercase_string)
lowercase_string.gsub(/\b(#{STOP_WORDS.join('|')})\b/mi, '')
end
In the view:
<%= remove_stop_words(tube_datum.content) %>

Move that code from your Controller into application_helper.rb in the app/helpers folder and wrap it in a method called stripstopwords.
Then in your view go stripstopwords tube_datum.content

Related

Change local Ruby variable with Ajax in Ruby on Rails

I'm creating a scheduling page that shows a weekly schedule and want to make arrows at the bottom of the page that switch forward and backward a week. To do this I'm creating a variable in the controller, #wbeg, that defaults to the beginning of the week and two actions, week_after and week_before, with respective js.erb files. I can't figure out how to change the #wbeg variable using ajax and need to do so to keep everything on the same page.
Here is the controller:
class HomeController < ApplicationController
def home
end
def scheduling
#work_days = WorkDay.all
#jobs = Job.all
#wbeg = Date.today - Date.today.wday + 1 #beginning of this week
end
def week_before
respond_to do |format|
format.html { redirect_to scheduling_path notice: "You should not see this message, contact admin if you do."}
format.js
end
end
def week_after
respond_to do |format|
format.html { redirect_to scheduling_path notice: "You should not see this message, contact admin if you do."}
format.js
end
end
end
The scheduling page:
<p id="notice"><%= notice %></p>
<h1>Work Day Scheduling</h1>
<table>
<thead>
<tr>
<th>Job #</th>
<th>Job</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
</tr>
</thead>
<tbody id = "main_table">
<%= render "home/main_table" %>
</tbody>
</table>
<%= link_to '<--', week_before_path, remote: true %>
<%= link_to '-->', week_after_path, remote: true %>
and the two js.erb pages:
$('#main_table').html("<%= j render 'main_table', locals: { wbeg: #wbeg - 7 } %>");
The other:
$('#main_table').html("<%= j render 'main_table', locals: { wbeg: #wbeg + 7 } %>");
I also already tried changing the variable in the week_before and after actions in the controller but it gives the same error 'nil cannot have operation "-"' or something, which just means it's not recognizing the variable.
The way you have it coded now, the value of the #wbeg variable at the time the javascript file is generated will be hard coded into the javascript file. That value will never change. Whatever it was when the javascript was generated is it.
What you need is a javascript variable that you can update in the javascript code which makes the AJAX call.

How to Pass or Refer to original controller instance variable? SOLUTION

I am trying to understand how to refer back to a controller's instance variable in calling a method in the same controller with rendered the view.
I basically instantiate an instance variable in the controller with a list of modems. In the view, I want to call an action to iterate over the instance variable array #modems and perform an action. My problem is that I don't seem to be able to either reference the #modems instance variable in the action called nor can I pass it from the view. If I try to pass the actual #modems back to the controller, I get a "connection reset" error page. And to reference the original instance variable fails. Haphazardly I tried to define/set #modem_cache in the application controller, then setting it equal to the #modems array in the getmodems method and then refer to it in the resetmodemcounters vs passing the instance variable back, did not work. I'm sure there some fundamentals that I'm overlooking, but not seeing them. I think I have included all the pertinent information. Please indicate if I need to provide additional. Any suggestions on what I'm doing wrong or how I might do it better I would greatly appreciate.
Thank you in Advance!
Controller app/controllers/cmts/cmts_controller.rb
def getmodems
# Array of modems and stats (NOT ActiveRecord)
#modems = Modems.all()
respond_to do |format|
format.html { render :modems, :layout => "application" }
end
end
# Called from modem.html.erb view
def resetmodemcounters
if (session[:admin])
params[:targets].each do |cm|
# do action
end
end
end
View app/views/cmts/modems.html.rb
....
<%= link_to image_tag('recycle-red-button.png', :height => '30', :width => '35'), resetmodemcounters_path(:targets => #modems), :confirm => 'Are you sure?', :method => :get %>
<% #modems.each do |cm| %>
<!-- Table of modem stats -->
<% end %>
....
Also tried this:
Controller app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
....
#sweep_cache = nil
....
end
Controller app/controllers/cmts/cmts_controller.rb
def getmodems
# Array of modems and stats (NOT ActiveRecord)
#modems = Modems.all()
#modems_cache = #modems
respond_to do |format|
format.html { render :modems, :layout => "application" }
end
end
def resetmodemcounters
log(params)
# {"_method"=>"get", "authenticity_token"=>"cZqSqAVKBpTiN5ZQSF1xUXMSiQmtDSQp8/6CRfis3Xs=", "target"=>"all", "controller"=>"cmts", "action"=>"resetmodemcounters"}
log(#modems_cache)
# nil
if (session[:admin])
if (params[:target] == 'all')
#modems_cache.each do |cm|
# do action
end
end
end
end
View app/views/cmts/modems.html.rb
....
<%= link_to image_tag('recycle-red-button.png', :height => '30', :width => '35'), resetmodemcounters_path(:targets => 'all'), :confirm => 'Are you sure?', :method => :get %>
<% #modems.each do |cm| %>
<!-- Table of modem stats -->
<% end %>
....
HERE'S WHAT I CAME UP WITH and works - Would there be a better way to handle this?
class ModemsCache
DATA = []
def update( modems )
DATA.replace modems
end
end
def getmodems
# Array of modems and stats (NOT ActiveRecord)
#modems = Modems.all()
# Update Cache
ModemsCache.new.update(#modems)
respond_to do |format|
format.html { render :modems, :layout => "application" }
end
end
def resetmodemcounters
if (session[:admin])
# Get Cached Data
modems = ModemsCache::DATA
modems.each do |cm|
# do action
end
end
end

How could I render to a string a JSON representation of a JBuilder view?

I'm using JBuilder as to return some JSON. I have a index.json.jbuilder that generates the data, and I need to render it to a string. However, I'm not sure how to do this, since: #my_object.to_json and #my_object.as_json don't seem to go through JBuilder.
How could I render the JBuilder view as a string?
I am rendering a collection of users as a json string in the controller like so:
#controllers/users_controller.rb
def index
#users = User.all
#users_json = render_to_string( template: 'users.json.jbuilder', locals: { users: #users})
end
#views/users/users.json.jbuilder
json.array!(users) do |json, user|
json.(user, :id, :name)
end
If the view users.json.jbuilder is at the default path relative to the controller and it cannot find the template, it may be due to a format discrepancy, as it may be trying to look for the html format file. There are two ways to fix this:
Have the client GET /users/index.json
or
Specify the formats option when calling render_to_string (also applies to render):
#controllers/users_controller.rb
def index
#users = User.all
#users_json = render_to_string( formats: 'json' ) # Yes formats is plural
end
This has been verified in Rails 4.1.
If you're doing this in the controller, a much simpler option is to try to the move the code into the view being rendered by the controller.
I described this here:
https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/docs/jbuilder.md
basically you can call render in the view, and you're done. Like this:
<%= react_component('App', render(template: "/comments/index.json.jbuilder"),
generator_function: true, prerender: true) %>
Here's the notes on what happens if you want to pass the data from the controller to the view:
class PagesController < ApplicationController
def index
#comments = Comment.all
# NOTE: The below notes apply if you want to set the value of the props in the controller, as
# compared to he view. However, it's more convenient to use Jbuilder from the view. See
# app/views/pages/index.html.erb:20
#
# <%= react_component('App', render(template: "/comments/index.json.jbuilder"),
# generator_function: true, prerender: true) %>
#
#
# NOTE: this could be an alternate syntax if you wanted to pass comments as a variable to a partial
# #comments_json_sting = render_to_string(partial: "/comments/comments.json.jbuilder",
# locals: { comments: Comment.all }, format: :json)
# NOTE: #comments is used by the render_to_string call
# #comments_json_string = render_to_string("/comments/index.json.jbuilder")
# NOTE: It's CRITICAL to call respond_to after calling render_to_string, or else Rails will
# not render the HTML version of the index page properly. (not a problem if you do this in the view)
# respond_to do |format|
# format.html
# end
end
end
You can also do it like this, which leaves your controller a bit cleaner.
# controller
def new
#data = Data.all
end
# view
<% content_for :head do %>
<script type="text/javascript">
var mydata = <%= raw render :partial => 'path/to/partial', :locals => {data: #data} %>;
</script>
<% end %>
# path/to/_partial.html.jbuilder
json.array!(#data) do |d|
json.extract! field1, :field2, :field3, :field4
json.url data_url(d, format: :json)
end
# layouts/application.html
<!DOCTYPE html>
<html>
<head>
<%= yield :head %>
</head>
<body>
...
</body>
</html>
Looking at the source code, it looks like you can do:
json_string = Jbuilder.encode do |json|
json.partial! 'path/to/index', #my_object
end
From console:
view = ApplicationController.view_context_class.new("#{Rails.root}/app/views")
JbuilderTemplate.encode(view){|json| json.partial!('path/to/index', #my_object) }
via https://github.com/rails/jbuilder/issues/84#issuecomment-38109709
in controller you can do like this
def index
json = JbuilderTemplate.new(view_context) do |json|
json.partial! 'index'
end.attributes!
do_something(json)
render json: json
end
note that you need "_index.json.jbuilder" because it calls partial renderer
Following justingordon's tip.
If you are using a React component, you can do the following.
In your controller:
#users = User.all
In your view:
<%= react_component("YourComponentName",
props: render('your_template.json.jbuilder')) %>
This was tested on Rails 5.1.

How to generate an XML list of certain fields from my database in Ruby on Rails?

I need to supply data from my Ruby on Rails database to a jquery grid plugin. At the moment, I'm doing it like this:
#notes = current_user.notes.all
respond_to do |format|
format.xml { render :xml => #notes }
end
But I only want to display certain fields in the grid, so I need to change the code to only send specified fields from the model. What is the easiest way to do this? Thanks for reading.
you can use XML Builder.
Taken from: http://danengle.us/2009/05/generating-custom-xml-for-your-rails-app/
respond_to do |format|
format.html # index.html.erb
format.xml # index.xml.builder
end
# index.xml builder contents
xml.instruct!
xml.posts do
#posts.each do |post|
xml.post do
xml.title post.title
xml.body post.body
xml.published_at post.published_at
xml.comments do
post.comments.each do |comment|
xml.comment do
xml.body comment.body
end
end
end
end
end
end
Another possibility is to override to_xml in your notes model as posted in the comments of the linked site above.

Rails validation over redirect

I'm trying out the beast forum written in rails and will use this as an example of a problem I keep facing.
The forum has a topics/show action and view with a form at the bottom to create a new post within the topic.
Submitting the form goes to posts/create and if the validation passes redirects back to topics/show and works fine, however if the validation fails (leaving out the body field) you're redirected to the same topics/show and back to the form, with no validation errors... normally if validation fails you're left on whatever/create with render :action => new.
Are the validations being lost in the redirect, and what's the best method of getting it working?
See code below:
PostsController.rb
def create
#post = current_user.reply #topic, params[:post][:body]
respond_to do |format|
if #post.new_record?
format.html { redirect_to forum_topic_path(#forum, #topic) }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
else
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(forum_topic_post_path(#forum, #topic, #post, :anchor => dom_id(#post))) }
format.xml { render :xml => #post, :status => :created, :location => forum_topic_post_url(#forum, #topic, #post) }
end
end
end
TopicsController.rb
def show
respond_to do |format|
format.html do
if logged_in?
current_user.seen!
(session[:topics] ||= {})[#topic.id] = Time.now.utc
end
#topic.hit! unless logged_in? && #topic.user_id == current_user.id
#posts = #topic.posts.paginate :page => current_page
#post = Post.new
end
format.xml { render :xml => #topic }
end
end
topics/show view
<% form_for :post, :url => forum_topic_posts_path(#forum, #topic, :page => #topic.last_page) do |f| %>
<%= f.error_messages %>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td rowspan="2" width="70%">
<%= f.text_area :body, :rows => 8 %>
</td>
<td valign="top">
<%= render :partial => "posts/formatting" %>
</td>
</tr>
<tr>
<td valign="bottom" style="padding-bottom:15px;">
<%= submit_tag I18n.t('txt.views_topics.save_reply', :default => 'Save reply') %>
</td>
</tr>
</table>
<% end %>
Many thanks.
I think you have two problems here.
Keeping validation errors through a redirect
Repopulating the form fields so the user doesn't have to enter all the information again.
Both of these things are connected.
Validation errors are usually displayed through the error_msg_for method which acts on an object. Usually provided by the controller as the an instance variable of object that could not be saved. That same instance variable is used to repopulate the form.
During a redirect the controller will usually instantiate an instance variable using the params hash. So any information used to determine why a save failed is lost. Normal resources will render on save failure and redirect on success, this causes two things happen.
The instance of the object is passed to error_msg_for creating that nice uniform error box.
The instance of the object is used to populate the fields of the form, allowing your user to edit only what is necessary.
I don't know Beast so well, so I'm not sure if the form to create threads is an active record model. But the following will give you an idea how to work around your problem. It would involve modifying your local copy of the Beast plugin, so if you're using a tool to keep it updated, your changes might get lost.
I've been using these following methods to get your validation problems. Assuming the form you're talking about is based on a nmodel they should provide you with everything you need to repopulate a form.
Essentially you store a shallow copy of the object with the errors in the flash hash, using clone_with_errors. You have to use a shallow copy or else you'll run into problems when displaying errors for records with multiple associations.
Then I use my_error_msg_for which takes the same options as the standard error_msg_for to build the error messages html. I only wrote it because for some reason the standard error_msg_for method didn't work with objects stored in the hash. It's almost identical to the official source version of error_msg_for which was troubling.
/app/controllers/examples_controller.rb
class ExamplesController < ApplicationController
def update
...
if #example.save
regular action
else
flash[:errors] = clone_with_errors(#example)
respond_to do |format|
format.html redirect_to(#example)
end
end
end
/app/views/examples/show.html.erb
<div id="error">
<% if flash[:errors] && !flash[:errors].empty? then -%>
<p ><%= my_error_msg_for flash[:errors] %></p>
<% end -%>
</div>
...
Here's the code you need to make it all work.
application_controller.rb
def clone_with_errors(object)
clone = object.clone
object.errors.each{|field,msg| clone.errors.add_to_base(msg)}
return clone
end
application_helper.rb
def _error_msg(*params)
options = params.extract_options!.symbolize_keys
if object = options.delete(:object)
objects = [object].flatten
else
objects = params.collect {|object_name| instance_variable_get("##{object_name}") }.compact
end
count = objects.inject(0) {|sum, this| sum + this.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
if options.include?(key)
value = options[key]
html[key] = value unless value.blank?
else
html[key] = 'errorExplanation'
end
end
options[:object_name] ||= params.first
options[:header_message] = "#{pluralize(count, 'error')} prohibited this #{options[:object_name].to_s.gsub('_', ' ')} from being saved" unless options.include?(:header_message) && !options[:header_messag].nil?
options[:message] ||= 'There were problems with the following fields:' unless options.include?(:message) && !options[:message].nil?
error_messages = objects.sum {|this| this.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
contents = ''
contents << content_tag(options[:header_tag] || :h2, options[:header_message]) unless options[:header_message].blank?
contents << content_tag(:p, options[:message]) unless options[:message].blank?
contents << content_tag(:ul, error_messages)
content_tag(:div, contents, html)
else
''
end
end
def my_error_msg_for(params)
_error_msg_test :object_name => params[:object].class.name.gsub(/([a-z])([A-Z])/,'\1 \2').gsub(/_/, " "),
:object => params[:object], :header_message => params[:header_message], :message => params[:message]
end
I'm afraid I don't know anything about Beast, but speaking generically, everything is lost when you redirect. It's a new page request, and everything is reset unless it's been stored somewhere (the database or the session, normally.)
The normal flow you tend to see with forms is to redirect if the object is saved, but to render if the save fails. The view file can then pick up whatever variables have been set in the controller - which would normally include the object that didn't save and its validation messages.
Sorry that doesn't solve your problem, but hopefully it may give you some clues.
My answer to a very similar question posted more recently here on StackOverflow covers a number of pros and cons to the redirect_to vs. render debate. I'd love to hear if anyone has any other pros/cons to add to the discussion.

Resources