Archived
1
0

* Rewrote all wiki note links to be of the form: /notebooks/notebookid?note_id=noteid

* Refactored some of validator decorator to use clearer variable names internally.
 * Validator decorator now supports treating arguments with default values as optional.
 * controller.Notebooks.default() takes an optional note_id argument.
 * controller.Notebooks.contents() takes an optional note_id argument.
 * Wiki.js now makes use of these new controller APIs.
 * Editor.js now takes a notebook_id argument to its constructor so it can properly make links.
This commit is contained in:
Dan Helfman 2007-07-28 04:22:44 +00:00
parent f81707ea2f
commit 2c20d60f9e
6 changed files with 82 additions and 49 deletions

View File

@ -134,7 +134,7 @@ class Valid_bool( object ):
raise ValueError() raise ValueError()
def validate( **kwarg_types ): def validate( **expected ):
""" """
validate() can be used to require that the arguments of the decorated method successfully pass validate() can be used to require that the arguments of the decorated method successfully pass
through particular validators. The validate() method itself is evaluated where it is used as a through particular validators. The validate() method itself is evaluated where it is used as a
@ -177,64 +177,62 @@ def validate( **kwarg_types ):
args = list( args ) args = list( args )
args_index = 1 # skip the self argument args_index = 1 # skip the self argument
for ( arg_name, arg_type ) in kwarg_types.items(): # determine the expected argument names from the decorated function itself
if arg_name == u"kwargs": code = function.func_code
key_type = kwarg_types[ u"kwargs" ].keys()[ 0 ] expected_names = code.co_varnames[ : code.co_argcount ]
value_type = kwarg_types[ u"kwargs" ].values()[ 0 ]
for ( key, value ) in kwargs.items(): # validate each of the expected arguments
if key not in kwarg_types: for expected_name in expected_names:
del( kwargs[ key ] ) if expected_name == u"self": continue
try: expected_type = expected.get( expected_name )
kwargs[ str( key_type( key ) ) ] = value_type( value )
except ( ValueError, TypeError ):
raise Validation_error( key, value, value_type )
continue # look for expected_name in kwargs and store the validated value there
if expected_name in kwargs:
# look for arg_name in kwargs and store the validated value there value = kwargs.get( expected_name )
if arg_name in kwargs: # if there's a tuple of multiple validators for this expected_name, use all of them
value = kwargs.get( arg_name ) if isinstance( expected_type, tuple ):
# if there's a tuple of multiple validators for this arg_name, use all of them for validator in expected_type:
if isinstance( arg_type, tuple ):
for validator in arg_type:
try: try:
value = validator( value ) value = validator( value )
except ( ValueError, TypeError ): except ( ValueError, TypeError ):
raise Validation_error( arg_name, value, validator ) raise Validation_error( expected_name, value, validator )
kwargs[ str( arg_name ) ] = value kwargs[ str( expected_name ) ] = value
# otherwise, there's just a single validator # otherwise, there's just a single validator
else: else:
try: try:
kwargs[ str( arg_name ) ] = arg_type( value ) kwargs[ str( expected_name ) ] = expected_type( value )
except ( ValueError, TypeError ): except ( ValueError, TypeError ):
raise Validation_error( arg_name, value, arg_type ) raise Validation_error( expected_name, value, expected_type )
continue continue
# arg_name wasn't found in kwargs, so use args instead # expected_name wasn't found in kwargs, so look for it in args. if it's not there either,
# raise unless there's a default value for the argument in the decorated function
if args_index >= len( args ): if args_index >= len( args ):
raise Validation_error( arg_name, None, arg_type, message = u"is required" ) if function.func_defaults and args_index >= len( args ) - len( function.func_defaults ):
continue
raise Validation_error( expected_name, None, expected_type, message = u"is required" )
value = args[ args_index ] value = args[ args_index ]
# if there's a tuple of multiple validators for this arg_name, use all of them # if there's a tuple of multiple validators for this expected_name, use all of them
if isinstance( arg_type, tuple ): if isinstance( expected_type, tuple ):
for validator in arg_type: for validator in expected_type:
try: try:
value = validator( value ) value = validator( value )
except ( ValueError, TypeError ): except ( ValueError, TypeError ):
raise Validation_error( arg_name, value, validator ) raise Validation_error( expected_name, value, validator )
args[ args_index ] = value args[ args_index ] = value
# otherwise, there's just a single validator # otherwise, there's just a single validator
else: else:
try: try:
args[ args_index ] = arg_type( value ) args[ args_index ] = expected_type( value )
except ( ValueError, TypeError ): except ( ValueError, TypeError ):
raise Validation_error( arg_name, value, arg_type ) raise Validation_error( expected_name, value, expected_type )
args_index += 1 args_index += 1
# if there are any unexpected arguments, raise
for ( arg_name, arg_value ) in kwargs.items(): for ( arg_name, arg_value ) in kwargs.items():
if not arg_name in kwarg_types: if not arg_name in expected_names:
print arg_name, kwarg_types print arg_name, expected
raise Validation_error( arg_name, arg_value, None, message = u"is an unknown argument" ) raise Validation_error( arg_name, arg_value, None, message = u"is an unknown argument" )
return function( *args, **kwargs ) return function( *args, **kwargs )

