NodeJS: сохранение файлов библиотеки СУХИМИ

#node.js #coffeescript #dry

#node.js #кофейный скрипт #сухой

Вопрос:

Недавно я начал работать над нетривиальным проектом в CoffeeScript, и я изо всех сил пытаюсь понять, как лучше всего справиться с регистрацией экспорта и т. Д. Я пишу это в очень «питоновской» манере, при этом отдельные файлы фактически являются «модулями» связанных классов и функций. То, что я ищу, — это лучший способ определять классы и функции локально И в exports / window с минимальным повторением, насколько это возможно.

На данный момент я использую следующее в каждом файле, чтобы сохранить запись exports.X = X для всего, что находится в файле:

 class module
  # All classes/functions to be included in exports should be defined with `@`
  # E.g.
  class @DatClass

exports[name] = item for own name, item of module
 

Я также рассмотрел возможность использования функции (скажем, publish ), которая помещает переданный класс в exports / window в зависимости от его имени:

 publish = (f) ->
  throw new Error 'publish only works with named functions' unless f.name?
  ((exports ? window).namespace ?= {})[f.name] = f

publish class A
# A is now available in the local scope and in `exports.namespace`
# or `window.namespace`
 

Это, однако, не работает с функциями, поскольку, насколько я знаю, они не могут быть «названы» в CoffeeScript (например f.name , всегда '' ) и поэтому publish не могут определить правильное имя.

Есть ли какой-либо метод, который работает как publish , но работает с функциями? Или какие-нибудь альтернативные способы справиться с этим?

Ответ №1:

Это уродливый взлом, но вы можете использовать следующее :

 class module.exports
  class @foo
    @bar = 3
 

И затем :

 require(...).foo.bar // 3
 

Комментарии:

1. Халтурно, конечно, но, по общему признанию, аккуратно. Работает / w функции тоже, если вы объявляете, как @fn = (аргументы …) -> ‘результаты fn()’. Я, например, буду испытывать сильное искушение воспользоваться этим..

2. Это очень аккуратно. Что делает его хакерским?

3. На мой взгляд, это лишь слегка хакерский метод, поскольку он не упоминается ни в одной официальной документации. Однако это может быть связано с тем, что он падает на стулья Node amp; CoffeeScript.

4. Хитрая часть заключается в том, что здесь вы назначаете функцию (фактически конструктор) module.exports и добавляете атрибуты к этой функции в виде аналогичных статических методов, вместо того, чтобы назначать объект module.exports . Более того, если вам нужно использовать bar что-то в этом модуле, вам нужно будет использовать полный путь exports.foo.bar , что является проблемой.

5. На самом деле это может быть сокращено в целом из-за того, что шаблонный Coffeescript присоединяется к скомпилированному javascript: (function() { ... }).call(this); — это означает, что область действия скрипта уже module.exports определена, поэтому вы можете отбросить внешнее class module.exports определение и перейти прямо к class @foo нему.

Ответ №2:

Старый

 (function (exports) {
  // my code

  exports.someLib = ...
})(typeof exports === "undefined" ? window : exports);
 

Это ловкий трюк, который должен делать то, что вы хотите.

Если написание этой шаблонной оболочки является проблемой, автоматизируйте ее с помощью сценария сборки.

Комментарии:

1. Я полагаю, что CoffeeScript уже делает именно это, когда компилирует скрипт. Моя проблема с этим заключается в том, что правильное определение классов приводит к повторению: exports.SomeClass = class SomeClass , без второго SomeClass результирующая функция не будет иметь name атрибута, который запрещает полезное отражение.

2. @connec ну и что? серьезно. просто сделайте class SomeClass , а затем сделайте exports.SomeClass = SomeClass в конце. Я не называю это повторением, я называю это наличием четко определенного экспорта.

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

4. @connec почему вы экспортируете десятки классов. Проблема не в том, что это утомительно, проблема в том, что ваш API раздут.

Ответ №3:

То, что я ищу, — это лучший способ определять классы и функции локально И в exports / window с минимальным повторением, насколько это возможно.

Невозможно сделать что-то вроде

 exports.x = var x = ...;
 

не записывая x дважды в JavaScript (не прибегая к черной магии, т. Е. eval ), И то же самое касается CoffeeScript. Облом, я знаю, но так оно и есть.

Мой совет был бы не слишком зацикливаться на этом; такое повторение является обычным явлением. Но спросите себя: «Мне действительно нужно экспортировать эту функцию или переменную и сделать ее доступной локально?» Чисто разделенный код обычно так не работает.

Ответ №4:

Есть исключение из правила «без именованных функций»: классы. Это работает: http://jsfiddle.net/PxBgn /

 exported = (clas) ->
  console.log clas.name
  window[clas.name] = clas
...
exported class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5