Development Tip

Ruby에서 attr_accessor는 무엇입니까?

yourdevel 2020. 9. 27. 14:09
반응형

Ruby에서 attr_accessor는 무엇입니까?


attr_accessorRuby를 이해하는 데 어려움을 겪고 있습니다. 누군가 나에게 이것을 설명 할 수 있습니까?


수업이 있다고 가정 해 보겠습니다 Person.

class Person
end

person = Person.new
person.name # => no method error

분명히 우리는 method를 정의하지 않았습니다 name. 그걸하자.

class Person
  def name
    @name # simply returning an instance variable @name
  end
end

person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error

아하, 우리는 이름을 읽을 수 있지만 이름을 지정할 수 있다는 의미는 아닙니다. 두 가지 방법이 있습니다. 전자를 독자 라고 하고 후자를 작가 라고 합니다. 우리는 아직 작가를 만들지 않았으니 그렇게합시다.

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"

대박. 이제 @namereader와 writer 메서드를 사용하여 인스턴스 변수 쓰고 읽을 수 있습니다 . 제외하고, 이것은 자주 수행됩니다. 왜 매번 이러한 방법을 작성하는 데 시간을 낭비합니까? 우리는 더 쉽게 할 수 있습니다.

class Person
  attr_reader :name
  attr_writer :name
end

이것조차 반복 될 수 있습니다. 독자와 작가를 모두 원할 때 접근자를 사용하십시오!

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Dennis"
person.name # => "Dennis"

같은 방식으로 작동합니다! 그리고 무엇을 추측하십시오. @nameperson 객체 의 인스턴스 변수 는 수동으로 할 때와 같이 설정되므로 다른 방법에서 사용할 수 있습니다.

class Person
  attr_accessor :name

  def greeting
    "Hello #{@name}"
  end
end

person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"

그게 다야. 방법을 이해하기 위해 attr_reader, attr_writer그리고 attr_accessor다른 답변, 책, 루비 문서를, 방법은 실제로 당신을위한 방법을 생성 읽습니다.


attr_accessor단지 메소드 입니다. (링크는 작동 방식에 대한 더 많은 통찰력을 제공해야합니다. 생성 된 메서드 쌍을 살펴보고 자습서에서 사용 방법을 보여줄 것입니다.)

트릭은 즉 class입니다 정의하지 (은 C ++ 및 Java와 같은 언어에서 "단지 정의"입니다) 루비, 그러나 그것은이다 평가하는 표현 . attr_accessor현재 클래스를 수정 하는 메서드가 호출되는 것은 이 평가 동안입니다 . 암시 적 수신자를 기억하십시오 .이 시점에서 "개방형"클래스 객체는 self.attr_accessor어디에 있습니까 self?

attr_accessor그리고 친구에 대한 필요성은 다음 과 같습니다.

  1. Ruby는 Smalltalk와 마찬가지로 해당 객체에 대한 메소드 1 외부에서 인스턴스 변수에 액세스하는 것을 허용하지 않습니다 . 즉, 인스턴스 변수 x.y는 일반적으로 Java 또는 Python과 같은 형식으로 액세스 할 수 없습니다 . 루비 y에서는 항상 보낼 메시지 (또는 "호출 방법")로 간주됩니다. 따라서 attr_*메서드는 @variable동적으로 생성 된 메서드를 통해 인스턴스 액세스를 프록시하는 래퍼를 생성합니다.

  2. Boilerplate 짜증

이것이 약간의 세부 사항을 명확히하기를 바랍니다. 즐거운 코딩입니다.


1 이것은 엄격하게 사실이 아니며 이에 대한 몇 가지 "기술"이 있지만 "공용 인스턴스 변수"액세스에 대한 구문 지원은 없습니다.


attr_accessor(@pst가 언급했듯이) 단지 방법입니다. 그것은 당신을 위해 더 많은 방법을 만드는 것입니다.

이 코드는 다음과 같습니다.

class Foo
  attr_accessor :bar
end

다음 코드와 동일합니다.

class Foo
  def bar
    @bar
  end
  def bar=( new_value )
    @bar = new_value
  end
end

Ruby에서 이런 종류의 메소드를 직접 작성할 수 있습니다.

