schema.rb and mysql database issue in ruby on rails - mysql

First of all, I've been learning Rails for 8 months but feel like I started yesterday, so please bear with me. I'm trying to resolve why my value, called visible, isn't functioning correctly. I'm using Simple_form. 'visible' is the radio button value:
<div class='review-form'>
<%= simple_form_for(#review) do |f| %>
# input boxes for current_user to put text here
#'public' radio button, checked by default, class is for css
<%= f.radio_button :visible, "true" , :class => "share_button" %>
#'private' radio button, class is for css
<%= f.radio_button :visible, "false", :class => "keep_private_button" %>
#user can cancel
<%= link_to I18n.t('write_review.cancel_button'), landing_page,
:class => 'btn' %>
#user can submit
<%= f.button :submit, I18n.t('write_review.submit_button'),
:class => 'btn btn-primary' %>
The idea is that the current_user writes a review - if they make it 'public', any other user can see it, if private, only they can see it. The radio buttons work ok - when I log out, look at the app on different machines etc, the radio button is in the last saved state. The problem is that the current review, #review, is always visible to everybody, regardless of the state of the radio button.
I put <%= #review.visible? %> on the review form. Every time I refresh the page it is 'true', even though the radio button might be false. I save the review and #review.visible then matches accordingly. But then I refresh and it goes back to true.
I think the problem could be:
In my schema.rb I have:
t.boolean "visible", :default => true
I deleted :default => trueand there was no improvement but I hear you're not supposed to manually interfere with the schema.rb anyway.
Do you think that default => true is causing the problem? Would doing a 'migration' be the way to go? How would I word that 'default => true' part in the migration?
Finally, while my schema.rb has 'boolean' my actual database has visible: tinyint(1) , which I thought was fishy, but I've read online they're in fact the same thing, so I don't think that's the problem - if you think differently, let me know.

You shouldn't touch db/schema.rb. That is automatically updated via the migrations and/or the state of your database. You have the right idea in that you need to remove the default value for 'visible', but you need to do it in a migration. change_column_default is probably what you want.
http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/change_column_default
Also, the boolean vs tinyint(1) is okay. It's a MySQL thing as MySQL doesn't have true boolean types.

To echo what Phillip said if you need to change your database, you shouldn't do it via schema.rb, you need a migration. You should have a default for booleans on mysql (because otherwise it's a tri-state instead of a binary (true, false, null).
If you think the problem is the default is wrong, you can do a change_column migration, you can generate a migration from the command line tools:
rails g migration UpdateTableName
Open the migration file (in models/db/migrate/timestamp_update_table_name.rb) and change the code to be:
def change
update_column :table_name, :columnname, :boolean, :default => false
end
then run this via command line tools:
rake db:migrate
If you think your problem is the form, you could always look at something like this to see if it's the values for your form that are the problem.

Related

How do I redirect the to index after submitting a new record at the front-end? [Rubymine 2020.2.3, Ruby.2.7.2p137, gem 3.1.2]

I am struggling with a college project course, and I have been stuck with this error for weeks now, despite the suggestions provided by colleagues and tutors.
In my create method for a given table. I am trying to have the page containing the forms for new record entries redirect back to the index page after saving. Instead, I get redirected to this error instead, highlighting #courier=Courier.new(courier_new_path) with the error stating that it is not a hash, and would not redirect me back into index. However, when manually searching the index, I see that the data string would indeed get updated.
I have tried renaming the path label, but Rubymine prompt suggestions appear limited, and any further deviation would cause a different error
The following is the create method in the controller page (courier_controller.rb):
def create
#courier=Courier.new(params.require(:courier).permit(:courier_name,:courier_email))
#courier.save
redirect_to courier_path(#courier)
#courier=Courier.new(courier_new_path)
if #courier.save
redirect_to(:controller=>'courier' ,:action=>'index')
else
render('new')
end
end
Here is the code for the form page (courier/new/html.erb):
<h1>Courier#new</h1>
<p>Find me in app/views/courier/new.html.erb</p>
<%= form_with scope: :courier, :url => {:action => 'create'}, local: true do |f| %>
<p>
<%= f.label :courier_name %><br/>
<%= f.text_field :courier_name %>
</p>
<p>
<%= f.label :courier_email %><br/>
<%= f.text_field :courier_email %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
I have tried renaming #courier as Courier.new(courier_create_path) or Courier.nww(courier_path), I have tried looking for arguments using a hash form, but none seemed equivocal nor translatable as a solution to my problem.
Any suggestions would help. This is part of a college project, and as a multimedia student not as savvy in programming compared to fellow peers, I would highly appreciate suggestions that I can try out.
Many thanks in advance.
Has anyone tried to explain what is actually going on in your create method? I've added comments after each line to say what that line is doing.
def create
#courier=Courier.new(params.require(:courier).permit(:courier_name,:courier_email))
# use the params to build a new courier record (the permit part is usually in a separate method that other methods can access but it works this way)
#courier.save
# Save the new courier record to the database
redirect_to courier_path(#courier)
# Send the user back to the "show" page of the courier record (not index!)
#courier=Courier.new(courier_new_path)
# this makes no sense, you are trying to create a new courier object using a path method
# Basically you are saying: #courier = Courier.new('/courier/new')
if #courier.save
#you are trying to save this record that will fail because you can't create a courier by passing it a url
redirect_to(:controller=>'courier' ,:action=>'index')
#send the user to the index page of the courier views.
else
render('new')
#something went wrong so go back to the new courier form.
end
end
When your program gets to line 4 redirect_to courier_path(#courier) it is going to exit the create method and send the user to http://my_app:3000/couriers/1 where the number 1 would be the ID in the database of the record you just created. This would relate to the file in your app in app/views/couriers/show.html.erb. It sounds like you want to get to http://my_app:3000/couriers which presents the user with the file app/views/couriers/index.html.erb Not sure why you are doing anything after that line.
Also it is unclear what error you are getting. You need to look at your webserver console, the place where you run "rails s" that shows all the communications between the browser and your app. find the stack trace that starts with the actual error, and add that to your question above (don't paste it in a comment, it will be too long and impossible to read).
I think you just need:
def create
#courier=Courier.new(params.require(:courier).permit(:courier_name,:courier_email))
if #courier.save
redirect_to #courier #if you want to redirect to their 'show" page
else
render('new')
#something went wrong so go back to the new courier form.
end
end
What might be confusing to a new programmer is that Rails is doing so much "magic" behind the scenes it gets very confusing if you don't already know the underlying concepts happening behind the scenes. You are saying redirect_to(:controller=>'courier' ,:action=>'index') which specifically says "go to the index page of the couriers" but you can say redirect_to #courier and it will assume that you want to go to the page that shows the record of the courier you just created. If you really want to go the table that shows all of the couriers you would replace that with redirect_to :couriers The symbol :couriers tells it to go to the index method of the couriers controller.

Rails migration -- not appearing on show.html.erb

I had a model named b_page I wanted to create another column , so I ran a migration:
rails g migration add_status_to_b_page status:string
so migration was successful. Users should be able to update their status so I put this on the _form.html.erb:
<div class="field">
<%= f.label :status %><br>
<%= f.text_field :status %>
</div>
was successful but then i added it to the show.html.erb
<%= #b_page.status %>
but everytime i make a new b_page or edit the current one I dont see it on show.html.erb
Without seeing your code I guess you have to whitelist the new parameter (status) in your BPageController (at the very bottom, in something like def bh_pages_params).
You can check the logs whether the parameter that comes into your controller (via the form) actually arrives at the data (ActiveRecord Model), this whitelisting approach (called Strong Parameters) is in place to safe guard your data.
thx I fixed my problem I forgot to add status in
params.require(:b_page).permit(:Bpage_name, :banner_img, :profile_img, :status) in my controller

Changing columns in rails

So I made a seperate migration file to change the name of a column in my database table. The problem is when I try to go the page with the table I get an undefined method error on the column name I tried to change.
My professor told me I have to change the View/controller before The columns will work but I can not find out what I have to do.. any help/suggestions?
The way I'm changing the columns is like this:
class FixColumnName < ActiveRecord::Migration
def change
rename_column :suppliers, :sName, :"first_name"
add_column :suppliers, :"last_name"
remove_column :suppliers, :Snum
remove_column :parts, :Ptnum
end
end
If you've chaned the name of the database column, then you'll also need to change the name of the associated Model's attribute in your application code.
That is, if you previously had #supplier.sName (or similar), you'll now need #supplier.first_name (or something similar - using the new column names).
sNum not longer exist.
remove
<%= #supplier.sNumb %>
you will change
<%= #supplier.sName %>
to
<%= #supplier.first_name %>
you will also want to add
<%= #supplier.last_name %>

Best way to simulate default value for TEXT column using Ruby on Rails?

I'm using a TEXT column in my MySQL database. As the documentation says, it is not possible to set a default value for these columns.
I'm currently using the following code to simulate this behavior:
class Data
before_save lambda {text_column ||= ''}
end
Is there any better, more railis/active_record way to do this?
If you're happy with a HTML5 solution, have you tried a :placeholder attribute on the :text_field?
Also do you really want to stuff a text_field (which captures a small amount of text) into a "text" type column? Did you mean text_area?
If you want the "default value" to actually be stored in the database if the user doesnt input anything then i suggest the following. It's the "Factory" pattern.
Instead of calling "new" on your ActiveRecord model class, you create a "setup" method in your model
def self.setup(params = {})
new(params).tap do |v|
v.text_column = "default value"
# other defaultings
end
end
In your controller instead of calling new on the class you call setup.
Add this in your migration
add_column :table_name, :column_name, :string, :default => 'your text'
Working for me
If you have a text field in a form for new, use a default value of this text field:
#views/controller/new.html.erb
<%= f.text_field :column_name, :value => "default value" %>
It is a good usability choice, since user is aware of the default value of that column.
Don't use it in edit.html.erb though, since in this case this filed will still have the default value, regardless of its original value in database.

In Rails, how should I implement a Status field for a Tasks app - integer or enum?

For a Rails 3.0 Todo app, I have a Tasks model with a Status field. What's the best way to store the Status field data (field type) and still display a human-readable version in a view (HTML table)? Status can be:
0 = Normal
1 = Active
2 = Completed
Right now I have this:
Rails Schema Here:
create_table "tasks", :force => true do |t|
t.integer "status", :limit => 1, :default => 0, :null => false
Rails Model Here:
class Task < ActiveRecord::Base
validates_inclusion_of :status, :in => 0..2,
:message => "{{value}} must be 0, 1, or 2"
Rails View Here:
<h1>Listing tasks</h1>
<table>
<tr>
<th>Status</th>
<th>Name</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #tasks.each do |task| %>
<tr>
<td><%= task.status %></td>
<td><%= task.name %></td>
<td><%= link_to 'Show', task %></td>
<td><%= link_to 'Edit', edit_task_path(task) %></td>
<td><%= link_to 'Delete', task, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
Requirements
Store a Task's status in the db such that the values are easily localizable, i.e. I'm not sure I want to store "normal", "active", "completed" as a string field.
Solution must work with Rails 3.0.
Questions:
Should I store the field as an integer (see above)? If so, how do I display the correct human readable status in an HTML table in my Rails view, e.g. show "Active" instead of "1" in the HTML table.
Should I use an enum? If so, is this easy to localize later?
Should I use straight strings, e.g. "Normal", "Active", "Completed"
Can you provide a quick code sample of the view helper, controller or view code to make this work?
1.It depends on how much you want to optimize queries on the DB.
2.Not really, it is not supported 'out of the box' by AR. # As of Rails 4 enums are supported out of the box.
3.IMHO you can use strings without a big performance penalty (just remember to add field to an index). I would do this because it's easier to internationalize and to maintain. However, you can go with integers if you need extra performance.
You may take a look on 2 SO threads here and here where this is debated.
4.If you want to keep them as integer, here is how you can accomplish this:
class Task << AR::Base
NORMAL = 1
ACTIVE = 2
COMPLETED = 3
STATUSES = {
NORMAL => 'normal',
ACTIVE => 'active',
COMPLETED => 'completed'
}
validates_inclusion_of :status, :in => STATUSES.keys,
:message => "{{value}} must be in #{STATUSES.values.join ','}"
# just a helper method for the view
def status_name
STATUSES[status]
end
end
and in view:
<td><%= task.status_name %></td>
If you want to use strings, it's more simplified:
STATUSES = ['normal', 'active', 'completed']
validates_inclusion_of :status, :in => STATUSES,
:message => "{{value}} must be in #{STATUSES.join ','}"
The easiest thing to do would be to just store the actual strings in the field instead of adding another table. Depending on your point of view this is either a bad idea as your database will not be sufficiently normalized or a great idea as your app is now more efficient for being normalized.
If you opt to not do that and to keep the values in a separate table; you need to setup the relationships in the model.
class Task < ActiveRecord::Base
has_one :status
end
class Status < ActiveRecord::Base
belongs_to :tasks
end
Then in your view you can reference the value by:
<%= task.status %>
I have used Enum-Column for such use cases. The plugin allows you to define a enum column type in your migration script and creates a MYSQL enum column type for the attribute.
create_table :tasks do |t|
...
t.enum :status, :limit => [:normal, :active, :completed], :default => :normal
...
end
Now in your code you can do the following:
task.status = "active"
task.status = :completed
p "Task status: #{task.status}" # prints Task status: completed
Task.find_by_status(:active)
Task.find_by_status("active")
Task.find_by_status(2)
Works well with serialization too:
task.to_xml # will result in status= active instead of status-2
Other nice aspect is, the status values are displayed as strings when viewed using a regular DB client(E.g: mysql command console or phpMyAdmin)
The plugin provides optimal storage and user friendly access for the enumeration types.
Caveat:
The plugin is quite old and not maintained. I am using it extensively with MySQL DB. I had to patch the code to get it work on PostgreSQL. If you are using MySQL, this plugin is a good choice.
I prefer to store "normal", "active", .. "completed" as string in the table because:
it's self documentation (for example, someone may look at the data but never read the Rails source code)
there is little (if not no) performance penalty
it is (still) easy to do i18n by means of Rails virtual attribute in the model or whatever layer in other languages
These days, I tend to decouple Rails constants from the database as much as I can. There are always some PHP/MSSQL/??DBA folks around us (who may not love Rails as much as we do ;-)
So, the answer is not integer nor enum (but a varchar ;-)
I know this is an old question but I wanted to mention 2 points that come from experience, especially if someone is looking for this now ( 2014 - OQ was in 2010) :
If you are starting a new project > Rails 4 ( technically ActiveRecord 4) - use Enums - the most efficient route. Especially if you need to create any kind of complicated SQL queries later on.
There is on more alternative - create a composite Status model that will hold statuses for all your other models. Make it an STI model (add type column)- then you can create things like OrderStatus < Status or CustomerStatus < Status and your Order and Customer would have status_id attributes. This makes for slower (!) and more cumbersome (!) queries, however you might want to go this route if you are a creating an app that will be shipped to client that has no technical expertise and they would need some kind of ability to add / remove statuses through something like ActiveAdmin on their own rather than modify your code base. This is also an option if your data layer can't handle enums.
Using integer to store the status is a good idea. Nonetheless, I think the code provided by the accepted answer is not elegant since it pollutes the whole model class just because of an attribute.
My suggestion is overriding the attribute getter:
def status
{
0 => "active",
1 => "inactive"
}[read_attribute(:status)] # use the read_attribute method to prevent infinite loop.
end
The logic of transforming the integer into a string will be only in this getter method, so you don't need to make the class dirty.