使用has_many作为自定义类/表单向导时,没有方法错误

时间:2018-07-10 20:48:07

标签: ruby-on-rails ruby forms class has-many-through

我已经创建了一个表单向导来创建对象,此后,我想通过创建ObjectCategories表并通过复选框将Categories添加到对象来对对象进行分类。

这适用于Object本身,但是我无法使其适用于向导,向导创建了一个自定义类,然后将该自定义类另存为对象,位于表单的最后一步。我使用了本指南:https://medium.com/@nicolasblanco/developing-a-wizard-or-multi-steps-forms-in-rails-d2f3b7c692ce

我认为我需要通过Goal类访问类别方法,但是我不确定该怎么做。我已经阅读了APIDock上的委托页面,但对我来说意义不大(我将业余项目构建为业余项目,因此没有人问这些愚蠢的问题)

错误:

NoMethodError in Wizards#step2
Showing /home/ubuntu/workspace/GoalMates/app/views/wizards/step2.html.erb where line #40 raised:

undefined method `category_ids' for #<Wizard::Goal::Step2:0x00000007454320>
Extracted source (around line #40):
38
39
40
41
42
43

      <%= 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 %>
  </div>  

Goal.rb(实际对象):

class Goal < ApplicationRecord
  has_many :goal_tasks, dependent: :destroy
  belongs_to :user

  has_many :flags, as: :flaggable
  has_many :goal_reflections, dependent: :destroy

  has_many :goal_categories, dependent: :destroy
  has_many :categories, through: :goal_categories

  after_update :send_notifications, if: :goalstatus_changed?

  profanity_filter! :goalname, :goaldesc, :method => 'hollow'



   enum goalstatus: [:active, :paused, :complete, :abandoned]

  def set_default_goalstatus
    self.goalstatus ||= :active
  end




  validates :goalname, presence:true, length: {minimum:10, maximum: 240}
  validates :goaldesc, presence:true, length: {minimum:10, maximum: 1140}
  validates :goalhow, presence:true, length: {minimum:5, maximum: 1140}
  validates :goalwhy, presence:true, length: {minimum:5, maximum: 1140}
  validates :goalreward, presence:true, length: {minimum:3, maximum: 1140}
  validates :goalduedate, presence:true




  scope :recentcomplete, -> { where("completed_at > ?", Time.now-7.days)  } 

  scope :activegoal, -> { where("goalstatus = 0") }
  scope :pausedgoal, -> { where("goalstatus = 1") }
  scope :completedgoal, -> { where("goalstatus = 2") }
  scope :abandonedgoal, -> { where("goalstatus = 3") }

  scope :recentcomplete, -> { where("completed_at > ?", Time.now-7.days)  } #todo this needs changing to complete at..
  #also needs changing to last complete

  def send_notifications
    user.friends.each do |friend|
      Notification.create(
        originator: user, user: friend, notifiable: self, action: 'update_status'
      )
    end
  end
end

Goal.rb(对象的向导版本,保存在form_models / wizard / goal.rb中)

module Wizard
  module Goal
    STEPS = %w(step1 step2).freeze

    class Base
      include ActiveModel::Model
      attr_accessor :goal

      delegate *::Goal.attribute_names.map { |attr| [attr, "#{attr}="] }.flatten, to: :goal

      def initialize(goal_attributes)
        @goal = ::Goal.new(goal_attributes)
      end


    end



    def set_default_goalstatus
      self.goalstatus ||= :active
    end

    class Step1 < Base
      validates :goalname, presence:true, length: {minimum:10, maximum: 240}
      validates :goaldesc, presence:true, length: {minimum:10, maximum: 1140}
      validates :goalhow, presence:true, length: {minimum:5, maximum: 1140}
    end

    class Step2 < Step1
      validates :goalwhy, presence:true, length: {minimum:5, maximum: 1140}
      validates :goalreward, presence:true, length: {minimum:4, maximum: 1140}
      validates :goalduedate, presence:true
    end


  end
end

表单中出现问题的部分:

<%= form.label "What categories 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 %>

GoalCategory.rb

class GoalCategory < ApplicationRecord
    belongs_to :goal
    belongs_to :category
end

Step2.html.erb:

<div class="col-md-10 col-md-offset-1">
<ol class="breadcrumb center">
  <li><%= link_to "Step 1", step1_wizard_path %></li>
  <li class="active">Step 2</li>
</ol>
<%= render 'layouts/errors', object: @goal_wizard %>

<%= form_for @goal_wizard, as: :goal_wizard, url: validate_step_wizard_path do |form| %>



  <%= hidden_field_tag :current_step, 'step2' %>

    <div class="col-md-12 goalform">
        <%= form.label "Why do you have this goal?" %><br>
        <%= form.text_area :goalwhy, :rows => 2, style: 'width:80%;', 
        placeholder: "I want to get in shape for my wedding...", class:"form-control" %>
       <p><i>Remembering why you have your goal will motivate you when it gets tough. It also helps in matching you with others.</i></p>
    </div>

    <div class="col-md-12 goalform">
        <%= form.label "Rewarding yourself" %><br>
        <%= form.text_area :goalreward, :rows => 2, style: 'width:80%;', 
        placeholder: "I'll buy a new pair of gym shoes", class:"form-control" %>
       <p><i>Achieving a goal is its own reward. Extra rewards, no matter how small, are also great motivation. Treat yourself, you'll have earned it!</i></p>
    </div>

  <div class="col-md-12 goalform">
      <%= form.label "When is your goal due?" %><br>
      <%= form.date_select :goalduedate %>
     <p><i>Now you've got a target goal, when will you achieve it by? It's important to be 
     realistic and set an achievable date. 
     TIP: We often set difficult goals that are hard to achieve and far away. Instead try setting a goal that's 1-2 months away. You can always create more goals!</i></p>
  </div>

  <div class="col-md-12 goalform">
      <%= form.label "What categories 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 %>
  </div>  


  <div class="col-md-12 goalform center">
    <%= form.submit class:"btn btn-primary", value: "Create Goal" %>
  </div>

<% end %>
</div>

目标控制器(对象):

class GoalsController < ApplicationController
  before_action :authenticate_user!, except: [:index, :show, :completedgoals]
  before_action :set_goal, only: [:show, :edit, :update, :destroy, :mark_completed, :goal_review, :goal_review_next, :toggle_goal_status ]

  def index
    @goals = Goal.activegoal.order("created_at DESC").page(params[:page]).per(6)
  end

  def new
    @goal = Goal.new
  authorize @goal
  end

  def edit
    authorize @goal
  end


  def create
    @goal = Goal.new(goal_params)
    @goal.set_default_goalstatus
    @goal.user_id = current_user.id
    authorize @goal

    respond_to do |format|
      if @goal.save
        format.html { redirect_to @goal, success: 'Goal was successfully created.' }
        format.json { render :show, status: :created, location: @goal }
      else
        format.html { redirect_to @goal, success: 'Goal could not be created.' }
        format.json { render json: @goal.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    authorize @goal
    update_params = goal_params
    if update_params['goalstatus'] && update_params['goalstatus'] == 'complete'
      update_params['completed_at'] = Time.zone.now
    end
    respond_to do |format|
      if @goal.update(update_params)
        format.html { redirect_to @goal, success: 'Goal was successfully updated.' }
        format.json { render :show, status: :ok, location: @goal }
      else
        format.html { render :edit }
        format.json { render json: @goal.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    authorize @goal
    @goal.destroy
    respond_to do |format|
      format.html { redirect_to goals_url, success: 'Goal was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

   private
    def set_goal
      @goal = Goal.find(params[:id])
    end

    def goal_params
      params.require(:goal).permit(:goalname, :goaldesc, :goalhow, :goalwhy, :goalreward, :goalduedate, :goalstatus, category_ids: [])
    end


end

wizards_controller.rb:

class WizardsController < ApplicationController
  before_action :load_goal_wizard, except: %i(validate_step)

  def validate_step
    current_step = params[:current_step]

    @goal_wizard = wizard_goal_for_step(current_step)
    @goal_wizard.goal.attributes = goal_wizard_params
    session[:goal_attributes] = @goal_wizard.goal.attributes

    if @goal_wizard.valid?
      next_step = wizard_goal_next_step(current_step)
      create and return unless next_step

      redirect_to action: next_step
    else
      render current_step
    end
  end

  def create
    @goal_wizard.user_id = current_user.id
    @goal_wizard.goalstatus = "active"
    if @goal_wizard.goal.save
      session[:goal_attributes] = nil
      redirect_to goal_path(@goal_wizard.id), success: "Goal created!"
    else
      redirect_to({ action: Wizard::Goal::STEPS.first }, alert: 'There was a problem when creating the goal.')
    end
  end

  private

  def load_goal_wizard
    @goal_wizard = wizard_goal_for_step(action_name)
  end

  def wizard_goal_next_step(step)
    Wizard::Goal::STEPS[Wizard::Goal::STEPS.index(step) + 1]
  end

  def wizard_goal_for_step(step)
    raise InvalidStep unless step.in?(Wizard::Goal::STEPS)

    "Wizard::Goal::#{step.camelize}".constantize.new(session[:goal_attributes])
  end

  def goal_wizard_params
      params.require(:goal_wizard).permit(:goalname, :goaldesc, :goalhow, :goalwhy, :goalreward, :goalduedate, :goalstatus, category_ids: [])
  end

  class InvalidStep < StandardError; end
end

0 个答案:

没有答案