class Module
  def var( method_name )
    inst_variable_name = "@#{method_name}".to_sym
    define_method method_name do
      instance_variable_get inst_variable_name
    end
    define_method "#{method_name}=" do |new_value|
      instance_variable_set inst_variable_name, new_value
    end
  end
end

class Foo
  var :bar
end

f = Foo.new
p f.bar     #=> nil
f.bar = 42
p f.bar     #=> 42

attr_accessor 매우 간단합니다.

attr_accessor :foo

다음에 대한 바로 가기입니다.

def foo=(val)
  @foo = val
end

def foo
  @foo
end

객체에 대한 getter / setter에 불과합니다.


기본적으로 공개적으로 액세스 할 수있는 데이터 속성을 위조하지만 Ruby에는 없습니다.


인스턴스 변수에 대한 getter 및 setter 메서드를 정의하는 메서드 일뿐입니다. 구현 예는 다음과 같습니다.

def self.attr_accessor(*names)
  names.each do |name|
    define_method(name) {instance_variable_get("@#{name}")} # This is the getter
    define_method("#{name}=") {|arg| instance_variable_set("@#{name}", arg)} # This is the setter
  end
end

코드없이 간단한 설명

위 답변의 대부분은 코드를 사용합니다. 이 설명은 비유 / 이야기를 통해 어떤 것도 사용하지 않고 대답하려고합니다.

외부 당사자는 내부 CIA 비밀에 액세스 할 수 없습니다.

  • 정말 비밀스러운 장소 인 CIA를 상상해 봅시다. CIA 내부 사람들을 제외하고는 CIA에서 무슨 일이 일어나고 있는지 아무도 모릅니다. 즉, 외부 사람들은 CIA의 정보에 액세스 할 수 없습니다. 그러나 완전히 비밀 인 조직을 갖는 것은 좋지 않기 때문에 특정 정보가 외부 세계에 제공됩니다. 물론 CIA가 모든 사람이 알기를 원하는 정보 만 제공됩니다. 예 : CIA 책임자,이 부서가 얼마나 환경 친화적인지 비교 다른 모든 정부 부서 등에 기타 정보 : 예를 들어 이라크 또는 아프가니스탄에서 비밀 요원이 누구인지-이러한 유형의 일들은 향후 150 년 동안 비밀로 남아있을 것입니다.

  • CIA 외부에있는 경우 공개 된 정보에만 액세스 할 수 있습니다. 또는 CIA 용어를 사용하려면 "삭제 된"정보에만 액세스 할 수 있습니다.

  • CIA가 CIA 외부의 일반 대중에게 제공하고자하는 정보를 속성 이라고 합니다.

읽기 및 쓰기 속성의 의미 :

  • CIA의 경우 대부분의 속성은 "읽기 전용"입니다. 즉, 귀하가 CIA 외부 당사자 인 경우 "CIA의 책임자가 누구입니까?"라고 질문 할 수 있습니다 . 그리고 당신은 바로 대답을 얻을 것입니다. 그러나 "읽기 전용"속성으로 수없는 것은 CIA를 변경하는 것입니다. 예를 들어 전화를 걸 수없고 갑자기 Kim Kardashian이 감독이되기를 원하거나 패리스 힐튼이 최고 사령관이되기를 원한다고 결정 합니다.

  • 속성이 "쓰기"액세스 권한을 부여한 경우 외부에 있더라도 원하는 경우 변경할 수 있습니다. 그렇지 않으면, 당신이 할 수있는 유일한 일은 읽는 것입니다.

    즉, 접근자를 사용하면 접근자가 읽기 또는 쓰기 접근 자인지 여부에 따라 외부 사용자를 허용하지 않는 조직에 대해 문의하거나 변경할 수 있습니다.

클래스 내의 개체는 서로 쉽게 액세스 할 수 있습니다.

  • 반면에 이미 CIA 내부에 있었다면 카불에있는 CIA 요원을 쉽게 호출 할 수 있습니다.이 정보는 이미 내부에있는 경우 쉽게 액세스 할 수 있기 때문입니다. 그러나 CIA 외부에 있는 경우 단순히 액세스 권한이 부여되지 않습니다. 사용자가 누구인지 (읽기 액세스) 알 수 없으며 그들의 임무 (쓰기 액세스)를 변경할 수 없습니다.

