Rails: duplicating information into another model if conditions met - ruby-on-rails

I've got a list of articles with checkboxes on a form. When a box is checked, the body of the selected article is copied into one of x text areas.
Should a user want to make a change to the body of an article in the text area I need to send that change through my controller into a new Rails model (called an Edit).
I already have the record being created, I just need the application to record the changes into a new Edit record. Here's my relevant controller code:
new
def new
#template = Template.find(params[:template])
#article_count = #template.pages-1
#doc = Doc.new
#doc.template_id = #template.id
#doc.user_id = current_user.id
#articles = current_user.brand.articles
respond_to do |format|
format.html
format.json { render json: #doc }
end
end
create
def create
#doc = Doc.new(params[:doc])
respond_to do |format|
if #doc.save
#make editable version
if current_user.brand.try(:editable?)
#doc.articles.each do |article|
#edit = Edit.new
#edit.name = article.name
#edit.video = article.video
#something here to get the bodies of the text areas
#edit.article_id = article.id
#edit.doc_id = #doc.id
#edit.user_id = current_user.id
#edit.save
end
end
#doc.create_activity :create, owner: current_user
format.html { redirect_to share_url(#doc.user.ftp, #doc) }
format.json { render json: #doc, status: :created, location: #doc }
else
format.html { render action: "new" }
format.json { render json: #doc.errors, status: :unprocessable_entity }
end
end
end
And here's the code that makes the text areas in the view.
<% #article_count.times do |article| %>
<div id="edit<%= article %>" class="tab-pane <%= "active" if article == 0 %>">
<%= text_area_tag("edit[#{article}][body]") %>
</div>
<% end %>
Records are created for each article, but I can't seem to be able to save the edits from the text areas. It's kind of a nested-form arrangement. Any help at all would be definitely appreciated. Cheers!

I've solved this by breaking the form up into two pages: one handles the selection, the second the editing. Given that a Doc has many edits, this is the method I made for part two of the form:
def edit_content
#doc = Doc.find(params[:id])
if !#doc.edits.exists?
#doc.articles.reverse.each do |article|
#doc.edits.build(:body => article.body)
end
end
end
Hope that helps someone.

Related

rails link_to posts to incorrect id - why?

I want my User to be able to change a boolean on a Share that they own, but my attempt at implementation updates the wrong record.
When I go to the show page for an Item with id:7, my controller loads the associated Share objects by looking for Shares that have item_id set to 7. When I then click the Hide or Show buttons, my code updates the associated Share's active attribute, and then redirects to that same Item.
But if I go to the show page for an Item with id:3, and click those same buttons, my code redirects to and updates the active attribute for the Share with item_id:7, instead of item_id:3. Can anyone give me an idea as to why this is happening?
My Share model:
class Share < ActiveRecord::Base
belongs_to :user
belongs_to :item
def activate
self.active = true
save
end
def deactivate
self.active = false
save
end
end
My Item model:
class Item < ActiveRecord::Base
has_many :shares
end
In my ItemsController#show action, I have this:
def show
#item = Item.friendly.find(params[:id])
#owned_share = current_user.shares.find_by(item_id: #item.id)
end
In my SharesController, I have this:
def activate
#owned_share = current_user.shares.find_by(params[:item_id])
#owned_share.activate
respond_to do |format|
format.html { redirect_to item_path(#owned_share.item) }
format.json { render :index, status: :ok, location: #owned_share }
end
end
def deactivate
#owned_share = current_user.shares.find_by(params[:item_id])
#owned_share.deactivate
respond_to do |format|
format.html { redirect_to item_path(#owned_share.item) }
format.json { render :index, status: :ok, location: #owned_share }
end
end
And in my Item show view, I have this:
<% if #owned_share.active == true %>
<div class="eight wide column">
<%= link_to "Hide", share_deactivate_path(#owned_share.item), class: "button wide-button functional-red-button", method: :post %>
</div>
<% else %>
<div class="eight wide column">
<%= link_to "Show", share_activate_path(#owned_share.item), class: "button wide-button functional-mint-button", method: :post %>
</div>
<% end %>
As stated in the comments, the param you're receiving isn't item_id, but share_id, that's why despite you modify your query adding the attribute which to look for, it doesn't give you the expected result.
Update the param which to use for getting user's share, like:
#owned_share = current_user.shares.find_by(item_id: params[:share_id])
Although in this case isn't clear why you're using share_id to look for an item_id, most probably you could update that part too.
As both actions share some specific functionality, you could make just one that just updates the active attribute "flipping" its value:
# model
def toggle_active
update(active: !active)
end
# controller
def update_active_status
#owned_share = current_user.shares.find_by(item_id: params[:share_id])
#owned_share.toggle_active
respond_to do |format|
format.html { redirect_to item_path(#owned_share.item) }
format.json { render :index, status: :ok, location: #owned_share }
end
end
It gets the current user's shares active value and alternate it by using !. Notice that if they don't have a default value, a negation of nil returns true.
!true # false
!false # true
!nil # true
Note #owned_share.active == true can also be #owned_share.active? or #owned_share.active.
Because this:
#owned_share = current_user.shares.find_by(params[:item_id])
should be:
#owned_share = current_user.shares.find_by_item_id(params[:item_id])

Rails Public Activity Gem - after create action controller

I am trying to make an app with Rails 4.
I have installed the public_activity gem.
I followed the Ryan Bates Railscast and took the controller based approach, and also the lighter Common option (as opposed to tracking the Model).
In my activities_controller I have:
class ActivitiesController < ApplicationController
def index
#activities = PublicActivity::Activity.order("created_at desc")
end
end
In my project.rb, I have:
include PublicActivity::Common
In my projects controller, create action, I have:
def create
logger.debug "xxx create project"
#authorise #project
#project = Project.new(project_params)
#project.creator_id = current_user.id
#project.users << current_user
#project.create_activity :create, owner: current_user
respond_to do |format|
if #project.save
format.html { redirect_to #project }
format.json { render action: 'show', status: :created, location: #project }
else
format.html { render action: 'new' }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
In my activity view - index, I have:
<% #activities.each do |activity| %>
<div class="col-md-4">
<div class="indexdisplay">
<span class="indexheading">
<%= link_to activity.owner.name, activity.owner if activity.owner %>
</span>
<span class="indexsubtext">
<%= render_activity activity %>
</span>
In my public activity (view folder)/project/_create.html.erb, I have:
<% if activity.trackable %>
<%= link_to activity.trackable.name, activity.trackable %>
<% else %>
which has since been removed
<% end %>
When I try this, it gives me a blank page (no new activities have been created). When I try to create a new project, I get stuck on submitting the form because I get this error:
ActiveRecord::RecordNotSaved at /projects
You cannot call create unless the parent is saved
The problem is this line in the create action of my projects controller.
#project.create_activity :create, owner: current_user
Is there somewhere else I can put this in the controller to make it run after the project is saved? I have read about call backs but they need to be in the model and I'd like to keep this process running from the controller.
As the error state, you are trying to create an activity to an unsaved Project. Move the creation of the activity after saving the #project:
def create
logger.debug "xxx create project"
#authorise #project
#project = Project.new(project_params)
#project.creator_id = current_user.id
respond_to do |format|
if #project.save
#project.users << current_user
#project.create_activity :create, owner: current_user
format.html { redirect_to #project }
format.json { render action: 'show', status: :created, location: #project }
else
format.html { render action: 'new' }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end

Setting an object attribute from a hidden field Ruby on Rails

I'm trying to update an Idea attribute challenge_id through a hidden form field. Here is the field:
<%= f.hidden_field :challenge_id, :value => #challenge.id %>
It successfully passes the challenge id as a param when an idea is created to the Idea Controller#create method:
Started POST "/ideas.js" for ::1 at 2015-06-18 15:39:49 -0400
Processing by IdeasController#create as JS
Parameters: {"utf8"=>"✓", "idea"=>{"title"=>"adsf", "description"=>"asf", "domain_tokens"=>"20", "challenge_id"=>"5"}, "commit"=>"Create Idea"}
This challenge_id => 5 should then be saved to the idea in the line #idea = Idea.new(idea_params) below:
ideas_controller.rb
def create
#idea = Idea.new(idea_params)
respond_to do |format|
if #idea.save
idea_count = #idea.user.ideas_created_count
#idea.user.update(:ideas_created_count => idea_count + 1)
#idea.domains.each do |domain|
current_user.add_points(1, category: domain.title)
end
#ideas = current_user.current_team.ideas.sort_by{|i| i.heat_index}.reverse
#ideas = #ideas.paginate(:page => params[:ideas_page], :per_page => 10)
format.html { redirect_to :back, notice: 'Idea was successfully created.' }
format.json { render :show, status: :created, location: #idea }
format.js
else
format.html { redirect_to :back, notice: "You must attach domains to your Idea." }
format.json { render json: #idea.errors, status: :unprocessable_entity }
format.js { render :create_failed }
end
end
end
.
.
def idea_params
params.require(:idea).permit(:title, :description, :challenge_id)
end
However, this challenge id isn't being saved to the idea with the other permitted idea_params, :title and :description. How can I get challenge_id to be saved to the Idea when it's created?
Instead of using hidden field, why not pass in the challenge_id in your form? Otherwise, you leave open the possibility that users can enter whatever they want in that hidden field.
Form:
form_for [#challenge, Idea.new] do |f|
And then:
def create
#challenge = Challenge.find(params[:challenge_id])
#idea = Idea.new(idea_params)
#idea.challenge_id = #challenge.id
end
I'm assuming you have challenge_id column in ideas table.
Try something like:
def create
#idea = Idea.new(idea_params)
#idea.challenge_id = params[:idea][:challenge_id]
#...
end
If you params or column you want to save it to is different, make sure to make the change, but you get the idea.

Why this shows two flash messages but the other one?

This is view
<%= '<br /><br />' if flash %>
<% flash.each do |name, msg| %>
<div class="alert alert-<%= name == :notice ? "success" : name.to_s %>">
<a class="close" data-dismiss="alert">×</a>
<%= content_tag :div, msg.html_safe, :id => "flash_#{name}" if msg.is_a?(String) %>
</div>
<% end %>
<%= yield %>
This code below shows only one flash message("Deleted") on the top of the screen when after I deleted a record. Then that's how I want.
def update
if params[:destroy]
if current_user.id == #code.user_id
#code.destroy
flash[:notice] = "Deleted"
else
flash[:notice] = "You don't have permission to edit"
end
respond_to do |format|
format.html { redirect_to community_codes_url }
format.json { head :no_content }
end
else
respond_to do |format|
if #code.update_attributes(params[:code])
format.html { redirect_to community_code_path(#community.community_name, #code), notice: 'Updated' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #code.errors, status: :unprocessable_entity }
end
end
end
end
But this code below is messing up this rule, and I'm totally confused:(
If I create new record, it should show just one flash message like previous one. Then it has tweet button in it.
This flash doesn't not contain only string but also contains html and javascript within flash message.
I don't think it matters whether if there will be 2 flash messages or not:(
How can I show only one flash message?
def create
#code = #community.codes.build (params[:code])
#code.user_id = current_user.id
respond_to do |format|
if #code.save
tag_strings = #community.tags.join(", ") + "," if #community.tags
flash[:notice] = 'Tweet<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>You can tweet if you click this button'
format.html { redirect_to community_code_path(#community.community_name, #code) }
format.json { render json: [#community, #code], status: :created, location: #code }
else
format.html { render action: "new" }
format.json { render json: #code.errors, status: :unprocessable_entity }
end
end
end
In the one that only shows one message, you're writing to the notice key of the flash hash, then writing to that same key again, thereby overwriting it. In the one that shows two messages, you're writing to the notice key once, and the error key once, resulting in two keys. Then, the flash mechanism displays both. Based on the flash display mechanisms you're using, they might appear in different colors, since they're two different 'levels' of problem.
If you want to only display one, you could keep writing to the same key of the flash hash.

Not getting any value back from function defined in controller

This is my controller. def random_generate is not giving me any output, and when I create the object, length is there but random_primer_generated is empty. Can someone help me?
class GeneratorsController < ApplicationController
before_action :set_generator, only: [:show, :edit, :update, :destroy]
helper_method :random_generate
# GET /generators
# GET /generators.json
def index
#generators = Generator.all
end
# GET /generators/1
# GET /generators/1.json
def show
end
# GET /generators/new
def new
#generator = Generator.new
end
# GET /generators/1/edit
def edit
end
# POST /generators
# POST /generators.json
def create
#generator = Generator.new(generator_params)
respond_to do |format|
if #generator.save
format.html { redirect_to #generator, notice: 'Generator was successfully created.' }
format.json { render action: 'show', status: :created, location: #generator }
else
format.html { render action: 'new' }
format.json { render json: #generator.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /generators/1
# PATCH/PUT /generators/1.json
def update
respond_to do |format|
if #generator.update(generator_params)
format.html { redirect_to #generator, notice: 'Generator was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #generator.errors, status: :unprocessable_entity }
end
end
end
# DELETE /generators/1
# DELETE /generators/1.json
def destroy
#generator.destroy
respond_to do |format|
format.html { redirect_to generators_url }
format.json { head :no_content }
end
end
def random_generate
if #generator.choice == 'Randomly'
length = #generator.primer_length
chars = 'atgc'
seq = ''
#length = length.to_i
length.times { seq << chars[rand(chars.size)] }
#generator.random_primer_generated = seq
end
return #generator.random_primer_generated
end
private
# Use callbacks to share common setup or constraints between actions.
def set_generator
#generator = Generator.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def generator_params
params.require(:generator).permit(:primer_length, :choice_random_primer_generated)
end
end
This is my Form_VIEW (to get the user's data - length and choice):
<%= form_for (#generator ) do |f| %>
<% if #generator.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#generator.errors.count, "error") %> prohibited this generator from being saved:</h2>
<ul>
<% #generator.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<label>Primer Length</label><br>
<%= f.number_field :primer_length %>
</div>
<label>Selection :</label><br>
<label>Randomly</label>
<%= radio_button_tag(:choice_random_primer_generated, 'Randomly')%>
<%= button_to('Generate', random_generate_generator_path(#generator))%>
<% end %>
Note: the form may be a red herring here. If you only want the code to be randomly generated when the user explicitly clicks that button, and NOT after every create or save, then you don't want any callbacks. You're doing it the right way. The only thing you are missing is the save call at the end of your method, and you'll also want to redirect the user back to a page.
However, if you DO want generate to happen after EVERY create or update, then you should probably change that button_to call to f.submit, and move that random_generate code to your model. Then, you can use rails callback for after_save and call that method from there within your controller.
More info:
http://guides.rubyonrails.org/active_record_callbacks.html
You are not calling your helper_method anywhere. When you post the form and it reaches the server, the create method is called. The helper_method is not triggered, therefore you are not creating a random number.
I didn't understand it very well, but what are you trying to randomize? Why don't you simply use a rand(999) to randomize numbers for example?
It looks like you want to call random_generate after your new and create action, but you are not calling it. All you have to do is change helper_method :random_generate to after_action :random_generate, only: [:create, :new]
You also want to make sure to save your new record in random_generate. You don't need the return #genator.random_primer_generated. See below:
def random_generate
if #generator.choice == 'Randomly'
length = #generator.primer_length
chars = 'atgc'
seq = ''
#length = length.to_i
length.times { seq << chars[rand(chars.size)] }
#generator.random_primer_generated = seq
end
#generator.save!
end

Resources