Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Разбор функции, что делает, в чем суть, функция def set, sinatra gem 
:(
    Опции темы
fell
Дата 17.7.2010, 16:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 41
Регистрация: 17.12.2008
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Добрый день
Изучаю ror и ruby потихоньку и недавно натолкнулся на такой вот код. Конкретно отсюда
ссылка. Sinatra microframework. Если еще конкретней, то эта функция используется в Padrino framework. Уже вторая строчка поставила в тупик. По идее функция работать не должна, но работает. В данном случае self должен быть объектом класса, метод вызывается в классе class Admin. Если говорить в общем и целом функция должна брать метод, который определяется символом, и параметры, но дело в том, что метод login_page не принимает параметры.
Кажется все очень сложно и навернуто.... 

вызов функции выглядит так:
set :login_page "app/controller/new"

сама функция

def set(option, value=self, &block)
       raise ArgumentError if block && value != self  
#  Если блок =nil и  value не self, тогда ArgumentError и выходим из функции, но в вызове функции блока нет, есть option =:login_page
 и value ="app/controller/new"

       value = block if block
       if value.kind_of?(Proc)
         metadef(option, &value)
         metadef("#{option}?") { !!__send__(option) }
         metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }
       elsif value == self && option.respond_to?(:to_hash)
         option.to_hash.each { |k,v| set(k, v) }
       elsif respond_to?("#{option}=")
         __send__ "#{option}=", value
       else
         set option, Proc.new{value}
       end
       self
     end
# в этом методе создается синглетон класс, и в нем определяется метод. Название метода = message, а тело метода &block
def metadef(message, &block)
       (class << self; self; end).
         send :define_method, message, &block
     end


Это сообщение отредактировал(а) fell - 17.7.2010, 16:32
PM MAIL WWW   Вверх
source777
Дата 17.7.2010, 19:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1878
Регистрация: 12.3.2007

Репутация: 6
Всего: 56



fell, это конечно очень занятно, но в чём вопрос то?

Цитата(fell @  17.7.2010,  16:30 Найти цитируемый пост)

raise ArgumentError if block && value != self  
# Если блок =nil и  value не self, тогда ArgumentError и выходим из функции

Уже неверный пересказ кода своими словами... Исключение будет как раз в противоположной ситуации - только если блок передан и value не self. А раз блок не передан, то исключения эта строка сгенерировать не может.

Цитата(fell @  17.7.2010,  16:30 Найти цитируемый пост)

но в вызове функции блока нет, есть option =:login_page и value ="app/controller/new"[

Ну правильно, именно поэтому value оборачивается в простейший блок с помощью строки 
Цитата

set option, Proc.new{value}

после чего на следующем вызове set будет value.kind_of?(Proc) == true.

И в результате имеем 3 метода:
Код

def login_page
  "app/controller/new"
end

def login_page?
  !!login_page
end

def login_page=(val)
   metadef(:login_page, &Proc.new{val})
end


Ясно?


--------------------
Если бы программистам платили за то, чтобы убирать код из программы вместо того, чтобы добавлять его, программы были бы намного лучше © Николас Негропонте
PM MAIL   Вверх
fell
Дата 17.7.2010, 20:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 41
Регистрация: 17.12.2008
Где: Санкт-Петербург

Репутация: нет
Всего: нет



да видимо чего то попутал

raise ArgumentError if block && value != self

если рассмотреть все случаи

raise ArgumentError if true # true =  true && true, если true&&false = false, false&&true=false - block = nil -> false, значит общее false 

дальше 
value = block if block # поскольку блок =nil, то присвоения не будет

if value.kind_of?(Proc)
пропускаем ибо оно не Proc

elsif value == self # value не self, пропускаем

elsif respond_to?("#{option}=") # тоже нет ибо нету option=

set option, Proc.new{value} # set :login_page, Proc.new{"app/controller/new"}

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

потом выполняется этот кусок

if value.kind_of?(Proc)
         metadef(option, &value)
         metadef("#{option}?") { !!__send__(option) }
         metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }

и в последней строке возвращается self
а self у нас = Admin

то есть функция возвращает класс ? объект типа класс, с именем Admin, наверно так правильней smile

а зачем эти методы отправлять в синглетон класс ?  в этой обертке, чтобы сделать их классовыми методами ?
def metadef(message, &block)
       (class << self; self; end).
         send :define_method, message, &block
     end

и последнее
!!login_page

какое то двойное отрицание ?это ! я знаю не, а там получается не, не ? !true=false -> !false =true, тогда какой смысл ..



Это сообщение отредактировал(а) fell - 17.7.2010, 20:56
PM MAIL WWW   Вверх
source777
Дата 17.7.2010, 21:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1878
Регистрация: 12.3.2007

