Sinatra: wrong number of arguments (4 for 0..2) - mysql

I'm currently developing an app with Sinatra, ActiveRecord and MySQL. I'm working on the sign up form, which looks like this:
app.rb:
post '/signup' do
password_salt = BCrypt::Engine.generate_salt
password_hash = BCrypt::Engine.hash_secret(params[:password], password_salt)
#usuarios = User.new(params[:nombre], params[:cedula], password_hash, "admin")
if #usuarios.save
redirect './signup', :notice => "Usuario creado exitosamente."
else
redirect './signup', :error => "Ha ocurrido un error, intente nuevamente."
end
end
And the view looks like this, signup.erb:
<form id="registro" action="/signup" method="POST">
<fieldset>
<legend>Ingrese sus datos</legend>
<label>Nombre
<input type="text" name="nombre">
</label>
<label>Cédula
<input type="text" maxlength="10" name="cedula">
</label>
<label>Contraseña
<input type="password" name="password">
</label>
<!-- TO-DO:
Dropdown list con los diferentes tipos de usuarios, i.e.: admin, secretario, etc.
-->
<input type="submit" id="registerButton" class="button small">Finalizar registro</a>
</fieldset>
</form>
Whenever I try to create a new user, I get the following error:
ArgumentError - wrong number of arguments (4 for 0..2)
Considering that the table I'm trying to insert the values has 4 columns, I don't understand why I'm getting this error.
Any insight to help me solve this inconvenience would be greatly appreciated!
Thanks in advance.

ActiveRecord::new method allows only 2 parameters as arguments, it should be a hash. fix:
User.new(params[:nombre], params[:cedula], password_hash, "admin")
to:
User.new(nombre: params[:nombre], cedula: params[:cedula], password: password_hash, role: "admin")
You should always check the documentation, in 99% cases you can find a problem:
New objects can be instantiated as either empty (pass no construction
parameter) or pre-set with attributes but not yet saved (pass a hash
with key names matching the associated table column names). In both
instances, valid attribute keys are determined by the column names of
the associated table – hence you can’t have attributes that aren’t
part of the table columns.
new(attributes = nil, options = {})
Examples:
# Instantiates a single new object
User.new(:first_name => 'Jamie')
# Instantiates a single new object using the :admin mass-assignment security role
User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
# Instantiates a single new object bypassing mass-assignment security
User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)

Related

Capybara::ElementNotFound: Unable to find checkbox that is not disabled

