Работа с отсутствием контекста в методах маршрута Синатры

У меня возникла проблема с отсутствующими экземплярами и ошибками nilClass при вызове моих маршрутов. После изучения исходного кода кажется, что вызов generate_method в основном создает новый метод, используя блок исходного метода.

get "/" do
    @some_local_instance.do_something()
end

Таким образом, в приведенном выше методе вполне может быть локальная переменная в этом классе с именем some_local_instance, однако, когда заучивание фактически оценивается, у него нет контекста относительно того, где был определен метод, поэтому он завершится ошибкой.

Причина, по которой я спрашиваю, заключается в том, что как часть моего сценария у меня есть внешние классы, которые загружаются при загрузке Sinatra, которые регистрируют маршруты, и когда эти маршруты вызываются, мне нужно получить доступ к некоторым локальным переменным в этих классах. Примером может быть:

class SomeRouteClass
    def initialize(sinatra, calculator)
        @calculator = calculator
        @sinatra = sinatra
    end

    def setup_routes
        @sinatra.get "/add" do
            return @calculator.add(1,1)
        end
    end
end

class Calculator
    def add(a,b)
        return a+b;
    end
end

sinatra = Sinatra.new
calculator = Calculator.new

routing_class = SomeRouteClass.new(sinatra, calculator)
routing_class.setup_routes

sinatra.run!

Простите любые орфографические/синтаксические ошибки, это всего лишь быстрый пример, но, как вы можете видеть, класс регистрирует маршруты, и когда этот маршрут попадает, возвращается некоторое значение, сгенерированное экземпляром калькулятора, который он взял при создании экземпляра.

Моя проблема заключается в том, что в этом примере, когда я пытаюсь запустить маршрут /add, он сообщает мне, что @calculator является nilClass, и я считаю, что это связано с тем, что Sinatra просто берет блок кода без контекста. Это кажется подходящим для любого простого рендеринга шаблонов, но если вам нужно сделать что-то более причудливое или вы хотите, чтобы ваш код был модульным, не используя статику и синглтоны, у вас, похоже, нет никакого способа обойти это...

Здесь мои предположения верны? и если да, то есть ли способ сохранить контекст, поскольку мне кажется, что он заставляет меня писать плохой и сложный в обслуживании код, если мне приходится писать все как статику и синглтоны для взаимодействия с маршрутом.

== Изменить ==

Реструктурировал вопрос и содержание, чтобы более точно отразить реальную проблему, теперь, когда у меня есть более четкое представление о библиотеке.


person Grofit    schedule 22.11.2011    source источник
comment
Просмотрев исходный код еще немного, кажется, что он просто копирует и вставляет метод, который используется с маршрутом, в новый метод, использующий define_method. Таким образом, вне этого метода нет контекста... что означает, что единственный способ обойти это - сделать все статическим, что просто кажется неправильным... так как его сложнее изменить и протестировать таким образом... Я мог бы быть полностью ошибаюсь в том, что он делает, хотя Ruby для меня все еще новый язык.   -  person Grofit    schedule 22.11.2011
comment
Пытались использовать шаблон объекта const, чтобы обойти это, но это все еще делает вещи ДЕЙСТВИТЕЛЬНО неприятными, поскольку ВСЕ должно быть статическим, чтобы это работало... есть ли причина, по которой они копируют тело метода в новый метод, поэтому теряется весь контекст? поскольку я не вижу в этом никакой пользы, только недостатки ... хотя, опять же, я новичок в этом, поэтому, возможно, не полностью понимаю некоторые сложности, которые требуют этого. Единственное, о чем я могу думать, это то, что объект тела метода может быть очищен или метод может быть обновлен или удален из экземпляра до его вызова, однако это маловероятно.   -  person Grofit    schedule 25.11.2011


Ответы (3)


Я могу не принять этот ответ, но после дополнительных исследований может оказаться, что на динамическом языке, таком как Ruby, статические классы не такие кошмарные с точки зрения обслуживания.

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

В любом случае, кажется на данный момент, ответ заключается в том, чтобы просто сделать константу для всего, что вам нужно выставить на маршрут, а затем, когда вы тестируете, просто установите для этой константы макет. Все еще кажется немного безумным называть эти вещи константами, хотя на самом деле они не являются константами в истинном смысле этого слова, поскольку их можно изменить в любое время... как и многие другие вещи для новых разработчиков ruby, это просто кажется запутанным ради it (т.е. elsif, @@blah, регистр переменной, определяющий, является ли она постоянной или нет).

Как я уже сказал, я не приму этот ответ, если кто-то другой может показать мне лучший способ сделать это, но на данный момент дам ему еще несколько дней.

person Grofit    schedule 24.11.2011
comment
Похоже, что с помощью помощника вы можете поместить туда часть логики, но все равно это не очень приятно. Я смотрю на Падрино, так как он, по крайней мере, предоставляет контроллер и помощник контроллера, судя по всему, лучше инкапсулирует ваши данные и логику, но все это просто построено поверх Sinatra, поэтому должна быть возможность сделать это внутри сборка Sinatra по умолчанию. - person Grofit; 05.12.2011

Блок, переданный get, оценивается в другом контексте, чем объект Calculator. Синатра, вероятно, звонит instance_eval или одному из его кузенов. Однако должна быть возможность захватить локальные переменные из окружающей области, используя что-то вроде следующего (увы, непроверенного) подхода:

def setup_routes
    calculator = @calculator
    @sinatra.get "/add" do
        return calculator.add(1,1)
    end
end
person Erin Dees    schedule 07.01.2012

person    schedule
comment
Вызывает исключение: неопределенная локальная переменная или метод `calculator' для #‹#‹Class:0x2b330e0›:0x29c9a60›, я вижу, что вы там пытаетесь сделать, но это все равно кажется странным. Точно так же, когда вы регистрируете маршрут, вы говорите, что PATTERN-X вызывает METHOD-Y, но неужели Синатра просто выдергивает метод и упаковывает класс вокруг него? так что у него больше нет контекста для области вне этого метода? - person Grofit; 22.11.2011