spine / spine Goto Github PK
View Code? Open in Web Editor NEWLightweight MVC library for building JavaScript applications
Home Page: http://spine.github.io
License: MIT License
Lightweight MVC library for building JavaScript applications
Home Page: http://spine.github.io
License: MIT License
Hi,
on my project (https://github.com/loicginoux/spine-annotation-plugin) i encountered an error where the 'proxy' function was called without paramter.
I haven't tried to understand what was causing the issue inside spine but i managed to fix the issue by adding a condition on the parameter existence like that:
proxy: function(m) {
var l = this;
return (function() {
if (m) return m.apply(l, arguments)
})
}
instead of the current:
proxy: function(m) {
var l = this;
return (function() {
return m.apply(l, arguments)
})
}
You can test the issue by downloading the project and opening edit.html.
I have the master branch with the initial spine.js (originally it was the minified version, I de-obfuscated it for debugging purpose).
The error happens when you drag a note (after creating one)
The other branch 'spine-fix' has the change in the proxy function and when testing the dragging, it works.
I was wondering if this is an issue from spine or if I am misusing the framework.
let me know what you think.
cheers
Are all new features going be added to the CS implementation of Spine?
How are you planning on making Coffeescript Classes vs Spine Classes work, since there's a fair bit of functionality crossover?
I'd really like to be able to use native CoffeeScript classes with Spine, but I'm not quite sure what the most sustainable approach is, or if there's a good way to get CoffeeScript classes working in harmony with Spine classes as-is. I am yet to try using the js branch of Spine with CoffeeScript classes, have you?
To utilize the class system, will CoffeeScript be required in the future? Will the existing extend/include system still work/be fallback for non-CoffeeScript users?
Perhaps I am wrong on this. This is related to Spine.Ajax when used with a Rails Backend, specifically when the destroy action is called on a Model , which in turns sends a Delete Request to the server trough Ajax.
The standard Rails Delete command returns: " " in its response. ( at least with a regular scaffold, don't know if it's Rest Standard )
So, Jquery Ajax tries to parse this and throws a Parse Error which instead of triggering the Success Event, triggers the Error Event; Even thou the response status is 200 and the instance was actually deleted on the server.
The quick solution is to change the Rails Delete Route, and make it return something that Jquery.Ajax can understand;
However, since that is the Rails Standard* way of doing things, I've been figuring out a solution so Spine works out of the box.
One of them involves checking the error function to see if the status is 200. This would mean that the error occurred on the client side, so the success function should be fired instead. However, if something actually went wrong , or the response being sent has a mistake, there would be no way of knowing. So I don't know if that will be a solution.
So this comment, is more an observation than anything to remind you that this issue should be documented so users of the Spine Framework know that they should change a bit the Delete Route on Rails when using Rest and make it response with something meaningful;.... or to start a discussion on how to handle it on Spine
In commit 2d8f0dd, you renamed Spine.Controller.Manager. However it is still exposed as Spine.Controller.Manager instead of Spine.Manager in the Spine object:
https://github.com/maccman/spine/blob/master/lib/spine.manager.js#L24
In spine.route.js, (line 33: https://github.com/maccman/spine/blob/master/lib/spine.route.js#L33), setting this.fragment = fragment causes the hashchange event (line 38: https://github.com/maccman/spine/blob/master/lib/spine.route.js#L38) to be ignored on page load.
Changing line 33 from:
To:
Seems to fix the issue. I would have made a pull request, but it's such a small change...
In Spine.Model.Ajax#send, it takes in parameters that can be passed into the fetch function. The problem is that when ensuring the parameters are set up any data values that could've been used for issuing a query are replaced with an empty object.
Instead it should do something like:
params = $.extend(params || {}, {
type: this.methodMap[method],
contentType: "application/json",
dataType: "json",
data: params.data || {}
});
Spine.Route add method has the following code:
if (typeof path === "object")
Consider path to be of regex type: path = /mypath[=]/
Firefox: typeof (path) => 'object'
Chrome: typeof (path) => 'function'
Special case handling would be needed:
if (typeof path === "object" && !(path instanceof RegExp))...
It looks like spine is in 2 branches now? One for coffee script one for js? I was originally using master and spent an hour trying to make it work before stumbling across a random issue mentioning the js branch :)
Could you add a quick note in the Readme on master about the separation? I grabbed the base js file from the github pages, but wanted to add ajax so grabbed that from master, and thats when the trouble started. Grabbing both from the js branch cleared it right up.
I hope you keep the js branch going, as spine is an awesome library and some of us don't like that coffeescript nonsense :)
When I try to pass parameters to fetch they are being overridden. It appears the the $.extend method is applying second object's (defaults) values over the top of the first's (params).
Perhaps I'm doing something wrong but I think this may be a bug. If I reverse the call to $.extend the params are passed.
From:
params = $.extend(params || {}, {
type: this.methodMap[method],
contentType: "application/json",
dataType: "json",
data: {}
});
To:
params = $.extend({
type: this.methodMap[method],
contentType: "application/json",
dataType: "json",
data: {}
}, params || {});
Hi,
I'm trying to work out how to correctly work with inheritance in Spine Classes. However I think I am missing something and I couldn't find anything about it in the documentation or tutorials on the web.
As a basic example in pseudocode I want to do something like this:
class Base {
constructor: function() {
this.foo = 'bar';
this.bar = 'woosh';
},
printContent: function() {
console.log(this.foo, this.bar);
}
}
class Extend extends Base {
constructor: function() {
parent:: constructor();
this.bar = 'blablabla';
}
}
i1 = Base();
i1.printContent(); // should print: bar woosh
i2 = Extend();
i2.printContent(); // should print: bar bladiebla
~
I have played around with the spine classes and did something like:
var Base = Spine.Class.create({
init: function() {
console.log('base init');
this.foo = 'bar';
this.bar = 'woosh';
},
dump: function() {
console.log('dump', this.foo, this.bar);
}
});
var Extend = Base.create({
init: function() {
console.log('extend init');
this.bar ='bladiebla';
},
dump2: function() {
console.log('dump2', this.foo, this.bar);
}
});
base = Base.init();
base.dump();
extend = Extend.init();
extend.dump2();
This results in:
base init
dump bar woosh
extend init
dump2 undefined bladiebla
Apparently this is not the approach to take in Spine. Although I think i'm pretty close (the functions seem to inherit correctly, but the proper tie settings are not.
Can you help out a bit?
Kind Regards,
Mark
From spine.ajax.js:
if (record.parent.ajaxPrefix) {
data = {};
data[record.parent.ajaxPrefix] = record;
} else {
data = record;
}
Thus if we do:
Spine.Model.ajaxPrefix = true;
like the docs instruct, then the serialization will be:
"data":"{"true":{"name":"arst","id":"05831A06-3A42-46E6-8768-783A1DBF1037"}}"
on all models.
There's also a slight quirk in this implementation for an ajax extended model:
myModel.create(serializedData) will send the long spine guid which the server has no need for in all the cases i can think of.
When creating a new record spine checks if an attribute named "id" exists. If it does not it assigns an id. Shouldn't the same happen for refresh too? It seems that calling refresh should do the following:
if ( !record.id ) record.id = Spine.guid();
Hi, Spine is really great!
Just a quick contribution :
Model.validate()
returns null
when the model is valid and a string when it is not.
So validation may looks like this:
if (!Model.validate()) {
alert('Model is valid');
} else {
alert('Model is not valid');
}
It looks weird to me. I expect (maybe I'm the only one) that Model.validate()
returns true
when the model is valid or false
when it is not.
So why not add an extra method Model.isValid()
with something like that:
isValid: function(){
if (typeof this.validate() == 'string') {
return false;
}
return true;
}
My two cents :)
Hi there,
Currently Events.unbind()
modifies the array in the _callbacks
object. This causes issues when .unbind()
is called on the same array being iterated over in the Events.trigger()
method. Hopefully the following example demonstrates this.
eventInstance.bind('once', function a() {});
eventInstance.bind('once', function b() { eventInstance.unbind('once', b) });
eventInstance.bind('once', function c() {});
eventInstance.trigger('once'); // throws reference error.
Cheers,
Aron
There doesn't seem to be support for querying a subset of data from the backend (for example, by supplying a custom URL). Are there plans to support this? If not, would you consider patches?
var Tasks = Spine.Controller.create({
elements: {".mySelector": "controllerProperty"},
})
The ordering here seems a little unintuitive, it would make more sense to have the elements the other way around, looking more like an assignment:
var Tasks = Spine.Controller.create({
elements: {"controllerProperty": ".mySelector"},
})
What do you think?
I was wondering why the move to CoffeeScript? Spine was a nice, zero-dependency alternative to Backbone with easy to understand Javascript... now I have to wade through CoffeeScript to see what's going on under the hood as the compiled version is full of CoffeeScript magic.
A real shame.
Hello,
I'm really liking Spine.js
, I make extensive use of it in my latest project. However the code base grew and grew and now I reached a point where I'd like to tidy things up a little.
One particular thing that I'm having trouble with is adding some more hierarchy to my models and controllers. I really like the Spine.Class
design and the docs state that Models
and Controllers
are just a special type of Spine.Class
, so this is all good. However I do not fully understand how I'm I supposed to make use of it.
What I'd like to achieve is to have some base model classes like so:
window.Base = Spine.Model.sub();
window.Base.include({
some_base_property: 123,
other_base_property: 234,
some_function: function() {
console.log("Base::some_function");
},
other_function: function() {
console.log("Base::other_function");
this.some_function();
}
});
window.ConcreteModel = Base.setup("ConcreteModel", ["additional_property"]);
window.ConcreteModel.include({
additional_property: 345,
some_function: function() {
console.log("ConcreteModel::some_function()");
},
other_function: function() {
console.log("ConcreteModel::other_function()");
// CALL SUPER.other_function() SOMEHOW, BUT IN THE PRESENT CONTEXT
}
});
someConcreteModel.other_function()
would read:ConcreteModel::other_function()
Base::other_function()
ConcreteModel::some_function()
Just as it would in a regular OOP language. Is there a way to achieve this with Spine?
2) Is there a way to have Base
attributes like base_property
and other_base_property
saved along with additional_property
when playing with ConcreteModel
instances without having to specify them over again inside the call to setup()
?
3) Finally what is the proper way to structure model tree and to bring common functionality to models?
Thanks in advance.
Found an interesting bug today.
My understanding is that binding to an event on a model model instance will add a callback to the Model callback list, capturing the instance in a closure so that only that instance will get event callbacks for this binding:
From Spine.Model:
bind: function(events, callback){
return this.parent.bind(events, this.proxy(function(record){
if ( record && this.eql(record) )
callback.apply(this, arguments);
}));
}
Also important: when saving via AJAX, the spine.ajax callbacks operate on a clone of the record:
create: function(){
this.trigger("beforeCreate", this);
if ( !this.id ) this.id = Spine.guid();
this.newRecord = false;
var records = this.parent.records;
records[this.id] = this.dup();
var clone = records[this.id].clone();
this.trigger("create", clone);
this.trigger("change", clone, "create");
},
So, when trying to process ajaxSuccess from spine.ajax, the clone's id is updated (not the original record, and thus not the record captured in the closure). From Spine.ajax:
// ID change, need to do some shifting
if (data.id && record.id != data.id) {
var records = record.parent.records;
records[data.id] = records[record.id];
delete records[record.id];
record.id = data.id;
}
// Update with latest data
Ajax.noSync(function(){
record.updateAttributes(data);
});
record.trigger("ajaxSuccess", record, status, xhr);
When we go to trigger ajaxSuccess here, because the clone has a different id than its original Model instance, there will be no matching callback found.
Works for me by not dup()-ing the record in create; however, I'm not sure if there are other rammifications caused by that.
I've tried to implement an example of the tabs found here:
http://maccman.github.com/spine.tutorials/layout.html
Unfortunately, I cannot get it to work. It seems like when calling this.active from spine.tabs.js it's returning a function instead of the active element.
Got any ideas? If I need to point to some code I can.
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.
Hi,
First of all, I'd like to say this is a great framework and the docs are great. I started to play around with this it saves a lot of frontend coding for me.
One thing I think is missing is Model ordering. For example, if I want to retrieve a model and order it by date, I'd have to retrieve everyone, parse out the date and then put it into a separate array ordered by date.
Thanks,
Ray
Why does Asset.each
yield direct references to assets, but Asset.find
yield clones?
It's confusing because if you set attributes (without saving) on assets from Asset.each
, the attributes are set on the models directly and are accessible everywhere, but if you set them on assets from Asset.find
, they get set on clones and are thus lost (unless you save()
).
Related question: How do I set attributes on a model and make sure they're accessible everywhere else without having to save()
the model?
Hi!
Model#update causes change/update event nomatter whether properties really change or not. Feature or just don't care?
TIA,
--Vladimir
I'm using the CS version of Spine, and I just noticed that all my constructors are being called twice. What could be the reason for this?
The spine code lacks comments. One might say it's self explanitory but any sane open source project should have some kind of doc-commenting-like comments. kthx.
In: "Creating models is slightly different from creating classes, since the create() function is already reserved by models. Models are created with the setup() function, passing in the model name and an array of attributes."
I guess it should be "since the create() function is already reserved by classes."
Hi!
Are there experiments/tutorials in reusing Models at server-side? Say, mongodb/redis-backed Models
TIA,
--Vladimir
It might be outside the scope of the delay function, but I have a situation where I wanted to cancel certain set timeout events.
For example, I have a keyup event which can fire an Ajax event. However when (within a specified time) another keypress is done, the ajax event should not be fired but instead the most recent delayed event.
Currently I implemented this the following way:
this.timeoutCalls = [];
<snip>
for( i=0; i<this.timeoutCalls.length; i++) {
window.clearTimeout( this.timeoutCalls[i] );
}
wrapThis = this;
this.timeoutCalls.push( window.setTimeout( function() { wrapThis.timeout(); }, 1000) );
This way I'm always sure only the last ajax call is fired.
However I can not use the delay() function of the controller as this does not return the setTimeout()'s return value.
I don't think I have any code that's overridden the clone method, but first
, each
, find
, etc. all didn't work with .clone when I had a nested object, changing it to .dup
fixed it.
I'm trying to use the CS branch with the Ajax adapter and Rails.
In this branch the ajaxPrefix option seems to have been removed. Is there a reason for this? Will it be added in, or am I missing something obvious?
Spine.List.render()
should skip destroyed: true
records
Hello, I am trying to learn spine.js reading docs and examples, but I've found that documentation is not full. There are many methods which are not covered in docs, something like refreshElements
or delegateEvents
in Controllers and so on. Even worse that code of the library has no comments. Backbone.js has good docs and good comments for all methods. I think that this is crucial for easy adaptation of your library. What do you think?
Hi!
Namely, the subj.
TIA,
--Vladimir
Spine still makes certain expectations of your DOM manipulation and event libraries, specifically Spine.Route and Spine.Manager, which use functions like
I have built a compatibility layer to get Spine to work with Dojo specifically and you can see in these files how I have modified Route and Manager to use Dojo functions:
https://github.com/lyleunderwood/spine/tree/master/dojo
This is obviously not a solution, but it clearly illustrates the problem. I'm wondering if there is any intention to change this. I don't really expect Spine.Tabs to be completely agnostic, but probably the router, Spine.Model.Ajax, etc.. I just want to open this discussion.
You might also notice that I've ported everything in Spine to work with module loaders that use the AMD format. I'm using it with requireJS right now which is really nice alongside Dojo.
While using Spine, we came across a situation where we wanted to initialize an attribute with an array. However, the attribute is a reference to the original array, rather than generating new arrays.
https://gist.github.com/1035948
Here is a test case for our desired behavior. Do you have any suggestions? We looked into modifying the setup to take an extra parameter init: Asset = Spine.Model.setup("AssetNew", ["name", "arrayTest"], function() {this.arrayTest = []});
However if we do this we don't want to break any other initialization behavior. Can you tell us where it is the best place to call this new init function?
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.
Hi,
First of all, I'd like to say this is a great framework and the docs are great. I started to play around with this it saves a lot of frontend coding for me.
One thing I think is missing is Model ordering. For example, if I want to retrieve a model and order it by date, I'd have to retrieve everyone, parse out the date and then put it into a separate array ordered by date.
Thanks,
Ray
It would be nice if there was a way to call the various methods on a model without triggering events.
This could be an additional parameter:
var task = Task.init({name: 'abc'});
task.save(false);
Task.create({name: 'abc'}, false);
Or maybe an option, so it could deal with optional validations as well:
Task.create({name: 'abc'}, {validate: false, silent: true});
What do you think? Or is this already possible and I just didn't see it?
Your code block that uses Unicode right-arrow characters looked mangled on my browser (Safari 5.0.4) and so I checked your markup. It contains a META element unlike any I've personally seen before. Below are two that will work:
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta charset=utf-8>
When going through the documentation at http://maccman.github.com/spine/ I came across certain oddities and wanted to check if this was due to mistypes, missing information or due to code imperfections.
In the validation topic it describes that you should extend your model with the validation function. However (after digging through spine.js) I found out that the validation function should be included instead of extended. This seems to be either an implementation issue or a documentation issue.
At certain times it is assumed that certain libraries are included, but this is not specified in the documentation. It took me some time to find out they were required. It would be helpful if this was named explicitly.
For example:
Under serialization it is said that Spine supports JSON natively. Although most browsers support natively include json functionality, it is not default behavior. Certain browsers do not include it and require you to include the json2.js library.
Under persistence it is not described that the external spine.model.local.js should be included. Althoug this is described at the end of the document, it would be nice if it was mentioned in the explanation of persistence.
Same goes for the spine.model.ajax.js.
Under the routing chapter it is described correctly :-)
Lastly, when you first come in contact with Spine it should be advised to go through the documentation first and then to the example's (instead of first referring to the exmaples). When you go to the examples cold-turkey, you end up flabbergasted. Once you read through the documentation first and then go to the examples, it is easier to understand.
Hi,
Reading the Spine.js' doc and also your book, I can't really get the difference between instance and class properties...
Could you explain when to use include
/ when to use extend
with the advantage of each?
Thanks in advance.
mode.dup() should remove id from its attributes so it can be used to create a separate instance in the db.
I may be wrong, but from what I understand of the code and can get from the documentation it's currently not possible to use both schemes on a model.
Hi,
I'm really liking the look of spine and although it is already a tiny library, I'm wondering if it could be split into a few pieces. I love the prototypal inheritance stuff and have a use for it without controllers and models, I also see times where models could be used without necessarily needing controllers... how about going super-micro and having spine.class.js, spine.model.js and spine.controller.js?
Hi!
Wonder if could consider adding node.js-like aliases for methods dealing with events
TIA,
--Vladimir
MyClass.create
is a factory for subclasses, not a factory for instances of the class, but there's nothing in the name that suggests that's true. Might I suggest MyClass.beget
instead?
Implementing a class like the following works:
class Item extends Spine.Model
@configure "Item", "attribute1"
@extend Spine.Model.Ajax
But when implemented like this:
class Item extends Spine.Model
@extend Spine.Model.Ajax
@configure "Item", "attribute1"
fails.. There wont be any ajax requests in this case.
Is there an example somewhere where this is working? Do you need to check what the Model's validation method returns somewhere in the Controller prior to creation? I've got my model alerting an error message when I've entered something not valid, but then the item is still created and placed into localstorage.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.