xavierlacot / joli.js Goto Github PK
View Code? Open in Web Editor NEWjoli.js is an Activerecord-like javascript ORM, particularly suited for being used in the Appcelerator Titanium Mobile framework.
License: MIT License
joli.js is an Activerecord-like javascript ORM, particularly suited for being used in the Appcelerator Titanium Mobile framework.
License: MIT License
In my attempts model extensions defined in a model's methods are not reachable as defined in the readme, including using the human model example.
First of all, I love your work !!! You really did a great job.
I tried your demo, and you have a line there that suppose to do "migrate" to database:
joli.models.migrate(current_migration_version);
Actually, it runs ONLY on the first time, when the emulator/device is cleared. On the second time when I run it again, it gives an error on line 600 (joli.js).
I checked it out and seems to be that after calling the next line:
// use the model specified by as() first, then from()
var model = joli.models.get(this.data.as || this.data.from);
the "model" is null.
Then it fails on line 600:
result.push(model.newRecord().fromArray(rowData));
When I comment the "migrate", line, the application works fine. But if joli can do this, it will be simply just great!
Thanks a lot.
I've a simple model with "title" and "text" attribute
var chapter = Models.Chapter.findOneById(_id);
chapter.title = "title 1";
chapter.text = "text 1";
when i do
chapter.save();
nothing happens to db.
This is the log of my model:
[INFO] {
"_data" = {
id = 1;
position = 1;
"project_id" = 1;
text = "";
title = "Primo capitolo";
};
"_options" = {
table = {
all = "<KrollCallback: 0xb573120>";
count = "<KrollCallback: 0xb575df0>";
deleteRecords = "<KrollCallback: 0xb55ea10>";
exists = "<KrollCallback: 0xb56c5c0>";
findBy = "<KrollCallback: 0xb563e40>";
findById = "<KrollCallback: 0xb55d610>";
findOneBy = "<KrollCallback: 0xb56cd00>";
findOneById = "<KrollCallback: 0xb56e910>";
getColumns = "<KrollCallback: 0xb563d70>";
newRecord = "<KrollCallback: 0xb568d60>";
options = {
columns = {
id = "INTEGER PRIMARY KEY AUTOINCREMENT";
position = INTEGER;
"project_id" = INTEGER;
text = TEXT;
title = TEXT;
};
objectMethods = {
getThumb = "<KrollCallback: 0xb552d50>";
};
table = chapter;
};
save = "<KrollCallback: 0xb56b070>";
table = chapter;
truncate = "<KrollCallback: 0xb56f1a0>";
};
};
"_originalData" = {
id = 1;
};
destroy = "<KrollCallback: 0xb56bbf0>";
fromArray = "<KrollCallback: 0xb571540>";
get = "<KrollCallback: 0xb5761c0>";
getThumb = "<KrollCallback: 0xb5691f0>";
id = 1;
isChanged = "<KrollCallback: 0xb56a9a0>";
isNew = "<KrollCallback: 0xb56c9f0>";
position = 1;
"project_id" = 1;
save = "<KrollCallback: 0xb5634d0>";
set = "<KrollCallback: 0xb576df0>";
text = "Prova 1 2 3";
title = "Primo capitolo";
toArray = "<KrollCallback: 0xb54b0b0>";
}
"Prior to Release 3.3.0 of the Titanium SDK, note that fieldCount is exposed as a method on iOS, but as a property on Android. Blackberry supported it both as a property and a method. Starting from Release 3.3.0 of the Titanium SDK, fieldCount is supported only as a read only property."
(Excerpt from http://docs.appcelerator.com/titanium/3.0/#!/api/Titanium.Database.ResultSet)
Therefore using 3.3.0 of Titanium SDK or higher, when trying to perform a select on iOS you will get the error: '' is not a function (evaluating 'rows.fieldCount()'...
This is due to line 586 in joli.js
fieldCount = rows.fieldCount();
Since this is now a property in iOS, the surrounding logic can be condensed to something like :
var fieldCount = rows.fieldCount;
Hope this helps...
Keep up the good work! I love joli.js!
Hi,
in Appcelerator 3.0 (not released yet) line 586
fieldCount = rows.fieldCount();
gives the error mentioned in the title. Seems to work by removing this call and replacing it by
fieldCount = rows.fieldCount;
Cheers
Hi Xavier:
Thanks a lot for the great work.
I was working with joli.js , and needed to retrieve the values of a record directly. I suppose we need to use the toArray method.
If that's the case I've noticed that you are initializing the return object as an array but assigning string keys to it. As a result the return value is an empty array.
Should it not be initialized as an object?
toArray: function() {
var result = {};
joli.each(this._options.table.getColumns(), function(colType, colName) {
result[colName] = this._data[colName];
}, this);
return result;
}
instead of:
toArray: function() {
var result = [];
joli.each(this._options.table.getColumns(), function(colType, colName) {
result[colName] = this._data[colName];
}, this);
return result;
}
I've changed that and I get the desired result (my desired result). Might I be breaking something?
Just noticed by printing out the return object of query() or find() methods really returns a WHOLE bunch of duplicate data. i.e. returns the fields in several different objects within the one object. example - https://gist.github.com/72329972fc50c8f1357c#comments
How do you return the inserted id when using the newRecord function
var record = model.newRecord({
description: data.description,
type: data.type,
userEntered: data.userEntered
});
Ti.API.info(record);
record.save();
How can i get the id of the newly inserted row
thanks
Hello,
To be honest I don't know if it is an issue or I am doing something wrong.
This is my query:
var q = new joli.query()
.select('item.*')
.from('orderitem')
.order(['orderitem_id desc'])
.join('orders','orders.order_id','orderitem.order_id')
.join('item','item.item_id','orderitem.item_id');
var items = q.execute();
So far I can see Joli is properly constructing the query :
select item.*
from orderitem left outer join orders on orders.order_id = orderitem.order_id
left outer join item on item.item_id = orderitem.item_id
order by orderitem_id desc
The problem is that items collection only return orderitem's fields rather than the item's fields.
This is what each returning record has:
I/TiAPI ( 1598): key in _options
I/TiAPI ( 1598): key in _data
I/TiAPI ( 1598): key in _originalData
I/TiAPI ( 1598): key in isNew
I/TiAPI ( 1598): key in orderitem_id
I/TiAPI ( 1598): key in order_id
I/TiAPI ( 1598): key in item_id
I/TiAPI ( 1598): key in item_quantity
I/TiAPI ( 1598): key in beer_quantity
I/TiAPI ( 1598): key in orderitem_type
I/TiAPI ( 1598): key in beer_id
Those are all fields belonging to orderitem instead of item table as I expected.
Thanks!
nico.
Hi, thanks for fixing #37
However, the fix changed a bit the behaviour of destroy function
Previously, if record was unsaved, it correctly threw an exception
After #37 , unsaved record have typeof this.id === 'object', so does not fire the exception
However, the deleteRecords function does not find the id, and so it does not create the 'where' clause, executing "delete from "
It looks like some_model.count() returns string instead of integer.
Tested on Titanium Developer 1.2.2 with Android SDK
Should isChanged return true only if the record has changed?
The reason of my question is the following: a simple query returns records that are 'changed' (to my great surprise): is really this the desired behaviour or did I miss something obvious?
it ('joli.record.isChanged()', function() {
var city = models.city.findOneBy('name', 'New York')
expect(city.isChanged()).toBeFalsy();
});
Is there any planned support for Limit & Offset queries?
Salut Xavier –
Have you ever considered a feature that would generate the javascript necessary to define joli models by reading an existing database?
So for example a utility could exists that takes as input a SQLite database file, and generates the model definitions by emitting javascript, which could then be used by joli.
This is sometimes referred to in the ORM world as a “database first” approach.
The advantage of this approach would be there is no need to manually define models for databases which already exist.
I am interested in your opinion on the feasibility of this approach with joli.
Regards,
Lee
Hello Xavier, thanks for sharing this.
I wonder if the field id is not actually named id but uid or something else, does the lib works as expected?
Because I expected when saving a new record that the save() method looks for if the record was already present in the database and in this case make a UPDATE query and not an INSERT query. The thing is in my case it's happening I get an error because joli tries to insert a row already existing and I wonder if that's because my field id as for name uid.
Could you give me an example showing how should users update a row in the database with convenient method like save.
Thanks for your help and time.
It looks like the database is never closing. In Android i am getting this error after about 5 seconds from me running a query:
close() was never explicitly called on database '/data/data/com.tb.test/databases/db10'
[ERROR][Database(18834)] android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
[ERROR][Database(18834)] at android.database.sqlite.SQLiteDatabase.(SQLiteDatabase.java:1847)
[ERROR][Database(18834)] at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)
[ERROR][Database(18834)] at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:854)
[ERROR][Database(18834)] at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:847)
[ERROR][Database(18834)] at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:547)
[ERROR][Database(18834)] at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:203)
[ERROR][Database(18834)] at ti.modules.titanium.database.DatabaseModule.open(DatabaseModule.java:72)
[ERROR][Database(18834)] at org.appcelerator.kroll.runtime.v8.V8Runtime.nativeRunModule(Native Method)
[ERROR][Database(18834)] at org.appcelerator.kroll.runtime.v8.V8Runtime.doRunModule(V8Runtime.java:146)
[ERROR][Database(18834)] at org.appcelerator.kroll.KrollRuntime.handleMessage(KrollRuntime.java:267)
[ERROR][Database(18834)] at org.appcelerator.kroll.runtime.v8.V8Runtime.handleMessage(V8Runtime.java:172)
[ERROR][Database(18834)] at android.os.Handler.dispatchMessage(Handler.java:95)
[ERROR][Database(18834)] at android.os.Looper.loop(Looper.java:130)
[ERROR][Database(18834)] at org.appcelerator.kroll.KrollRuntime$KrollRuntimeThread.run(KrollRuntime.java:104)
Here is my code :
joli = require('/lib/joli').connect('db10');
var profiles = new joli.model({
table: 'profiles',
columns: {
id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
name: 'TEXT',
orm: 'INTEGER'
}
});
joli.models.initialize();
var q = new joli.query()
.select('*')
.from('profiles')
.execute();
I get the records I want so everything is setup correctly. I just thought under the guidelines of the titanium api you should be opening and closing your database connection on each query. You don't want to just leave the connection open.
Hopefully this is a simple question...
I have included the library as suggested but keep getting the error that my user table cannot be found. The SQL file in the iPhone simulator is also zero bytes.
It looks like the db file might get created but nothing else happens.
Are there any migration steps required to build the app for the first time?
many thanks
Ed
I really like this little js orm library and have used it most of its functionality with success.
I see an under utilized area (from our use and through lack of examples out there) for the built in migration capability.
Other than the fact that there no actual specific migrate commands (such create, add, remove, rename, etc), one major obstacle I see is that ( https://github.com/xavierlacot/joli.js/blob/master/joli.js#L394) any migration is always dropping the table if it exists. This basically makes it a destructive migration and seems to me to question the point of migrating vs just recreating whenever you want.
Here is the function below and the line in question is the DROP for each model... - its not wrapped with any conditional other than if the version is outdated...
migrate: function(version, migrationCallback) {
// create migration table
var query = 'CREATE TABLE IF NOT EXISTS ' + this.migration.table + ' (version)';
joli.connection.execute(query);
if (this.migration.getVersion() < version) {
joli.each(this.models, function(model, modelName) {
var query = 'DROP TABLE IF EXISTS ' + modelName;
joli.connection.execute(query);
});
// optional migration callback
if (migrationCallback && 'function' === joli.getType(migrationCallback)) {
migrationCallback({
table: this.migration.table,
newVersion: version
});
}
// insert migration
this.migration.setVersion(version);
}
},
A true migration, in my experience, would be able to add/modify an existing database table (one with rows, data, etc already) without destroying it first.
Am I missing something here?
My plan is to update / modify this functionality so that the default migration path does not destroy the table and only as an optional argument passed into the function would the drop occur first. This way you can always preserve existing user data in the database and still migrate in a non destructive manner. This will also be followed up with adding in the basic expected migrate specific commands
createTable
dropTable
addColumn
removeColumn
etc
Before I get too far down this path, I'd like to know the reason the current default is to drop first when migrating table in case there is some glaring problem with this thinking. Was it simply to avoid the need for having the additional commands listed above?
L814 in fromArray function,
if ((this._originalData && !this._originalData[colName]) || this.isNew()) {
this._originalData[colName] = data[colName];
}
it will be error when this._originalData == null and this.isNew() returned true.
In django's ORM it is possible to chain query functions and query model tables immediately. Example:
Entry.objects.filter(headline__startswith='What').exclude(pub_date__gte=datetime.now()).filter(pub_date__gte=datetime(2005, 1, 1))
Example 2:
q1 = Entry.objects.filter(headline__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.now())
q3 = q1.filter(pub_date__gte=datetime.now())
Now you have to use joli.query().select().from('dbtable_name').where('name', 'Roel'); Which is quite long and also introduces where, select and query functions. Using this also has little benefits over using the SQL language.
It would be nice if it is possible to chain query functions and query tables immediately like this:
dbtable_name.findBy('name', 'Roel').findBy('lastname', 'kramer') or like this:
dbtable_name.where('name', 'Roel').where(lastname', 'kramer')
See the django docs for a better explanation. It uses other function names, which might inspire you, both for good new functionality and naming of functions. Docs: https://docs.djangoproject.com/en/dev/topics/db/queries/
Hi Xavier
Love your library, thanks.
One small issue I have when using Joli is that the Javascript does not pass the JSLint set of tests, whereas all my other code does. I realise this is not necessarily an error, but it is advisable to conform to JSLint generally. Here are the errors that come up with JSLint and Joli.js http://www.pastie.org/2196158
Would you be willing to make the necessary changes? If not, would you be willing to merge in the changes if I go ahead and do that?
When creating a model with an 'id' row, this doesn't function as an autoincrement row. Therefore, inserting a record without any mention of 'id' in the model writes a NULL to the db instead of an incremented id.
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.