View File

@ -62,6 +62,12 @@ class Test_notebooks( Test_controller ):
assert result.get( u"notebook_id" ) == self.notebook.object_id assert result.get( u"notebook_id" ) == self.notebook.object_id
def test_default_with_note( self ):
result = self.http_get( "/notebooks/%s?note_id=%s" % ( self.notebook.object_id, self.note.object_id ) )
assert result.get( u"notebook_id" ) == self.notebook.object_id
assert result.get( u"note_id" ) == self.note.object_id
def test_contents( self ): def test_contents( self ):
self.login() self.login()
@ -76,6 +82,24 @@ class Test_notebooks( Test_controller ):
assert len( notebook.startup_notes ) == 1 assert len( notebook.startup_notes ) == 1
assert notebook.startup_notes[ 0 ] == self.note assert notebook.startup_notes[ 0 ] == self.note
def test_contents_with_note( self ):
self.login()
result = self.http_get(
"/notebooks/contents?notebook_id=%s&note_id=%s" % ( self.notebook.object_id, self.note.object_id ),
session_id = self.session_id,
)
notebook = result[ "notebook" ]
assert notebook.object_id == self.notebook.object_id
assert len( notebook.startup_notes ) == 1
assert notebook.startup_notes[ 0 ] == self.note
note = result[ "note" ]
assert note.object_id == self.note.object_id
def test_contents_without_login( self ): def test_contents_without_login( self ):
result = self.http_get( result = self.http_get(
"/notebooks/contents?notebook_id=%s" % self.notebook.object_id, "/notebooks/contents?notebook_id=%s" % self.notebook.object_id,

View File

@ -1,7 +1,8 @@
note_titles = {} // map from note title to the open editor for that note note_titles = {} // map from note title to the open editor for that note
function Editor( id, note_text, revisions_list, insert_after_iframe_id, read_write, startup, highlight, focus ) { function Editor( id, notebook_id, note_text, revisions_list, insert_after_iframe_id, read_write, startup, highlight, focus ) {
this.id = id; this.id = id;
this.notebook_id;
this.initial_text = note_text; this.initial_text = note_text;
this.revisions_list = revisions_list; this.revisions_list = revisions_list;
this.read_write = read_write; this.read_write = read_write;
@ -273,11 +274,11 @@ Editor.prototype.mouse_clicked = function ( event ) {
var id; var id;
var link_title = scrapeText( link ); var link_title = scrapeText( link );
var editor = note_titles[ link_title ]; var editor = note_titles[ link_title ];
var href_leaf = link.href.split( "/" ).pop(); var href_leaf = link.href.split( "?note_id=" ).pop();
// if the link's title corresponds to an open note id, set that as the link's destination // if the link's title corresponds to an open note id, set that as the link's destination
if ( editor ) { if ( editor ) {
id = editor.id; id = editor.id;
link.href = "/notes/" + id; link.href = "/notebooks/" + this.notebook_id + "?note_id=" + id;
// if this is a new link, get a new note id and set it for the link's destination // if this is a new link, get a new note id and set it for the link's destination
} else if ( href_leaf == "new" ) { } else if ( href_leaf == "new" ) {
signal( this, "load_editor_by_title", link_title, this.iframe.id ); signal( this, "load_editor_by_title", link_title, this.iframe.id );
@ -363,7 +364,7 @@ Editor.prototype.start_link = function () {
range.setStart( container, range.startOffset - 1 ); range.setStart( container, range.startOffset - 1 );
} }
this.exec_command( "createLink", "/notes/new" ); this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
var links = getElementsByTagAndClassName( "a", null, parent = this.document ); var links = getElementsByTagAndClassName( "a", null, parent = this.document );
for ( var i in links ) { for ( var i in links ) {
@ -382,7 +383,7 @@ Editor.prototype.start_link = function () {
} }
// otherwise, just create a link with the selected text as the link title // otherwise, just create a link with the selected text as the link title
} else { } else {
this.exec_command( "createLink", "/notes/new" ); this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
} }
} else if ( this.document.selection ) { // browsers such as IE } else if ( this.document.selection ) { // browsers such as IE
var range = this.document.selection.createRange(); var range = this.document.selection.createRange();
@ -395,7 +396,7 @@ Editor.prototype.start_link = function () {
range.select(); range.select();
} }
this.exec_command( "createLink", "/notes/new" ); this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
} }
} }

View File

