如何从rails中的模型调用\ lib中的类方法?

时间:2015-07-07 18:54:29

标签: ruby-on-rails ruby-on-rails-4

我试图为表单创建的角色生成统计信息。用户输入名称,种族,类别,对齐方式,以及是否随机生成统计数据,或者优先级(从最高到最低分配值)。表单完美无瑕,因为我可以在视图中看到输出。

我现在要做的是从模型中的/ lib类调用一个方法来生成统计数据;但是,我一直收到以下错误(我无法发布图片):

NoMethodError in CharactersController#create 


undefined method `[]' for nil:NilClass


Extracted source (around line #14): 


12  before_save do
13      generate_stats
14      self.strength = @character_stats[:strength]
15      self.dexterity = @character_stats[:dexterity]
16      self.constitution = @character_stats[:constitution]
17      self.intelligence = @character_stats[:intelligence]

以下是我的部分代码的副本:

在controllers \ characters_controller.rb

class CharactersController < ApplicationController
    def create
        @character = Character.new(character_info_params)
        @character.name = params[:character][:name].capitalize

        @character.alignment = "#{params[:character][:alignment_lr]} #{params[:character][:alignment_ud]}"


        if @character.save
            redirect_to @character
        else
            render 'new'
        end
    end
    private

    def character_info_params
        params.require(:character).permit(:name, :race, :class_, :alignment)
    end
end

在models \ character.rb

class Character < ActiveRecord::Base
    require 'random_stats_generator'    
    attr_accessor :rand_stat_gen

    def generate_stats
        if @rand_stat_gen == true
            @character_stats_inst = RandomStatGenerator.new
            @character_stats = @character_stats_inst.generate
        end
    end

    before_save do
        generate_stats
        self.strength = @character_stats[:strength]
        self.dexterity = @character_stats[:dexterity]
        self.constitution = @character_stats[:constitution]
        self.intelligence = @character_stats[:intelligence]
        self.wisdom = @character_stats[:wisdom]
        self.charisma = @character_stats[:charisma]
    end

#validation passed this point

end

在initializers \ stat_builders.rb

require "./lib/random_stat_generator.rb"

在lib / random_stat_generator.rb

class RandomStatGenerator

    def initialize
        @strength = :strength
        @dexterity = :dexterity
        @constitution = :constitution
        @intelligence = :intelligence
        @wisdom = :wisdom
        @charisma = :charisma
        @character_stats = HashWithIndifferentAccess.new()
    end

    def self.generate
        roll_stats
    end

    def roll(stat)
        @roll_value_1 = (1 + (rand(6)))
        @roll_value_2 = (1 + (rand(6)))
        @roll_value_3 = (1 + (rand(6)))
        @roll_value_4 = (1 + (rand(6)))
        @roll_array = [@roll_value_1,@roll_value_2,@roll_value_3,@roll_value_4]
        @roll_array = @roll_array.sort_by {|x| x }
        @roll_array = @roll_array.reverse
        stat = @roll_array[0] + @roll_array[1] + @roll_array[2]
    end

    def roll_stats
        @strength = roll(@strength)
        @dexterity = roll(@dexterity)
        @constitution = roll(@constitution)
        @intelligence = roll(@intelligence)
        @wisdom = roll(@wisdom)
        @charisma = roll(@charisma)

        @character_stats[:strength] = @strength
        @character_stats[:dexterity] = @dexterity
        @character_stats[:constitution] = @constitution
        @character_stats[:intelligence] = @intelligence
        @character_stats[:wisdom] = @wisdom
        @character_stats[:charisma] = @charisma

        return @character_stats
    end
end

对我来说,看起来该方法不会返回任何内容,或者根本没有被调用。

我尝试过很多我在网上遇到过的解决方案,但都没有。可能有些事情从这些解决方案中遗留下来并不合理。我只是刚刚开始使用rails,所以我仍然试图习惯一切。

非常感谢你的帮助。

2 个答案:

答案 0 :(得分:1)

我想为您的图书馆建议以下组织

# Use a module at top level
module RandomStatGenerator

    STATS = [:strength, :dexterity, :constitution, :intelligence, :wisdom, :charisma]

    # Use a class Stats if you need to but I don't see why...
    class Stats
        def initialize
            RandomStatGenerator::STATS.each do |stat|
                # Below line will do @stat = :stat
                instance_variable_set("@#{stat.to_s}", stat)
            @character_stats = HashWithIndifferentAccess.new()
        end

        def roll_stats
            @character_stats = RandomStatGenerator.roll_stats
        end
    end

    module_function
    # below lines will be considered as module functions 
    # => call RandomStatGenerator.function_name

    def roll
        roll_value_1 = (1 + (rand(6)))
        roll_value_2 = (1 + (rand(6)))
        roll_value_3 = (1 + (rand(6)))
        roll_value_4 = (1 + (rand(6)))
        roll_array = [roll_value_1,roll_value_2,roll_value_3,roll_value_4]
        roll_array = roll_array.sort_by {|x| x }
        roll_array = roll_array.reverse
        roll_array[0] + roll_array[1] + roll_array[2]
    end

    def roll_stats
        character_stats = {}
        STATS.each do |stat|
            character_stats[stat] = RandomStatGenerator.roll
        end

        return character_stats
    end
end

然后在你的character.rb

def generate_stats
    @character_stats = RandomStatGenerator.roll_stats
end

答案 1 :(得分:1)

Ruby拥有非常强大的功能来处理哈希和数组。 输入重复的作业,例如:

self.strength = @character_stats[:strength]
self.dexterity = @character_stats[:dexterity]
self.constitution = @character_stats[:constitution]

非常沉闷。因此,我们可以简单地重写方法来传递哈希值。

class RandomStatGenerator

  # This is just a constant containing all the stats we want to generate.
  STATS = [:strength, :dexterity, :constitution, :intelligence, :wisdom, :charisma]

  # Create a hash with random roll values for each stat
  def self.roll_stats
    # This is kind of scary looking but actually just creates an 
    # hash from an array of keys
    Hash[STATS.map {|k| [k, self.roll ] } ]
  end

  private

  def self.roll
    # Create an array with 4 elements (nil) 
    ary = Array.new(4)
    # We then replace the nil value with a random value 1-6
    ary = ary.map do 
      (1 + (rand(6)))
    end
    # sort it and drop the lowest roll. return the sum of all rolls.
    ary.sort.drop(1).sum

    # a ruby ninja writes it like this
    Array.new(4).map { 1 + rand(6) }.sort.drop(1).sum
  end
end

输出:

irb(main):032:0> RandomStatGenerator.roll_stats
=> {:strength=>14, :dexterity=>14, :constitution=>14, :intelligence=>13, :wisdom=>10, :charisma=>9}

但是如果你不打算真正创建一个类的实例,那么你应该使用一个模块。

可以使用哈希创建Rails模型,也可以使用哈希值替换其值:

Character.new(RandomStatGenerator.roll_stats)
@character.assign_attributes(RandomStatGenerator.roll_stats)

因此我们可以在Character#generate_stats中使用它:

def generate_stats
  assign_attributes(RandomStatGenerator.roll_stats)
end

你应该使用具有极端偏见的ActiveModel回调。在您的应用程序中以及何时在模型生命周期中进行规范通常是一项挑战。由于before_save在验证后运行,因此任何验证(例如validates_presence_of :constitution)都将失败。

在您的情况下,最好在控制器中执行此操作或使用:

before_validation :generate_stats, if: -> { new_record? && @rand_stat_gen }