Archive for the ‘Без рубрики’ Category

Свой шаблон для генератора контроллеров в Rails 3

Среда, Апрель 20th, 2011

Как известно, в Rails 3 можно использовать новый стиль кодирования, основанный на применении респондеров (Responders). То есть, форматы ответов задаются при помощи ключевого слова respond_to, а в экшенах используется respond_with.

Однако, нисмотря на то, что в таком стиле вы можете писать свои контроллеры уже сейчас, rails-генераторы (scaffold, controller и resource) все еще генерируют контроллеры на основе старого шаблона, в котором используется старый стиль кодирования. Раньше мне приходилось вручную переводить вновь сгенерированные контроллеры в новый стиль, пока я не переопределил стандартный шаблон контроллера.

В Rails 3 теперь можно удобно переопределить стандартный шаблон генератора контроллеров. Самый простой способ, это просто положить готовый шаблон (он приведен в конце статьи) в файл:

yourapp/lib/templates/rails/scaffold_controller/controller.rb

В этом случае, шаблон будет автоматически “подхватываться” при генерировании контроллера.

Второй способ может понадобиться, если нужно переопределить шаблон из гема или плагина. Для этого нужно сначала прописать в файле lib/yourgemname.rb вашего гема следующее:

require 'rails/all'

module Yourgemname
  class Railtie < ::Rails::Railtie
    config.generators.scaffold_controller = :yourgemname_controller
  end
end

Этим мы указываем, что генератор контроллеров должен использовать наш генератор. Файл генератора должен располагаться по пути lib/generators/rails/yourgemname_controller_generator.rb Теперь давайте посмотрим, как выглядит наш собственный генератор:

require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'

module Rails
  module Generators
    class YourgemnameControllerGenerator < ScaffoldControllerGenerator
      source_root File.expand_path("../templates", __FILE__)
    end
  end
end

Здесь все просто - мы наследуемся от базового класса генератора и указываем новый путь к шаблонам. Шаблон должен располагаться по пути lib/generators/rails/templates/controller.rb. Теперь посмотрим, как выглядит сам шаблон в стиле Rails 3:

class <%= controller_class_name %>Controller < ApplicationController
<% unless options[:singleton] -%>
  # GET /<%= table_name %>
  # GET /<%= table_name %>.xml
  def index
    @<%= table_name %> = <%= orm_class.all(class_name) %>
    respond_with(@<%= table_name %>)
  end
<% end -%>

  # GET /<%= table_name %>/1
  # GET /<%= table_name %>/1.xml
  def show
    @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
    respond_with(@<%= file_name %>)
  end

  # GET /<%= table_name %>/new
  # GET /<%= table_name %>/new.xml
  def new
    @<%= file_name %> = <%= orm_class.build(class_name) %>
    respond_with(@<%= file_name %>)
  end

  # GET /<%= table_name %>/1/edit
  def edit
    @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
  end

  # POST /<%= table_name %>
  # POST /<%= table_name %>.xml
  def create
    @<%= file_name %> = <%= orm_class.build(class_name, "params[:#{file_name}]") %>
    flash[:notice] = '<%= class_name %> was successfully created.' if @<%= orm_instance.save %>
    respond_with(@<%= file_name %>)
  end

  # PUT /<%= table_name %>/1
  # PUT /<%= table_name %>/1.xml
  def update
    @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
    flash[:notice] = '<%= class_name %> was successfully updated.' if @<%= orm_instance.update_attributes("params[:#{file_name}]") %>
    respond_with(@<%= file_name %>)
  end

  # DELETE /<%= table_name %>/1
  # DELETE /<%= table_name %>/1.xml
  def destroy
    @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %>
    @<%= orm_instance.destroy %>
    respond_with(@<%= file_name %>)
  end
end

Вот и все. Теперь при генерировании нового контроллера будет использоваться наш шаблон.

P.S: Хочу предупредить, что в Rails 3.1 (которая edge на момент написания статьи) это не будет работать в некоторых случаях, так как в этой версии есть небольшие изменения в шаблоне, а в приведенном мною шаблоне они не учитываются.

Подробнее о хранении товаров с различными свойствами в БД

Суббота, Март 5th, 2011

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

Первый класс - это класс, который обеспечивает бестабличную ActiveRecord модель, если от него отнаследоваться. Он лежит в app/lib/tableless.rb

