I'm trying to get the checkSwear method to run on each textfield before it's submitted..
I have basically this: (stripped down)
<%= form_for(#profile) do |f| %>
<div class="field">
<%= f.label 'I love to ' %>
<%= f.text_field :loveTo %>
</div>
<div class="field">
<%= f.label 'I hate to ' %>
<%= f.text_field :hateTo %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In my controller I have:
def checkSwear
antiSwear.checkSwear(What goes here?)
end
In routes:
match '/check' => 'profiles#checkSwear'
Any help much appreciated!
(checkSwear is a separate gem; i.e. a separate problem! The what does here means what kind of variable is received from the form, to be put through the checkswear gem)
UPDATE:
Sorry for the camelcasing, I'm a Java developer studying Rails etc., old habits die hard. This is for a project. I'm supposed to be writing a small gem to do some ruby logic and apply it to something. The contents of the gem are:
module antiSwear
#swearwords = ["f**k", "f***ing", "shit", "shitting", "lecturer"]
#replacements = ["fornicate", "copulating", "poop", "pooping", "Jonathan"]
def self.checkText(text)
#swearwords.each do |swearword|
if text.include?(swearword)
index = #swearwords.index(swearword)
replacement = #replacements[index]
text.gsub(swearword, replacement)
end
end
return text
end
end
:/
This should really be done in model validations.
class Profile < ActiveRecord::Base
validate :deny_swearing
private
def deny_swearing
if AntiSwear.check_swear(love_to) || AntiSwear.check_swear(hate_to)
errors.add_to_base('Swearing is not allowed.')
end
end
end
That said, if you insist on this being in controller, you can check params[:profile][:love_to] and params[:profile][:hate_to] to see what's been submitted.
P.S. In this example I used proper ruby naming conventions, since we don't use "camelCasing".
Are you doing this as part of validation? You can do it one of a few ways. You can run the check before save, via a custom validation method or override the setter directly. I show you the custom validation approach here:
class Profile < ActiveRecord::Base
validate :clean_loveTo
protected
def clean_loveTo
errors.add(:loveTo, "can't contain swears") if antiSwear.checkSwear(loveTo)
end
end
I'm assuming checkSwear returns a boolean here.
I'd use an intersection on arrays, one of which is the source text split into words, then gsub the replacements in. You have to be sure to have a 1:1 relationship between the words and their replacements, in which case I'd suggest using a hash for your dictionary (coincidentally what hashes are sometimes called in other languages).
module antiSwear
# var names changed for formatting
#swears = ["f**k", "f***ing", "shit", "shitting", "lecturer"]
#cleans = ["fornicate", "copulating", "poop", "pooping", "Jonathan"]
def self.checkText(text)
# array intersection. "which elements do they have in common?"
bad = #swears & text.split # text.split = Array
# replace swear[n] with clean[n]
bad.each { |badword| text.gsub(/#{badword}/,#cleans[#swears.index(badword)] }
end
end
You might need to futz with text.split arguments if the replacement gets hung up on \n & \r stuff.
Related
I'm trying to refactor some code out of my rails controller to my model, and I've discovered a gap in my understanding of how rails works. I'm attempted to make the 2 methods available to the Raffle class, and I'm struggling to understand how to do so. When I hit this code, the error "undefined local variable or method `params' for #Raffle:0x00007f88c9af8940" is returned.
How can I work around params not being available to the model? Apologies if this is a beginner question- I am definitely a beginner.
#app/models/raffle.rb
class Raffle < ApplicationRecord
has_many :tickets
has_many :users, through: :tickets
def bid_tickets(tier)
params[:raffle][:"tier"].to_i #error returned here
end
def bid_total
(bid_tickets(gold) * 3) + (bid_tickets(silver) * 2) + bid_tickets(bronze)
end
#app/views/edit.html.erb
<%= form_for(#raffle) do |f| %>
<%=f.label :ticket, "Gold" %>: <br>
<%= image_tag "gold.jpg", class: "small_ticket" %><br>
<%=f.number_field :gold%><br>
<%=f.label :ticket, "Silver" %>:<br>
<%= image_tag "silver.jpg", class: "small_ticket" %><br>
<%=f.number_field :silver %><br>
<%=f.label :ticket, "Bronze" %>:<br>
<%= image_tag "bronze.jpg", class: "small_ticket" %><br>
<%=f.number_field :bronze %> <br><br>
<%= f.submit "Use Tickets" %>
<% end %><br>
#app/controllers/raffles_controller.rb
def update
if #raffle.enough_slots? + #raffle.current_bids > #raffle.number_of_ticket_slots
if enough_tickets?
redirect_to edit_raffle_path, notice: "You do not have enough tickets."
else
redirect_to edit_raffle_path, notice: "There aren't enough spots left in this raffle to handle your entry! Please use less tickets."
end
else #raffle.update_tickets(current_user)
if #raffle.slots_filled?
#raffle.select_winner
end
redirect_to edit_raffle_path(#raffle)
end
end
returned parameters:
{"_method"=>"patch",
"authenticity_token"=>"xyz",
"raffle"=>{"gold"=>"1", "silver"=>"", "bronze"=>""},
"commit"=>"Use Tickets",
"id"=>"1"}
EDIT:
#app/controllers/raffles_controller.rb (StrongParameters)
class RafflesController < ApplicationController
private
def raffle_params
params.require(:raffle).permit(:product_name, :product_description,
:product_image, :number_of_ticket_slots, :filter)
end
end
params is made available to you in the Controller (by Rails) because it's a "web request thing". It's a good practice not to pass it deeper into your system, as it contains external "untrusted" input. That's why it's not automatically available in Models.
For example:
# controller
def update
safe_value = validate_value(params[:unsafe_value]) # extract and validate
# no "web request things" passed in
result = MyFeature.do_stuff(#current_user, safe_value) # pass it to other parts of system
# then convert the result back into a "web request thing", e.g:
# return render json: as_json(result) # return result as json
# return redirect_to endpoint_for(result) # redirect based on result
# etc
end
It's good practice to pre-process the params (extracting the values out, validating them, etc) before passing that data to other parts of your system as arguments, like your models.
This will keep the rest of your system agnostic of "web request things", keeping them purpose-focused and well-organized.
For one, StrongParameters is a built-in rails feature that helps with this.
Another suggestion would be put a binding.pry or byebug (after making sure it's installed and updated via bundle) in code where params are utilized and then run your code. After it's triggered type params and you will see the breakdown.
I have this code which I don't really understand:
app\controllers\look_controller.rb
class LookController < ApplicationController
def at
#data_hash = params[:cruncher]
#cruncher = Cruncher.new(#data_hash[:crunch])
#data = #cruncher.crunch
end
def input
end
end
app\models\cruncher.rb
class Cruncher
attr_reader :crunch
attr_writer :crunch
def initialize(data)
#crunch = data
end
end
app\views\look\input.rhtml:
<html>
<head>
<title>Using Text Fields</title>
</head>
<body>
<h1>Working With Text Fields</h1>
This Ruby on Rails application lets you read data from text fields.
<br>
<%= start_form_tag ({:action => “at”}, {:method => “post”}) %>
Please enter your name.
<br>
<%= text_field (“cruncher”, “crunch”, {“size” => 30}) %>
<br>
<br>
<input type=”submit”/>
<%= end_form_tag %>
</body>
</html>
I do not understand what is the relationship between <%= text_field (“cruncher”, “crunch”, {“size” => 30}) %> and the model. What do text_fields attributes cruncher and crunch have to do with the model?
As I understand the params is a special hash that stores the data from the user, and by using #data_hash = params[:hash] inside the controller we store that data.
But what about this #cruncher = Cruncher.new(#data_hash[:crunch]), why do we now use #data_hash[:crunch]?
Why not just #data_hash?
Thanks.
if you look at he html produced by the input view, you'll see something like this for the text field:
<input type="text" name="cruncher_crunch" value="cruncher[crunch]" size="30" />
this means that the params hash, created when the form is submitted, and sent to the LookController#at method will be formatted like this:
{cruncher: {crunch: 'somevalue'}}
which is exactly the format that the Cruncher.new(#data_hash[:cruncher]) expects.
Its not that strange that you don't understand it.
This code is probably ludicrously old (.rhtml and start_form_tag put it at Rails 1 or 2) and really bad, it does not even run as there are two syntax errors as well as the quotes that look like an artifact from pasting the code into MS Word
# don't put a space before parens when calling methods in Ruby!
text_field (“cruncher”, “crunch”, {“size” => 30})
It would also give NoMethodError on #data = #cruncher.crunch.
In Rails 5 the same example can be written as:
class Cruncher
include ActiveModel::Model
attr_accessor :crunch
def crunch
# have no idea what this was supposed to do
end
end
class LookController < ApplicationController
def at
#cruncher = Cruncher.new(cruncher_params)
#data = #cruncher.crunch
end
private
def cruncher_params
params.fetch(:cruncher).permit(:crunch)
end
end
# I really have no idea what the actual routes are supposed to be
<%= form_for(#cruncher, url: '/look/at') do %>
<%= f.text_field(:crunch size: 30) %>
<% end %>
Its still just a strange and non RESTful example though. Sometimes garbage code is best left buried.
I do not understand what is the relationship between <%= text_field
(“cruncher”, “crunch”, {“size” => 30}) %> and the model. What do
text_fields attributes cruncher and crunch have to do with the model?
Nothing. There is no data binding. Its just a plain-jane text input.
But what about this #cruncher = Cruncher.new(#data_hash[:crunch]), why
do we now use #data_hash[:crunch]?
Because the author didn't know what they where doing. And probably had not figured out that you can pass hashes to your methods.
I am looking to build a simple set of calculators in a Rails 4 application and I am at a bit of a loss as to how I should go about setting up my models and controllers.
In this example I have a series of calculators that will have very similar but in some cases slightly different inputs / variables and slightly different calculation methods.
My first attempt was to simply create a Calculator controller without a model but quickly became lost as to where I would handle things like form params and calculation logic.
Creating a model also made little sense to me given that the calculators require some slightly different inputs and calculation methods.
Finally, creating multiple models also seemed like an extremely messy approach here in this scenario.
So with all of that in mind I was wondering if someone could show me the Rails way as to how I should approach this problem. If it helps to have further information I am looking to build out the same approach found in the following set of spreadsheets: http://www.widerfunnel.com/proof/roi-calculators
Any help would be seriously appreciated!
You should keep in mind that Rails is not only about MVC. You can create your custom classes, and use them in a model, or a controller.
In this case, you could create a Calculator class inside app/lib and use it inside your controller. For example:
# app/lib/calculator.rb
class Calculator
def self.sum(a, b)
a.to_i + b.to_i
end
def self.subtr(a, b)
a.to_i - b.to_i
end
end
.
# app/controllers/calculator_controller
class CalculatorController < ApplicationController
def index
end
def new
#result = Calculator.send(params[:operation], *[params[:a], params[:b]])
render :index
end
end
.
# app/views/calculator/index.html.erb
<%= form_for :calculator, url: { action: :new }, method: :get do |f| %>
<%= number_field_tag :a, params[:a] %>
<%= select_tag :operation, options_for_select([['+', :sum], ['-', :subtr]], params[:operation]) %>
<%= number_field_tag :b, params[:b] %>
<%= f.submit 'Calculate!' %>
<% end %>
<% unless #result.nil? %>
<p> = <%= #result %> </p>
<% end %>
This is just a very simple example on what is possible to do by creating your own classes and use them on Rails.
;)
I have the following form field in my index.html.erb view:
<%= form_tag("/calls/call", :method => "post") do %>
<br />
<%= text_field_tag(:call_to, "Number to call") %>
<br />
<%= text_field_tag(:call_from, "Number to call from") %>
<br />
<%= submit_tag("Dial") %></a>
<br />
<% end %>
I want to constrain the field to allow only 10 digit US phone numbers without using a model validation (as there is no model).
There's probably a lot of ways to do this, but what do you folks find to be the simplest to implement?
I've tried using the 'active_attr' gem, but didn't have much luck. With 'active_attr' I created a model called Call. Here's what the model looks like:
class Call
include ActiveAttr::Model
attribute :call_to
attribute :call_from
# attr_accessible :call_to, :call_from
validates_presence_of :call_to
validates_presence_of :call_from
end
My controller looks like this:
class CallsController < ApplicationController
def call
call_to = params["call_to"]
call_from = params["call_from"]
call_to.to_i
call_from.to_i
puts call_to
puts call_from
end
end
Am I supposed to instantiate an instance of a Call class in the controller or something along those lines?
#call = Call.new
Thanks in advance!
One client side option would be using the HTML5 type=tel input. Here's a link to some documentation. You will still want some server side validation though.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
As a companion to Hidden features of Ruby.
Try to keep it to Rails since the other is a better place for Ruby-specific examples. One per post please.
To avoid duplicate form submissions, Rails has a nice option for submit tags:
submit_tag "Submit", :disable_with => "Saving..."
This adds behavior to the submit button to disable it once clicked, and to display "Saving..." instead of "Submit".
Rails 4+
DEPRECATION WARNING: :disable_with option is deprecated and
will be removed from Rails 4.1. Use 'data: { disable_with: 'Text' }' instead.
Thus the above becomes:
submit_tag 'Submit', data: { disable_with: 'Text' }
integer.ordinalize is one little method that I just stumbled upon not to long ago.
1.ordinalize = "1st"
3.ordinalize = "3rd"
I'm currently in love with div_for and content_tag_for
<% div_for(#comment) do %>
<!-- code to display your comment -->
<% end %>
The above code renders this:
<div id="comment_123" class="comment">
<!-- code to display your comment -->
</div>
Want the CSS class to be comment other_class? No problem:
<% div_for(#comment, :class => 'other_class') do %>
<!-- code to display your comment -->
<% end %>
Want a span and not a div? No problem, content_tag_for to the rescue!
<% content_tag_for(:span, #comment) do %>
<% end %>
# Becomes...
<span id="comment_123" class="comment">
<!-- code to display your comment -->
</span>
content_tag_for is also great if you want to prefix you id. I use it for loading gifs.
<% content_tag_for(:span, #comment, 'loading') do %>
<%= image_tag 'loading.gif' -%>
<% end %>
# Becomes...
<span id="loading_comment_123" class="comment">
<img src="loading.gif" />
</span>
To see a list of gems that are installed, you can run:
gem server
Then point your browser at:
http://localhost:8808
You get a nicely formatted list of your gems with links to rdoc, the web and any dependencies. Much nicer than:
gem list
You can take advantage of the fact that Ruby class definitions are active and that Rails caches classes in the production environment, to ensure that constant data is only fetched from the database when your application starts up.
For example, for a model that represents countries you'd define a constant that performs a Country.all query when the class is loaded:
class Country < ActiveRecord::Base
COUNTRIES = self.all
.
.
.
end
You can use this constant within a view template (perhaps within a select helper) by referring to Country::COUNTRIES. For example:
<%= select_tag(:country, options_for_select(Country::COUNTRIES)) %>
in your environment.rb, you can define new date/time formats e.g.
[Time::DATE_FORMATS, Date::DATE_FORMATS].each do |obj|
obj[:dots] = "%m.%d.%y"
end
so then in your views you can use:
Created: <%= #my_object.created_at.to_s(:dots) %>
which will print like:
Created: 06.21.09
If you have a model with some class methods and some named scopes:
class Animal < ActiveRecord::Base
named_scope 'nocturnal', :conditions => {'nocturnal' => true}
named_scope 'carnivorous', :conditions => {'vegetarian' => true}
def self.feed_all_with(food)
self.all.each do |animal|
animal.feed_with(food)
end
end
end
Then you can call the class methods through the named scope:
if night_time?
Animal.nocturnal.carnivorous.feed_all_with(bacon)
end
Rails 2.3.x now allows you to do:
render #items
much simpler..
I'll start with one of my favorites. When calling a partial with a collection, instead of looping through your collection and calling it for each item, you can use this:
render :partial => 'items', :collection => #items
This will call the partial once per item, and pass a local variable item each time. You don't have to worry about nil checking #items either.
You can change the behaviour of a model for your test suite. Say you have some after_save method defined and you do not want it to happen in your unit tests. This is how it works:
# models/person.rb
class Person < ActiveRecord::Base
def after_save
# do something useful
end
end
# test/unit/person_test.rb
require 'test_helper'
class PersonTest < ActiveSupport::TestCase
class ::Person
def after_save
# do nothing
end
end
test "something interesting" do
# ...
end
end
Funny feature is that array has special method for accessing its 42 element
a = []
a.forty_two
http://railsapi.com/doc/rails-v2.3.8/classes/ActiveSupport/CoreExtensions/Array/Access.html#M003045
If you add routing for a resource:
ActionController::Routing::Routes.draw do |map|
map.resources :maps
end
And register additional mime-types:
Mime::Type.register 'application/vnd.google-earth.kml+xml', :kml
You don't need a respond_to block in your controller to serve these additional types. Instead, just create views for the specific types, for example 'show.kml.builder' or 'index.kml.erb'. Rails will render these type-specific templates when requests for '/maps.kml' or '/maps/1.kml' are received, setting the response type appropriately.
ActionView::Base.default_form_builder = MyFormBuilderClass
Very useful when you're creating your own form builders. A much better alternative to manually passing :builder, either in your views or in your own custom_form_for helper.
The returning block is a great way to return values:
def returns_a_hash(id)
returning Hash.new do |result|
result["id"] = id
end
end
Will return a hash. You can substitute any other types as well.
Get everything printed with rake routes programmatically:
Rails.application.routes