kindred 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +316 -0
- data/Rakefile +2 -0
- data/app/assets/javascripts/app.coffee +1 -0
- data/app/assets/javascripts/kindred.js +13 -0
- data/app/assets/javascripts/models/active_page.coffee +97 -0
- data/app/assets/javascripts/models/base.coffee +153 -0
- data/app/assets/javascripts/models/setup.coffee +97 -0
- data/app/assets/javascripts/utilities/binder.coffee +29 -0
- data/app/assets/javascripts/utilities/listener.coffee +74 -0
- data/app/assets/javascripts/utilities/logger.coffee +12 -0
- data/app/assets/javascripts/utilities/stack_trace.js +511 -0
- data/app/assets/javascripts/utilities/template.coffee +3 -0
- data/app/assets/javascripts/utilities/uuid.coffee +9 -0
- data/app/assets/javascripts/utilities/virtual_class.coffee +25 -0
- data/app/helpers/template_helper.rb +59 -0
- data/kindred.gemspec +26 -0
- data/lib/kindred/engine.rb +8 -0
- data/lib/kindred/version.rb +3 -0
- data/lib/kindred.rb +5 -0
- metadata +123 -0
| @@ -0,0 +1,97 @@ | |
| 1 | 
            +
            class App.Setup
         | 
| 2 | 
            +
              constructor: (@opts) ->
         | 
| 3 | 
            +
                @_set_name_properties()
         | 
| 4 | 
            +
                @_set_self()
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                @opts ||= {}
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                @attributes = {}
         | 
| 9 | 
            +
                template = @opts.template || App[@constructor.name].template
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                @_set_opts_to_attributes()
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                @_setup_route()
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                @uuid = @opts.uuid || @attributes.uuid || App.UUID.generate()
         | 
| 16 | 
            +
                @id = @opts.id || @attributes.id
         | 
| 17 | 
            +
                @target_uuid = @opts.target_uuid || @attributes.target_uuid
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                @attributes["uuid"] = @uuid
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                if template?
         | 
| 22 | 
            +
                  @template = template.replace(/\b(data-k-uuid)\.?[^\s|>]+/g, "data-k-uuid=" + @uuid)
         | 
| 23 | 
            +
                  @template = @template.replace(/\b(data-id)\.?[^\s|>]+/g, "data-id=" + @id)
         | 
| 24 | 
            +
                  @_build_attrs_template()
         | 
| 25 | 
            +
                  @_setup_interpolated_vars()
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              @set_class_name: (class_name) ->
         | 
| 28 | 
            +
                @class_name = class_name
         | 
| 29 | 
            +
                @dash_name =  @_get_dash_name(class_name)
         | 
| 30 | 
            +
                @snake_name = @_get_snake_name(class_name)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              @_get_class_name: ->
         | 
| 33 | 
            +
                @name
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              @_get_dash_name: (name) ->
         | 
| 36 | 
            +
                dash_str = name.replace /([A-Z])/g, ($1) ->
         | 
| 37 | 
            +
                  "-" + $1.toLowerCase()
         | 
| 38 | 
            +
                dash_str[1 .. dash_str.length - 1]
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              @_get_snake_name: (name) ->
         | 
| 41 | 
            +
                under_str = name.replace /([A-Z])/g, ($1) ->
         | 
| 42 | 
            +
                  "_" + $1.toLowerCase()
         | 
| 43 | 
            +
                under_str[1 .. under_str.length - 1]
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              _set_self: ->
         | 
| 46 | 
            +
                @_self = @
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              _set_name_properties: =>
         | 
| 49 | 
            +
                @class_name ||= App[@constructor.name].class_name
         | 
| 50 | 
            +
                @dash_name =  App[@constructor.name]._get_dash_name(@class_name)
         | 
| 51 | 
            +
                @snake_name = App[@constructor.name].snake_name
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              _build_attrs_template: ->
         | 
| 54 | 
            +
                $.each @attributes, (attr, val) =>
         | 
