Как разработчики Ruby, мы часто забываем, насколько мы хороши. У нас есть замечательная система распространения библиотек в Rubygems, мы используем мощный и гибкий язык, который просто нужно использовать для DSL, а также культуру открытой разработки и улучшения сообщества. В этой статье я расскажу о том, как вы можете добавить этот дополнительный блеск в свой новый полезный камень. Мы рассмотрим некоторые распространенные шаблоны использования и то, как они достигаются в популярных драгоценных камнях.
Ни один из этих советов не предоставляет каких-либо дополнительных функций для вашего драгоценного камня, но они обеспечивают хороший пользовательский опыт. Если у вас есть амбиции, чтобы ваш драгоценный камень стал популярным, UI имеет значение, а код — это интерфейс для программистов.
Блок конфигурации
Если вы даже были ненадолго рубином, вы видели это. Метод, который я собираюсь показать, полезен для глобальной настройки драгоценного камня. Давайте взглянем:
#Usage: | |
# Awesome.configure do |a| | |
# a.magic_number = 3 | |
# end | |
module Awesome | |
class << self | |
attr_accessor :configuration | |
def config | |
self.configuration ||= Configuration.new | |
end | |
def configure | |
yield(config) | |
end | |
end | |
# The configuration object. | |
class Configuration < Hashie::Mash | |
end | |
end |
Итак, здесь есть несколько вещей, на которые стоит обратить внимание. Во-первых, я решил, что объект Configuration
наследуется от фантастического камня hashie mash . Это позволяет нам рассматривать наш объект Configuration
как хеш-код внутри, но означает, что мы можем предоставить хороший синтаксис точек в самом блоке. В примере a.magic_number = 3
приводит к эквиваленту a[:magic_number] = 3
. Во-вторых, метод config
действует так же, как метод instance
который вы написали бы, если бы создавали одноэлементный класс, поэтому нам не нужно беспокоиться о множественных экземплярах конфигурации, которые могут возникнуть.
Цепная реакция
Если вы пишете гем, который выполняет какой-то поиск в интерфейсе, то вы сталкиваетесь с некоторой проблемой API: как я могу позволить своим пользователям создавать сложные поиски, избегая при этом неправильного хеширования? Чтобы проиллюстрировать, что я имею в виду, давайте посмотрим на Active Record 2 против 3:
##AR 2 | |
Person.find(:all, :conditions => {:name => «Joe», :is_admin => true}, :limit => 10) | |
##=> Array | |
#AR 3 | |
Person.where(:name => ‘joe’, :is_admin => true).limit(10) | |
##=> ActiveRecord::Relation |
В примере Active Record 3 цепочка методов используется вместо потенциально массивного хэша для определения нашего запроса. Это также дает дополнительное преимущество, позволяя нам легко определять наш запрос с течением времени и выполнять его только тогда, когда мы уверены, что он нам нужен, вызывая метод, который вызывает фактическое выполнение запроса — например, each
.
Давайте создадим действительно простой метод цепочки поиска объекта:
class Search | |
class << self | |
def where(args) | |
Search.new(args) | |
end | |
end | |
def initialize(args) | |
@search_arguments = args | |
end | |
def where(args) | |
@search_arguments.merge!(args) | |
self | |
end | |
def each | |
results.each{|a| yield(a)} | |
end | |
def results | |
#Go and get some results! | |
end | |
end | |
#irb(main):084:0> Search.where(:foo => ‘bar’).where(:magic_number => 3) | |
#=> #<Search:0x10aa5e9d8 @search_arguments={:foo=>»bar», :magic_number=>3}> |
Как вы можете видеть, мы создали довольно убедительный поисковый интерфейс, который на первый взгляд выглядит как синтаксис запросов в Active Records 3. Все, что вам нужно сделать, это вернуть self
в конце цепных методов, чтобы получить эффект. Обратите внимание, что each
является хорошим дополнением, поскольку он устраняет необходимость явного вызова results
.
Включите все, где это уместно.
Как я уверен, вы знаете, что вы можете include
модуль для добавления методов экземпляра модуля в класс или extend
модуль для добавления методов класса. Часто при написании драгоценных камней нам нужно делать и то, и другое. Добавление нескольких строк в документацию, чтобы объяснить пользователям, что нам нужно включить Library::InstanceMethods
и расширить Library::ClassMethods
— это один из подходов, но это еще одна вещь, о которой пользователь должен беспокоиться, и нет никаких причин, по которым пользователь может Library::ClassMethods
не улучшится
HTTParty , пожалуй, наиболее часто используемый пример этого шаблона.
Использование HTTPart по умолчанию выглядит примерно так:
class Thingy | |
include HTTParty | |
end |
Класс Thingy
теперь будет иметь очень удобный экземпляр метода post
для запросов POST, но также будет иметь возможность вызывать post
класса, если вашему приложению на самом деле не нужно создавать новые Thingy
а просто использовать их.
Как это работает? Давайте посмотрим на источник — скопированный с github с некоторыми битами, вырезанными для ясности:
module HTTParty | |
def self.included(base) | |
base.extend ClassMethods | |
#snip | |
base.instance_variable_set(«@default_options», {}) | |
end | |
end |
self.included
— это метод, определенный в self.included
классе Module
Ruby, который вызывается при включении модуля в другой класс. base
, в данном примере, является класс Thingy
. Переопределив его, мы теперь можем расширять Thingy
с помощью HTTPart::ClassMethods
сохраняя пользователю дополнительное расширение.
Кроме того, поскольку у нас есть дескриптор Thingy
мы можем выполнить некоторую настройку объекта — как в приведенном здесь примере, мы установили некоторые значения по умолчанию.
included
метод и его extended
аналог действительно эффективны, но им легко злоупотреблять. В тех местах, где вам не нужно включать и расширять, на самом деле нет необходимости мутить воду, переопределяя included
просто чтобы вы могли использовать include, чтобы ввести методы класса — там будут драконы.
Оказаться полезным
Это не подсказка кода — просто то, что я наткнулся на этой неделе и не могу рекомендовать достаточно. Как разработчики, возможно, особенно как веб-разработчики, мы все должны знать, что пользователю требуется по x секунд, чтобы покинуть страницу, если у него плохое время . Я подозреваю, что то же самое относится и к программистам, когда они плохо настраивают гем.
Дружественные исключения — один из способов помочь. Попробуйте заменить сообщение об исключении «Файл конфигурации не найден» на «Не удается найти файл конфигурации: config / awesome.yml» — возможно, вы могли бы даже вставить URL-адрес в документацию там.
В конечном счете, опыт вашего драгоценного камня в основном зависит от качества и полезности кода. Даже библиотеки, которые не очень удобны для пользователя, могут добиться большого успеха так же, как и программное обеспечение, которое не очень удобно для пользователя, может необъяснимым образом процветать.
Мне было бы интересно услышать ваши опыты с популярными жемчужинами. Есть ли что-то, что недавно привлекло внимание программиста? Есть ли что-то, что из больших драгоценных камней делает это раздражающим? Как всегда, я буду скрываться в разделе комментариев или на машине Tweet — свяжитесь с нами.