Репутация: 6
Всего: 56



Цитата(fell @  17.7.2010,  20:36 Найти цитируемый пост)
то есть функция возвращает класс ? объект типа класс, с именем Admin, наверно так правильней

И что с того? Хотя правильнее сказать, что функция возвращает self, а что это будет по факту будет зависеть от контекста её вызова.

Цитата(fell @  17.7.2010,  20:36 Найти цитируемый пост)
!!login_page
какое то двойное отрицание ?это ! я знаю не, а там получается не, не ? !true=false -> !false =true, тогда какой смысл .. 

Смысл в том, что на выходе получиться значение булева типа, как правило такой приём используется в библиотечном коде для перестраховки, мало ли как этот код будут применять конечные пользователи, например
Код

if login_page? == true
  # если бы не двойное отрицание, то до сюда бы дело никогда не дошло, т.к
  # ("app/controller/new" == true)  # => false 
  # как в впрочем и
  # ("app/controller/new" == false) # => false
end


Проще говоря, метод с ? на конце должен возвращать либо true, либо false и ничего другого. Таково соглашение.


Цитата(fell @  17.7.2010,  20:36 Найти цитируемый пост)
а зачем эти методы отправлять в синглетон класс ?

Какой такой синглетон класс? Синглетон - это вообще паттерн проектирования и он тут абсолютно не причём. 
Просто такой синтаксис, чтобы динамически создать методы класса, а не методы объекта, как это было бы в случае с self.send ... 
Т.е. полный эквивалент этому динамическому коду будет выглядеть примерно так:

Код

class Admin
  class << self
      def login_page
        "app/controller/new"
      end

      def login_page?
        !!login_page
      end

      def login_page=(val)
         metadef(:login_page, &Proc.new{val})
      end
  end
end




--------------------
Если бы программистам платили за то, чтобы убирать код из программы вместо того, чтобы добавлять его, программы были бы намного лучше © Николас Негропонте
PM MAIL   Вверх
fell
Дата 17.7.2010, 23:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 41
Регистрация: 17.12.2008
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Цитата

Какой такой синглетон класс

здрасти приехали, почитайте Dave Thomas, Programming ruby, страница 387, singleton class, ghost class, Eigenclasses, анонимный класс, который создается в цепочке классов. 

class  <<  obj
   body
end

an anonymous (singleton) class is associated with the specific object

Все тоже самое 

Код

class Admin
  class << self
      def login_page
        "app/controller/new"
      end
      def login_page?
        !!login_page
      end
      def login_page=(val)
         metadef(:login_page, &Proc.new{val})
      end
  end
end


можно было переписать так

Код

class Admin
      def self.login_page
        "app/controller/new"
      end
      def self.login_page?
        !!login_page
      end
      def self.login_page=(val)
         metadef(:login_page, &Proc.new{val})
      end
end


Dave Thomas рекомендует пользоваться именно таким способом. 

Цитата

И что с того? Хотя правильнее сказать, что функция возвращает self, а что это будет по факту будет зависеть от контекста её вызова.


в данном случае метод set вызывается в классе Admin, я об этом писал уже, поэтому self = Admin

login_page? -  в чем смысл этой функции не очень понятно. 
Пока я нашел только одно объяснение. Если в функции есть строка, то !! вернет true, а если нет там ничего, то !! вернет false. То есть можно проверить возвращает что то функция или нет.

Это сообщение отредактировал(а) fell - 18.7.2010, 11:42
PM MAIL WWW   Вверх
source777
Дата 19.7.2010, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1878
Регистрация: 12.3.2007

Репутация: 6
Всего: 56



Цитата(fell @  17.7.2010,  23:11 Найти цитируемый пост)
class  <<  obj
   body
end

an anonymous (singleton) class is associated with the specific object

Для данного действия есть устоявшийся термин - примесь (mix in). В случае когда примесь осуществляется в объект, действительно создаётся анонимный класс (хотя это лишь детали реализации, что не даёт права называть примесь анонимным классом), который в качестве побочного эффекта, можно с натяжкой сказать, довольно своеобразно реализует паттерн синглетон, да и то хреновато: раз можно создать ещё один независимый объект, вызвав obj.clone, у которого будет тот же набор примесей, то реализация паттерна получается косячная. 
Однако в твоём случае примесь осуществляется вообще в класс, а не в объект. Так что учи мат.часть внимательнее и желательно опираясь на несколько источников, а не на одного Томаса.


Цитата(fell @  17.7.2010,  23:11 Найти цитируемый пост)
Все тоже самое 
...
можно было переписать так
...
Dave Thomas рекомендует пользоваться именно таким способом. 