| 55 | 
            +
                  j_attr = $(@template).find("[data-k-uuid='" + @uuid + "'][data-attr='" + attr + "']")
         | 
| 56 | 
            +
                  clone = j_attr.clone()
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  replace_string = clone.wrap('<span/>').parent().html()
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  cloned_template = $(@template).clone()
         | 
| 61 | 
            +
                  updated_template = $("<div />").append($(@template).clone()).html()
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  if replace_string? && replace_string.length
         | 
| 64 | 
            +
                    replace_regex = new RegExp(replace_string)
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    new_attr_string = replace_string.replace(/\b(data-val)\.?[^\s]+/g, "data-val='" + val + "'")
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    @template = updated_template.replace(replace_regex, new_attr_string)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              _setup_interpolated_vars: =>
         | 
| 71 | 
            +
                template_match = new RegExp(/{{([^{}]+)}}/g)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                new_template = @template.replace(template_match, (match, p1) =>
         | 
| 74 | 
            +
                  class_name = match.split(".")[0].substr(2)
         | 
| 75 | 
            +
                  attribute = match.split(".")[1].slice(0, - 2)
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  if class_name == @snake_name && (typeof @get(attribute) != 'undefined')
         | 
| 78 | 
            +
                    @get(attribute)
         | 
| 79 | 
            +
                  else
         | 
| 80 | 
            +
                    match
         | 
| 81 | 
            +
                )
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                @template = new_template
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              _set_opts_to_attributes: ->
         | 
| 86 | 
            +
                if @opts?
         | 
| 87 | 
            +
                  $.each @opts, (key, val) =>
         | 
| 88 | 
            +
                    @set key, val
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              _setup_route: =>
         | 
| 91 | 
            +
                url_match = new RegExp(/{{([^{}]+)}}/g)
         | 
| 92 | 
            +
                @route ||= App[@constructor.name].route
         | 
| 93 | 
            +
                @route ||= "/" + @snake_name + "s"
         | 
| 94 | 
            +
                @route = @route.replace(url_match, (match, p1) =>
         | 
| 95 | 
            +
                  attribute = match.slice(2, - 2)
         | 
| 96 | 
            +
                  @get(attribute)
         | 
| 97 | 
            +
                )
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            App.DataBinder = (object_id, model_name) ->
         | 
| 2 | 
            +
              # Use a jQuery object as simple PubSub
         | 
| 3 | 
            +
              pubSub = jQuery({})
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              # We expect a `data` element specifying the binding
         | 
| 6 | 
            +
              # in the form: data-bind-<object_id>="<property_name>"
         | 
| 7 | 
            +
              data_attr = "input"
         | 
| 8 | 
            +
              message = object_id + ":change"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              jQuery(document).on "keydown", "[data-input]", (evt) ->
         | 
| 11 | 
            +
                $input = jQuery(this)
         | 
| 12 | 
            +
                pubSub.trigger message, [
         | 
| 13 | 
            +
                  $input.data(data_attr)
         | 
| 14 | 
            +
                  $input.val()
         | 
| 15 | 
            +
                ]
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              # PubSub propagates changes to all bound elements, setting value of
         | 
| 18 | 
            +
              # input tags or HTML content of other tags
         | 
| 19 | 
            +
              pubSub.on message, (evt, prop_name, new_val, id) ->
         | 
| 20 | 
            +
                # model_name = "purchase-order"
         | 
| 21 | 
            +
                model_elem = jQuery("[data-model-" + model_name + "=" + object_id + "]")
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                attr_elm = model_elem.find("[data-attr=" + prop_name + "]")
         | 
| 24 | 
            +
                input_elm = $("[data-input=" + prop_name + "]")
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                input_elm.val new_val
         | 
| 27 | 
            +
                attr_elm.html new_val
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              pubSub
         | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            class App.Listener
         | 
| 2 | 
            +
              # Create a closure so that we can define intermediary
         | 
| 3 | 
            +
              # method pointers that don't collide with other items
         | 
