2016-02-04 3 views
0

Я новичок в Ruby, я родом из PHP и не может понять код из образца:Как наследовать класс ш свойства в рубин

module Twitter 
    class API < Grape::API 
    version 'v1', using: :header, vendor: 'twitter' 
    format :json 
    prefix :api 

Example from here

В принципе, мне нужно, чтобы наследовать этот класс следующим образом:

class MyAPI < Twitter::API 

но в этом классе MyAPI format, prefix и version не работает, и я не могу понять, почему и не один ману al или учебник не отвечает на мой вопрос.

Например, format устанавливает для api результат вывода в формате json. В классе Twitter :: API он работает хорошо, но в дочернем случае он просто не применяется. Поэтому мне нужно написать это на каждом дочернем классе, что плохо.

version и format на самом деле есть? Это переменные (свойства класса) или вызовы методов родительского класса?

Я предполагал, что это вызовы и попробовать в Twitter :: API что-то вроде:

def initialize 
    format :json 
end 

Но получить ошибку:

TypeError: no implicit conversion of Symbol into String

или

def initialize 
    self.format :json 
end 

NoMethodError: private method `format' called for #<MyAPI>

Пожалуйста, будьте как можно подробнее. Также вы можете указать мне документацию, где она была объяснена?

+3

Это вызовы методов, которые, вероятно, устанавливают некоторые атрибуты. Что вы подразумеваете под «в том, что класс« MyAPI »' format', 'prefix' и' version' не работает?? Они не приносят никакой ошибки, они совершенно юридический код. Продемонстрируйте, как именно они не работают (с кодом и сообщением об ошибке или нежелательным поведением, проявленным этим кодом). – Amadan

+1

Parenthesis являются необязательными, поэтому 'format (: json)' такой же, как 'format: json'. –

+0

@Amadan Например, 'format' устанавливает для api вывод результата в формате json. В классе Twitter :: API он работает хорошо, но в дочернем случае он просто не применяется. – Yaroslav

ответ

2

format что-то вроде

class API 
    private_class_method def self.format(kind) 
    #... 
    end 
end 

частных методов в Ruby, не может быть вызван с явным приемником (с точкой перед ними), только неявные один (отсылка к текущему значению self). Внутри определения класса self определяется классом. Именно поэтому вы можете написать

def self.format(...) 

вместо

def API.format(...) 

Таким образом, в вашем коде,

class MyAPI < API 
    format :json 
    # ... 
end 

это вызов метода format на объект класса MyAPI, в силу наследование от API, как класс определяется. Если посмотреть на источник Grape, format будет (в конечном итоге) установить значение в переменной экземпляра. Давайте упростим его до @format (на самом деле это не так, и оно унаследовано, а не на API, но ... упрощение для примера). Давайте также сделаем другой метод, чтобы увидеть, что такое значение переменной экземпляра.

class API 
    private_class_method def self.format(what) 
    @format = what 
    end 

    def self.peek_format 
    @format 
    end 
end 

Теперь давайте создадим подкласс:

class SubAPI < API 
end 

Теперь, чтобы установить формат, мы хотели бы использовать что-то вроде API.format(:json), но мы не можем, потому что это личное. Таким образом, мы произведем контекст, в котором format(:json) естественно переходит к API:

class API 
    format :json 
end 
API.peek_format 
# => :json 

Формат устанавливается. Все выглядит хорошо. Теперь давайте посмотрим, что происходит с подклассом:

class SubAPI 
    format :txt 
end 
SubAPI.peek_format 
# => :txt 

Но

API.peek_format 
# => :json 

методы наследуются; переменные экземпляра нет. Каждый экземпляр класса (т. Е. Объект типа Class) имеет свой собственный набор переменных экземпляра, так же как каждый другой объект имеет свой собственный набор переменных экземпляра, не разделяемый с другими объектами одного и того же класса.

Если вы действительно хотите каждый подкласс, чтобы сделать то же инициализации, вы можете сделать что-то вроде этого (хотя это не лучшая идея):

class API 
    private_class_method def self.format(what) 
    @format = what 
    end 

    def self.peek_format 
    @format 
    end 

    # common initialisation 
    private_class_method def self.initialize_class 
    format :json 
    end 

    # whenever we get a new subclass, run the initialisation 
    private_class_method def self.inherited(subclass) 
    subclass.instance_eval do 
     initialize_class 
    end 
    end 

    # now initialise this class too 
    initialize_class 
end 

class SubAPI < API 
end 

API.peek_format 
# => :json 
SubAPI.peek_format 
# => :json 

Но я призываю вас против этого. Если вы используете MyAPI, то вы, вероятно, не используете API; вам не нужно устанавливать в нем формат (или другие параметры).

+0

Благодарим вас за разъяснение. Теперь стало ясно. Хотя это не решение моей проблемы, я точно принимаю ее как правильный ответ, потому что это поможет мне реализовать то, что я точно хочу. Я делаю что-то вроде шаблона ApplicationController. Не только «формат» дела. Мне нужно сделать некоторые auth и другие вещи, общие для всех моих «контроллеров» (например, выходной формат) – Yaroslav

Смежные вопросы