Нельзя, такая запись возможна только в статическом варианте, для мета-кода нужна именно та, которую я указал. Поэтому именно она и будет эквивалентна выполнению мета-кода. А раз уж ты записался в пиарщики к Dave Thomas, то не забывай писать заодно почему он что-то рекомендует и в чём разница, кроме как в синтаксисе.


Цитата(fell @  17.7.2010,  23:11 Найти цитируемый пост)
Если в функции есть строка, то !! вернет true, а если нет там ничего, то !! вернет false. То есть можно проверить возвращает что то функция или нет.

И что по этому поводу думает Dave Thomas?  smile 


Это сообщение отредактировал(а) source777 - 19.7.2010, 20:48


--------------------
Если бы программистам платили за то, чтобы убирать код из программы вместо того, чтобы добавлять его, программы были бы намного лучше © Николас Негропонте
PM MAIL   Вверх
fell
Дата 20.7.2010, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 41
Регистрация: 17.12.2008
Где: Санкт-Петербург

Репутация: нет
Всего: нет



Цитата

И что по этому поводу думает Dave Thomas?


это вряд ли я узнаю, на этом форуме он явно не отметится. Хотелось бы услышать твое мнение все же... и подтвердить мою оценку или опровергнуть или что то добавить. 
Что касается синглетон класса, наверно лучше все же говорить о метаклассе. Мат часть я изучил хорошо и как раз вчера все стало на свои места. Mixin вызывается через методы include extend так что , 
Код

class << obj
   body
end

никак не mixin. Эта запись как раз и означает вызов(конструирование) метакласса объекта. 
Видимо да, для динамического создания методов класса, лучше использовать метакласс. Может и есть какие то другие варианты, но пока не соображу какие. 
Одна из причин использования self.method описана опять же у Dave Thomas в его скринкастах о метапрограммировании. Объявление большого кол-ва методов класса через class << obj затрудняет понимание и разбор кода. И да, кстати, 90% использующих этот способ, думают что это просто синтаксический сахар smile отнюдь smile
PM MAIL WWW   Вверх
source777
Дата 22.7.2010, 21:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1878
Регистрация: 12.3.2007

Репутация: 6
Всего: 56



Цитата(fell @  20.7.2010,  09:39 Найти цитируемый пост)
Хотелось бы услышать твое мнение все же... и подтвердить мою оценку или опровергнуть или что то добавить. 

Если ты знаком со строго типизированными языками программирования, то тебе должно быть известно понятие "явное приведение типа", вот тут и есть явное приведение к типу Boolean, несмотря на то что в Ruby и типа такого нет. Собственно как раз из-за отсутствия такого типа нельзя написать Boolean("string") или "string".to_bool, как это происходит с другими примитивными типами, поэтому в качестве обходного пути для реализации отсутствующего в Ruby приведения к типу Boolean используется двойное отрицание. Это один из примеров программирования с использованием языка, а не на языке.

Цитата(fell @  20.7.2010,  09:39 Найти цитируемый пост)
Mixin вызывается через методы include extend

Необязательно, mix-in - это подмешивание новых функций в существующие классы, модули, объекты любым способом, хоть через class_eval, хоть через метакласс, хоть через методы include, extend. Важно понимать разницу между понятием и способами его практической реализации, чтобы не отождествлять их. 
В данном случае, если конструкция "class << self" используется внутри определения класса, то это не mix-in, а если она используется в мета-коде для динамического подмешивания методов, то mix-in однозначно.


Цитата(fell @  20.7.2010,  09:39 Найти цитируемый пост)
Объявление большого кол-ва методов класса через class << obj затрудняет понимание и разбор кода.

Ну это довольно субъективно. Если у класса куча длинных методов, то его уже на этом основании надо перепроектировать, объявление методов класса через self тут уже никак не спасёт читабельность.




--------------------
Если бы программистам платили за то, чтобы убирать код из программы вместо того, чтобы добавлять его, программы были бы намного лучше © Николас Негропонте
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Ruby: Общие вопросы"
source777
  • С чего начать? начинаем
  • Ссылки на полезные ресурсы смотрим тут
  • Обязательно следуйте правилам Vingrad.
  • Пожалуйста, прочитайте рекомендации по работе в форуме и навигации по Vingrad.
  • Для вставки кодов Ruby используйте тег: [code=ruby]код[/code]. Когда в будущем подсветка синтаксиса для Ruby будет реализована, весь исходных код преобразится.
  • Используйтe чекбокс "Транслит" (возле кнопок кодов), если у Вас нет русских шрифтов.
  • Помните, для каждого вопроса должна быть своя тема.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, source777.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Ruby: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0580 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.