如何整合:错过的日子:养成习惯的日子.rb?

时间:2015-03-01 21:48:18

标签: ruby-on-rails ruby integer rails-activerecord days

我们如何将t.integer :missedt.text :committed集成以便

  1. 当用户在:missed中检查:committed 3 :level天后,他必须重新启动:level

  2. 在每次:missed天结束时,:committed天会被添加回:level天,以便他必须在推进之前进行弥补?

  3. 每个习惯在达到"Mastery"之前有5个等级!

    
    
    class Habit < ActiveRecord::Base
    	belongs_to :user
    	before_save :set_level
    	acts_as_taggable
    	serialize :committed, Array
    
      def self.comitted_for_today
        today_name = Date::DAYNAMES[Date.today.wday].downcase
        ids = all.select { |h| h.committed.include? today_name }.map(&:id)
        where(id: ids)
      end
    
    	def levels
    			committed_wdays = committed.map { |day| Date::DAYNAMES.index(day.titleize) }
    			n_days = ((date_started.to_date)..Date.today).count { |date| committed_wdays.include? date.wday }
    
      case n_days	  
    	  when 0..9
    	    1
    	  when 10..24
    	    2
    	  when 25..44
    	    3
    	  when 45..69
    	    4
    	  when 70..99
    	    5
    	  else
    	    "Mastery"
    		end
    	end
    
    private
    	def set_level
    	 self.level = levels
    	end	
    end
    &#13;
    &#13;
    &#13;

    我猜我们必须在此区分:missed:missed,具体取决于它所指的级别。

    习惯/ _form.html.erb

    &#13;
    &#13;
      <label> Missed: </label>
      <div>
      <label> Level 1: </label>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      </div>
      <div>
      <label> Level 2: </label>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      </div>
      <div>
      <label> Level 3: </label>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      </div>
      <div>
      <label> Level 4: </label>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      </div>
      <div>
      <label> Level 5: </label>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      <%= f.check_box :missed %>
      </div>
    &#13;
    &#13;
    &#13;

    habits_controller.rb

    &#13;
    &#13;
    class HabitsController < ApplicationController
      before_action :set_habit, only: [:show, :edit, :update, :destroy]
      before_action :logged_in_user, only: [:create, :destroy]
    
      def index
        if params[:tag]
          @habits = Habit.tagged_with(params[:tag])
        else
          @habits = Habit.all.order("date_started DESC")
          @habits = current_user.habits
        end
      end
                                                  
    private
                                                  
        def habit_params
          params.require(:habit).permit(:missed, :left, :level, :date_started, :trigger, :target, :positive, :negative, :tag_list, :committed => [])
        end
    end
    &#13;
    &#13;
    &#13;

    _create_habits.rb

    &#13;
    &#13;
    class CreateHabits < ActiveRecord::Migration
      def change
        create_table :habits do |t|
          t.integer :missed
          t.integer :level
          t.text :committed
          t.datetime :date_started
          t.string :trigger
          t.string :target
          t.string :positive
          t.string :negative
          t.references :user, index: true
    
          t.timestamps null: false
        end
        add_foreign_key :habits, :users
        add_index :habits, [:user_id, :created_at]
      end
    end
    &#13;
    &#13;
    &#13;

    :committed效果很好,但现在:missed没有任何意义。请帮助我添加适当的逻辑,以便将:missed:committed进行整合。

    非常感谢你的时间!

    更新

    @ Dimitry_N的回答并没有达到这个问题的1)或2),就像我试图让它发挥作用一样。也许你会更好地融入他的逻辑。通过他的回答,我也收到了这个错误:How to fix level.rb to work with :committed days?

2 个答案:

答案 0 :(得分:3)

我认为程序设计必须稍加重新评估。我认为levelsdays应该是包含levelmissed等列的单独模型(遵循 SRP 的概念,如@dgilperez中提到的那样)他的评论)。因此,我们最终得到四个模型:UserHabitLevelDay,具有以下关联:

  • 用户has_many :habitshas_many :levels
  • 习惯belongs_to:userhas_many :levelshas_many :days, through: :levels #for being able to access Habit.find(*).days
  • 级别belongs_to :userbelongs_to :habithas_many :days
  • belongs_to :levelbelongs_to :habit

通过这些关联,您可以使用嵌套属性创建表单。有一个awesome RailCast explaining nested forms

<%= form_for @habit do |habit| %>
  <% 5.times.each_with_index do |number, index| %> 
    <h1>Level <%= index + 1 %></h1>
    <%= habit.fields_for :levels do |level| %>
      <%= level.fields_for :days do |day| %>
        <%= day.label :missed %>
        <%= day.check_box :missed %> <br/>
      <% end %>
    <% end %>
  <% end %>
  <%= habit.submit "submit" %>
<% end %>

&#34;魔法&#34;发生在habits_controller,如下所示:

class HabitsController < ApplicationController
  ...
  def new
    @habit = @user.habits.new
    @level = @habit.levels.new
    3.times { @level.days.build }
  end

  def create
    @habit = @user.habits.new(habit_params)
    @levels = @habit.levels

    if @habit.save
      @habit.evaluate(@user) 
      redirect_to ...
    else
      ...
    end
  end

...
  private

  def habit_params 
    params.require(:habit).permit(
      :user_id,
      levels_attributes:[
      :passed,
      days_attributes:[
      :missed,:level_id]])
  end
...  
end

请注意nested strong params@habit.evalulate(@user)方法(我将在下面显示)和3.times { @level.days.build }调用,它会在您的视图中为嵌套表单构建字段。

habit.evauate(用户)方法: 保存新Habit后调用此方法。评估属性,并将错过的天数和级别的ID分别附加到用户的missed_daysmissed_levels属性。逻辑有点笨拙,因为你要将一个数组附加到另​​一个数组,所以你可能会想出更有效的东西。同时:

  def evaluate(user)
    levels.each { |level| level.evaluate }
    user.missed_levels << levels.where(passed: false).ids 
    user.missed_days << days.where(missed: true).ids 
    user.save
  end

请注意level.evaluate的来电,如下所示:

  def evaluate
    if days.where(missed: true ).count == 3
      update_attributes(passed: false)
    else
      update_attributes(passed: true)
    end
  end

架构如下所示:

  create_table "days", force: true do |t|
    t.integer "level_id"
    t.integer "habit_id"
    t.boolean "missed",   default: false
  end

  create_table "habits", force: true do |t|
    ...
    t.integer "user_id"
    ...
  end

  create_table "levels", force: true do |t|
    t.integer "user_id"
    t.integer "habit_id"
    t.boolean "passed",   default: false
  end

  create_table "users", force: true do |t|
    ...
    t.string   "name"
    t.text     "missed_days" #serialize to Array #serialize to Array in model
    t.text     "missed_levels" #serialize to Array in model
    ...
  end

并且不要忘记将accepts_nested_attributes_for :levels, :days用于习惯模型,并accepts_nested_attributes_for :days 用户 Here is a git with all my code. 让我知道。

答案 1 :(得分:1)

你应该打破这个问题,因为在一个问题中要求很多。 Dimitry_N似乎在正确的轨道上,但你现在需要将一些逻辑添加到关卡模型中。如果您想了解详细信息,请与我聊聊。