Подключение сторонних гемов

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
11 года 8 мес. назад #72629 от Iren_Rin
Всем привет, я сразу напишу вывод, может кто нибудь не захочет читать дальше :)
Вывод: К игре можно подключить гемы, но я пока что не знаю как подключить любой гем. Во первых есть проблема с тем, что руби в игре немного старое, и немного урезанное, некоторые классы стандартной библиотеки отсутствуют (Etc к примеру), а многие гемы их используют. Во вторых я не знаю пока как работать с гемами с extension (но я точно знаю что можно).

И так, сразу напишу что такое гемы - это крассивое название для сторонних библиотек, оформленных стандартным образом. Это ненмого похоже на сторонние скрипты, которые вы копируете в игру, но чаще всего файлов там гораздо больше чем один. Давайте рассмотрим гем cancan , который мы будем сегодня вставлять в проект. Пока что все что нас интересует - это папка lib, в ней лежит весь код, который нам нужен. Обычно используют Rubygems (часть руби, которая работает с гемами, и которая кстати вырезана из руби игры) для установки гема. Все что rubygems сделают для cancan - это скачают гем, сохранят его, и что самое важное - добавят путь к папке lib гема в $LOAD_PATH. В нормальном проекте вы теперь можете просто написать
Code:
require 'cancan'
и использовать библиотеку.
Так же расскожу о extensions. Стандартная реализация руби написана на Си (да да, один язык написан на другом языке, можете на досуге подумать о проблеме яйца и курицы). Руби - скриптовый язык, что означает что его код не нужно компилировать (переводить в бинарный машинный код) перед исполнением. Так же плюс скриптовых языков - на них обычно писать гораздо легче, чем на компилируемых, они крассивее (руби вообще самый крассивый язык :)), программы на них гораздо легче поддерживать и дебажить (искать ошибки). А минус фактически один - они гораздо медленнее, чем компилируемые языки. Но в руби есть одна лазейка - вы можете узкую часть программы написать на Си, а поверх этой части написать обертку на руби, чтобы остальной код мог с ним работать. Это называется расширение (extension), и да, расширение нужно компилировать. Многие гемы реализуют extension. Rubygems берет на себя задачу компиляции, и конечный юзер обычно ничего не замечает. Я пока что не знаю как работать из игры с такими гемами, но точно знаю что возможно. А это такие вкусные гемы как RMagic (удобное рисование граффики на лету) pry (офигенный дебагер) и многие другие.

Довольно лирики, приступим к методике.
Для начала создадим новый проект, открываем редактор скриптов и вставляем в новый скрипт следуещее:
Code:
puts Dir.pwd #puts печатает строку, Dir.pwd возращает путь к текущей папке.
Сохраняем, запускаем игру (не забываем кликнуть по пункту меню "Показывать консоль") - в консоли вы увидите путь к папке текущего исполняемого файла. Так вот файла этого самого там нет, игра генерирует его динамически, но путь - есть, это корневая папка проекта. Запоминаем эту полезную информацию.
Продолжаем эксперимент - берем редактор кода (не из игры, сторонний, рекомендую subline), создаем файл hello.rb со следующим кодом:
Code:
puts "Hello World" #этот код просто напечатает строку в ковычках
и сохраняем его в папке проекта. Теперь открываем редактор скриптов игры и заменяем код, который мы написали на следующее:
Code:
require File.expand_path('hello.rb', Dir.pwd) #requrie подключает файл #File.expand_path возвращает абсолютный путь к файлу (первый аргумент), #используя абсолютный путь из второго аргумента
Запускаем игру и видим в консоли заветное 'Hello World'. Это означает что мы можем подгружать сторонние скрипты в игру. Теперь ясно, что мы должны сделать - cохранить гем где нибудь в проекте, добавить в $LOAD_PATH путь к папке lib этого гема и сделать require.
Однако беда в том, что многие гемы имеют зависимости от других гемов, т.е. чтобы такой зависимый гем работал, мы должны еще и подгрузить в проект код другого гема. Добавте к этому версионность гемов, и можно сойти сума :). Будем все таки делать так, чтобы остаться в здравом рассудке. Используем bundler . Для начала установим руби, которые нужны для бандлера. Идем сюда и качаем инсталятор. Советую 1.9.3 - будете ближе к версии руби из игры, если захотите потом поиграться с руби, но это не принципиально, главное чтобы не 1.8.7. Устанавливаем руби, нажимаем на клавиатуре windows + r (вы же на винде все это делаете, да?), в появившемся окошке вводим cmd. Знакомьтесь - консоль. Набираем
Code:
ruby -v
Если все прошло удачно - вы увидите версию только что установленного руби. Руби от версии 1.9 идут вместе с rubgems в комплекте. Набираем
Code:
gem install bundler
Привет старый друг!
Теперь самое сложное в нашей затее - перейти в этой гребанной недоконсоли в папку с проектом. Для начала набираем (заменяем Е на диск где у вас лежит проект)
Code:
E:
дальше переходим к самому проекту (тут мой путь, замените на свой)
Code:
CD rpg_maker/projects/gems_testing
Не закрываем консоль, она нам еще пригодится.
Теперь открываем subline и создаем файл Gemfile (именно так, без расширения вообще)
Code:
source 'https://rubygems.org' gem 'cancan', '1.5.1'
Тут вроде все прозрачно - первая строка - где брать гемы. Дальше функция gem с двумя параметрами - имя гема и версия. Немного о версии гема - у нас руби в игре версии 1.9.2 (RUBY_VERSION подсказал), они вышли в конце 2010 года, гемы стоит искать начала 2011 года. Более ранние или более поздние могут просто не заработать. Идем на rubygems , вверху справа есть поле поиска, вбиваем туда cancan. Переходим по exact match. Кликаем по show all versions, и ищем версию гему близкую нам по дате. Переходим на его страницу - cancan 1.5.1 . Нас интересует 'runtime dependencies' которых тут о чудо - нет! Это значит что мы будем устанавливать только один гем. На development dependencies можно забить.
ВНИМАНИЕ: Спойлер!