클래스와 그 안의 변수, 속성 및 메서드에 액세스 할 수있는 능력도 똑같습니다. HTH! 질문이 있으시면 물어보십시오. 내가 명확히 할 수 있기를 바랍니다.


OOP 개념에 익숙하다면 getter 및 setter 방법에 익숙해야합니다. attr_accessor는 Ruby에서 동일한 작업을 수행합니다.

일반적인 방식의 게터 및 세터

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Eshaan'
person.name # => "Eshaan"

세터 방법

def name=(val)
  @name = val
end

게터 방법

def name
  @name
end

Ruby의 Getter 및 Setter 메서드

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"

나는이 문제에 직면했고이 질문에 대해 다소 긴 답을 썼다. 이미 이에 대한 훌륭한 답변이 있지만 더 자세한 설명을 원하는 사람은 내 답변이 도움이되기를 바랍니다.

초기화 방법

Initialize를 사용하면 클래스의 새 인스턴스를 만들 때마다 코드에서 별도의 줄에 데이터를 설정하지 않고 인스턴스를 만들 때 개체의 인스턴스에 데이터를 설정할 수 있습니다.

class Person

  def initialize(name)
    @name = name
  end


  def greeting
    "Hello #{@name}"
  end
end

person = Person.new("Denis")
puts person.greeting

위 코드에서 Initialize의 매개 변수를 통해 Dennis를 전달하여 initialize 메소드를 사용하여 "Denis"라는 이름을 설정합니다. initialize 메소드없이 이름을 설정하려면 다음과 같이 할 수 있습니다.

class Person
  attr_accessor :name

  # def initialize(name)
  #     @name = name
  # end

  def greeting
    "Hello #{name}"
  end
end

person = Person.new
person.name = "Dennis"
puts person.greeting

위 코드에서는 객체 초기화시 값을 설정하는 대신 person.name을 사용하여 attr_accessor setter 메서드를 호출하여 이름을 설정합니다.

이 작업을 수행하는 두 "방법"이 있지만 초기화하면 시간과 코드 줄이 절약됩니다.

이것은 초기화의 유일한 작업입니다. 메서드로 초기화를 호출 할 수 없습니다. 실제로 인스턴스 객체의 값을 얻으려면 getter 및 setter (attr_reader (get), attr_writer (set) 및 attr_accessor (both))를 사용해야합니다. 이에 대한 자세한 내용은 아래를 참조하십시오.

게터, 세터 (attr_reader, attr_writer, attr_accessor)

Getter, attr_reader : getter의 전체 목적은 특정 인스턴스 변수의 값을 반환하는 것입니다. 이에 대한 분석은 아래 샘플 코드를 참조하십시오.

class Item

  def initialize(item_name, quantity)
    @item_name = item_name
    @quantity = quantity
  end

  def item_name
    @item_name
  end

  def quantity
     @quantity
  end
end

example = Item.new("TV",2)
puts example.item_name
puts example.quantity

위의 코드에서 "example"항목의 인스턴스에 대해 "item_name"및 "quantity"메서드를 호출합니다. "puts example.item_name"및 "example.quantity"는 "example"에 전달 된 매개 변수의 값을 반환 (또는 "get")하여 화면에 표시합니다.

다행히 Ruby에는이 코드를보다 간결하게 작성할 수있는 고유 한 방법이 있습니다. attr_reader 메소드. 아래 코드를 참조하십시오.

class Item

attr_reader :item_name, :quantity

  def initialize(item_name, quantity)
    @item_name = item_name
    @quantity = quantity
  end

end

item = Item.new("TV",2)
puts item.item_name
puts item.quantity

이 구문은 정확히 같은 방식으로 작동하지만 6 줄의 코드 만 절약 할 수 있습니다. Item 클래스로 인한 상태가 5 개 더 있다고 상상해보십시오. 코드는 빨리 길어질 것입니다.

Setters, attr_writer : 처음에 setter 메서드를 사용하여 저를 교란시킨 것은 제 눈에는 initialize 메서드와 동일한 기능을 수행하는 것 같았습니다. 아래에서 내 이해를 바탕으로 차이점을 설명합니다.

