diff --git a/LICENSE b/LICENSE index 4808ef39..2567d120 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,10 @@ -Copyright (c) 2013 StrongLoop, Inc. +Copyright (c) 2013-2014 StrongLoop, Inc. + +loopback-datasource-juggler uses a 'dual license' model. Users may use +loopback-datasource-juggler under the terms of the MIT license, or under the +StrongLoop License. The text of both is included below. + +MIT license Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -17,3 +23,289 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +StrongLoop License + +STRONGLOOP SUBSCRIPTION AGREEMENT +PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU +ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE +AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT +AGREE TO THESE TERMS, YOU SHOULD NOT AGREE TO THE TERMS OF THIS AGREEMENT OR +INSTALL OR USE THE SOFTWARE. +This StrongLoop Subscription Agreement ("Agreement") is made by and between +StrongLoop, Inc. ("StrongLoop") with its principal place of business at 107 S. +B St, Suite 220, San Mateo, CA 94401 and the person or entity entering into this +Agreement ("Customer"). The effective date ("Effective Date") of this Agreement +is the date Customer agrees to these terms or installs or uses the Software (as +defined below). This Agreement applies to Customer's use of the Software but it +shall be superseded by any signed agreement between you and StrongLoop +concerning the Software. +1. Subscriptions and Licenses. +1.1 Subscriptions. StrongLoop offers five different subscription levels to its +customers, each as more particularly described on StrongLoop's website located +at www.strongloop.com (the "StrongLoop Site"): (1) Free; (2) Developer; (3) +Professional; (4) Gold; and (5) Platinum. The actual subscription level +applicable to Customer (the "Subscription") will be specified in the purchase +order that Customer issues to StrongLoop. This Agreement applies to Customer +regardless of the level of the Subscription selected by Customer and whether or +not Customer upgrades or downgrades its Subscription. StrongLoop hereby agrees +to provide the services as described on the StrongLoop Site for each +Subscription level during the term for which Customer has purchased the +applicable Subscription, subject to Customer paying the fees applicable to the +Subscription level purchased, if any (the "Subscription Fees"). StrongLoop may +modify the services to be provided under any Subscription upon notice to +Customer. +1.2 License Grant. Subject to the terms and conditions of this Agreement, +StrongLoop grants to Customer, during the Subscription Term (as defined in +Section 7.1 (Term and Termination) of this Agreement, a limited, non-exclusive, +non-transferable right and license, to install and use the StrongLoop Suite +software (the "Software") and the documentation made available electronically as +part of the Software (the "Documentation"), either of which may be modified +during the Term (as defined in Section 7.1 below), solely for development, +production and commercial purposes so long as Customer is using the Software to +run only one process on a given operating system at a time. This Agreement, +including but not limited to the license and restrictions contained herein, +apply to Customer regardless of whether Customer accesses the Software via +download from the StrongLoop Site or through a third-party website or service, +even if Customer acquired the Software prior to agreeing to this Agreement. +1.3 License Restrictions. Customer shall not itself, or through any parent, +subsidiary, affiliate, agent or other third party: + 1.3.1 sell, lease, license, distribute, sublicense or otherwise transfer + in whole or in part, any Software or the Documentation to a third party; + or + 1.3.2 decompile, disassemble, translate, reverse engineer or otherwise + attempt to derive source code from the Software, in whole or in part, nor + shall Customer use any mechanical, electronic or other method to trace, + decompile, disassemble, or identify the source code of the Software or + encourage others to do so, except to the limited extent, if any, that + applicable law permits such acts notwithstanding any contractual + prohibitions, provided, however, before Customer exercises any rights that + Customer believes to be entitled to based on mandatory law, Customer shall + provide StrongLoop with thirty (30) days prior written notice and provide + all reasonably requested information to allow StrongLoop to assess + Customer's claim and, at StrongLoop's sole discretion, to provide + alternatives that reduce any adverse impact on StrongLoop's intellectual + property or other rights; or + 1.3.3 allow access or permit use of the Software by any users other than + Customer's employees or authorized third-party contractors who are + providing services to Customer and agree in writing to abide by the terms + of this Agreement, provided further that Customer shall be liable for any + failure by such employees and third-party contractors to comply with the + terms of this Agreement and no usage restrictions, if any, shall be + exceeded; or + 1.3.4 create, develop, license, install, use, or deploy any third party + software or services to circumvent or provide access, permissions or + rights which violate the license keys embedded within the Software; or + 1.3.5 modify or create derivative works based upon the Software or + Documentation; or disclose the results of any benchmark test of the + Software to any third party without StrongLoop's prior written approval; + or + 1.3.6 change any proprietary rights notices which appear in the Software + or Documentation; or + 1.3.7 use the Software as part of a time sharing or service bureau + purposes or in any other resale capacity. +1.4 Third-Party Software. The Software may include individual certain software +that is owned by third parties, including individual open source software +components (the "Third-Party Software"), each of which has its own copyright and +its own applicable license conditions. Such third-party software is licensed to +Customer under the terms of the applicable third-party licenses and/or copyright +notices that can be found in the LICENSES file, the Documentation or other +materials accompanying the Software, except that Sections 5 (Warranty +Disclaimer) and 6 (Limitation of Liability) also govern Customer's use of the +third-party software. Customer agrees to comply with the terms and conditions +of the relevant third-party software licenses. +2. Support Services. StrongLoop has no obligation to provide any support for +the Software other than the support services specifically described on the +StrongLoop Site for the Subscription level procured by Customer. However, +StrongLoop has endeavored to establish a community of users of the Software who +have provided their own feedback, hints and advice regarding their experiences +in using the Software. You can find that community and user feedback on the +StrongLoop Site. The use of any information, content or other materials from, +contained in or on the StrongLoop Site are subject to the StrongLoop website +terms of use located here http://www.strongloop.com/terms-of-service. +3. Confidentiality. For purposes of this Agreement, "Confidential Information" +means any and all information or proprietary materials (in every form and media) +not generally known in the relevant trade or industry and which has been or is +hereafter disclosed or made available by StrongLoop to Customer in connection +with the transactions contemplated under this Agreement, including (i) all trade +secrets, (ii) existing or contemplated Software, services, designs, technology, +processes, technical data, engineering, techniques, methodologies and concepts +and any related information, and (iii) information relating to business plans, +sales or marketing methods and customer lists or requirements. For a period of +five (5) years from the date of disclosure of the applicable Confidential +Information, Customer shall (i) hold the Confidential Information in trust and +confidence and avoid the disclosure or release thereof to any other person or +entity by using the same degree of care as it uses to avoid unauthorized use, +disclosure, or dissemination of its own Confidential Information of a similar +nature, but not less than reasonable care, and (ii) not use the Confidential +Information for any purpose whatsoever except as expressly contemplated under +this Agreement; provided that, to the extent the Confidential Information +constitutes a trade secret under law, Customer agrees to protect such +information for so long as it qualifies as a trade secret under applicable law. +Customer shall disclose the Confidential Information only to those of its +employees and contractors having a need to know such Confidential Information +and shall take all reasonable precautions to ensure that such employees and +contractors comply with the provisions of this Section. The obligations of +Customer under this Section shall not apply to information that Customer can +demonstrate (i) was in its possession at the time of disclosure and without +restriction as to confidentiality, (ii) at the time of disclosure is generally +available to the public or after disclosure becomes generally available to the +public through no breach of agreement or other wrongful act by Customer, (iii) +has been received from a third party without restriction on disclosure and +without breach of agreement by Customer, or (iv) is independently developed by +Customer without regard to the Confidential Information. In addition, Customer +may disclose Confidential Information as required to comply with binding orders +of governmental entities that have jurisdiction over it; provided that Customer +gives StrongLoop reasonable written notice to allow StrongLoop to seek a +protective order or other appropriate remedy, discloses only such Confidential +Information as is required by the governmental entity, and uses commercially +reasonable efforts to obtain confidential treatment for any Confidential +Information disclosed. Notwithstanding the above, Customer agrees that +StrongLoop, its employees and agents shall be free to use and employ their +general skills, know-how, and expertise, and to use, disclose, and employ any +generalized ideas, concepts, know-how, methods, techniques or skills gained or +learned during the Term or thereafter. +4. Ownership. StrongLoop shall retain all intellectual property and proprietary +rights in the Software, Documentation, and related works, including but not +limited to any derivative work of the foregoing and StrongLoop's licensors shall +retain all intellectual property and proprietary rights in any Third-Party +Software that may be provided with or as a part of the Software. Customer shall +do nothing inconsistent with StrongLoop's or its licensors' title to the +Software and the intellectual property rights embodied therein, including, but +not limited to, transferring, loaning, selling, assigning, pledging, or +otherwise disposing, encumbering, or suffering a lien or encumbrance upon or +against any interest in the Software. The Software (including any Third-Party +Software) contain copyrighted material, trade secrets and other proprietary +material of StrongLoop and/or its licensors. +5. Warranty Disclaimer. THE SOFTWARE (INCLUDING ANY THIRD-PARTY SOFTWARE) AND +DOCUMENTATION MADE AVAILABLE TO CUSTOMER ARE PROVIDED "AS-IS" AND STRONGLOOP, +ON BEHALF OF ITSELF AND ITS LICENSORS, EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE, +PERFORMANCE, AND ACCURACY AND ANY IMPLIED WARRANTIES ARISING FROM STATUTE, +COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE. STRONGLOOP DOES +NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR +ERROR-FREE, THAT DEFECTS IN THE SOFTWARE WILL BE CORRECTED OR THAT THE SOFTWARE +WILL PROVIDE OR ENSURE ANY PARTICULAR RESULTS OR OUTCOME. NO ORAL OR WRITTEN +INFORMATION OR ADVICE GIVEN BY STRONGLOOP OR ITS AUTHORIZED REPRESENTATIVES +SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. +STRONGLOOP IS NOT OBLIGATED TO PROVIDE CUSTOMER WITH UPGRADES TO THE SOFTWARE, +BUT MAY ELECT TO DO SO IN ITS SOLE DISCRETION. SOME JURISDICTIONS DO NOT ALLOW +THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO +CUSTOMER.WITHOUT LIMITING THE GENERALITY OF THE FOREGOING DISCLAIMER, THE +SOFTWARE AND DOCUMENTATION ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE IN +THE PLANNING, CONSTRUCTION, MAINTENANCE, CONTROL, OR DIRECT OPERATION OF NUCLEAR +FACILITIES, AIRCRAFT NAVIGATION, CONTROL OR COMMUNICATION SYSTEMS, WEAPONS +SYSTEMS, OR DIRECT LIFE SUPPORT SYSTEMS. +6. Limitation of Liability. + 6.1 Exclusion of Liability. IN NO EVENT WILL STRONGLOOP OR ITS LICENSORS + BE LIABLE UNDER THIS AGREEMENT FOR ANY INDIRECT, RELIANCE, PUNITIVE, + CONSEQUENTIAL, SPECIAL, EXEMPLARY, OR INCIDENTAL DAMAGES OF ANY KIND AND + HOWEVER CAUSED (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF + BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION AND + THE LIKE), EVEN IF STRONGLOOP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. CUSTOMER BEARS FULL RESPONSIBILITY FOR USE OF THE SOFTWARE AND + THE SUBSCRIPTION AND STRONGLOOP DOES NOT GUARANTEE THAT THE USE OF THE + SOFTWARE AND SUBSCRIPTION WILL ENSURE THAT CUSTOMER'S NETWORK WILL BE + AVAILABLE, SECURE, MONITORED OR PROTECTED AGAINST ANY DOWNTIME, DENIAL OF + SERVICE ATTACKS, SECUITY BREACHES, HACKERS AND THE LIKE. IN NO EVENT WILL + STRONGLOOP'S CUMULATIVE LIABILITY FOR ANY DAMAGES, LOSSES AND CAUSES OF + ACTION (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE) + ARISING OUT OF OR RELATED TO THIS AGREEMENT EXCEED THE GREATER OF ONE + HUNDRED DOLLARS (US$100) OR THE TOTAL SUBSCRIPTION FEES PAID BY CUSTOMER + TO STRONGLOOP IN THE TWELVE (12) MONTHS PRECEDING THE DATE THE CLAIM + ARISES. + 6.2 Limitation of Damages. IN NO EVENT WILL STRONGLOOP'S LICENSORS HAVE + ANY LIABILITY FOR ANY CLAIM ARISING IN CONNECTION WITH THIS AGREEMENT. + THE PROVISIONS OF THIS SECTION 6 ALLOCATE RISKS UNDER THIS AGREEMENT + BETWEEN CUSTOMER, STRONGLOOP AND STRONGLOOP'S SUPPLIERS. THE FOREGOING + LIMITATIONS, EXCLUSIONS AND DISCLAIMERS APPLY TO THE MAXIMUM EXTENT + PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL + PURPOSE. + 6.3 Failure of Essential Purpose. THE PARTIES AGREE THAT THESE + LIMITATIONS SHALL APPLY EVEN IF THIS AGREEMENT OR ANY LIMITED REMEDY + SPECIFIED HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE. + 6.4 Allocation of Risk. The sections on limitation of liability and + disclaimer of warranties allocate the risks in the Agreement between the + parties. This allocation is an essential element of the basis of the + bargain between the parties. +7. Term and Termination. +7.1 This Agreement shall commence on the Effective Date and continue for so long +as Customer has a valid Subscription and is current on the payment of any +Subscription Fees required to be paid for that Subscription (the "Subscription +Term"). Either party may terminate this Agreement immediately upon written +notice to the other party, and the Subscription and licenses granted hereunder +automatically terminate upon the termination of this Agreement. This Agreement +will terminate immediately without notice from StrongLoop if Customer fails to +comply with or otherwise breaches any provision of this Agreement. +7.2 All Sections other than Section 1.1 (Subscriptions) and 1.2 (Licenses) shall +survive the expiration or termination of this Agreement. +8. Subscription Fees and Payments. StrongLoop, Customer agrees to pay +StrongLoop the Subscription Fees as described on the StrongLoop Site for the +Subscription purchased unless a different amount has been agreed to in a +separate agreement between Customer and StrongLoop. In addition, Customer shall +pay all sales, use, value added, withholding, excise taxes and other tax, duty, +custom and similar fees levied upon the delivery or use of the Software and the +Subscriptions described in this Agreement. Fees shall be invoiced in full upon +StrongLoop's acceptance of Customer's purchase order for the Subscription. All +invoices shall be paid in US dollars and are due upon receipt and shall be paid +within thirty (30) days. Payments shall be made without right of set-off or +chargeback. If Customer does not pay the invoices when due, StrongLoop may +charge interest at one percent (1%) per month or the highest rate permitted by +law, whichever is lower, on the unpaid balance from the original due date. If +Customer fails to pay fees in accordance with this Section, StrongLoop may +suspend fulfilling its obligations under this Agreement (including but not +limited to suspending the services under the Subscription) until payment is +received by StrongLoop. If any applicable law requires Customer to withhold +amounts from any payments to StrongLoop under this Agreement, (a) Customer shall +effect such withholding, remit such amounts to the appropriate taxing +authorities and promptly furnish StrongLoop with tax receipts evidencing the +payments of such amounts and (b) the sum payable by Customer upon which the +deduction or withholding is based shall be increased to the extent necessary to +ensure that, after such deduction or withholding, StrongLoop receives and +retains, free from liability for such deduction or withholding, a net amount +equal to the amount StrongLoop would have received and retained absent the +required deduction or withholding. +9. General. +9.1 Compliance with Laws. Customer shall abide by all local, state, federal and +international laws, rules, regulations and orders applying to Customer's use of +the Software, including, without limitation, the laws and regulations of the +United States that may restrict the export and re-export of certain commodities +and technical data of United States origin, including the Software. Customer +agrees that it will not export or re-export the Software without the appropriate +United States or foreign government licenses. +9.2 Entire Agreement. This Agreement constitutes the entire agreement between +the parties concerning the subject matter hereof. This Agreement supersedes all +prior or contemporaneous discussions, proposals and agreements between the +parties relating to the subject matter hereof. No amendment, modification or +waiver of any provision of this Agreement shall be effective unless in writing +and signed by both parties. Any additional or different terms on any purchase +orders issued by Customer to StrongLoop shall not be binding on either party, +are hereby rejected by StrongLoop and void. +9.3 Severability. If any provision of this Agreement is held to be invalid or +unenforceable, the remaining portions shall remain in full force and effect and +such provision shall be enforced to the maximum extent possible so as to effect +the intent of the parties and shall be reformed to the extent necessary to make +such provision valid and enforceable. +9.4 Waiver. No waiver of rights by either party may be implied from any actions +or failures to enforce rights under this Agreement. +9.5 Force Majeure. Neither party shall be liable to the other for any delay or +failure to perform due to causes beyond its reasonable control (excluding +payment of monies due). +9.6 No Third Party Beneficiaries. Unless otherwise specifically stated, the +terms of this Agreement are intended to be and are solely for the benefit of +StrongLoop and Customer and do not create any right in favor of any third party. +9.7 Governing Law and Jurisdiction. This Agreement shall be governed by the +laws of the State of California, without reference to the principles of +conflicts of law. The provisions of the Uniform Computerized Information +Transaction Act and United Nations Convention on Contracts for the International +Sale of Goods shall not apply to this Agreement. The parties shall attempt to +resolve any dispute related to this Agreement informally, initially through +their respective management, and then by non-binding mediation in San Francisco +County, California. Any litigation related to this Agreement shall be brought +in the state or federal courts located in San Francisco County, California, and +only in those courts and each party irrevocably waives any objections to such +venue. +9.8 Notices. All notices must be in writing and shall be effective three (3) +days after the date sent to the other party's headquarters, Attention Chief +Financial Officer. diff --git a/examples/inclusion.js b/examples/inclusion.js new file mode 100644 index 00000000..4fe57ff9 --- /dev/null +++ b/examples/inclusion.js @@ -0,0 +1,129 @@ +var jdb = require('../index'); + +var User, Post, Passport, City, Street, Building; +var nbSchemaRequests = 0; + +setup(function () { + + Passport.find({include: 'owner'}, function (err, passports) { + console.log('passports.owner', passports); + }); + + User.find({include: 'posts'}, function (err, users) { + console.log('users.posts', users); + }); + + Passport.find({include: {owner: 'posts'}}, function (err, passports) { + console.log('passports.owner.posts', passports); + }); + + Passport.find({ + include: {owner: {posts: 'author'}} + }, function (err, passports) { + console.log('passports.owner.posts.author', passports); + }); + + User.find({include: ['posts', 'passports']}, function (err, users) { + console.log('users.passports && users.posts', users); + }); + +}); + +function setup(done) { + var db = new jdb.DataSource({connector: 'memory'}); + City = db.define('City'); + Street = db.define('Street'); + Building = db.define('Building'); + User = db.define('User', { + name: String, + age: Number + }); + Passport = db.define('Passport', { + number: String + }); + Post = db.define('Post', { + title: String + }); + + Passport.belongsTo('owner', {model: User}); + User.hasMany('passports', {foreignKey: 'ownerId'}); + User.hasMany('posts', {foreignKey: 'userId'}); + Post.belongsTo('author', {model: User, foreignKey: 'userId'}); + + db.automigrate(function () { + var createdUsers = []; + var createdPassports = []; + var createdPosts = []; + createUsers(); + function createUsers() { + clearAndCreate( + User, + [ + {name: 'User A', age: 21}, + {name: 'User B', age: 22}, + {name: 'User C', age: 23}, + {name: 'User D', age: 24}, + {name: 'User E', age: 25} + ], + function (items) { + createdUsers = items; + createPassports(); + } + ); + } + + function createPassports() { + clearAndCreate( + Passport, + [ + {number: '1', ownerId: createdUsers[0].id}, + {number: '2', ownerId: createdUsers[1].id}, + {number: '3'} + ], + function (items) { + createdPassports = items; + createPosts(); + } + ); + } + + function createPosts() { + clearAndCreate( + Post, + [ + {title: 'Post A', userId: createdUsers[0].id}, + {title: 'Post B', userId: createdUsers[0].id}, + {title: 'Post C', userId: createdUsers[0].id}, + {title: 'Post D', userId: createdUsers[1].id}, + {title: 'Post E'} + ], + function (items) { + createdPosts = items; + done(); + } + ); + } + + }); +} + +function clearAndCreate(model, data, callback) { + var createdItems = []; + model.destroyAll(function () { + nextItem(null, null); + }); + + var itemIndex = 0; + + function nextItem(err, lastItem) { + if (lastItem !== null) { + createdItems.push(lastItem); + } + if (itemIndex >= data.length) { + callback(createdItems); + return; + } + model.create(data[itemIndex], nextItem); + itemIndex++; + } +} diff --git a/examples/nesting-schema.js b/examples/nesting-schema.js index 3ae83684..899e4e7c 100644 --- a/examples/nesting-schema.js +++ b/examples/nesting-schema.js @@ -29,4 +29,5 @@ var user = new User({name: 'Joe', age: 20, address: {street: '123 Main St', 'cit {label: 'work', email: 'xyz@sample.com'} ], friends: ['John', 'Mary']}); +console.log(user); console.log(user.toObject()); diff --git a/index.js b/index.js index 79171c1b..0090da6b 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,3 @@ -var fs = require('fs'); - exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder; exports.DataSource = exports.Schema = require('./lib/datasource.js').DataSource; exports.ModelBaseClass = require('./lib/model.js'); @@ -14,7 +12,7 @@ exports.__defineGetter__('BaseSQL', function () { exports.__defineGetter__('version', function () { - return JSON.parse(fs.readFileSync(__dirname + '/package.json')).version; + return require('./package.json').version; }); var commonTest = './test/common_test'; diff --git a/lib/dao.js b/lib/dao.js index eaaab854..f5edfe13 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -6,13 +6,12 @@ module.exports = DataAccessObject; /** * Module dependencies */ -var util = require('util'); var jutil = require('./jutil'); var validations = require('./validations.js'); var ValidationError = validations.ValidationError; -require('./relations.js'); -var Inclusion = require('./include.js'); var Relation = require('./relations.js'); +var Inclusion = require('./include.js'); +var List = require('./list.js'); var geo = require('./geo'); var Memory = require('./connectors/memory').Memory; var utils = require('./utils'); @@ -557,12 +556,16 @@ DataAccessObject.find = function find(params, cb) { var includes = params.include || []; if (typeof includes === 'string') { includes = [includes]; - } else if (typeof includes === 'object') { + } else if (!Array.isArray(includes) && typeof includes === 'object') { includes = Object.keys(includes); } includes.forEach(function (inc) { // Promote the included model as a direct property - obj.__data[inc] = obj.__cachedRelations[inc]; + var data = obj.__cachedRelations[inc]; + if(Array.isArray(data)) { + data = new List(data, null, obj); + } + obj.__data[inc] = data; }); delete obj.__data.__cachedRelations; } diff --git a/lib/datasource.js b/lib/datasource.js index 356a58c9..0bff0eee 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -7,11 +7,8 @@ var jutil = require('./jutil'); var utils = require('./utils'); var ModelBaseClass = require('./model.js'); var DataAccessObject = require('./dao.js'); -var List = require('./list.js'); var EventEmitter = require('events').EventEmitter; var util = require('util'); -var path = require('path'); -var fs = require('fs'); var assert = require('assert'); var async = require('async'); @@ -540,7 +537,7 @@ DataSource.prototype.mixin = function (ModelCtor) { var DAO = this.DataAccessObject; // mixin DAO - jutil.mixin(ModelCtor, DAO, {proxyFunctions: true}); + jutil.mixin(ModelCtor, DAO, {proxyFunctions: true, override: true}); // decorate operations as alias functions Object.keys(ops).forEach(function (name) { diff --git a/lib/jutil.js b/lib/jutil.js index 03e21500..c33ec24c 100644 --- a/lib/jutil.js +++ b/lib/jutil.js @@ -55,39 +55,39 @@ exports.mixin = function (newClass, mixinClass, options) { } if (options.staticProperties) { - var staticProxies = []; - Object.keys(mixinClass).forEach(function (classProp) { - if (classProp !== 'super_' && classProp !== '_mixins' - && (!newClass.hasOwnProperty(classProp) || options.override)) { - var pd = Object.getOwnPropertyDescriptor(mixinClass, classProp); - if (options.proxyFunctions && pd.writable - && typeof pd.value === 'function') { - pd.value = exports.proxy(pd.value, staticProxies); - } - Object.defineProperty(newClass, classProp, pd); - } - }); + mixInto(mixinClass, newClass, options); } - if (options.instanceProperties) { - if (mixinClass.prototype) { - var instanceProxies = []; - Object.keys(mixinClass.prototype).forEach(function (instanceProp) { - if (!newClass.prototype.hasOwnProperty(instanceProp) || options.override) { - var pd = Object.getOwnPropertyDescriptor(mixinClass.prototype, instanceProp); - if (options.proxyFunctions && pd.writable && typeof pd.value === 'function') { - pd.value = exports.proxy(pd.value, instanceProxies); - } - Object.defineProperty(newClass.prototype, instanceProp, pd); - } - }); - } + if (options.instanceProperties && mixinClass.prototype) { + mixInto(mixinClass.prototype, newClass.prototype, options); } - + return newClass; }; -exports.proxy = function (fn, proxies) { +function mixInto(sourceScope, targetScope, options) { + var proxies = []; + + Object.keys(sourceScope).forEach(function (propertyName, options) { + var targetPropertyExists = targetScope.hasOwnProperty(propertyName); + var sourceProperty = Object.getOwnPropertyDescriptor(sourceScope, propertyName); + var targetProperty = targetPropertyExists && Object.getOwnPropertyDescriptor(targetScope, propertyName); + var sourceIsFunc = typeof sourceProperty.value === 'function'; + var isFunc = targetPropertyExists && typeof targetProperty.value === 'function'; + var isDelegate = isFunc && targetProperty.value._delegate; + var shouldOverride = options.override || !targetPropertyExists || isDelegate; + + if (shouldOverride) { + if (sourceIsFunc) { + sourceProperty.value = exports.proxy(sourceProperty.value, proxies); + } + + Object.defineProperty(targetScope, propertyName, sourceProperty); + } + }); +} + +exports.proxy = function createProxy(fn, proxies) { // Make sure same methods referenced by different properties have the same proxy // For example, deleteById is an alias of removeById proxies = proxies || []; diff --git a/lib/list.js b/lib/list.js index b5be01ad..d679d570 100644 --- a/lib/list.js +++ b/lib/list.js @@ -1,154 +1,75 @@ +var util = require('util'); + module.exports = List; -/** - * List class provides functionality of nested collection - * - * @param {Array} data - array of items. - * @param {Crap} type - array with some type information? TODO: rework this API. - * @param {AbstractClass} parent - owner of list. - * @constructor - */ -function List(data, type, parent) { +function List(items, itemType, parent) { var list = this; if (!(list instanceof List)) { - return new List(data, type, parent); + return new List(items, itemType, parent); } - if (typeof data === 'string') { + if (typeof items === 'string') { try { - data = JSON.parse(data); + items = JSON.parse(items); } catch (e) { - throw new Error('could not create List from JSON string: ', data); + throw new Error('could not create List from JSON string: ', items); } } - if (data && data instanceof List) data = data.items; + var arr = []; + arr.__proto__ = List.prototype; - Object.defineProperty(list, 'parent', { - writable: false, - enumerable: false, - configurable: false, - value: parent - }); + items = items || []; + if (!Array.isArray(items)) { + throw new Error('Items must be an array: ' + items); + } - Object.defineProperty(list, 'nextid', { + if(!itemType) { + itemType = items[0] && items[0].constructor; + } + + if (Array.isArray(itemType)) { + itemType = itemType[0]; + } + + Object.defineProperty(arr, 'itemType', { writable: true, enumerable: false, - value: 1 + value: itemType }); - data = list.items = data || []; - var Item = list.ItemType = ListItem; - - if (typeof type === 'object' && type.constructor.name === 'Array') { - Item = list.ItemType = type[0] || ListItem; - } - - data.forEach(function (item, i) { - data[i] = Item(item, list); - Object.defineProperty(list, data[i].id, { + if (parent) { + Object.defineProperty(arr, 'parent', { writable: true, enumerable: false, - configurable: true, - value: data[i] + value: parent }); - if (list.nextid <= data[i].id) { - list.nextid = data[i].id + 1; - } - }); - - Object.defineProperty(list, 'length', { - enumerable: false, - configurable: true, - get: function () { - return list.items.length; - } - }); - - return list; - -} - -var _; -try { - var underscore = 'underscore'; - _ = require(underscore); -} catch (e) { - _ = false; -} - -if (_) { - var _import = [ - // collection methods - 'each', - 'map', - 'reduce', - 'reduceRight', - 'find', - 'filter', - 'reject', - 'all', - 'any', - 'include', - 'invoke', - 'pluck', - 'max', - 'min', - 'sortBy', - 'groupBy', - 'sortedIndex', - 'shuffle', - 'toArray', - 'size', - // array methods - 'first', - 'initial', - 'last', - 'rest', - 'compact', - 'flatten', - 'without', - 'union', - 'intersection', - 'difference', - 'uniq', - 'zip', - 'indexOf', - 'lastIndexOf', - 'range' - ]; - - _import.forEach(function (name) { - List.prototype[name] = function () { - var args = [].slice.call(arguments); - args.unshift(this.items); - return _[name].apply(_, args); - }; - }); -} - -['slice', 'forEach', 'filter', 'reduce', 'map'].forEach(function (method) { - var slice = [].slice; - List.prototype[method] = function () { - return Array.prototype[method].apply(this.items, slice.call(arguments)); - }; -}); - -List.prototype.find = function (pattern, field) { - if (field) { - var res; - this.items.forEach(function (o) { - if (o[field] == pattern) res = o; - }); - return res; - } else { - return this.items[this.items.indexOf(pattern)]; } + + items.forEach(function (item, i) { + if (itemType && !(item instanceof itemType)) { + arr[i] = itemType(item); + } else { + arr[i] = item; + } + }); + + return arr; +} + +util.inherits(List, Array); + +var _push = List.prototype.push; + +List.prototype.push = function (obj) { + var item = this.itemType && (obj instanceof this.itemType) ? obj : this.itemType(obj); + _push.call(this, item); + return item; }; List.prototype.toObject = function (onlySchema) { var items = []; - this.items.forEach(function (item) { + this.forEach(function (item) { if (item.toObject) { items.push(item.toObject(onlySchema)); } else { @@ -163,75 +84,15 @@ List.prototype.toJSON = function () { }; List.prototype.toString = function () { - return JSON.stringify(this.items); + return JSON.stringify(this.toJSON()); }; -List.prototype.autoincrement = function () { - return this.nextid++; -}; - -List.prototype.push = function (obj) { - var item = new ListItem(obj, this); - this.items.push(item); - return item; -}; - -List.prototype.remove = function (obj) { - var id = obj.id ? obj.id : obj; - var found = false; - this.items.forEach(function (o, i) { - if (id && o.id == id) { - found = i; - if (o.id !== id) { - console.log('WARNING! Type of id not matched'); - } - } - }); - if (found !== false) { - delete this[id]; - this.items.splice(found, 1); - } -}; - -List.prototype.sort = function (cb) { - return this.items.sort(cb); -}; - -List.prototype.map = function (cb) { - if (typeof cb === 'function') return this.items.map(cb); - if (typeof cb === 'string') return this.items.map(function (el) { - if (typeof el[cb] === 'function') return el[cb](); - if (el.hasOwnProperty(cb)) return el[cb]; - }); -}; - -function ListItem(data, parent) { - if (!(this instanceof ListItem)) { - return new ListItem(data, parent); - } - if (typeof data === 'object') { - for (var i in data) this[i] = data[i]; - } else { - this.id = data; - } - Object.defineProperty(this, 'parent', { - writable: false, - enumerable: false, - configurable: true, - value: parent - }); - if (!this.id) { - this.id = parent.autoincrement(); - } - if (parent.ItemType) { - this.__proto__ = parent.ItemType.prototype; - if (parent.ItemType !== ListItem) { - parent.ItemType.apply(this); - } - } - - this.save = function (c) { - parent.parent.save(c); - }; -} +/* + var strArray = new List(['1', 2], String); + strArray.push(3); + console.log(strArray); + console.log(strArray.length); + console.log(strArray.toJSON()); + console.log(strArray.toString()); + */ diff --git a/lib/types.js b/lib/types.js index e5dbcc1a..7c35adce 100644 --- a/lib/types.js +++ b/lib/types.js @@ -1,44 +1,48 @@ -module.exports = function (Types) { +var Types = {}; +/** + * Schema types + */ +Types.Text = function Text(value) { + if (!(this instanceof Text)) { + return value; + } + this.value = value; +}; // Text type + +Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () { + return this.value; +}; + +Types.JSON = function JSON(value) { + if (!(this instanceof JSON)) { + return value; + } + this.value = value; +}; // JSON Object +Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () { + return this.value; +}; + +Types.Any = function Any(value) { + if (!(this instanceof Any)) { + return value; + } + this.value = value; +}; // Any Type +Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () { + return this.value; +}; + +module.exports = function (modelTypes) { - var List = require('./list.js'); var GeoPoint = require('./geo').GeoPoint; - /** - * Schema types - */ - Types.Text = function Text(value) { - if (!(this instanceof Text)) { - return value; - } - this.value = value; - }; // Text type + for(var t in Types) { + modelTypes[t] = Types[t]; + } - Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () { - return this.value; - }; - - Types.JSON = function JSON(value) { - if (!(this instanceof JSON)) { - return value; - } - this.value = value; - }; // JSON Object - Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () { - return this.value; - }; - - Types.Any = function Any(value) { - if (!(this instanceof Any)) { - return value; - } - this.value = value; - }; // Any Type - Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () { - return this.value; - }; - - Types.schemaTypes = {}; - Types.registerType = function (type, names) { + modelTypes.schemaTypes = {}; + modelTypes.registerType = function (type, names) { names = names || []; names = names.concat([type.name]); for (var n = 0; n < names.length; n++) { @@ -46,16 +50,18 @@ module.exports = function (Types) { } }; - Types.registerType(Types.Text); - Types.registerType(Types.JSON); - Types.registerType(Types.Any); + modelTypes.registerType(Types.Text); + modelTypes.registerType(Types.JSON); + modelTypes.registerType(Types.Any); - Types.registerType(String); - Types.registerType(Number); - Types.registerType(Boolean); - Types.registerType(Date); - Types.registerType(Buffer, ['Binary']); - Types.registerType(Array); - Types.registerType(GeoPoint); - Types.registerType(Object); -}; \ No newline at end of file + modelTypes.registerType(String); + modelTypes.registerType(Number); + modelTypes.registerType(Boolean); + modelTypes.registerType(Date); + modelTypes.registerType(Buffer, ['Binary']); + modelTypes.registerType(Array); + modelTypes.registerType(GeoPoint); + modelTypes.registerType(Object); +}; + +module.exports.Types = Types; \ No newline at end of file diff --git a/package.json b/package.json index 7b28d083..495f0414 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-datasource-juggler", - "version": "1.3.2", + "version": "1.3.3", "description": "LoopBack DataSoure Juggler", "keywords": [ "StrongLoop", @@ -24,13 +24,16 @@ ], "devDependencies": { "should": "~1.2.2", - "mocha": "~1.12.1" + "mocha": "~1.17.1" }, "dependencies": { "async": "~0.2.10", - "inflection": "~1.2.7", + "inflection": "~1.3.5", "traverse": "~0.6.6", "qs": "~0.6.6" }, - "license": "MIT" + "license": { + "name": "Dual MIT/StrongLoop", + "url": "https://github.com/strongloop/loopback-datasource-juggler/blob/master/LICENSE" + } } diff --git a/test/include.test.js b/test/include.test.js index 8b26ee71..d8ca49fd 100644 --- a/test/include.test.js +++ b/test/include.test.js @@ -117,8 +117,15 @@ describe('include', function () { // The relation should be promoted as the 'owner' property user.should.have.property('posts'); user.should.have.property('passports'); + + var userObj = user.toJSON(); + userObj.should.have.property('posts'); + userObj.should.have.property('passports'); + userObj.posts.should.be.an.instanceOf(Array); + userObj.passports.should.be.an.instanceOf(Array); + // The __cachedRelations should be removed from json output - user.toJSON().should.not.have.property('__cachedRelations'); + userObj.should.not.have.property('__cachedRelations'); user.__cachedRelations.should.have.property('posts'); user.__cachedRelations.should.have.property('passports');