<commit_before>_ = require 'underscore'
$ = require 'jquery'
Backbone = require 'backbone'
Backbone.$ = require 'jquery' # TODO remove after upgrading to backbone 1.2+
inflection = require 'inflection'

Utils = require './utils'
StorageManager = require './storage-manager'


class Model extends Backbone.Model

  #
  # Properties

  @OPTION_KEYS =  ['name', 'include', 'cacheKey']


  #
  # Class Methods

  # Retreive details about a named association.  This is a class method.
  #     Model.associationDetails("project") # => {}
  #     timeEntry.constructor.associationDetails("project") # => {}
  @associationDetails: (association) ->
    @associationDetailsCache ||= {}
    if @associations && @associations[association]
      @associationDetailsCache[association] ||= do =>
        associator = @associations[association]
        isArray = _.isArray associator
        if isArray && associator.length > 1
          {
            type: 'BelongsTo'
            collectionName: associator
            key: "#{association}_ref"
            polymorphic: true
          }
        else if isArray
          {
            type: 'HasMany'
            collectionName: associator[0]
            key: "#{inflection.singularize(association)}_ids"
          }
        else
          {
            type: 'BelongsTo'
            collectionName: associator
            key: "#{association}_id"
          }

  # Parse ISO8601 attribute strings into date objects
  @parse: (modelObject) ->
    for k,v of modelObject
      # Date.parse will parse ISO 8601 in ECMAScript 5, but we include a shim for now
      if /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}[-+]\d{2}:\d{2}$/.test(v)
        modelObject[k] = Date.parse(v)
    return modelObject


  #
  # Init

  constructor: (attributes = {}, options = {}) ->
    @storageManager = StorageManager.get()

    try cache = @storageManager.storage(@brainstemKey)

    if options.cached != false && attributes.id && @brainstemKey && cache
      existing = cache.get(attributes.id)
      blacklist = options.blacklist || @_associationKeyBlacklist()
      valid = existing?.set(_.omit(attributes, blacklist))

      return existing if valid

    super

  _associationKeyBlacklist: ->
    return [] unless @constructor.associations

    _.chain(@constructor.associations)
      .keys()
      .map((association) => @constructor.associationDetails(association).key)
      .value()


  #
  # Accessors

  # Override Model#get to access associations as well as fields.
  get: (field, options = {}) ->
    if details = @constructor.associationDetails(field)
      if details.type == 'BelongsTo'
        pointer = super(details.key) # project_id
        if pointer
          if details.polymorphic
            id = pointer.id
            collectionName = pointer.key
          else
            id = pointer
            collectionName = details.collectionName

          model = @storageManager.storage(collectionName).get(pointer)

          if not model && not options.silent
            Utils.throwError("
              Unable to find #{field} with id #{id} in our cached {details.collectionName} collection.
              We know about #{@storageManager.storage(details.collectionName).pluck('id').join(', ')}
            ")

          model
      else
        ids = super(details.key) # time_entry_ids
        models = []
        notFoundIds = []
        if ids
          for id in ids
            model = @storageManager.storage(details.collectionName).get(id)
            models.push(model)
            notFoundIds.push(id) unless model
          if notFoundIds.length && not options.silent
            Utils.throwError("
              Unable to find #{field} with ids #{notFoundIds.join(', ')} in our
              cached #{details.collectionName} collection.  We know about
              #{@storageManager.storage(details.collectionName).pluck('id').join(', ')}
            ")
        if options.order
          klass = @storageManager.getCollectionDetails(details.collectionName).klass
          comparator = klass.getComparatorWithIdFailover(options.order)
          collectionOptions = { comparator: comparator }
        else
          collectionOptions = {}
        if options.link
          @_linkCollection(details.collectionName, models, collectionOptions, field)
        else
          @storageManager.createNewCollection(details.collectionName, models, collectionOptions)
    else
      super(field)

  className: ->
    @paramRoot


  #
  # Control

  fetch: (options) ->
    options = if options then _.clone(options) else {}

    id = @id || options.id

    options.only = [id] if id
    options.parse = options.parse ? true
    options.name = options.name ? @brainstemKey<commit_msg>Use multi-line interpolation comment blocks correctly (-_-｡)<commit_after>_ = require 'underscore'
$ = require 'jquery'
Backbone = require 'backbone'
Backbone.$ = require 'jquery' # TODO remove after upgrading to backbone 1.2+
inflection = require 'inflection'

Utils = require './utils'
StorageManager = require './storage-manager'


class Model extends Backbone.Model

  #
  # Properties

  @OPTION_KEYS =  ['name', 'include', 'cacheKey']


  #
  # Class Methods

  # Retreive details about a named association.  This is a class method.
  #     Model.associationDetails("project") # => {}
  #     timeEntry.constructor.associationDetails("project") # => {}
  @associationDetails: (association) ->
    @associationDetailsCache ||= {}
    if @associations && @associations[association]
      @associationDetailsCache[association] ||= do =>
        associator = @associations[association]
        isArray = _.isArray associator
        if isArray && associator.length > 1
          {
            type: 'BelongsTo'
            collectionName: associator
            key: "#{association}_ref"
            polymorphic: true
          }
        else if isArray
          {
            type: 'HasMany'
            collectionName: associator[0]
            key: "#{inflection.singularize(association)}_ids"
          }
        else
          {
            type: 'BelongsTo'
            collectionName: associator
            key: "#{association}_id"
          }

  # Parse ISO8601 attribute strings into date objects
  @parse: (modelObject) ->
    for k,v of modelObject
      # Date.parse will parse ISO 8601 in ECMAScript 5, but we include a shim for now
      if /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}[-+]\d{2}:\d{2}$/.test(v)
        modelObject[k] = Date.parse(v)
    return modelObject


  #
  # Init

  constructor: (attributes = {}, options = {}) ->
    @storageManager = StorageManager.get()

    try cache = @storageManager.storage(@brainstemKey)

    if options.cached != false && attributes.id && @brainstemKey && cache
      existing = cache.get(attributes.id)
      blacklist = options.blacklist || @_associationKeyBlacklist()
      valid = existing?.set(_.omit(attributes, blacklist))

      return existing if valid

    super

  _associationKeyBlacklist: ->
    return [] unless @constructor.associations

    _.chain(@constructor.associations)
      .keys()
      .map((association) => @constructor.associationDetails(association).key)
      .value()


  #
  # Accessors

  # Override Model#get to access associations as well as fields.
  get: (field, options = {}) ->
    if details = @constructor.associationDetails(field)
      if details.type == 'BelongsTo'
        pointer = super(details.key) # project_id
        if pointer
          if details.polymorphic
            id = pointer.id
            collectionName = pointer.key
          else
            id = pointer
            collectionName = details.collectionName

          model = @storageManager.storage(collectionName).get(pointer)

          if not model && not options.silent
            Utils.throwError("""
              Unable to find #{field} with id #{id} in our cached {details.collectionName} collection.
              We know about #{@storageManager.storage(details.collectionName).pluck('id').join(', ')}
            """)

          model
      else
        ids = super(details.key) # time_entry_ids
        models = []
        notFoundIds = []
        if ids
          for id in ids
            model = @storageManager.storage(details.collectionName).get(id)
            models.push(model)
            notFoundIds.push(id) unless model
          if notFoundIds.length && not options.silent
            Utils.throwError("""
              Unable to find #{field} with ids #{notFoundIds.join(', ')} in our
              cached #{details.collectionName} collection.  We know about
              #{@storageManager.storage(details.collectionName).pluck('id').join(', ')}
            """)
        if options.order
          klass = @storageManager.getCollectionDetails(details.collectionName).klass
          comparator = klass.getComparatorWithIdFailover(options.order)
          collectionOptions = { comparator: comparator }
        else
          collectionOptions = {}
        if options.link
          @_linkCollection(details.collectionName, models, collectionOptions, field)
        else
          @storageManager.createNewCollection(details.collectionName, models, collectionOptions)
    else
      super(field)

  className: ->
    @paramRoot


  #
  # Control

  fetch: (options) ->
    options = if options then _.clone(options) else {}

    id = @id || options.id

    options.only = [id] if id
    options.parse = options.parse ? true
    options.name = options.name ? @brainstemKey
{'fraction_non_alphanumeric': 0.11240922008209663, 'fraction_numerical': 0.004420587306599305, 'mean_word_length': 2.367115520907158, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '301256', 'n_tokens_mistral': 2663, 'n_tokens_neox': 2562, 'n_words': 623}