앞에서 언급했듯이 initialize 메소드를 사용하면 객체 생성시 객체 인스턴스에 대한 값을 설정할 수 있습니다.

하지만 인스턴스가 생성 된 후 나중에 값을 설정하거나 초기화 된 후 변경하려면 어떻게해야합니까? 이것은 setter 메서드를 사용하는 시나리오입니다. 그것이 차이입니다. 처음에 attr_writer 메서드를 사용할 때 특정 상태를 "설정"할 필요가 없습니다.

아래 코드는 setter 메서드를 사용하여 Item 클래스의이 인스턴스에 대해 item_name 값을 선언하는 예입니다. 코드를 직접 테스트하려는 경우에 대비하여 값을 가져 와서 화면에 인쇄 할 수 있도록 계속해서 getter 메서드 attr_reader를 사용합니다.

class Item

attr_reader :item_name

  def item_name=(str)
    @item_name = (str)
  end

end

아래 코드는 attr_writer를 사용하여 코드를 다시 줄이고 시간을 절약하는 예입니다.

class Item

attr_reader :item_name
attr_writer :item_name

end

item = Item.new
puts item.item_name = "TV"

아래 코드는 생성시 item_name의 객체 값을 설정하기 위해 initialize를 사용하는 위의 초기화 예제를 반복 한 것입니다.

class Item

attr_reader :item_name

  def initialize(item_name)
    @item_name = item_name
  end

end

item = Item.new("TV")
puts item.item_name

attr_accessor : attr_reader 및 attr_writer의 기능을 모두 수행하여 코드 한 줄을 더 절약합니다.


새로운 루비 스트 / 프로그래머 (나와 같은)를 혼란스럽게하는 부분은 다음과 같습니다.

"인스턴스에게 주어진 속성 (예 : 이름)이 있다는 것을 알리고 속성 값을 한 번에 모두 제공 할 수없는 이유는 무엇입니까?"

좀 더 일반화되었지만 이것이 나를 위해 클릭 한 방법입니다.

주어진:

class Person
end

우리는 Person 을 그 문제에 대한 이름 이나 다른 속성을 가질 수있는 것으로 정의하지 않았습니다 .

따라서 다음과 같은 경우 :

baby = Person.new

... 그리고 그들에게 이름을 주려고 ...

baby.name = "Ruth"

우리는 얻을 오류를 Rubyland에, 객체의 Person 클래스는하지와 관련된 또는 "이름"을 가질 수있다 뭔가 때문에 아직 ...!

그러나 우리는 주어진 방법 (이전 답변 참조) 중 하나를 사용하여 "Person 클래스 ( baby) 의 인스턴스 는 이제 'name'이라는 속성을 가질 있으므로 구문 적 방법으로 얻을 수 있습니다. 이름을 정했지만 그렇게하는 것이 합리적입니다. "

다시 말하지만, 약간 다르고 더 일반적인 각도에서이 질문을 던졌지 만 이것이이 스레드로가는 길을 찾는 Person 클래스의 다음 인스턴스에 도움이되기를 바랍니다.


간단히 말해서 클래스에 대한 setter와 getter를 정의합니다.

참고

attr_reader :v is equivalant to 
def v
  @v
end

attr_writer :v is equivalant to
def v=(value)
  @v=value
end

그래서

attr_accessor :v which means 
attr_reader :v; attr_writer :v 

클래스에 대한 setter 및 getter를 정의하는 것과 동일합니다.


지정된 속성에 대한 메서드를 간단히 attr-accessor만듭니다.gettersetter


이를 이해하는 또 다른 방법은 .NET을 사용하여 제거하는 오류 코드를 파악하는 것 attr_accessor입니다.

예:

class BankAccount    
  def initialize( account_owner )
    @owner = account_owner
    @balance = 0
  end

  def deposit( amount )
    @balance = @balance + amount
  end

  def withdraw( amount )
    @balance = @balance - amount
  end
end

다음 방법을 사용할 수 있습니다.

$ bankie = BankAccout.new("Iggy")
$ bankie 
$ bankie.deposit(100)
$ bankie.withdraw(5)

다음 메서드는 오류를 발생시킵니다.