class Tableless
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  def initialize(props = {})
    props.each do |name, value|
      send("#{name}=", value)
    end
  end

  def persisted?
    false
  end
end

Этот код лежит в app/models/product_kinds/base.rb. Код обеспечивает поддержку групп свойств и свойств для моделей товаров (:group, :prop), а так же содержит базовую модель Base с двумя общими для всех типов товаров свойствами :name и :price.

Все остальные классы типов товаров должны наследоваться именно от Base. Смотрим код:

module ProductKinds
  module Props
    class PropOptions
      attr_accessor :as, :default, :collection

      def add(options)
        options.each_pair do |option, value|
          send "#{option}=".to_sym, value
        end
      end
    end

    class Prop
      attr_accessor :name

      def options
        @options
      end

      def options=(options)
        @options = PropOptions.new
        @options.add(options)
      end
    end

    module ClassMethods
      def self.extended(base)
        class_eval { attr_accessor :props, :current_group, :options }
      end

      # Set up group
      def group(name, &block)
        self.props       ||= {}
        self.options     ||= {}
        self.current_group = name;

        if block_given?
          instance_eval(&block)
        end
      end

      def prop(name, *options)
        self.props[self.current_group] ||= []

        prop = Prop.new
        prop.name    = name;
        prop.options = options.extract_options!

        self.props[self.current_group] << prop
        attr_accessor name
      end

      def grouped_prop_names
        if self == ProductKinds::Base
          self.props
        else
          properties = superclass.grouped_prop_names

          # Dirty, but works
          properties[:common] ||= []
          properties[:other]  ||= []
          self.props[:common] ||= []
          self.props[:other]  ||= []

          h = {
            :common => properties[:common] + self.props[:common],
            :other  => properties[:other] + self.props[:other]
          }

          h
        end
      end
    end

    module InstanceMethods
      def grouped_prop_names
        self.class.grouped_prop_names
      end
    end
  end

  class Base < Tableless
    extend  Props::ClassMethods
    include Props::InstanceMethods

    group :common do
      prop :name,  :as => :string
      prop :price, :as => :numeric
    end

    validates :name, :presence => true
    validates :price, :presence => true, :numericality => true
  end
end

Так мы описываем тип товара “Автомобиль”. Этот код лежит в app/models/product_kinds/automobile.rb

module ProductKinds
  class Automobile < Base
    group :common do
      prop :color, :as => :string
    end

    group :other do
      prop :max_speed, :as => :numeric
    end

    validates :color,     :presence => true
    validates :max_speed, :presence => true, :numericality => true
  end
end

Вот модель, описывающая тип товара. В БД у нас есть табличка со всеми существующими типами товаров. Этот код лежит в app/models/product_kind.rb

class ProductKind < ActiveRecord::Base
  validates :name, :presence => true, :uniqueness => true
  validates :classname, :presence => true, :uniqueness => true

  has_many :products

  def self.find_model_by_kind(kind)
    self.require_model_by_kind(kind)
    eval("ProductKinds::#{kind.camelize}")
  end

  def self.require_model_by_kind(kind)
    ProductKind.find_by_classname!(kind) # Проверяем существование
    require File.dirname(__FILE__) + "/product_kinds/#{kind}"
  end
end

А это - непосредственно модель, описывающая сам товар. У товара есть свойство :props, в котором в сериализованном виде хранится объект со свойствами товара, например, тот же Automobile. Мы видим геттер и сеттер для props, которые сериализуют и десериализую объект и, соответственно, строку:

require File.dirname(__FILE__) + '/product_kinds/base'

class Product < ActiveRecord::Base
  validates :props,           :presence => true
  validates :product_kind_id, :presence => true

  belongs_to :product_kind

  def props=(model)
    write_attribute(:props, Marshal::dump(model))
  end

  # Restore concrete product model from :props attr
  def props
    return @props_cache unless @props_cache.nil?
    props = read_attribute(:props)

    unless new_record?
      ProductKind.require_model_by_kind(product_kind.classname)
    end

    @props_cache = Marshal::restore(props) if props
    @props_cache
  end
end

Используется в контроллере это примерно так (создание нового товара):

