Skip to content

Commit

Permalink
work around Bookshelf.Model.extend manipulating __proto__ for adding …
Browse files Browse the repository at this point in the history
…static properties to child class
  • Loading branch information
bogus34 committed Mar 15, 2016
1 parent 0ba22f5 commit 349d816
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 15 deletions.
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
0.2.3
=====

* Work around weird __proto__ manipulations used in Bookshelf.Model.extend (issue #4)

0.2.2
=====

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bookshelf-schema",
"version": "0.2.2",
"version": "0.2.3",
"description": "Plugin for adding schema to Bookshelf models",
"main": "lib/index.js",
"scripts": {
Expand All @@ -18,15 +18,15 @@
"url": "https://github.com/bogus34/bookshelf-schema"
},
"devDependencies": {
"bookshelf": ">=0.8.2 <0.10",
"bookshelf": ">=0.8.2 <0.11",
"chai": "^3.3.0",
"chai-as-promised": "^5.2.0",
"chai-spies": "^0.7.1",
"co": "^4.6.0",
"coffee-coverage": "^1.0.1",
"coffee-script": "^1.10.0",
"istanbul": "^0.4.2",
"knex": ">=0.8.6 <0.10",
"knex": ">=0.8.6 <0.11",
"mocha": "^2.3.3",
"pg": "^4.4.2",
"sqlite3": "^3.1.0"
Expand All @@ -36,6 +36,6 @@
"inflection": "^1.8.0"
},
"peerDependencies": {
"bookshelf": ">=0.8.2 <0.10"
"bookshelf": ">=0.8.2 <0.11"
}
}
34 changes: 31 additions & 3 deletions src/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,18 @@ plugin = (options = {}) -> (db) ->
options.createProperties ?= true
options.validation ?= true

originalModel = db.Model
#
# Bookshelf.Model.extend manipulates __proto__ instead of copying static
# methods to child class and it breaks normal CoffeeScript inheritance.
# So if any plugin added before Schema extends Model, we have a problem.
# This function is trying to fix it.
#
fixInheritance = (base, cls) ->
proto = base.__proto__
while typeof proto is 'function'
for own k, v of proto when not cls.hasOwnProperty(k)
cls[k] = v
proto = proto.__proto__

buildSchema = (entities) ->
schema = []
Expand All @@ -52,10 +63,23 @@ plugin = (options = {}) -> (db) ->
contributeToModel this, @__schema

@extend: (props, statics) ->
if statics.schema
if statics?.schema
schema = statics.schema
delete statics.schema
cls = originalModel.extend.call this, props, statics

#
# As with fixInheritance but for the case when someone extends Model after Schema
# We copy static properties over to fix CoffeeScript class inheritance after it.
#
ctor = if props.hasOwnProperty 'constructor'
props.constructor
else
-> Model.apply(this, arguments)
ctor[k] = v for own k, v of Model
props.constructor = ctor

cls = super props, statics

return cls unless schema
cls.schema schema
cls
Expand Down Expand Up @@ -156,6 +180,8 @@ plugin = (options = {}) -> (db) ->
for e in target.__schema when e.liftScope?
e.liftScope(to)

fixInheritance db.Model, Model

class Collection extends db.Collection
for method in ['fetch', 'fetchOne']
do (method) ->
Expand Down Expand Up @@ -189,6 +215,8 @@ plugin = (options = {}) -> (db) ->

_applyScopes: Model::_applyScopes

fixInheritance db.Collection, Collection

db.Model = Model
db.Collection = Collection

Expand Down
10 changes: 8 additions & 2 deletions test/init.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ Bookshelf = require 'bookshelf'
Schema = require '../src/'

db = null
init = ->
return db if db?

initDb = ->
db_variant = process.env.BOOKSHELF_SCHEMA_TESTS_DB_VARIANT
db_variant ?= 'sqlite'

Expand All @@ -16,6 +15,7 @@ init = ->
debug: process.env.BOOKSHELF_SCHEMA_TESTS_DEBUG?
connection:
filename: ':memory:'
useNullAsDefault: true
when 'pg', 'postgres'
Knex
client: 'pg'
Expand All @@ -26,9 +26,14 @@ init = ->
password: 'test'
database: 'test'
charset: 'utf8'
useNullAsDefault: true
else throw new Error "Unknown db variant: #{db_variant}"

db = Bookshelf knex

init = ->
return db if db?
db = initDb()
db.plugin Schema()
db

Expand Down Expand Up @@ -102,6 +107,7 @@ inviters = co ->
table.integer 'user_id'

module.exports =
initDb: initDb
init: init
truncate: truncate
users: users
Expand Down
64 changes: 58 additions & 6 deletions test/issues.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,68 @@ describe "Issues", ->
db.model 'Journal', JournalModel

it 'should deduce relation name more properly', ->
# It should not throw
JournalModel.schema [
HasMany JournalItemsModel
]
expect( ->
JournalModel.schema [
HasMany JournalItemsModel
]
).not.to.throw()

it 'should allow to use camelcase name for relations', co ->
it 'should allow to use camelcase name for relations', ->
JournalModel.schema [
HasMany JournalItemsModel, name: 'JournalItems'
]

journal = yield JournalModel.forge().save()

yield JournalModel.forge(id: journal.id).fetch(withRelated: ['JournalItems'])
JournalModel.forge(id: journal.id).fetch(withRelated: ['JournalItems']).should.be.fullfiled

describe '#4', ->
describe 'should work with plugins that extends Model', co ->
it 'added after Schema', ->
db.plugin 'virtuals'
db.plugin 'visibility'

yield init.users()

class User extends db.Model
tableName: 'users'

schema: [
Fields.StringField 'username'
]

User.forge(id: 1).fetch().should.be.fullfiled

it 'added before Schema', co ->
db = init.initDb()
db.plugin 'virtuals'
db.plugin 'visibility'
db.plugin Schema()

yield init.users()

class User extends db.Model
tableName: 'users'

schema: [
Fields.StringField 'username'
]

User.forge(id: 1).fetch().should.be.fullfiled

it 'added around Schema', co ->
db = init.initDb()
db.plugin 'virtuals'
db.plugin Schema()
db.plugin 'visibility'

yield init.users()

class User extends db.Model
tableName: 'users'

schema: [
Fields.StringField 'username'
]

User.forge(id: 1).fetch().should.be.fullfiled

0 comments on commit 349d816

Please sign in to comment.