@ -16,7 +16,8 @@ function Wiki() {
if ( this.notebook_id ) { if ( this.notebook_id ) {
this.invoker.invoke( this.invoker.invoke(
"/notebooks/contents", "GET", { "/notebooks/contents", "GET", {
"notebook_id": this.notebook_id "notebook_id": this.notebook_id,
"note_id": getElement( "note_id" ).value
}, },
function( result ) { self.populate( result ); } function( result ) { self.populate( result ); }
); );
@ -121,9 +122,17 @@ Wiki.prototype.populate = function ( result ) {
var note = this.notebook.startup_notes[ i ]; var note = this.notebook.startup_notes[ i ];
if ( !note ) continue; if ( !note ) continue;
this.startup_notes[ note.object_id ] = true; this.startup_notes[ note.object_id ] = true;
var focus = ( i == 0 );
this.create_editor( note.object_id, note.contents, note.revisions_list, undefined, undefined, false, focus ); // don't actually create an editor if a particular note was provided in the result
if ( !result.note ) {
var focus = ( i == 0 );
this.create_editor( note.object_id, note.contents, note.revisions_list, undefined, undefined, false, focus );
}
} }
// if one particular note was provided, then just display an editor for that note
if ( result.note )
this.create_editor( result.note.object_id, result.note.contents, result.note.revisions_list, undefined, undefined, false, true );
} }
Wiki.prototype.background_clicked = function ( event ) { Wiki.prototype.background_clicked = function ( event ) {
@ -214,7 +223,7 @@ Wiki.prototype.create_editor = function ( id, note_text, revisions_list, insert_
for ( var i in links ) { for ( var i in links ) {
// a link matches if its contained text is the same as this note's title // a link matches if its contained text is the same as this note's title
if ( scrapeText( links[ i ] ) == note_title ) if ( scrapeText( links[ i ] ) == note_title )
links[ i ].href = "/notes/" + id; links[ i ].href = "/notebooks/" + this.notebook_id + "?note_id=" + id;
} }
} }
@ -232,7 +241,7 @@ Wiki.prototype.create_editor = function ( id, note_text, revisions_list, insert_
} }
var startup = this.startup_notes[ id ]; var startup = this.startup_notes[ id ];
var editor = new Editor( id, note_text, revisions_list, undefined, this.read_write, startup, highlight, focus ); var editor = new Editor( id, this.notebook_id, note_text, revisions_list, undefined, this.read_write, startup, highlight, focus );
if ( this.read_write ) { if ( this.read_write ) {
connect( editor, "state_changed", this, "editor_state_changed" ); connect( editor, "state_changed", this, "editor_state_changed" );
@ -575,7 +584,7 @@ Pulldown.prototype.shutdown = function () {
} }
Options_pulldown = function ( notebook_id, invoker, editor ) { function Options_pulldown( notebook_id, invoker, editor ) {
Pulldown.call( this, notebook_id, "options_" + editor.id, editor.options_button ); Pulldown.call( this, notebook_id, "options_" + editor.id, editor.options_button );
this.invoker = invoker; this.invoker = invoker;
@ -620,7 +629,7 @@ Options_pulldown.prototype.shutdown = function () {
} }
Changes_pulldown = function ( notebook_id, invoker, editor ) { function Changes_pulldown( notebook_id, invoker, editor ) {
Pulldown.call( this, notebook_id, "changes_" + editor.id, editor.changes_button ); Pulldown.call( this, notebook_id, "changes_" + editor.id, editor.changes_button );
this.invoker = invoker; this.invoker = invoker;

View File

@ -4,7 +4,7 @@ from Tags import Html, Head, Title, Style, Meta, Body, H1, Div, Span, Hr, A
class Html_file( Html ): class Html_file( Html ):
NOTE_LINK_PATTERN = re.compile( u'<a\s+href="\/notes\/([a-z0-9]*)"\s*>', re.IGNORECASE ) NOTE_LINK_PATTERN = re.compile( u'<a\s+href="\/notebooks\/[a-z0-9]*\?note_id=([a-z0-9]*)"\s*>', re.IGNORECASE )
def __init__( self, notebook_name, notes ): def __init__( self, notebook_name, notes ):
relinked_notes = {} # map from note id to relinked note contents relinked_notes = {} # map from note id to relinked note contents

View File

@ -6,13 +6,14 @@ from Toolbar import Toolbar
class Main_page( Page ): class Main_page( Page ):
def __init__( self, notebook_id = None ): def __init__( self, notebook_id = None, note_id = None ):
title = None title = None
Page.__init__( Page.__init__(
self, self,
title, title,
Input( type = u"hidden", name = u"notebook_id", id = u"notebook_id", value = notebook_id or "" ), Input( type = u"hidden", name = u"notebook_id", id = u"notebook_id", value = notebook_id or "" ),
Input( type = u"hidden", name = u"note_id", id = u"note_id", value = note_id or "" ),
Div( Div(
id = u"status_area", id = u"status_area",
), ),