2014-01-19 46 views
2

我已經完全重寫了這個問題,因爲我覺得這更準確地反映了我第一次想問什麼以一種不太迂迴的方式。如何從一個類方法內部添加一個實例方法,該方法接受要在該實例的上下文中執行的塊

在實例化FormObject之後,對動態定義的方法的調用不會在我嘗試的上下文中評估它們的塊參數。例如:

@registration = RegistrationForm.new 
@registration.user 
# undefined local variable or method `user_params' for RegistrationForm:Class 

RegistrationForm調用一個類的方法exposing(:user) { User.new(user_params) }我想有定義,看起來像這樣的新方法:

def user 
    @user ||= User.new(user_params) 
end 

我實現不使用@ivar ||=緩存值(因爲錯誤的值將導致該方法被重新評估)。我從rspec的memoized_helpers中借鑑了這個想法,我想'我明白它是如何工作的。我不明白的是我應該用lib/form_object/memoized_helpers.rb來代替class_eval

謝謝

的lib/form_object/base.rb

class FormObject::Base 
    include ActiveModel::Model 
    include FormObject::MemoizedHelpers 

    attr_reader :params, :errors 

    def initialize(params = {}) 
    @params = ActionController::Parameters.new(params) 
    @errors = ActiveModel::Errors.new(self) 
    end 

    def save 
    valid? && persist 
    end 
end 

的lib/form_object/memoized_helpers.rb

module FormObject 
    module MemoizedHelpers 
    private 
    def __memoized 
     @__memoized ||= {} 
    end 

    def self.included(mod) 
     mod.extend(ClassMethods) 
    end 

    module ClassMethods 
     def exposing(name, &block) 
     raise "#exposing called without a block" unless block_given? 

     class_eval do 
      define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = block.call } } 
     end 
     end 
    end 
    end 
end 

應用/表格/ registration_form.rb

class RegistrationForm < FormObject::Base 
    exposing(:user) { User.new(user_params) { |u| u.is_admin = true } } 
    exposing(:tenant) { user.build_tenant(tenant_params) } 

    validate do 
    tenant.errors.each do |key, value| 
     errors.add("#{tenant.class.name.underscore}_#{key}", value) 
    end unless tenant.valid? 
    end 

    validate do 
    user.errors.each do |key, value| 
     errors.add("#{user.class.name.underscore}_#{key}", value) 
    end unless user.valid? 
    end 

    private 

    def persist 
    user.save 
    end 

    def user_params 
    params.fetch(:user, {}).permit(:first_name, :last_name, :email, :password, :password_confirmation) 
    end 

    def tenant_params 
    params.fetch(:tenant, {}).permit(:name) 
    end 
end 
+0

麻煩的,我認爲是會在#exposing。我認爲我們需要更多地瞭解它的實施。 –

+0

有人告訴我,我應該把問題中的所有代碼都內聯,而不是分開。儘管如此,我不知道它是多麼恰當。我現在編寫的所有代碼都可以在問題開始處的要點中找到。 https://gist.github.com/frankjmattia/0096f7436a14d95b5ed9 –

+0

外部鏈接往往變差,折騰問題和答案,依靠他們毫無意義。你認爲你可以把它縮減到一個最小的子集來重現問題,並將它發佈到這裏(或者它已經很小了嗎?) –

回答

1

所以,我可能已經簡化了這個例子太多了,但我認爲這是你想要什麼:

module Exposing 
    def exposing(name, &block) 
    instance_eval do 
     define_method(name, block) 
    end 
    end 
end 

class Form 
    extend Exposing 

    exposing(:user) { user_params } 

    def user_params 
    {:hi => 'ho'} 
    end 
end 

Form.new.user 

你可以在這裏反覆折騰:http://repl.it/OCa

+0

這讓我非常接近,幾乎是一個完美的答案。你可以通過在__memoized哈希中緩存塊的結果來記憶創建的方法,或者如果你可以建議一個更簡單的方法嗎? –

+0

我通常只給「幫助自己」的答案。剩下的就是功課,你知道嗎? – phoet

+0

我想這很公平,我通常喜歡這些挑戰。我想在這一點上,有一些基本的東西我沒有看到,但我會繼續與它們混在一起。既然你提供了最可接受的答案,我會將其標記爲這樣。 Thankya。 –

相關問題