| 4 | 
            +
              # in the global name space.
         | 
| 5 | 
            +
              (->
         | 
| 6 | 
            +
                # Store a reference to the original remove method.
         | 
| 7 | 
            +
                originalOnMethod = jQuery.fn.on
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                # Define overriding method.
         | 
| 10 | 
            +
                jQuery.fn.on = ->
         | 
| 11 | 
            +
                  if (jQuery.type( arguments[0] ) == "string")
         | 
| 12 | 
            +
                    listener_function = arguments[2]
         | 
| 13 | 
            +
                    element = arguments[1]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    listener_namespace = arguments[0].split(".")
         | 
| 16 | 
            +
                    event_trigger = listener_namespace[0]
         | 
| 17 | 
            +
                    listener_name = listener_namespace[listener_namespace.length - 1]
         | 
| 18 | 
            +
                    namespaces = listener_namespace.slice(1, -1)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    listener_info = {
         | 
| 21 | 
            +
                      trigger: event_trigger,
         | 
| 22 | 
            +
                      element: element,
         | 
| 23 | 
            +
                      name: listener_name,
         | 
| 24 | 
            +
                      funct: listener_function
         | 
| 25 | 
            +
                    }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    App.Listener.createNestedObject(App, namespaces, listener_info)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    # Execute the original method.
         | 
| 30 | 
            +
                    originalOnMethod.apply this, arguments
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  else
         | 
| 33 | 
            +
                    originalOnMethod.apply this, arguments
         | 
| 34 | 
            +
              )()
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              @createNestedObject = (base, names, value) ->
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # If a value is given, remove the last name and keep it for later:
         | 
| 39 | 
            +
                lastName = (if arguments.length is 3 then names.pop() else false)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                # Walk the hierarchy, creating new objects where needed.
         | 
| 42 | 
            +
                # If the lastName was removed, then the last object is not set yet:
         | 
| 43 | 
            +
                i = 0
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                while i < names.length
         | 
| 46 | 
            +
                  base = base[names[i]] = base[names[i]] or {}
         | 
| 47 | 
            +
                  i++
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                # If a value was given, set it to the last name:
         | 
| 50 | 
            +
                if Array.isArray(base[lastName])
         | 
| 51 | 
            +
                  base[lastName].push(value)
         | 
| 52 | 
            +
                else
         | 
| 53 | 
            +
                  base[lastName] = [value]
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                # Return the last object in the hierarchy:
         | 
| 56 | 
            +
                base
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              @params: (obj) ->
         | 
| 59 | 
            +
                # Stupid hack because jQuery converts data to camelCase
         | 
| 60 | 
            +
                keys = Object.keys(obj)
         | 
| 61 | 
            +
                n = keys.length
         | 
| 62 | 
            +
                newobj = {}
         | 
| 63 | 
            +
                while n--
         | 
| 64 | 
            +
                  key = keys[n]
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  if keys[n] == "kUuid"
         | 
| 67 | 
            +
                    snake_key = "uuid"
         | 
| 68 | 
            +
                  else
         | 
| 69 | 
            +
                    snake_key = key.replace(/([A-Z])/g, ($1) ->
         | 
| 70 | 
            +
                      "_" + $1.toLowerCase()
         | 
| 71 | 
            +
                      )
         | 
| 72 | 
            +
                  newobj[snake_key] = obj[key]
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                newobj
         | 
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            class App.Logger
         | 
| 2 | 
            +
              @add_error: (error_object) ->
         | 
| 3 | 
            +
                @errors ||= []
         | 
| 4 | 
            +
                @errors.push(error_object)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            jQueryInit = $.fn.init
         | 
| 7 | 
            +
            $.fn.init = (selector, context) ->
         | 
| 8 | 
            +
              element = new jQueryInit(selector, context)
         | 
| 9 | 
            +
              if selector and element.length is 0
         | 
| 10 | 
            +
                App.Logger.add_error({selector_not_found: selector, stack_trace: printStackTrace()})
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              element
         |