class ProductsController < ApplicationController
  respond_to :html

  def create
    @kind          = params[:product][:kind]
    @product       = Product.new
    @props         = ProductKind.find_model_by_kind(@kind).new(params[:product][:props])
    @product.props           = @props
    @product.product_kind_id = ProductKind.find_by_classname!(@kind).id

    if @props.valid? && @product.valid?
      @product.save!

      flash[:notice] = 'Product was successfully created.'
      redirect_to new_product_image_path(@product)
    else
      render :new
    end
  end
end

P.S.: Знаю, что некоторые куски кода далеко не оптимальны и можно написать лучше и проще. Все в ваших руках - этот код только для примера, как можно сделать :)

Надеюсь, я сумел разъяснить некоторые детали, которые читатели сего блога хотели знать.

Бестабличные модели в Rails 3 и как их можно использовать

Четверг, Март 3rd, 2011

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

В Rails 3 ребята подошли к этому с умом. Разработчики вынесли часть логики ActiveRecord в отдельные модули и классы, обеспечив этим слабую связанность компонентов. Теперь ничто не мешает подмешать нужную логику в свой класс. Давайте посмотрим, как может выглядеть такая бестабличная модель:

class Tableless
  # Подмешиваем валидаторы, методы преобразований
  # и методы соглашения об именовании для ActiveRecord
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  # Полезняшка, чтобы создавать свойства из конструктора
  def initialize(props = {})
    props.each do |name, value|
      send("#{name}=", value)
    end
  end

  # Переопределяем метод persisted?, чтобы он всегда
  # возвращал false. Это сообщает ActiveRecord,
  # что данные модели не сохраняются в БД.
  def persisted?
    false
  end
end

Всё, теперь можно спокойно наследовать модели от класса Tableless - вы получите нормальную модель с валидацией, но без привязки к БД:

class Person < Tableless
  validates :name, :presence => true
  attr_accessible :name
end

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

У каждого товара, например у мобильного телефона или автомобиля, могут быть как общие свойства (такие как цена или масса), так и свои собственные, как наличие bluetooth для телефона или максимальная скорость для автомобиля.

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

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

module ProductKinds
  class Cellphone < Base
    group :common do
      prop :color,  :as => :string
      prop :weight, :as => :numeric
    end

    validates :color,  :presence => true
    validates :weight, :presence => true, :numericality => true
  end
end

module ProductKinds
  class Automobile < Base
    group :common do
      prop :color, :as => :string
    end

    group :other do
      prop :max_speed, :as => :numeric
    end

    validates :color,     :presence => true
    validates :max_speed, :presence => true, :numericality => true
  end
end

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

Это связано с тем, что необходимо было наличие общих валидируемых свойств (цена) для всех дочерних моделей. Еще вы можете заметить вызов методов group и prop - они подмешиваются в модели Base. Эти методы определяют свойства товара и разбивают эти свойства по группам. Надеюсь, из кода моделей все понятно. Я не буду тут выкладывать код базового класса моделей Base, так как там нет ничего экстраординарного, но если кому потребуется, то попросите в комментах.

Конечно, у такого подхода (хранение сериализованных моделей в БД) есть и минусы. Например, если код модели изменится, то вам нужно будет пройтись по всем сериализованным моделям в БД и обновить их в соответствии с новым кодом. Это, конечно же, можно и нужно автоматизировать в виде rake-задачи.

Второй минус это поиск. Вы не сможете напрямую искать по БД (кто-то еще так ищет, серьезно?) при помощи SQL запросов, но по-моему, это не так уж важно, когда есть такие офигенные инструменты как sphinx и thinking_sphinx. Можно отдавать данные сфинксу в виде XML, рассериализовав модели - не так уж трудно добавить такой функционал.

Хотя, я считаю, плюсов тут гораздо больше. Вроде это все, о чем я хотел рассказать.

Шаблоны приложений в Rails 3

Вторник, Март 1st, 2011

Rails 3 вышел несколько месяцев назад, и одним из многих изменений было обновление API для генераторов и шаблонов приложений. Если вы еще не знаете, то теперь в Rails 3 используется Thor для этих целей, что даёт большую модульность и настраиваемость.

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

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

Вот так, например, выглядит шаблон, подготовленный Аароном Самнером:

# Создание rvmrc файла
create_file ".rvmrc", "rvm gemset use #{app_name}"

gem "haml-rails"
gem "sass"
# hpricot и ruby_parser используются гемом haml
gem "hpricot", :group => :development
gem "ruby_parser", :group => :development
gem "nifty-generators"
gem "simple_form"
gem "jquery-rails"