I've checked all the SO questions I can find related to this issue.
The rest of the form works and the test passes if I remove the validation that a PlanTemplate must have a Category.
I have created a Category with a name of 'Planets' in the Test script elsewhere.
I've just got Capybara screenshot working and have now updated it to include the text - it doesn't seem to be appearing in Capybara, but is appearing in local - I can't tell from my code why it would be hidden?
Full error:
1) PlanTemplate can be created by admin admin can create new plan
Failure/Error: expect(page).to have_content('Planets')
expected to find text "Planets" in "window.addEventListener(\"load\", function(){ window.cookieconsent.initialise({ \"palette\": { \"popup\": { \"background\": \"#252e39\" }, \"button\": { \"background\": \"#428bca\" } }, \"theme\": \"classic\", \"content\": { \"href\": \"Toggle navigation Communities Communities People Mates Weekly Activity view Recent Activity list view All Mate Goals Manage Mates Plans Goals All Goals Manage My Goals New Goal Admin Admin Account Profile Email Settings Manage Mates My Interests Log out New Plan Template Plan name: Plan description: What additional information is required? (optional) What is the delivery medium & requirements of plan? (optional) What should you do once the plan is complete? (optional) How many hours per week does this goal take to complete? How many days in total does this take to complete? /7 for weeks. This will be used to calculate start times What level of user is this plan for? beginner novice intermediate experienced expert What category is this goal in? Feed My Goals My Mates All goals Home Sign Up Log in Browse Users Feedback © Browse Goals Privacy TOS Cookies window.setTimeout(function() { $(\".alert\").fadeTo(500, 0).slideUp(500, function() { $(this).remove(); }); }, 6000);"
Things I have tried to date are commented out below (have also tried others and removed them):
let(:category) { create :category, name: 'Planets', id: 99 }
scenario 'admin can create new plan' do
login_as(create(:admin))
visit("/plans/new")
find('#plan-name').set('A test plan name')
find('#plan-desc').set('A test plan description, I think these need to be longish')
find('#plan-weekly-hours').set(1)
# page.check('Planets')
# find("label[for='Planets']").click
# find_field(['plan_template_category_ids_1']).check
# find('.checkbox').check
# check('Planets')
# find(:label, 'Planets').click
# check('.checkbox', allow_label_click: true)
# find(:label, 'plan_template_category_ids_1').click
# find('#plan_template_category_ids_1', visible: false).trigger('click')
find('#plan-days-to-complete').set(35)
find('#submit-new-plan').click
expect(page).to have_content('Plan template was successfully created.')
expect(page).to have_content('A test plan description, I think these need to be longish')
end
I have also added to rails_helper:
Capybara.automatic_label_click = true
Capybara.ignore_hidden_elements = false
The form partial code in question:
<%= form.label "What category is this goal in?" %><br>
<%= form.collection_check_boxes :category_ids, Category.all, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
The html snapshot from Capybara screenshot - have to paste it as an image as SO doesn't like it in code.
How it displays in the DEV browser *not test:
<div class="col-md-12 goalform">
<label>What category is this goal in?</label><br>
<input type="hidden" name="plan_template[category_ids][]" value=""><label class="checkbox-inline input_checkbox" for="plan_template_category_ids_2">
<input class="checkbox" type="checkbox" value="2" name="plan_template[category_ids][]" id="plan_template_category_ids_2">Dream Chasing</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_1"><input class="checkbox" type="checkbox" value="1" name="plan_template[category_ids][]" id="plan_template_category_ids_1">Weightloss</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_4"><input class="checkbox" type="checkbox" value="4" name="plan_template[category_ids][]" id="plan_template_category_ids_4">Productivity</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_11"><input class="checkbox" type="checkbox" value="11" name="plan_template[category_ids][]" id="plan_template_category_ids_11">Popular</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_3"><input class="checkbox" type="checkbox" value="3" name="plan_template[category_ids][]" id="plan_template_category_ids_3">Fitness</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_12"><input class="checkbox" type="checkbox" value="12" name="plan_template[category_ids][]" id="plan_template_category_ids_12">Health</label>
</div>
This could've been diagnosed by using a debugging tool (e.g. using the pry gem and adding 'binding.pry' inside of the test (and before the failing expect line) which would have shown me that the Categories were not being generated correctly due to the lazy loading of the 'let'
First, nowhere in the HTML you've shown is 'Planets' mentioned, so it's not surprising that Capybara can't find the checkbox. If you're trying to select the checkbox with a value of 1 then its label contents is 'Weightloss' according to the HTML.
Second, assuming the fields you're using set on are input elements, stop doing find(...).set(...) and just use fill_in.
Those changes would make your test code
scenario 'admin can create new plan' do
login_as(create(:admin))
visit("/plans/new")
fill_in('plan-name', with: 'A test plan name')
fill_in('plan-desc', with: 'A test plan description, I think these need to be longish')
find('#plan-weekly-hours').set(1) # I don't know what type of element this is? If a select use `choose`
# check accepts the id, unique name, or associated label text as a locator
check('Weightloss')
find('#plan-days-to-complete').set(35) # same as above
click('submit-new-plan')
expect(page).to have_content('Plan template was successfully created.')
expect(page).to have_content('A test plan description, I think these need to be longish')
end
If that doesn't work for you, please copy the full error you get and add it to your question.
Additionally, never set Capybara.ignore_hidden_elements = false when writing tests. Doing so makes your tests invalid, from a users perspective, and basically pointless. The only time that setting may make sense is when web scraping.
The error I was having was because I was using let(:category) { create :category, name: 'Planets', id: 99 } & let(:category) { create :category } as a way to create Categories, but they weren't usable in the test.
I created them without using a factory inside the test and it's working fine.
Category.create(name:" Category 1")
I also thought my PlanTemplate factory would create a category that could be used as I have inside of the factory categories { [create(:category)] }, but that isn't usable the way I expected.

SQL Alchemy + WTForms FormField with default but don't insert empty data

