среда, 29 марта 2017 г.

Про Рельсы философское

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

Идеальный вариант, это когда рельсы такие как есть, но написаны вами самостоятельно с нуля.

среда, 25 января 2017 г.

Про Docker и Ruby on Rails sprockets and precompiled assets.

Рельсы идут не тем путем.

По-хорошему монолит на рельсах нужно дробить, а то получается 1С:Бухгалтерия, со всеми включенными галочками по-умолчанию. В данном случае умолчания в рельсах слишком greedy.

Взять вот sprockets. Его задача вообще один раз запуститься, минифицировать css и js, и замолчать навеки. При этом он требует кучу разнообразных зависимостей типа execjs, nodejs или v8 от гугла в качестве движка минификации.

Но что делает фреймворк? Sprockets включают в Gemfile, он грузится вместе с полезным кодом, занимает кучу места, мешается под ногами и заставляет программиста танцевать с бубном вокруг задач связанных с деплоем!

Как было бы делать правильно? Минификацию JS и CSS отрезать и делать ее опциональной, отдельной утилитой, если хотите, в составе рельс. Такой же как и rake. Назвать ее minirake и дать набор ключей для запуска. Но! У нас по всему фреймворку нужно протянуть новые названия файлов, которые генерятся при минификации! Потому что как же нам еще разделить код и данные? Короче, считаю это ошибкой дизайна, когда оптимизация НАВЯЗАНА разработчику, то есть для 99% сайтов она будет делаться ПРЕЖДЕВРЕМЕННО.

Тоже самое можно сказать и про turbolinks. Во-первых очень мало народу понимает как оно в реальности работает, соответственно каких косяков ожидать. Это та же самая, мать ее, premature optimization. Всегда гораздо сложнее упрощать что-то сложное, чем добавлять и ускорять по мере необходимости.

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

И вот здесь детальнее, о чем я имею в виду https://blog.red-badger.com/blog/2016/06/22/docker-and-assets-and-rails-oh-my

вторник, 24 января 2017 г.

Вот сегодня запиливая очередной апдейт в простеньком Ruby on Rails сайте я понял

Для чего подходит хорошо Ruby on Rails?

Для быстрого bootstrap, quick'n'dirty прототипа приложения. Чтобы всю схему с базой данных запилить за 5 секунд с шаблонами и простой управлялкой. Потому что ты уже знаешь все косяки и подводные камни и названия библиотек, все это проходит за пять секунд.

Но! Разрабатывать долго и сложно на Рельсах превращается в филиал ада на Земле. Очень много магии, механизмы кеширования как часть фреймворка тесно переплетенная с логикой. А чего стоит интеграция с jQuery и вот эта вот магия с .js.erb, .json.jbuilder ? WTF? Вот так сидишь и смотришь на очередной суперпупер gem который делает за тебя весь кодинг. А I18n? А управление очередями, а Live Stream или как это сейчас называется?

Всего этого не знаешь и не замечаешь, пока не пробуешь изучать функциональные языки на подобие Clojure.

Изучение Clojure абсолютно точно делает тебя лучше, умнее, быстрее. Применение функциональных паттернов в Руби делает код более изящным и читаемым и разделение смыслов более явным. 

Вот сегодня я почти полностью ушел от sqlite3 в пользу плоского YAML для сайта. Потому что зачем? В Clojure подобный фукнционал несет в себе формат EDN.

Вот например, отображать только те комментарии у которых есть поле коммент. Как это делается в базе, типа sqlite3, выборкой select * where comment is not NULL. Как делается через YAML? 

myfile = YAML.load(File.read('my.yaml'))

затем уже 
myfile["myarray"].reject{|i| !i.logo} 

оставит в коллекции только те значения, которые нужны. Минус задержка на обращение к базе, код автоматически становится быстрее, минус еще одна зависимость, плюс к управляемости данными. Данные же сейчас доступны в простом текстовом редакторе, вам для этого не нужен никакой SQL клиент! Минус сложность связанная с языком запросов, он здесь становится просто не нужен!

И главное, программа стала проще.

Миграции как в рельсах для Clojure

Below is a list of all the popular migration libraries that are currently available:
  • Drift - Drift is a migration library written in Clojure. Drift works much like Rails migrations where a directory in your project contains all of the migration files. Drift will detect which migration files need to be run and run them as appropriate.
  • Lobos - Lobos is a SQL database schema manipulation and migration library written in Clojure. It currently support supports H2, MySQL, PostgreSQL, SQLite and SQL Server.
  • Ragtime - a general migration framework, with an implementation for database migrations.
  • Joplin - Joplin is a library for flexible datastore migration and seeding
Luminus http://www.luminusweb.net/docs/migrations.md#popular_migrations_alternatives

Хотя лично я справился и с Ragtime.
Забавный факт, database.yml и вообще поддержка баз данных гвоздями вколочена в Rails. Что печалит, конечно.

Нужно танцевать с бубном чтобы выключить процессинг database и ActiveRecord. Такие дела.

пятница, 11 ноября 2016 г.

How to select to radio_buttons plain array values


  = f.collection_radio_buttons :locale, I18n.available_locales, :itself, :itself

четверг, 27 октября 2016 г.

How to install datomic-pro in project.clj

I received issue
"Could not find artifact com.datomic:datomic-pro"

So our next step is to "brew install maven", then go to datomic distributive and install bin/maven-install necessaty library.

After that lein deps should be satisfied.

вторник, 20 октября 2015 г.

Как сделать rebuild для ThinkingSphinx в resque-scheduler

Озаботился я переходом от delayed job + whenever на resque + rescue-scheduler.

Вот хорошее описание кейса на тостере:

И в целом на почитать:

Очень много танцев с бубном,  и вот мой cron остался только для запуска переиндексации сфинкса.


однако вот такой джоб раз в полчаса делает свою работу:

class ReindexThinkingSphinxJob < ActiveJob::Base
  queue_as :ts

  def perform(*args)
    config = ThinkingSphinx::Configuration.instance
    config.controller.index :verbose => true
  end
end

в resque-schedule.yml добавляем

ReindexThinkingSphinxJob:
  every:
    - '30m'
  description: "Reindex for thingin sphinx"
  queue: ts


и вуаля!

пятница, 9 октября 2015 г.

Запилил Server Side Events на Rails 4.0

Подробнее здесь.
Несколько косяков.

1. Нужен сервер с поддержкой SSE. В моем случае это Puma.
2. Если не включить config.cache_classes = true и config.eager_load = true, то работать не будет, будет вечное ожидание отработки контроллера вызываемого через events.
P.S. Эта проблема решается включением опции Just add config.allow_concurrency = true to your development.rb.
3. При включении опций, указанных выше, есть проблема с автоматической подгрузкой измененного кода. Нужно останавливать вебсервер и заново его запускать, что, конечно, замедляет разработку.

Мои эксперименты закончились вот таким небольшим контроллером:

class Api::EventsController < ApplicationController
 include ActionController::Live
 skip_before_filter :authenticate_account!, only: [:index]

def index
      response.headers["Content-Type"] = "text/event-stream"
   
      redis = Redis.new
      redis.psubscribe('tickets.*') do |on|
      on.pmessage do |pattern, event, data|
      response.stream.write "event: #{event}\n"
      response.stream.write "data: #{data}\n\n"
      end
      end

    rescue IOError
      logger.info "stream closed"
    ensure
      redis.quit
      response.stream.close
    end
end


И в модели ticket.rb я добавил такие хуки

after_create :notify_create
after_destroy :notify_destroy


def notify_destroy 
    $redis.publish('tickets.destroy', self.to_json)
end
def notify_create
    $redis.publish('tickets.create', self.to_json)
end

В Javascript (Coffeescript) я делаю примерно следующее:

@source = new EventSource("/api/events")
source.addEventListener 'tickets.create', (e) =>
      jQuery.gritter.add
        title: 'Новый тикет'
        text: "Добравлен тикет"

      console.log "pushed ticket"
      @tickets.push(new Ticket($.parseJSON(e.data)))

    source.addEventListener 'tickets.destroy', (e) =>
      data = $.parseJSON(e.data)
      @tickets.remove (item) => item for item in @tickets() when item.id is data.id

пятница, 25 сентября 2015 г.

Более простой способ генерить class: "active" для активного контроллера

Все равно у нас все RESTful,

так что нашел хорошим способ делать так (для HAML)

%li{class: (request.path.include?(incidents_path) && "active")}

выставит у
  • если путь с контроллером будет содержаться в URL, так можно делать и с субконтроллерами.
  • пятница, 8 мая 2015 г.

    Rails 4.1+ не нужен Timecop для тестиррования

    Теперь для тестирования начиная с версии 4.1 есть встроенный фукнционал Time.travel

    ActiveSupport::Testing::TimeHelpers#travel

    Time.travel 1.day
    Time.travel -1.day
    
    Time.travel 1.day do
      User.create.created_at # => 1 day from now
    end
    
    Time.travel_to Time.new(2004, 11, 24, 01, 04, 44)
    Time.travel_to Date.new(2004, 11, 24)
    
    Time.travel_to Time.new(2004, 11, 24, 01, 04, 44) do
      User.create.created_at # => 2004-11-24 01:04:44 -0500
    end

    пятница, 23 августа 2013 г.

    Has many through polymorphic association

    http://stackoverflow.com/questions/6997141/rails-has-many-through-with-polymorphic-association-will-this-work
     

    Как сделать OR вместо AND в запросах Rails?

    http://stackoverflow.com/questions/3684311/rails-how-to-chain-scope-queries-with-or-instead-of-and

    вторник, 20 августа 2013 г.

    Array of arrays flatten

    Внезапно узнал про метод flatten у array

     

    http://stackoverflow.com/questions/8282096/ruby-how-to-concatenate-array-of-arrays-into-one

    вторник, 16 апреля 2013 г.

    пятница, 18 января 2013 г.

    Делать линки в тексте

    Можно делать через gsub, а можно, по-спортивному через auto_link, который выпал в отдельный джем.

    Впрочем: моднее подключать gem 'rinku'

    четверг, 17 января 2013 г.

    о пользе чтения документации

    Узнал, что можно делать и такое:

    cycle('a1','a2') будет поочередно показывать то a1, то a2 в цикле

    excerpt выкусит из большого текста нужный кусок с окружающими словами, что полезно при поиске

    highlight поможет выделить кусок в тексте и обрамить его в :highlighter блок

     

    word_wrap поможет разбить текст кусками по n символов и вставить \n\n между кусками

    You can also supply an alternate image to show on mouseover:

    <%= image_tag "home.gif", :onmouseover => "menu/home_highlight.gif" %>

    понедельник, 14 января 2013 г.

    Ну я как знал, что наступит западло

    Только собрался засекьюрить Rails XML hole путем обновления версии, как пошло и поехало.

    Короче, тронул версию, тут проснулся Paperclip и стал требовать новую версию, а она уже под 1.8.7 не собирается.

    Тьфу, пришлось на старых рельсах затыкать дыры путем 

     

    ActionController::Base.param_parsers.delete(Mime::XML)

    вторник, 8 января 2013 г.

    jQuery + coffeescript

    Из документации:

    As a shortcut for this.property, you can use @property.

    так что можно писать и так:

    @find('a').after('test')

    вместо

    $(this).find('a').after('test')