# Аутентификация и авторизация
gem "devise"
gem "cancan"

# rspec, factory girl, webrat, autotest для тестирования
gem "rails3-generators", :group => [ :development ]
gem "rspec-rails", :group => [ :development, :test ]
gem "factory_girl_rails", :group => [ :development, :test ]
gem "webrat", :group => :test
gem "ffaker", :group => :test
gem "autotest", :group => :test

run 'bundle install'

rake "db:create", :env => 'development'
rake "db:create", :env => 'test'

generate 'nifty:layout --haml'
remove_file 'app/views/layouts/application.html.erb' # вместо этого используется nifty layout
generate 'simple_form:install'
generate 'nifty:config'
remove_file 'public/javascripts/rails.js' # jquery-rails заменит стандартный prototype-ujs
generate 'jquery:install --ui'
generate 'rspec:install'
inject_into_file 'spec/spec_helper.rb', "\nrequire 'factory_girl'", :after => "require 'rspec/rails'"
inject_into_file 'config/application.rb', :after => "config.filter_parameters += [:password]" do
  <<-eos

    # Настройка генераторов
    config.generators do |g|
      g.stylesheets false
      g.form_builder :simple_form
      g.fixture_replacement :factory_girl, :dir => 'spec/factories'
    end
  eos
end
run "echo '--format documentation' >> .rspec"

# Настройка аутентификации и авторизации
generate "devise:install"
generate "devise User"
generate "devise:views"
run "db:migrate"
generate "cancan:ability"

# Удаляем ненужные файлы, копируем конфиг БД и добавляем его в игнор
remove_file 'public/index.html'
remove_file 'rm public/images/rails.png'
run 'cp config/database.yml config/database.example'
run "echo 'config/database.yml' >> .gitignore"

# Создаем git-репозиторий и делаем первый коммит
git :init
git :add => "."
git :commit => "-a -m 'create initial application'"

say <<-eos
  ============================================================================
  Your new Rails application is ready to go.

  Don't forget to scroll up for important messages from installed generators.
eos

Достаточно просто, не правда ли? Воспользоваться таким шаблоном можно так:

$ wget https://gist.github.com/848735.txt
$ rails new appname -m 848735.txt

Я сейчас создаю специальный гем, который поможет упростить создание новыйх приложений Rails 3 - этот гем называется playmo-rails, и уже можно посмотреть некоторые наработки. Мой гем так же использует Thor для генерации различных файлов. Этот гем создан для быстрого старта - можно создать новое приложения, добавляя в него нужные библиотеки и расширения, просто отвечая на вопросы установщика. И это очень удобно.

Пока playmo-rails умеет не так уж много:

  • Создает приложение и использованием Compass
  • Лайот приложения использует html5 boilerplate
  • Можно выбрать установку JQuery либо Mootools
  • Удаляет ненужные файлы из нового rails-приложения

Планов по playmo-rails достаточно много, например, добавить поддержку других фреймворков, полезных общеиспользуемых хелперов, таких как title, динамические страницы 500, 404, 422. В общем, в скором времени, playmo-rails будет той штукой, которая избавит вас от всех манипуляций которые вы делаете с каждым новым rails-приложением.

bundle exec - чтобы не забыть

Вторник, Февраль 22nd, 2011

Очень помогли сегодня в группе ror2ru.
Я хотел подправить один гем, он у меня лежал на диске, склонированный с гитхаба. Но, чтобы установить этот гем (при установке он генерирует несколько файлов, которые-то и нужно было мне подправить), нужно использовать консольную программу, в моем случае compass, которой указываем название гема, который нужно установить:

compass init rails -r html5-boilerplate ...

Но, так как гем у меня в виде склонированной репы, то естественно, программа compass его “не видит”. Выход нашелся в следующем.

Сначала подключаем в Gemfile требуемый гем локально:

gem 'html5-boilerplate', :path => '~/sandbox/gems/html5-boilerplate'

А затем, запускаем нужную команду через bundle exec:

bundle exec compass init rails -r html5-boilerplate ...

Это запустит вашу команду в контексте текущего Gemfile, поэтому, для программы compass гем html5-boilerplate будет прекрасно “виден”. Полезная штука.

DesignMode: инициализация

Среда, Февраль 2nd, 2011

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