So I have this wtforms:
ProfessionalAddress = FormField(AdressForm, default=lambda: ProfessionalAddress())
which links a sub form to my main form. To make sure the subform shows up, I added the default behavior that creates the empty ProfessionalAddress if the relationship doesn't exists.
But the problem is that in some cases I don't need this subform and it is not shown, hence no fields are submitted.
But when I call form = MainForm(formdata=request.form), as no fields are submitted, form.ProfessionalAddress is set to ProfessionalAddress() which generates an error as my DB doesn't allow empty entries there.
I could handle it at controller level (something like if form.ProfessionalAddress is empty: form.ProfessionalAddress = None) but it feels hacky, and I'd rather deal with this in the Form description.
How can I handle this case?
Can I differentiate the form generation case from the form population case?
Or can I populate the default value later (eg. at view generation time)?
Or is my approach completely wrong?
Hope it's clear!
Thanks
One way to do this would be to override the formfield's data property, forcing it to return None if all the formfield's fields' values are None (or whatever test you wish to apply).
class MyFormField(wtforms.FormField):
#property
def data(self):
data = {name: f.data for name, f in self._fields.items()}
if any(filter(lambda v: v is not None, data.values())):
return data
return None
class MyForm(wtforms.Form):
name = wtforms.StringField()
professional_address = MyFormField(AddressForm, default=ProfessionalAddress())
if we try it out
form = MyForm(data={'name': 'Alice'})
for f in form:print(f)
form = MyForm(formdata=MultiDict({'name': 'Alice'}))
form.validate()
print(form.data)
form = MyForm(data={'name': 'Bob', 'professional_address': ProfessionalAddress(line1='1 High Street', line2='Dullsville')})
for f in form:print(f)
form = MyForm(formdata=MultiDict([('name', 'Bob'), ('professional_address-line1', '1 High Street')]))
form.validate()
print(form.data)
we can see that the form is rendered as expected, and None is returned if the professional address fields are not populated.
<input id="name" name="name" type="text" value="Alice">
<table id="professional_address"><tr><th><label for="professional_address-line1">Line1</label></th><td><input id="professional_address-line1" name="professional_address-line1" required type="text" value=""></td></tr><tr><th><label for="professional_address-line2">Line2</label></th><td><input id="professional_address-line2" name="professional_address-line2" required type="text" value=""></td></tr></table>
{'name': 'Alice', 'professional_address': None}
<input id="name" name="name" type="text" value="Bob">
<table id="professional_address"><tr><th><label for="professional_address-line1">Line1</label></th><td><input id="professional_address-line1" name="professional_address-line1" required type="text" value="1 High Street"></td></tr><tr><th><label for="professional_address-line2">Line2</label></th><td><input id="professional_address-line2" name="professional_address-line2" required type="text" value="Dullsville"></td></tr></table>
{'name': 'Bob', 'professional_address': {'line1': '1 High Street', 'line2': None}}

Simple_form not displaying file_field upload error message in Ruby on Rails 6

