2010-03-08 4 views
0

질문을 사용자 정의 속성을 추가 :이 클래스를 기존 방법을 사용자 정의을 추가 할 수 있지만, 당신은 어떻게 정의 속성을 추가하려면 간단 루비를 사용 ? 여기에 내가 뭘하려고 오전의 예입니다루비 : 내장 클래스에

myarray = Array.new(); 
myarray.concat([1,2,3]); 
myarray._meta_ = Hash.new();  # obviously, this wont work 
myarray._meta_['createdby'] = 'dreftymac'; 
myarray._meta_['lastupdate'] = '1993-12-12'; 

## desired result 
puts myarray._meta_['createdby']; #=> 'dreftymac' 
puts myarray.inspect()   #=> [1,2,3] 

목표는 예상대로 예에서 작동하지 않는 물건 위의 작동 방식으로 클래스 정의를 구성하는 것입니다.

업데이트 : 원래의 질문에서 제외 된 한 측면 (질문을 명확히) : 또한 통상 의 설정은 클래스의 방법을 초기화 할 것 "기본값"을 추가하는 목표입니다 .

업데이트 : (이유는 무엇입니까?) 일반적으로 Array (또는 에뮬레이트하려는 내장 클래스)를 상속하는 사용자 지정 클래스를 만드는 것은 매우 간단합니다. 이 질문은 일부 "테스트 전용"코드에서 파생되었으며이 일반적으로 허용되는 접근 방식을 무시하려는 시도가 아닙니다.

+0

단순히 Array에서 상속받은 클래스를 만들고 원하는 기능을 추가 할 수없는 이유가 있습니까? –

+0

이것이 바람직한 방법이지만,이 질문은 테스트 목적으로 만 실험 코드와 관련이 있습니다 ... 질문을 업데이트 할 것입니다. – dreftymac

+0

이미 내 답변을 수락 했음에도 추가 한 속성의 기본값을 설정하는 방법에 대한 설명이 추가되었습니다 (질문 업데이트 당). –

답변

1

루비에서는 인스턴스 외부의 속성 (인스턴스 변수)에 액세스 할 수 없다는 것을 상기하십시오. 인스턴스의 public 메소드에만 액세스 할 수 있습니다.

당신은 당신이 설명하는대로 속성 역할을하는 클래스의 방법을 만들 attr_accessor를 사용할 수 있습니다

irb(main):001:0> class Array 
irb(main):002:1> attr_accessor :_meta_ 
irb(main):003:1> end 
=> nil 
irb(main):004:0> 
irb(main):005:0* x = [1,2,3] 
=> [1, 2, 3] 
irb(main):006:0> x._meta_ = Hash.new 
=> {} 
irb(main):007:0> x._meta_[:key] = 'value' 
=> "value" 
irb(main):008:0> 

를 접근에 대한 기본 초기화를 할 수있는 간단한 방법을 위해, 우리는 기본적으로해야합니다 reimplement attr_accessor ourselves :

class Class 
    def attr_accessor_with_default accessor, default_value 
    define_method(accessor) do 
     name = "@#{accessor}" 
     instance_variable_set(name, default_value) unless instance_variable_defined?(name) 
     instance_variable_get(name) 
    end 

    define_method("#{accessor}=") do |val| 
     instance_variable_set("@#{accessor}", val) 
    end 
    end 
end 

class Array 
    attr_accessor_with_default :_meta_, {} 
end 

x = [1,2,3] 
x._meta_[:key] = 'value' 
p x._meta_ 

y = [4,5,6] 
y._meta_[:foo] = 'bar' 
p y._meta_ 

기다려라! 출력이 올바르지 않습니다.

{:key=>"value"} 
{:foo=>"bar", :key=>"value"} 

리터럴 해시의 기본값을 기준으로 클로저를 만들었습니다. 같은 문자 해시 때마다 재사용에 반대 Hash.new가 기본 값이 검색 될 때마다 호출되기 때문에

class Class 
    def attr_accessor_with_default accessor, &default_value_block 
    define_method(accessor) do 
     name = "@#{accessor}" 
     instance_variable_set(name, default_value_block.call) unless instance_variable_defined?(name) 
     instance_variable_get(name) 
    end 

    define_method("#{accessor}=") do |val| 
     instance_variable_set("@#{accessor}", val) 
    end 
    end 
end 

class Array 
    attr_accessor_with_default :_meta_ do Hash.new end 
end 

x = [1,2,3] 
x._meta_[:key] = 'value' 
p x._meta_ 

y = [4,5,6] 
y._meta_[:foo] = 'bar' 
p y._meta_ 

이제 출력이 올바른지 :

더 좋은 방법은 단순히 블록을 사용할 수 있습니다.

{:key=>"value"} 
{:foo=>"bar"} 
+0

예, 작동합니다. 답장을 보내 주셔서 감사합니다. 질문의 원래 편집에서 제외 된 한 가지는 : * initialize * 메소드에서 일반적으로 설정 될 값을 초기화하는 방법입니다. 이것은 나를 위해 일하는 것 같지 않은 측면이었다. – dreftymac

2

재산은 단지 취득자이자 세터가 아닙니다. 그렇다면, 당신은 할 수 없었 :

class Array 
    # Define the setter 
    def _meta_=(value) 
    @_meta_ = value 
    end 

    # Define the getter 
    def _meta_ 
    @_meta_ 
    end 
end 

그런 다음, 당신이 할 수 있습니다

x = Array.new 
x._meta_ 
# => nil 

x._meta_ = {:name => 'Bob'} 

x._meta_ 
# => {:name => 'Bob'} 

가 도움이됩니까?

+0

예, 작동합니다. 답장을 보내 주셔서 감사합니다. 질문의 원래 편집에서 제외 된 한 가지는 : * initialize * 메소드에서 일반적으로 설정 될 값을 초기화하는 방법입니다. 이것은 나를 위해 일하는 것 같지 않은 측면이었다. – dreftymac