Lightweight MVC library for building JavaScript applications

Related tags

Frameworks spine
Overview

Spine

Build Status Gitter

Spine is a lightweight MVC library for building JavaScript web applications. Spine gives you structure and then gets out of your way, allowing you to concentrate on the fun stuff: building awesome web applications.

Spine is opinionated in its approach to web application architecture and design. Spine's architecture complements patterns such as de-coupled components and CommonJS modules, markedly helping with code quality and maintainability.

The library is written in CoffeeScript, and though it doesn't necessarily require CoffeeScript to develop applications - you can use whichever language you're most familiar with or prefer - the documentation and some associated tools like Hem and spine.app cater to those who prefer CoffeeScript's syntax.

Learn it

Documentation is often incomplete or just lies waiting to happen. Approachable source code reduces knowledge dependencies. This is an area where Spine really excels compared to other MVC frameworks. Spine is tiny; the core library comes in at less than 700 lines of CoffeeScript code. It is written in such a way to prefer readability over terseness or clever tricks, and it is small enough that within a rather small timeframe you can understand how all the pieces work together. Expertise is achievable within days or weeks rather than months or years. For these reasons, remaining lightweight and simple is fundamental to Spine.

For documentation, usage, and examples, see: spine.github.io

The test suite can also occasionally provide additional useful examples, especially if you are looking for non-coffeescript examples.

Contributing

Reporting issues

To file a bug report, please visit the GitHub issues page. It's great if you can attach code (test cases and fixes for bugs, and test cases and a proposed implementation for features), but reproducible bug reports are also welcome.

For support or help with using spine please use the Spine Google Group and/or StackOverflow rather than opening an issue on Github. If you post in those places you are more likely to get more people to chime in, and others can benefit from it more readily.

Cloning master and running the test suite

To get started contributing to Spine, first clone the repository and make sure you can run the test suite. If you're not familiar with Git, visit the Git homepage to download Git for your platform.

First, clone the repository:

$ git clone git://github.com/spine/spine.git
$ cd spine

Next, You will need node and npm to pull in the testing libraries. Once you're all set with those then from within the Spine repo directory run

$ npm install

This will install CoffeeScript and the Karma test runner.

At this point you can easily run the complete test suite using

$ npm test

But this isn't very practical for development, since it runs the test suite multiple times agains different versions of jQuery.

A better approach is to install the Karma CLI

$ npm install -g karma-cli

Then you can use $ karma start to run the tests using the latest stable version of jQuery. Karma will keep running in the background and re-run tests whenever you change any files. When the Karma server is running, you can debug tests in your browser by visiting http://localhost:9876/debug.html.

It's also possible to test agains a specific version of jQuery if needed: $ JQUERY_VERSION=1.9.1 karma start.

Contributing to the Spine documentation

Perhaps the easiest way to get started with contributing is through the docs. If you find typos, bugs, or omissions in the docs, please submit a pull request to fix. The Spine website, which is the primary documentation, is a very simple Wintersmith app at spine.github.io. Basic markdown familiarity is probably all you need to be able to make changes.

Contributing to the Spine code

This recommended contribution process is based on the Ruby on Rails contribution guide. In general, please include tests with new features or bugfixes, work in a feature branch until you're ready to submit or discuss your code, then fork the repository, push to your fork, and issue a pull request.

CoffeeScript

When submitting a pull request for code, please submit in CoffeeScript. Building the effected js files is required for testing sake, but submitting those js files is optional.

Assuming you have Node.js and npm already installed then proceed by installing local dev dependencies:

$ npm install

Then use the provided build scripts to compile your CoffeeScript files:

$ cake build
$ cake watch

These tasks use a locally installed copy of CoffeeScript to ensure all contributors use the same version of the compiler.

Git

Let's say I'm going to submit a patch to add someFeatureFix:

$ git checkout dev

Feature branches should start from dev not master. If you branch off of, or do builds on the master branch you will get CoffeeScript source map files, which are cool, but tend to ruin automatic merges with git.

$ git checkout -b someFeatureFix
$ vim test/...
  # (...add tests...)
$ cake watch
  # (...this should recompile and changes you make in your CoffeeScript...)

-- figure out what spine module your changes belong in
$ vim src/spine.coffee
or
$ vim src/[otherSpineComponent].coffee
  # (...add the feature/fix...)
$ open test/index.html
  # (...make sure tests run for each component that was changed...)
  # (...test in other browsers with various jquery versions if you feel like there is risk... )
$ git commit -m "Add Some Feature Fix"

Then, fork the Spine repository, and push your branch to your fork:

$ git remote add <your user name> [email protected]:<your user name>/spine.git
$ git push <your user name> someFeatureFix