Using slim and rails 6, with simple form, I can add an error message to a variety of fields. For example, the following code will generate a form that will display the error "Your name is required" if it is left blank and the user tries to submit / save the form.
= simple_form_for(#dating_card) do |f|
.form_inputs
= f.input :FirstName, label: 'Your First Name:', error: 'You name is required'
However, when I do the following, the error for the file_field doesn't show up if I leave it blank and try to submit:
= simple_form_for(#dating_card) do |f|
.form_inputs
= f.input :FirstName, label: 'Your First Name:', error: 'Your name is required'
= f.file_field :images, multiple: true, required: true, accept: ' image/png,image/gif,image/jpeg', error: "There was an error with the images. Please try again"
I've looked through the simple-form documentation, and can't find anything. I can add errors via the controller validations, but I don't know how to get that to be consistent with the simple-form, inline validations. Any help is much appreciated.
UPDATE:
Here are the two pieces of rendered HTML which correspond with the form inputs above. The first is for the First Name field:
<div class="form-group string required dating_card_FirstName form-group-invalid" data-children-count="1"><label class="string required" for="dating_card_FirstName">Your First Name: <abbr title="required" data-children-count="0">*</abbr></label><input class="form-control is-invalid string required" aria-invalid="true" type="text" value="" name="dating_card[FirstName]" id="dating_card_FirstName" data-kwimpalastatus="alive" data-kwimpalaid="1595368187456-0"><div class="invalid-feedback">You name can't be left blank.</div>
and
<input multiple="multiple" required="required" accept="image/png,image/gif,image/jpeg" error="There was an error with the images" type="file" name="dating_card[images][]" id="dating_card_images">
...
In the model validations, I can add a custom validator, but I'm not sure how to get the front end to render the errors in the same way as the rest of the simple_form.

if statement not executed in JSP scriptlet

i encountered the following problem. I have made a table in mysql named 'users' and i want to add users using JSP. The primary key is the Email and i want to check before adding another user if the email already exists. If the email already exists the users will not be added to the database and a message "email already exists" will appear.
I created ResultSet rs object and extracted the "Email" column into a String email using a while loop.Now, here comes the problem. Suppose i have a user with the "denis#yahoo.com" email. If i try to add an user with the exact same email an error will occur which means my if statement was ignored. I looked up in the debug mode for the variables email and Email and they had the exact same "denis#yahoo.com" and still the if statement was ignored.
<!-- file name: addUser.jsp -->
<%
String Email=request.getParameter("Email");
String Name = request.getParameter("Name");
String Adress = request.getParameter("Adress");
String Phone = request.getParameter("Phone");
if (Email != null)
{
jb.connect(); // connect to the database using JavaBean class
ResultSet rs;
rs=jb.seeTable("users");// rs gets first row of table
String email;
int a=1;
while(rs.next())
{
email=rs.getString("Email");
if(Email==email)
{
a=0;
jb.disconnect();
%>
<p> The email already exists</p>
<%
break;
}
}
if(a==1){
jb.addUser(Email,Name, Adress, Phone);
jb.disconnect();
%>
<p>Data has been added.</p>
<%
}
} else {
%>
<form action="addUser.jsp" >
Email: <input type="text" name="Email"><br>
Name: <input type="text" name="Name"><br>
Adress: <input type="text" name="Adress"><br>
Phone: <input type="text" name="Phone"><br>
<button type="submit" >Add User</button><br>
</form>
<%
}
%>
I use NetBeans IDE 8.2, and something that i think is odd appeared in the debug mode. One of the email i try to test appeared with bold characters meanwhile the other was not bold.
Why is my if statement ignored if i add an user with the exact same email of another? Please help me out!

HTML Form: POST an array of objects

Submitting a class roster. Adding 3 students at once. Each student has first, last, age.
Question: How can we get all of the students in an array of arrays?
students[0] => Array (
["first"] => "first name for 0",
["last"] => "last name for 0",
["age"] => "age for 0"
),
students[1] => Array (
["first"] => "first name for 1",
["last"] => "last name for 1",
["age"] => "age for 1"
),
...
Details
For one student:
<input type="text" name="first">
<input type="text" name="last">
<input type="text" name="age">
We can return multiple students in separate arrays like this:
<input type="text" name="students[first][]">
<input type="text" name="students[last][]">
<input type="text" name="students[age][]">
which returns an array of firsts, lasts and ages
students["first"] = [array of first names]
students["last"] = [array of last names]
students["age"] = [array of ages]
Theoretically we can get all the info for a student by accessing the same index (say "3" for each array).
We do not want to programatically add an index in the form.
Do not want:
<input type="text" name="students[hardcoded_index][first]">
<input type="text" name="students[hardcoded_index][last]">
<input type="text" name="students[hardcoded_index][age]">
If for any reason it matters, we are using Rails for views but can use form helpers or HTML.
tl;dr: Add empty brackets ([]) after students to the input names.
Fiddling with Rack::Utils.parse_nested_query it seems you can get the payload you want like this:
<!-- first student -->
<input type="text" name="students[][first]">
<input type="text" name="students[][last]">
<input type="text" name="students[][age]">
<!-- second student -->
<input type="text" name="students[][first]">
<input type="text" name="students[][last]">
<input type="text" name="students[][age]">
Note the empty brackets ([]) after students. This tells Rack you want the students param to be an array. Subsequent params encountered (with the same name) will start a new element.
POST /myroute?students[][first]=foo&students[][last]=bar&students[][age]=21&students[][first]=baz&students[][last]=qux&students[][age]=19
Gets parsed like this:
{"students" => [
{
"first" => "foo",
"last" => "bar",
"age" => "21"
},
{
"first" => "baz",
"last" => "qux",
"age" => "19"
}
]}
Further reading: http://codefol.io/posts/How-Does-Rack-Parse-Query-Params-With-parse-nested-query
I know the question is old , but I would like to add my experiences
also for future readers.
For those of you who want to process the data in a PHP enviroment ,
#messanjah's method won't work ,
The methods for parsing data like the built-in serializeArray or serialize are not parsing it as expected either.
This is what I tried so far ...
students[name]
students[][name] - Very Strange since it was meant to automatically index the array
students[name][]
Neither of them worked, but this students[<hardcoded-index>][name] worked for PHP ,
I know that although the question is against this method , but it
will be useful for PHP users who will land here in the nearby future
as the asker needed it for Ruby On Rails.
The method you choose to hard code the indexes is upto you , you can use a cleaner method by using javascript or you can manually hard code them initially in your form element.
Cheers