Я начал работу над своим редактором и хочу представить первые наработки. Проект называется DesignMode. Это - WYSIWYM редактор для редактирования текста с веб-страницы. Я выложил проект на Github: design_mode. Скоро, скорее всего, сяду писать Roadmap проекта.

Код проекта на Github

Форки приветствуются :)

Как должен выглядеть идеальный WYSIWYG

Понедельник, Январь 31st, 2011

В студии Артемия нашего Лебедева выпустили визуальный редактор на JS под названием Реформатор. Именно так в моем понимании должен выглядеть WYSIWYG.
Мне для моих проектов нужен визуальный редактор, чтобы удобно было возиться с текстами на странице. Так вот, я решил написать собственный. Пока у него будет поддержка только FF > 3, мне этого пока достаточно. Я уже написал немного кода и скоро выложу его на github.

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

  • Никаких возможностей для изменения цвета текста или бэкграунда
  • Все стили берем из стилей css файла для контент-зоны сайта, т.е. того места, где располагается текст.
  • В редакторе все должно выглядеть именно так, как будет выглядеть на сайте.
  • Редактор увеличивает свою высоту по мере набора текста.
  • Удобное боковое меню со стилями (как в Реформаторе).
  • Картинки можно перетаскивать с рабочего стола прямо в редактор и сразу изменять их размер при помощи курсора мыши. Не нужно будет сначала загружать картинку на сайт,а потом вставлять ее в текст.
  • Не будет кучи кнопок, в которых легко запутаться.
  • Не будет выравнивания текста. У нас же будут стили. Если стиль предполагает выравнивание текста по правому краю - то пожалуйста. Это - как задал дизайнер. Редактор сайта не должен хотеть сделать того, чего не задумал дизайнер.
  • По-умолчанию будут: выделение (полужирный, италик, зачеркивание), нумерованный и ненумерованный списки, добавление гиперссылки.
  • Будет undo, redo, очистка форматирования, вставка из word (пока не знаю, как сделать такую вставку наиболее удобно).
  • Текст будет автоматически заворачиваться в параграфы. Озаглавленные параграфы будут заворачиваться в тег section.
  • Поддержка только HTML5.
  • Никаких тебе спелл-чекеров, таблиц спец-символов и прочей ненужной блуды.
  • Все это будет написано на mootools, как расово верном фреймворке.
  • Специальная парсилка для стилей контент-зоны.
  • Мануал, как правильно работать с визуальным редактором.
  • … может - чето забыл, напишу, если вспомню.

Еще одна очень важная фишка, которую хочу выделить:

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

  1. Вокруг текста статьи появилась бы рамка - признак того, что мы можем редактировать статью. Или нет - еще лучше: все остальное на странице, кроме статьи, затемнилось бы.
  2. Сбоку бы появилось плавающее меню с кнопками. И это меню можно было бы перетаскивать в любую часть вьюпорта по своему усмотрению, а так же сворачивать.
  3. При ухода фокуса со статьи плавающее меню бы исчезало.
  4. Если мы попытаемся закрыть окно, то появляется диалог с прогрессбаром, в котором отображается процесс сохранения статьи. Только после этого окно закрывается.
  5. Где-то будет расположена кнопка принудительного сохранения статьи. Или ее совсем не будет. Тут подумаю еще.

Ждите ссылку на проект, кому это интересно. А от вас жду предложений по улучшению.

Javascript: текущий элемент списка

Четверг, Январь 13th, 2011

Навеяно работой. Давно хотел об этом написать.

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

Опишу подробней в виде кода. Итак, даны стили и список:

<style type="text/css">
    ul li.current { color: red; }
</style>

<ul>
  <li>First</li>
  <li class="current">Second</li>
  <li>Third</li>
</ul>

Как обычно делают разрабы:

$().ready(function() {
    var ul  = $('body ul');

    ul.find('li').click(function() {
        ul.find('li.current').removeClass('current');
        $(this).addClass('current');
    })
})

При таком подходе приходится обходить все элементы списка, что негативно сказывается на производительности, особенно, если списки огромные.

Как нужно делать:

$().ready(function() {
    var ul  = $('body ul');
    var cur = ul.find('li.current');

    ul.find('li').click(function() {
        if (cur.length) {
            cur.removeClass('current');
        }

        cur = $(this);
        cur.addClass('current');
    })
})

Тут мы просто запоминаем текущий элемент. Вроде бы все просто, но чаще всего, почему-то встречается первый подход. Да, кода получилось больше (хотя тут можно его уменьшить), но зато мы меньше теребим DOM.