Ок, мы разобрались с нашим Gemfile для канкан. Теперь разворачиваем консоль (вы же ее не закрыли, да?) и запускаем
Code:
bundle install
Наш старый друг Бандлер установит все гемы и их зависимости из Gemfile куда то в систему. Мы бы могли попросить его установить все сразу в проект, но лучше пускай будут и в системе тоже, может потом поиграетесь с cancan. В консоли вводим
Code:
bundler show cancan
И бандлер покажет нам куда же он скачал гем. Переходим туда, копируем папку с гемом (она будет называться как гем + версия), переходим в папку проекта, создаем папку gems, скидываем туда папку с гемом.
На данном этапе у нас есть проект, файл Gemfile, папочка gems, папочка cancan-1.5.1 в ней. Теперь нам нужно подгрузить cancan в проект.
Открываем редактор скриптов в игре, создаем скрипт в самом верху (гемы нужно загружать до их использования) называем его как нибудь Gems_Loader и вводим туда
Code:
Dir.entries(File.expand_path 'gems', Dir.pwd).each do |entry| #Берем все что есть в папке gems и перебираем unless %w(. ..).include? entry # пока наш entry не бдует равен . или .. (это ссылки на текущий каталог и на родительский) lib_dir = File.expand_path File.join('gems', entry, 'lib'), Dir.pwd #генерим путь к папке lib каждого гема $LOAD_PATH.push lib_dir if Dir.exist? lib_dir # и включаем этот путь в $LOAD_PATH, если такая папка есть end end require 'cancan' #привет cancan!
Запускаем игру и... может вы не заметили, но мы подключили сторонний гем! Если бы что-то пошло не так, мы бы уже увидели бы ошибку!
Ну а для тех кто мне не верит, напишу немножко кода, который докажет что мы можем использовать cancan в игре. Для начала три слова о том что такое cancan - это гем для RoR, который позволяет гибко управлять правами юзера (так нам автор говорит). Но на самом деле он позволяет гибко описывать условия, для if, while и т.п. Короткий пример от балды:
Code:
if enemy.alive? && actor.alive? && !enemy.fire_creature? && !enemy.imune_to_spells? #иногда так лучше не писать actor.cast Spells.firbolt, on: enemy end #VS if actor.can? :hit, enemy, with: Spells.firebolt actor.cast Spells.firbolt, on: enemy end

Теперь к делу - создаем скрипт в редакторе скриптов в игре, называем его Ability
Code:
class Ability include CanCan::Ability # Я уже не буду описывать что такое include, и так затянул статью def initialize(object) can :manage, :all # в общем это не очень полезное правило, оно разрешает всем делать все end end
Создаем еще один скрипт, называем его Dog
Code:
class Dog def ability #Создаем объект абилити, сохраняем его в @ability, если интересно что такое ||= - спрашивайте @ability ||= Ability.new self end %w(can? cannot?).each do |method_name| #Тут метопрограммирование для ускорения процесса, тоже спрашивайте если не ясно define_method method_name do |*args| ability.public_send method_name, *args end end def bar(target) #каждая сабака должна уметь лаять, да? target.puts "BAR!" end end dog = Dog.new #объект сабаки if dog.can? :bar, $stdout #проверяем может ли сабака гакать на STDOUT dog.bar $stdout #гав! end