$ bankie.owner     #undefined method `owner'... 
$ bankie.balance   #undefined method `balance'...

ownerbalance하지, 기술적이다 방법 ,하지만 속성. BankAccount 클래스에는 def ownerdef balance. 그렇다면 아래 두 가지 명령을 사용할 수 있습니다. 그러나 그 두 가지 방법은 없습니다. 그러나 !! 를 통해 메소드에 액세스 하는 것처럼 속성에 액세스 할 수 있습니다 . 따라서 단어 . 속성. 접근 자. 메소드에 액세스하는 것처럼 속성에 액세스합니다.attr_accessorattr_accessor

추가 attr_accessor :balance, :owner하면 읽고 쓰기 balanceowner"방법"을 사용할 수 있습니다. 이제 마지막 두 가지 방법을 사용할 수 있습니다.

$ bankie.balance
$ bankie.owner

이 모듈의 명명 된 속성을 정의합니다. 여기서 이름은 symbol.id2name이고 인스턴스 변수 (@name) 및이를 읽을 해당 액세스 방법을 만듭니다. 또한 속성을 설정하기 위해 name =이라는 메서드를 만듭니다.

module Mod
  attr_accessor(:one, :two)
end
Mod.instance_methods.sort   #=> [:one, :one=, :two, :two=]

속성 접근자를 요약하기 위해 attr_accessor는 두 가지 무료 메서드를 제공합니다.

Java와 마찬가지로 getter 및 setter라고합니다.

많은 답변이 좋은 예를 보여 주었으므로 간략히 설명하겠습니다.

#the_attribute

# the_attribute =

이전 루비 문서에서 해시 태그 #은 메소드를 의미합니다. 클래스 이름 접두사를 포함 할 수도 있습니다 ... MyClass # my_method


속성 및 접근 자 메서드

속성은 객체 외부에서 액세스 할 수있는 클래스 구성 요소입니다. 다른 많은 프로그래밍 언어에서는 속성으로 알려져 있습니다. 해당 값은 object_name.attribute_name에서와 같이 "점 표기법"을 사용하여 액세스 할 수 있습니다. Python 및 몇 가지 다른 언어와 달리 Ruby는 객체 외부에서 인스턴스 변수에 직접 액세스하는 것을 허용하지 않습니다.

class Car
  def initialize
    @wheels = 4  # This is an instance variable
  end
end

c = Car.new
c.wheels     # Output: NoMethodError: undefined method `wheels' for #<Car:0x00000000d43500>

위의 예에서 c는 Car 클래스의 인스턴스 (객체)입니다. 객체 외부에서 wheels 인스턴스 변수의 값을 읽는 데 실패했습니다. 무슨 일이 있었는지 Ruby가 c 객체 내에서 wheels라는 메서드를 호출하려고 시도했지만 그러한 메서드가 정의되지 않았습니다. 간단히 말해 object_name.attribute_name은 개체 내에서 attribute_name이라는 메서드를 호출하려고합니다. 외부에서 wheels 변수의 값에 액세스하려면 해당 이름으로 인스턴스 메서드를 구현해야합니다.이 메서드는 호출 될 때 해당 변수의 값을 반환합니다. 이를 접근 자 메서드라고합니다. 일반적인 프로그래밍 컨텍스트에서 개체 외부에서 인스턴스 변수에 액세스하는 일반적인 방법은 getter 및 setter 메서드라고도하는 접근 자 메서드를 구현하는 것입니다.

다음 예제에서는 객체 외부에서 wheels 변수에 액세스하기 위해 Car 클래스에 getter 및 setter 메서드를 추가했습니다. 이것은 게터와 세터를 정의하는 "루비 방식"이 아닙니다. getter 및 setter 메서드가 수행하는 작업을 설명하는 역할 만합니다.

class Car
  def wheels  # getter method
    @wheels
  end

  def wheels=(val)  # setter method
    @wheels = val
  end
end

f = Car.new
f.wheels = 4  # The setter method was invoked
f.wheels  # The getter method was invoked
# Output: => 4

