Я вполне могу подходить к этому неправильно, поэтому, пожалуйста, простите меня за мою наивность:
Чтобы изучить Clojure, я начал переносить свою клиентскую библиотеку OAuth для Python на Clojure. Я делаю это, обертывая clj-http так же, как обертываю запросы Python в библиотеке Python. Похоже, что до сих пор это работает довольно хорошо, и мне действительно нравится видеть, как реализация в Clojure оживает.
Однако у меня возникла проблема: я планирую поддерживать как OAuth 1.0, так и 2.0, и разделил соответствующие функции на два файла: oauth1.clj и oauth2.clj. Теперь каждый файл в идеале должен предоставлять набор функций, соответствующих HTTP-командам.
(ns accord.oauth2)
...
(defn get
[serv uri & [req]]
((:request serv) serv (merge req {:method :get :url uri})))
Эти функции будут практически идентичны и фактически полностью идентичны сейчас между oauth1.clj и oauth2.clj. Моей первой реакцией было переместить эти функции в core.clj, а затем потребовать их в соответствующих пространствах имен OAuth (oauth1, oauth2), чтобы не писать один и тот же код дважды.
Это нормально, если я использую указанные в файле функции, то есть oauth1.clj или oauth2.clj. Но предположим, что мы хотим использовать эту библиотеку так, как я намереваюсь (здесь, в REPL, или в вашей программе), примерно так:
=> (require '[accord.oauth2 :as oauth2]) ;; require the library's oauth2 namespace
...
=> (oauth2/get my-service "http://example.com/endpoint") ;; use the HTTP functions
Var oauth2/get
не найден, потому что перетаскивание его в пространство имен только в oauth2.clj, похоже, не раскрывает его, как если бы он действительно находился в этом пространстве имен. Я не хочу оборачивать их дополнительными функциями, потому что это в основном сводит на нет цель; функции настолько просты (они просто обертывают request
функцию), я бы написал их, по сути, в трех местах, если бы я сделал это.
Я уверен, что я не разбираюсь в пространствах имен в Clojure должным образом и, более того, возможно, в общем идиоматическом мышлении о проблемах абстракции и совместном использовании кода.
Так что мне интересно, что это за идиоматическое решение? Я ошибаюсь?
Изменить:
Вот упрощение проблемы: https://gist.github.com/maxcountryman/5228259
Обратите внимание, что цель состоит в том, чтобы один раз написать функции HTTP-глагола. Им не нужны особые типы отправки или что-то в этом роде. Они и так в порядке. Проблема в том, что они не доступны из accord.oauth1
или accord.oauth2
, т.е. когда вашей программе, например, требуется accord.oauth2
.
Если бы это был Python, мы могли бы просто импортировать такие функции: from accord.core import get, post, put, ...
в accord.oauth1
и accord.oauth2
, а затем, когда мы использовали бы модуль accord.oauth1
, у нас был бы доступ ко всем этим импортированным функциям, например import accord.oauth2 as oauth2
... oauth2.get(...)
.
Как мы можем сделать это в Clojure или как идиоматически обеспечить такую абстракцию DRY?
accord.core
. Затем пространства именaccord.oauth1
иaccord.oauth2
требуют этих функций путем ссылки: all fromaccord.core
на их соответствующие пространства имен. Проблема в том, что вы не можете затем потребоватьaccord.oauth2
, скажем, в REPL или из вашей программы, а затем использовать функции HTTP таким образом:accord.oauth2/get
. Если бы функции действительно были написаны дважды в каждом файле, это сработало бы. Однако я пытаюсь этого избежать. :) - person maxcountryman   schedule 23.03.2013=> (oauth2/get foo "http://example.com/") CompilerException java.lang.RuntimeException: No such var: oauth2/get, compiling:(NO_SOURCE_PATH:1)
- person maxcountryman   schedule 23.03.2013accord.oauth-common
, и импортируйте его, чтобы получить общие функции, или 2) просто используйтеdef
для повторного связывания функций, которые вы хотите в каждом пространстве имен (вместо того, чтобы полностью повторно объявлять их) , например(def get oauth1/get)
. Я лично выбрал бы первый вариант. - person DaoWen   schedule 23.03.2013require
или что-то еще? - person maxcountryman   schedule 23.03.2013require
илиuse
. - person DaoWen   schedule 23.03.2013accord.core
, где accord.core был третьим пространством имен. (Это сработало бы, если бы я использовал def, как вы указываете во втором варианте, но я бы не стал этого делать.) - person maxcountryman   schedule 23.03.2013(ns testing.bar (:use [test core baz]))
вbar.clj
. - person DaoWen   schedule 23.03.2013(require '[testing.bar :as bar])
...(bar/qux "test")
не дает такой var: bar / qux. Однако в bar.clj:(def qux baz/qux)
будет работать. Видишь, что я пытаюсь сделать? - person maxcountryman   schedule 23.03.2013(:use [test core baz])
, поскольку это всего лишь несколько дополнительных символов. - person DaoWen   schedule 24.03.2013