Запускаем игру - видим "BAR!" в консоли игры.

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

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Проект месяца 1 место Программист Ruby Писатель 3 место 3 место Учитель Организатор конкурсов 1 место в Готв Ветеран Проект месяца 2 место
Больше
11 года 8 мес. назад #72636 от DeadElf79
Давно хотел взять и найти, как подключать скрипт из внешнего файла (встроенный редактор не очень радует), большее спасибо за помощь))

Также, спасибо за статью, она получилась очень подробной и простой для понимания, это приятно)

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
11 года 8 мес. назад #72704 от Iren_Rin
Подключение скриптов из вне пока что работает только для проекта, путь к которому задан полностью в ANSII-8BIT символах. Я работаю над этим, надеюсь удастся починить.

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Проект месяца 1 место Программист Ruby Писатель 3 место 3 место Учитель Организатор конкурсов 1 место в Готв Ветеран Проект месяца 2 место
Больше
11 года 8 мес. назад #73634 от DeadElf79
Если указанный ранее способ сможет работать с гемами, требую демку с примером дога и канкана ^_^

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
11 года 8 мес. назад - 11 года 8 мес. назад #73635 от Iren_Rin
Работает твой метод, я же писал в чате :), перепишу loader на досуге.
Последнее редактирование: 11 года 8 мес. назад пользователем Iren_Rin.

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
11 года 8 мес. назад - 11 года 4 мес. назад #73647 от Iren_Rin
Огромное спасибо DeadElf79 за то что подсказал этот способ!

Если вы хотите, чтобы ваши гемы работали в проекте, в пути к которому есть не ANSII символы, то рекомендую использовать этот загрузчик
ВНИМАНИЕ: Спойлер!


Что важно - предполагается, что гемы вы сохраняете в папке gems.
Скрипт заюзает стандартный механизм, когда проект сохранен в папке с нормальным путем, и загрузит файлы через File.load, когда стандартный механизм дал сбой.
Последнее редактирование: 11 года 4 мес. назад пользователем Iren_Rin. Причина: Перенс код загрузчика на гитхаб
Спасибо сказали: DeadElf79

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Проект месяца 1 место Программист Ruby Писатель 3 место 3 место Учитель Организатор конкурсов 1 место в Готв Ветеран Проект месяца 2 место
Больше
10 года 4 мес. назад - 10 года 4 мес. назад #88021 от DeadElf79
Ирен, у меня проблема. Не могу подключить гем i18n ( отсюда )

Все зависимости внёс в папку, но выдает LoadError.
Текст ошибки


Код загрузки гемов:
Code:
gems_list = [ 'json', 'tzinfo', 'minitest', 'concurrent-ruby', 'activesupport', 'actionpack', 'thor', 'method_source', 'railties', 'i18n' ] success = true gems_list.each do |gem| wr "Inlcude gem: #{gem}...",0 begin SideScriptsLoader.add_gems_to_path require gem rescue Exception => msg wr msg.message wr msg.backtrace.join("\n") success = false break else wr 'successful' end end wr success ? "Current locale is #{I18n.locale}" : "Cannot load system locale"

* wr - аналог puts, взял отсюда
Возможно, немного ошибся темой.
Последнее редактирование: 10 года 4 мес. назад пользователем DeadElf79.

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
10 года 4 мес. назад #88028 от Iren_Rin
1. json тоже гем? Если нет то на нем все и валится - в мейкере нет стандартного json.
2. Зачем тебе i18n от рельсов? Подключай github.com/svenfuchs/i18n/blob/master/i18n.gemspec , гем от рельсов это обертка над этим гемом. Тогда уйдет зависимость от ralities, и видимо, от activesupport, actionpack и т.п. Sourcecode точно имеет extension а их, как минимум, скомпилировать нужно будет. Только скорее всего внутри i18n все равно загружается yaml, а его тоже в мейкере нет.
3. SideScriptsLoader.add_gems_to_path нужно делать только один раз.

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Проект месяца 1 место Программист Ruby Писатель 3 место 3 место Учитель Организатор конкурсов 1 место в Готв Ветеран Проект месяца 2 место
Больше
10 года 4 мес. назад #88032 от DeadElf79
1. Да, тоже гем, в зависимостях нашел.
2. Спасибо, посмотрю. Видимо, не в ту сторону рыть начал.
3. О, вот этого не было в примерах или я не так прочитал их.

А ведь всего лишь нужно получать текущую локаль системы, хех.

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Программист Ruby Коммерсант Проект года 1 место Учитель Проект месяца 1 место
Больше
10 года 4 мес. назад #88033 от Iren_Rin
I18n не поможет тебе получить текущую локаль. Это система для хранения переводов. В ней можно установить текущую локаль для самой этой системы, но получить ее из ОСи из вне - нет.

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.

Время создания страницы: 0.112 секунд
Работает на Kunena форум