위의 예제는 작동하며 유사한 코드는 일반적으로 다른 언어로 getter 및 setter 메서드를 만드는 데 사용됩니다. 그러나 Ruby는이를 수행하는 더 간단한 방법을 제공합니다. attr_reader, attr_writer 및 attr_acessor라는 세 가지 내장 메소드가 있습니다. attr_reader 메소드는 인스턴스 변수를 외부에서 읽을 수 있도록 만들고 attr_writer는 쓰기 가능하게 만들고 attr_acessor는 읽기 및 쓰기 가능하게 만듭니다.

위의 예는 이렇게 다시 작성할 수 있습니다.

class Car
  attr_accessor :wheels
end

f = Car.new
f.wheels = 4
f.wheels  # Output: => 4

위의 예에서 wheels 속성은 객체 외부에서 읽고 쓸 수 있습니다. attr_accessor 대신 attr_reader를 사용했다면 읽기 전용입니다. attr_writer를 사용하면 쓰기 전용입니다. 이 세 가지 메서드는 자체적으로 getter 및 setter가 아니지만 호출되면 getter 및 setter 메서드를 생성합니다. 다른 메서드를 동적으로 (프로그래밍 방식으로) 생성하는 메서드입니다. 이를 메타 프로그래밍이라고합니다.

Ruby의 내장 메서드를 사용하지 않는 첫 번째 (더 긴) 예제는 getter 및 setter 메서드에 추가 코드가 필요한 경우에만 사용해야합니다. 예를 들어, setter 메서드는 인스턴스 변수에 값을 할당하기 전에 데이터의 유효성을 검사하거나 일부 계산을 수행해야 할 수 있습니다.

instance_variable_get 및 instance_variable_set 내장 메소드를 사용하여 객체 외부에서 인스턴스 변수에 액세스 (읽기 및 쓰기) 할 수 있습니다. 그러나 캡슐화를 우회하면 모든 종류의 혼란을 일으키는 경향이 있기 때문에 이것은 거의 정당화 할 수 없으며 일반적으로 나쁜 생각입니다.


나는 루비를 처음 접했고 다음과 같은 이상한 점을 이해해야 만했다. 미래에 다른 사람을 도울 수 있습니다. 결국 위에서 언급했듯이 2 개의 함수 (def myvar, def myvar =)는 모두 @myvar에 액세스하기 위해 암시 적으로 얻지 만 이러한 메서드는 로컬 선언으로 재정의 될 수 있습니다.

class Foo
  attr_accessor 'myvar'
  def initialize
    @myvar = "A"
    myvar = "B"
    puts @myvar # A
    puts myvar # B - myvar declared above overrides myvar method
  end

  def test
    puts @myvar # A
    puts myvar # A - coming from myvar accessor

    myvar = "C" # local myvar overrides accessor
    puts @myvar # A
    puts myvar # C

    send "myvar=", "E" # not running "myvar =", but instead calls setter for @myvar
    puts @myvar # E
    puts myvar # C
  end
end

흠. 많은 좋은 답변. 여기에 몇 센트가 있습니다.

  • attr_accessor반복되는 방법 을 청소 ( DRY-ing ) 하는 데 도움이되는 간단한 방법입니다 .getter and setter

  • 그래서 우리는 setter와 getter에 대해 걱정하지 않고 비즈니스 로직 작성에 더 집중할 수 있습니다.


attr_accessor 의 주요 기능은 다른 파일에서 데이터에 액세스하는 기능입니다.
따라서 일반적으로 attr_reader 또는 attr_writer가 있지만 좋은 소식은 Ruby가이 두 가지를 attr_accessor와 결합 할 수 있다는 것입니다. 좀 더 둥글거나 다재다능하기 때문에 내 방법 이라고 생각합니다 . 또한 Rails에서는 백엔드에서이 작업을 수행하기 때문에 제거되었습니다. 즉, 다른 두 개에 대해 attr_acessor를 사용하는 것이 더 낫습니다. 왜냐하면 특정에 대해 걱정할 필요가 없기 때문에 접근자가 모든 것을 다룹니다. 나는 이것이 일반적인 설명에 가깝다는 것을 알고 있지만 초보자로서 나를 도왔습니다.

이것이 도움이 되었기를 바랍니다!

참고 URL : https://stackoverflow.com/questions/4370960/what-is-attr-accessor-in-ruby

반응형