Некий Karneds написал комментарий, как по его мнению стоит решить этот вопрос. Я было засомневался и убрал пост с главной, чтобы проверить, так ли это на самом деле. Догадка была такая, что сначала при обработке дерева DOM находятся все указанные в селекторе таги и только потом происходит поиск по атрибутам найденных тагов. Чтобы проверить мою догадку я обратился к небезызвестному Сергею Чикуёнку:

Есть список:

<ul>
  <li>First</li>
  <li class="current">Second</li>
  <li>Third</li>
</ul>

Если мы напишем обработчик (jquery):

$('ul li.current').click(function() { ... })

то будет ли это означать, что селектор ul li.current проверит сначала первый элемент списка (First) на наличие класса current, прежде чем совпасть со вторым элементом?

И вот что он ответил:

зависит от браузера. В новых используется встроенный querySelectorAll(), в старых (например IE) будет бегать по элементам, и в данном случае будут проверены все LI-элементы (насколько я знаю, поиск идёт справа налево, то есть сначала выберутся все LI элементы на странице, а потом отфильтруются те, которые находятся внутри UL).

То есть для старых браузеров — да, сначала проверит First (порядок элементов сохраняется)

Так что, догадка подтвердилась. Это, кончно, не касается новых браузеров, которые умеют искать и по атрибутам. Хотя, если текущий элемент будет помечен не атрибутом, а свойством объекта, то всё же касается.

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

P.S: Для Тормоза - извиняй, специально для тебя публикую пост с новым адресом, дабы он отметился в RSS ленте.

Собираюсь писать клиентскую библиотеку для работы с XMPP на JS

Воскресенье, Октябрь 31st, 2010

Да, есть такое желание. Объясню, зачем мне захотелось такого извращения.

Я посмотрел все существующие библиотеки на JS для работы с XMPP. Все эти библиотеки стремятся отсылать запросы через BOSH и похожие технологии, но ни в одной из них не реализованы вебсокеты.

Я же хочу простого: пусть у меня будет имплементация jabber-мессенджера на сайте, которая будет работать в реальном времени. Я не хочу отсылать xmpp-запросы с сайта прямо на Jabber-сервер. Вместо этого я хочу написать ретранслятор, который был бы посредником между клиентом (то есть веб-страницей) и jabber-сервером. Эта штука всего лишь будет передавать запросы от клиента jabber-серверу, а от jabber-сервера получать ответы и передавать их клиенту. Мой ретранслятор должен быть выполнен в виде вебсокет-сервера, потому что это обеспечивает наивысшую скорость и убирает весь оверхед, в отличие от того же BOSH.

Кстати, вебсокет-сервер я уже написал, прикрутить туда ретрансляцию на jabber-сервер думаю, не займет много времени.

Вернемся обратно к клиентской части на JS. От нее мне хочется простого - чтобы она умела парсить XMPP, а так же формировать его. Она не будет ничего передавать - только парсить и генерировать. Как дополнение - можно сделать обсервер, который бы реагировал на определенные события, чтобы можно было удобно обновлять интерфейс клиента - но это в виде отдельного компонента.

Я планирую все обдумать и, если мое решение не изменится - открыть проект на Github. Посему, мне требуются помощники, возможно - контрибуторы. Люди нужны такие - те, кто разбираются в XMPP, javascript-разработчики, ruby-разработчики, понимающие в создании socket-серверов.

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

Так что чуваки. Давайте объединяться. Если возникло желание, отписывайтесь в комментах, там разберемся. Ну и вообще, жду всяческих комментариев по данной теме - может я где-то неправ?

Ссылки:

  1. RFC 3920 — Протокол XMPP: Ядро
  2. XMPP Libraries

Про диджеев

Понедельник, Октябрь 11th, 2010

Я вообще не уважаю диджеев, которые сводят чужие треки и играют их в клубах. Офигеваю за тех, кто может сказать: “Пошли в клуб N, там будет играть такой-то Вася.”. Это особенно заметно в среде т.н. клабберов. При том все достижения этого Васи заключаются в том, что он научился сводить треки и стоять за пультом с таким видом, как будто управляет АЭС, иногдя отходя покурить или перетереть с местными свистками. Для меня это чмыри.

А уважаю тех, кто пишет и играет свою музыку, особенно trance :)