Finally, issue a pull request from inside the GitHub interface to the dev branch of spine, and your contribution is ready for consideration, discussion, and (hopefully) merging in!

Comments
  • fromForm sets true/false for boolean checkboxes & sets arrays for checkboxes for sets

    fromForm sets true/false for boolean checkboxes & sets arrays for checkboxes for sets

    When instantiating a model from a form with checkboxes, determine the value of the property from the checkbox's checked state. And set array values for checkboxes used to select many from a set.

    opened by jonstorer 29
  • Swap out Cake for Grunt

    Swap out Cake for Grunt

    Hi, I just try to swap out cake for grunt

    Cake build -> grunt build Cake watch -> grunt watch

    Additionally, I add a lint task(coffeelint) for coffeescript, I do find some lint errors in the source, like mix tab indentation and spaces, maybe we can add some configurations for our code style later, I'm not sure yet.

    refactor 
    opened by hulufei 29
  • Why does SomeModel.find(id) return non-identical instances?

    Why does SomeModel.find(id) return non-identical instances?

    I can see that by design every call to find(id) returns a distinct instance. I wonder what is the reason behind it.

    This kind of behaviour causes problems at times, as it makes updating the same instance virtually impossible. This is very often inconvenient as different controllers might keep references to models and want respond to their changes. However the way it works now, references just become obsolete. It's very difficult to predict when exactly that happens.

    opened by julkiewicz 21
  • Model instance unbind() behavior inconsistent

    Model instance unbind() behavior inconsistent

    Most Spine objects have the standard Events behavior, bind('event', callback) and unbind('event', callback). Unbinding specific callbacks is important when dynamically creating/releasing controllers so that they are properly garbage collected.

    The behavior of the Model instance's unbind(), which is to unbind all callbacks for all events on that model instance (ignoring arguments), is a little surprising. I would expect all events to be unbound only if calling it with no arguments, and wiping all bound events can cause unexpected behavior if you have modularized your controllers to only clean up their own artifacts and not touch other controllers' events.

    I realize that instance-level events on models end up creating wrapper events on the parent model's class, but it seems like a good idea to keep those wrapper functions around so that calls to unbind() can work as they do throughout the rest of the app, without outside code having to worry about implementation details.

    opened by ghost 19
  • Allow url and scope to be set to functions on both Spine.Model class and instance (Fixes #472)

    Allow url and scope to be set to functions on both Spine.Model class and instance (Fixes #472)

    This commit changes the Spine.Ajax module to allow both url and scope to be set at a function. So that all of the rolling work:

    User.scope = -> 'foo'
    var user = new User id: 1
    Spine.Ajax.getURL(User) is '/foo/users'
    Spine.Ajax.getURL(user) is '/foo/users/1'
    
    User.url = -> 'bar'
    Spine.Ajax.getURL(User) is '/foo/bar'
    Spine.Ajax.getURL(user) is '/foo/bar/1'
    
    user.scope = -> 'baz'
    Spine.Ajax.getURL(user) is '/baz/bar/1'
    
    user.url = -> 'qux'
    Spine.Ajax.getURL(user) is '/baz/qux/1'
    

    The biggest issue that was preventing this from working properly before, was that the Ajax module was checking to see if url was set as a function in order to test if it had been changed (presumably to a string). If url was a function the module assumed that it should continue to the default behavior, instead of calling the function. I've introduced another function that is included on the model—generateURL—that mirrors the default value of url, so that changes to the url method can be checked more reliably. Not sure if this is a particularly elegant solution, but it seems to work fine.

    All tests still pass without changes, so I'm pretty sure that this should be fully backwards compatible. I've added tests for the use cases that I couldn't accomplish in previous versions.

    opened by jackjennings 15
  • Model updateAttribute sometimes won't stick

    Model updateAttribute sometimes won't stick

    I have a method that updates all the items attribute, and a method that updates only a single items attribute. But when I update a single attribute, it no longer updates when I call the method that updates all the attributes.

    Simplified testcase

    opened by sindresorhus 15
  • The Model can accept the scope to do the url scoping, especially useful for nested resources.

    The Model can accept the scope to do the url scoping, especially useful for nested resources.

    The scope can either be a function or string. The generated url will be "scope/resources".

    For example,

    Ticket.scope = "/projects/1"
    Ticket.url() ->  "/projects/1/tickets"
    new Ticket({id: 1}).url() -> "/projects/1/tickets/1"
    
    Ticket.prototype.scope = function() { return "/projects/" + this.project_id }
    new Ticket({id: 1, project_id: 2}).url() -> "/projects/2/tickets/1"
    
    opened by sishen 14
  • Fixed TypeError when trigger is false which results in routes being undefined

    Fixed TypeError when trigger is false which results in routes being undefined

    Fixed Uncaught TypeError: Cannot read property 'length' of undefined when trigger is false which results in routes being undefined.

    The Problem

    When you pass in options to Route.navigate({trigger: false}) setting trigger to false, routes is never defined and the TypeError is thrown.

    Relavent Code

    Only sets routes if options.trigger is true.

    routes = @matchRoutes(@path, options) if options.trigger
    

    Produces TypeError when options.trigger is false because routes is undefined.

    unless routes.length
    

    Other Questions/Issues/Changes to be aware of

    Looking at the current code it appears that @trigger('navigate', @path) is always triggered. I'm not sure if that is intended or not. This Pull Request only calls @trigger('navigate', @path) if options.trigger is true. Please advise.

    The statement return if options.shim has been moved from before the redirect block to after the redirect block. Previously the redirect block would never get called if options.shim was true. I'm not sure if this was intentional or desired. Please advise.

    NOTE: Jasmine tests are still passing with the changes in this Pull Request.

    opened by richard-flosi 13
  • Added a test for #226

    Added a test for #226

    #226 was closed a long time ago, but I think it deserves a second chance.

    I think it makes a lot of sense to get the Collection API as close as possible to Model.

    opened by adambiggs 13
  • Assignment of Spine.Stack controllers to Spine.Manager instance leads to conflicts

    Assignment of Spine.Stack controllers to Spine.Manager instance leads to conflicts

    If an application has a controller with key "manager" in its Stack, then the instantiation of the Stack overwrites the Stack instance's "manager" property with an instance of the controller named "manager" managed by the Stack.

    Stack example with conflict:

    class App.Stack extends Spine.Stack
      controllers:
        manager: App.Manager
    

    Here's where it happens (manager.coffee lines 64 - 68):

        @manager = new Spine.Manager
    
        for key, value of @controllers
          @[key] = new value(stack: @)
          @add(@[key])
    

    Here's a link to the code in the repo: https://github.com/maccman/spine/blob/master/src/manager.coffee#L67

    opened by vail130 13
  • Models that extend Spine.Ajax don't attempt to find out resource is available remotely before failing

    Models that extend Spine.Ajax don't attempt to find out resource is available remotely before failing

    There may be a situation where an API only returns a limited subset of all of it's data. Calling Model.find when the Model extends Spine.Ajax should see if the API has the resource before throwing the exception that the record doesn't exist.

    opened by csaunders 13
  • Pug support

    Pug support

    Hello, as you know Jade is deprecated and it became Pug. After migration form jade to pug, we are faced with a problem, such as interpolation, that worked in Jade, but not in Pug. Is it old runtime.js in slug json still compatible for pug? (by the way all links to runtime.js file in Spine official docs is already 404)

    opened by dLiubchenko 0
  • Model.find unable to get correct result

    Model.find unable to get correct result

    Create a model, which is added some records with default ID, (c-0, c-1, c-2, c-3). Then, get discontinuity records by delete the middle, (c-0, c-2, c-3). After save and reload model, (such as using Model.Local and refresh page), the method Model.find cannot get correct result:

    Mod = Spine.Model.setup("Mod",["rec"])
    Mod.create({rec: "A"})
    b = Mod.create({rec: "B"})
    Mod.create({rec: "C"})
    Mod.create({rec: "D"})
    b.destroy()     // delete record "B""
    
    // simulate Model.saveLocal
    storStr = JSON.stringify(Mod)
    
    NewMod = Spine.Model.setup("Mod",["rec"])
    NewMod.on("refresh",function(items){
        for (item of items) {
            // By record C's ID (c-2) to find the record D, there were two D 
            console.log(item.id, NewMod.find(item.id).rec)
        }
            /*output:
                c-0 A
                c-2 D
                c-3 D
            */
    })
    
    // simulate Model.loadLocal
    NewMod.refresh(storStr)
    

    The problem should appear in calling addRecord in Model.fefresh:

    record.id or= record.cid
    @irecords[record.id] = @irecords[record.cid] = record
    
    bug core 
    opened by chocolatl 0
  • Model foreign key reference with no id broken

    Model foreign key reference with no id broken

    Create a model object with a hasMany relationship that has no ID (i.e. it hasn't been saved yet). Example: Model bar { @configure('Bar', 'id', 'foo_id', 'whee_id') } Model foo { @hasMany 'bars' } Model whee { @hasMany 'bars' } The associated 'bar' collection has many 'bar' Model objects in it, all with a FK reference to 'whee'.

    Attempt to do a check on @foo.bars().all(). It will return all of the 'whee' bar instances because @foo.id is undefined and bar.foo_id is also undefined. Ideally, a record with no ID should return nothing for @all() on an associated collection.

    bug module core 
    opened by wschmrdrNS 2
  • Model#changeID does not persist the new id

    Model#changeID does not persist the new id

    after calling record.changeID(new_id), I can find the changed record with the new id but when I call the id on the found record the previous id is returned.

    In the spine.coffeee source code on changeID method, the save() call is commented out due this issue: #597

    record.changeID(33)
    console.info record.id # ==> 33, correct
    x = Record.find(33) # finds the correct record
    console.info x.id # ==> 'c-3', should be 33
    
    bug 
    opened by hrigu 1
Owner
Spine JS Project
For Spine.js related libraries/tools
Spine JS Project
Super minimal MVC library

Espresso.js Espresso.js is a tiny MVC library inspired by Backbone and React with a focus on simplicity and speed. We've aimed to bring the ideas of u

TechLayer 534 Dec 11, 2022
A JavaScript Framework for Building Brilliant Applications

mithril.js What is Mithril? Installation Documentation Getting Help Contributing What is Mithril? A modern client-side JavaScript framework for buildi

null 13.5k Dec 26, 2022
The tiny framework for building hypertext applications.

Hyperapp The tiny framework for building hypertext applications. Do more with less—We have minimized the concepts you need to learn to get stuff done.

Jorge Bucaran 18.9k Jan 4, 2023
:fire: An extremely fast, React-like JavaScript library for building modern user interfaces

Inferno is an insanely fast, React-like library for building high-performance user interfaces on both the client and server. Description The main obje

Inferno 15.6k Jan 3, 2023
JavaScript UI library for data-driven web applications

Road to 2.0 The master branch has new, in-progress version of w2ui. You might want to consider 1.5 branch that is stable and supports older browsers.

Vitali Malinouski 2.4k Jan 3, 2023
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

Supporting Vue.js Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome ba

vuejs 201.7k Jan 8, 2023
🌟 DataFormsJS 🌟 A minimal JavaScript Framework and standalone React and Web Components for rapid development of high quality websites and single page applications.

?? Welcome to DataFormsJS! Thanks for visiting! ?? ?? ?? ?? ?? ?? 中文 (简体) 欢迎来到 DataFormsJS Español Bienvenido a DataFormsJS Português (do Brasil) Bem

DataFormsJS 156 Dec 8, 2022
Ember.js - A JavaScript framework for creating ambitious web applications

Ember.js is a JavaScript framework that greatly reduces the time, effort and resources needed to build any web application. It is focused on making yo

Ember.js 22.4k Jan 8, 2023
A framework for real-time applications and REST APIs with JavaScript and TypeScript

A framework for real-time applications and REST APIs with JavaScript and TypeScript Feathers is a lightweight web-framework for creating real-time app

Feathers 14.2k Dec 28, 2022
A framework for building native apps with React.

React Native Learn once, write anywhere: Build mobile apps with React. Getting Started · Learn the Basics · Showcase · Contribute · Community · Suppor

Facebook 106.8k Jan 3, 2023
A tiny foundation for building reactive views

ripple.js A tiny foundation for building reactive views with plugins. It aims to have a similar API to Reactive, but allow composition of views, like

ripple.js 1.3k Dec 9, 2022
Lightweight and powerful data binding.

Rivets.js Rivets.js is a lightweight data binding and templating system that facilitates building data-driven views. It is agnostic about every aspect

Michael Richards 3.2k Dec 28, 2022
Simple, lightweight, persistent two-way databinding

way.js Simple, lightweight, persistent, framework-agnostic two-way databinding Javascript library. With no to little JS code to write. And no dependen

gwendall 2.9k Dec 30, 2022
Simple and elegant component-based UI library

Simple and elegant component-based UI library Custom components • Concise syntax • Simple API • Tiny Size Riot brings custom components to all modern

Riot.js 14.7k Jan 4, 2023
🙋‍♀️ 3kb library for tiny web apps

3kb library for tiny web apps. Sometimes, all you want to do is to try and do something—No boilerplate, bundlers, or complex build processes. Lucia ai

Aiden Bai 699 Dec 27, 2022
An unofficial SmartThings websocket API library (alpha)

An unofficial SmartThings websocket API library (alpha) ?? Installation This is a Node.js module available through the npm registry. $ npm i -S smart-

Stephen Mendez 4 Sep 20, 2021
Formily + SemiDesign: The Awesome Components Library with Formily & Semi

Formily + SemiDesign: The Awesome Components Library with Formily & Semi

Formily Organization 12 Dec 19, 2022
HTML Framework that allows you not to write JavaScript code.

EHTML (or Extended HTML) can be described as a set of custom elements that you can put on HTML page for different purposes and use cases. The main ide

Guseyn Ismayylov 171 Dec 29, 2022