galaxy-commits
Threads by month
- ----- 2026 -----
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
27 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/32205f911e74/
Changeset: 32205f911e74
User: greg
Date: 2013-11-27 21:53:29
Summary: A bit of code cleanup.
Affected #: 1 file
diff -r 8b613225f1def637af4774b860578a77a7fb0fe4 -r 32205f911e7442fb6eed7155f8306df7aafb731e lib/tool_shed/util/container_util.py
--- a/lib/tool_shed/util/container_util.py
+++ b/lib/tool_shed/util/container_util.py
@@ -1066,9 +1066,6 @@
else:
containing_folder = tool_test_results_root_folder
test_results_dict_id += 1
- #folder_id += 1
- #test_results_folder = Folder( id=folder_id, key='test_results', label='Automated test environment', parent=containing_folder )
- #containing_folder.folders.append( test_results_folder )
folder_id += 1
folder = Folder( id=folder_id, key='test_environment', label='Automated test environment', parent=containing_folder )
containing_folder.folders.append( folder )
@@ -1204,7 +1201,6 @@
subfolder.tool_dependency_installation_errors.append( tool_dependency_installation_error )
installation_error_base_folder.folders.append( subfolder )
containing_folder.installation_errors.append( installation_error_base_folder )
- #containing_folder.folders.append( containing_folder )
if multiple_tool_test_results_dicts:
tool_test_results_root_folder.folders.append( containing_folder )
else:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
27 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8b613225f1de/
Changeset: 8b613225f1de
User: Dave Bouvier
Date: 2013-11-27 21:37:02
Summary: Fix imports.
Affected #: 1 file
diff -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 -r 8b613225f1def637af4774b860578a77a7fb0fe4 lib/tool_shed/scripts/check_repositories_for_functional_tests.py
--- a/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
+++ b/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
@@ -15,6 +15,7 @@
import ConfigParser
import galaxy.webapps.tool_shed.config as tool_shed_config
import galaxy.webapps.tool_shed.model.mapping
+import tool_shed.util.shed_util_common as suc
import logging
import shutil
import tempfile
@@ -31,8 +32,6 @@
from mercurial import __version__
from optparse import OptionParser
from time import strftime
-from tool_shed.util.shed_util_common import clone_repository
-from tool_shed.util.shed_util_common import get_configured_ui
log = logging.getLogger( 'check_repositories_for_functional_tests' )
assert sys.version_info[ :2 ] >= ( 2, 6 )
@@ -129,9 +128,9 @@
testable_revision = False
# Clone the repository up to the changeset revision we're checking.
repo_dir = repository.repo_path( app )
- repo = hg.repository( get_configured_ui(), repo_dir )
+ repo = hg.repository( suc.get_configured_ui(), repo_dir )
work_dir = tempfile.mkdtemp( prefix="tmp-toolshed-cafr" )
- cloned_ok, error_message = clone_repository( repo_dir, work_dir, changeset_revision )
+ cloned_ok, error_message = suc.clone_repository( repo_dir, work_dir, changeset_revision )
if cloned_ok:
# Iterate through all the directories in the cloned changeset revision and determine whether there's a
# directory named test-data. If this directory is not present update the metadata record for the changeset
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Fixes for the tool shed's install and test framework and enhancements to allow a configurable number of test runs to be stored and displayed.
by commits-noreply@bitbucket.org 27 Nov '13
by commits-noreply@bitbucket.org 27 Nov '13
27 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/11028b31d3f0/
Changeset: 11028b31d3f0
User: greg
Date: 2013-11-27 21:28:03
Summary: Fixes for the tool shed's install and test framework and enhancements to allow a configurable number of test runs to be stored and displayed.
Affected #: 10 files
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/galaxy/webapps/tool_shed/api/repositories.py
--- a/lib/galaxy/webapps/tool_shed/api/repositories.py
+++ b/lib/galaxy/webapps/tool_shed/api/repositories.py
@@ -453,5 +453,7 @@
value_mapper = { 'id' : trans.security.encode_id,
'repository_id' : trans.security.encode_id }
if repository_metadata.time_last_tested is not None:
- value_mapper[ 'time_last_tested' ] = time_ago
+ # For some reason the Dictifiable.to_dict() method in ~/galaxy/model/item_attrs.py requires
+ # a function rather than a mapped value, so just pass the time_ago function here.
+ value_mapper[ 'time_last_tested' ] = time_ago
return value_mapper
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/galaxy/webapps/tool_shed/api/repository_revisions.py
--- a/lib/galaxy/webapps/tool_shed/api/repository_revisions.py
+++ b/lib/galaxy/webapps/tool_shed/api/repository_revisions.py
@@ -166,12 +166,11 @@
repository_metadata = metadata_util.get_repository_metadata_by_id( trans, repository_metadata_id )
flush_needed = False
for key, new_value in payload.items():
- if hasattr( repository_metadata, key ):
+ if key == 'time_last_tested':
+ repository_metadata.time_last_tested = datetime.datetime.utcnow()
+ flush_needed = True
+ elif hasattr( repository_metadata, key ):
setattr( repository_metadata, key, new_value )
- if key in [ 'tools_functionally_correct', 'time_last_tested' ]:
- # Automatically update repository_metadata.time_last_tested.
- repository_metadata.time_last_tested = datetime.datetime.utcnow()
- flush_needed = True
flush_needed = True
if flush_needed:
trans.sa_session.add( repository_metadata )
@@ -192,5 +191,7 @@
value_mapper = { 'id' : trans.security.encode_id,
'repository_id' : trans.security.encode_id }
if repository_metadata.time_last_tested is not None:
+ # For some reason the Dictifiable.to_dict() method in ~/galaxy/model/item_attrs.py requires
+ # a function rather than a mapped value, so just pass the time_ago function here.
value_mapper[ 'time_last_tested' ] = time_ago
return value_mapper
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/galaxy/webapps/tool_shed/config.py
--- a/lib/galaxy/webapps/tool_shed/config.py
+++ b/lib/galaxy/webapps/tool_shed/config.py
@@ -53,6 +53,8 @@
self.tool_data_table_config_path = resolve_path( kwargs.get( 'tool_data_table_config_path', 'tool_data_table_conf.xml' ), self.root )
self.shed_tool_data_table_config = resolve_path( kwargs.get( 'shed_tool_data_table_config', 'shed_tool_data_table_conf.xml' ), self.root )
self.ftp_upload_dir = kwargs.get( 'ftp_upload_dir', None )
+ # Install and test framework for testing tools contained in repositories.
+ self.num_tool_test_results_saved = kwargs.get( 'num_tool_test_results_saved', 5 )
# Location for dependencies
if 'tool_dependency_dir' in kwargs:
self.tool_dependency_dir = resolve_path( kwargs.get( "tool_dependency_dir" ), self.root )
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -1515,51 +1515,59 @@
tool_shed_url = web.url_for( '/', qualified=True )
functional_test_results = []
- for metadata_row in trans.sa_session.query( trans.model.RepositoryMetadata ) \
+ for repository_metadata in trans.sa_session.query( trans.model.RepositoryMetadata ) \
.filter( metadata_filter ) \
.join( trans.model.Repository ) \
.filter( and_( trans.model.Repository.table.c.deleted == False,
trans.model.Repository.table.c.private == False,
trans.model.Repository.table.c.deprecated == False,
trans.model.Repository.table.c.user_id == user.id ) ):
- if not metadata_row.tool_test_results:
- continue
- if metadata_row.changeset_revision != metadata_row.repository.tip( trans.app ):
- continue
- current_repository_errors = []
- tool_dependency_errors = []
- repository_dependency_errors = []
- description_lines = []
- # Per the RSS 2.0 specification, all dates in RSS feeds must be formatted as specified in RFC 822
- # section 5.1, e.g. Sat, 07 Sep 2002 00:00:01 UT
- time_tested = metadata_row.time_last_tested.strftime( '%a, %d %b %Y %H:%M:%S UT' )
- repository = metadata_row.repository
- # Generate a citable URL for this repository with owner and changeset revision.
- repository_citable_url = suc.url_join( tool_shed_url, 'view', user.username, repository.name, metadata_row.changeset_revision )
- passed_tests = len( metadata_row.tool_test_results.get( 'passed_tests', [] ) )
- failed_tests = len( metadata_row.tool_test_results.get( 'failed_tests', [] ) )
- missing_test_components = len( metadata_row.tool_test_results.get( 'missing_test_components', [] ) )
- installation_errors = metadata_row.tool_test_results.get( 'installation_errors', [] )
- if installation_errors:
- tool_dependency_errors = installation_errors.get( 'tool_dependencies', [] )
- repository_dependency_errors = installation_errors.get( 'repository_dependencies', [] )
- current_repository_errors = installation_errors.get( 'current_repository', [] )
- description_lines.append( '%d tests passed, %d tests failed, %d tests missing test components.' % \
- ( passed_tests, failed_tests, missing_test_components ) )
- if current_repository_errors:
- description_lines.append( '\nThis repository did not install correctly. ' )
- if tool_dependency_errors or repository_dependency_errors:
- description_lines.append( '\n%d tool dependencies and %d repository dependencies failed to install. ' % \
- ( len( tool_dependency_errors ), len( repository_dependency_errors ) ) )
- title = 'Revision %s of %s' % ( metadata_row.changeset_revision, repository.name )
- # The guid attribute in an RSS feed's list of items allows a feed reader to choose not to show an item as updated
- # if the guid is unchanged. For functional test results, the citable URL is sufficiently unique to enable
- # that behavior.
- functional_test_results.append( dict( title=title,
- guid=repository_citable_url,
- link=repository_citable_url,
- description='\n'.join( description_lines ),
- pubdate=time_tested ) )
+ repository = repository_metadata.repository
+ repo_dir = repository.repo_path( trans.app )
+ repo = hg.repository( suc.get_configured_ui(), repo_dir )
+ latest_downloadable_changeset_revsion = suc.get_latest_downloadable_changeset_revision( trans, repository, repo )
+ if repository_metadata.changeset_revision == latest_downloadable_changeset_revsion:
+ # We'll display only the test run for the latest installable revision in the rss feed.
+ tool_test_results = repository_metadata.tool_test_results
+ if tool_test_results is not None:
+ # The tool_test_results column used to contain a single dictionary, but was recently enhanced to contain
+ # a list of dictionaries, one for each install and test run. We'll display only the latest run in the rss
+ # feed for nwo.
+ if isinstance( tool_test_results, list ):
+ tool_test_results = tool_test_results[ 0 ]
+ current_repository_errors = []
+ tool_dependency_errors = []
+ repository_dependency_errors = []
+ description_lines = []
+ # Per the RSS 2.0 specification, all dates in RSS feeds must be formatted as specified in RFC 822
+ # section 5.1, e.g. Sat, 07 Sep 2002 00:00:01 UT
+ time_tested = repository_metadata.time_last_tested.strftime( '%a, %d %b %Y %H:%M:%S UT' )
+ # Generate a citable URL for this repository with owner and changeset revision.
+ repository_citable_url = suc.url_join( tool_shed_url, 'view', user.username, repository.name, repository_metadata.changeset_revision )
+ passed_tests = len( tool_test_results.get( 'passed_tests', [] ) )
+ failed_tests = len( tool_test_results.get( 'failed_tests', [] ) )
+ missing_test_components = len( tool_test_results.get( 'missing_test_components', [] ) )
+ installation_errors = tool_test_results.get( 'installation_errors', [] )
+ if installation_errors:
+ tool_dependency_errors = installation_errors.get( 'tool_dependencies', [] )
+ repository_dependency_errors = installation_errors.get( 'repository_dependencies', [] )
+ current_repository_errors = installation_errors.get( 'current_repository', [] )
+ description_lines.append( '%d tests passed, %d tests failed, %d tests missing test components.' % \
+ ( passed_tests, failed_tests, missing_test_components ) )
+ if current_repository_errors:
+ description_lines.append( '\nThis repository did not install correctly. ' )
+ if tool_dependency_errors or repository_dependency_errors:
+ description_lines.append( '\n%d tool dependencies and %d repository dependencies failed to install. ' % \
+ ( len( tool_dependency_errors ), len( repository_dependency_errors ) ) )
+ title = 'Revision %s of %s' % ( repository_metadata.changeset_revision, repository.name )
+ # The guid attribute in an RSS feed's list of items allows a feed reader to choose not to show an item as updated
+ # if the guid is unchanged. For functional test results, the citable URL is sufficiently unique to enable
+ # that behavior.
+ functional_test_results.append( dict( title=title,
+ guid=repository_citable_url,
+ link=repository_citable_url,
+ description='\n'.join( description_lines ),
+ pubdate=time_tested ) )
trans.response.set_content_type( 'application/rss+xml' )
return trans.fill_template( '/rss.mako',
title='Tool functional test results',
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/galaxy/webapps/tool_shed/model/__init__.py
--- a/lib/galaxy/webapps/tool_shed/model/__init__.py
+++ b/lib/galaxy/webapps/tool_shed/model/__init__.py
@@ -229,11 +229,14 @@
class RepositoryMetadata( object, Dictifiable ):
- dict_collection_visible_keys = ( 'id', 'repository_id', 'changeset_revision', 'malicious', 'downloadable', 'has_repository_dependencies', 'includes_datatypes',
- 'includes_tools', 'includes_tool_dependencies', 'includes_tools_for_display_in_tool_panel', 'includes_workflows', 'time_last_tested' )
- dict_element_visible_keys = ( 'id', 'repository_id', 'changeset_revision', 'malicious', 'downloadable', 'tools_functionally_correct', 'do_not_test',
- 'test_install_error', 'time_last_tested', 'tool_test_results', 'has_repository_dependencies', 'includes_datatypes',
- 'includes_tools', 'includes_tool_dependencies', 'includes_tools_for_display_in_tool_panel', 'includes_workflows' )
+ dict_collection_visible_keys = ( 'id', 'repository_id', 'changeset_revision', 'malicious', 'downloadable', 'missing_test_components',
+ 'tools_functionally_correct', 'do_not_test', 'test_install_error', 'has_repository_dependencies',
+ 'includes_datatypes', 'includes_tools', 'includes_tool_dependencies', 'includes_tools_for_display_in_tool_panel',
+ 'includes_workflows', 'time_last_tested' )
+ dict_element_visible_keys = ( 'id', 'repository_id', 'changeset_revision', 'malicious', 'downloadable', 'missing_test_components',
+ 'tools_functionally_correct', 'do_not_test', 'test_install_error', 'time_last_tested', 'tool_test_results',
+ 'has_repository_dependencies', 'includes_datatypes', 'includes_tools', 'includes_tool_dependencies',
+ 'includes_tools_for_display_in_tool_panel', 'includes_workflows' )
def __init__( self, id=None, repository_id=None, changeset_revision=None, metadata=None, tool_versions=None, malicious=False, downloadable=False,
missing_test_components=None, tools_functionally_correct=False, do_not_test=False, test_install_error=False, time_last_tested=None,
@@ -242,8 +245,8 @@
self.id = id
self.repository_id = repository_id
self.changeset_revision = changeset_revision
- self.metadata = metadata or dict()
- self.tool_versions = tool_versions or dict()
+ self.metadata = metadata
+ self.tool_versions = tool_versions
self.malicious = malicious
self.downloadable = downloadable
self.missing_test_components = missing_test_components
@@ -251,7 +254,7 @@
self.do_not_test = do_not_test
self.test_install_error = test_install_error
self.time_last_tested = time_last_tested
- self.tool_test_results = tool_test_results or dict()
+ self.tool_test_results = tool_test_results
self.has_repository_dependencies = has_repository_dependencies
# We don't consider the special case has_repository_dependencies_only_if_compiling_contained_td here.
self.includes_datatypes = includes_datatypes
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/tool_shed/scripts/api/common.py
--- a/lib/tool_shed/scripts/api/common.py
+++ b/lib/tool_shed/scripts/api/common.py
@@ -158,7 +158,7 @@
print e.read( 1024 )
sys.exit( 1 )
else:
- return 'Error. '+ str( e.read( 1024 ) )
+ return 'Error. ' + str( e.read( 1024 ) )
if not return_formatted:
return r
print 'Response'
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/tool_shed/scripts/check_repositories_for_functional_tests.py
--- a/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
+++ b/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
@@ -25,6 +25,7 @@
from galaxy.model.orm import and_
from galaxy.model.orm import not_
from galaxy.model.orm import select
+from galaxy.util import listify
from mercurial import hg
from mercurial import ui
from mercurial import __version__
@@ -41,16 +42,17 @@
"""Application that enables updating repository_metadata table records in the Tool Shed."""
def __init__( self, config ):
- if config.database_connection is False:
- config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % str( config.database )
- log.debug( 'Using database connection: %s' % str( config.database_connection ) )
+ self.config = config
+ if self.config.database_connection is False:
+ self.config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % str( config.database )
+ log.debug( 'Using database connection: %s' % str( self.config.database_connection ) )
# Setup the database engine and ORM
- self.model = galaxy.webapps.tool_shed.model.mapping.init( config.file_path,
- config.database_connection,
+ self.model = galaxy.webapps.tool_shed.model.mapping.init( self.config.file_path,
+ self.config.database_connection,
engine_options={},
create_tables=False )
self.hgweb_config_manager = self.model.hgweb_config_manager
- self.hgweb_config_manager.hgweb_config_dir = config.hgweb_config_dir
+ self.hgweb_config_manager.hgweb_config_dir = self.config.hgweb_config_dir
log.debug( 'Using hgweb.config file: %s' % str( self.hgweb_config_manager.hgweb_config ) )
@property
@@ -72,19 +74,7 @@
"id": "tool_wrapper",
"name": "Map with Tool Wrapper",
"requirements": [],
- "tests": [
- {
- "inputs": [ [ "parameter", "value" ], [ "other_parameter", "other_value" ], ],
- "name": "Test-1",
- "outputs": [
- [
- "output_field_name",
- "output_file_name.bed"
- ]
- ],
- "required_files": [ '1.bed', '2.bed', '3.bed' ]
- }
- ],
+ "tests": [],
"tool_config": "database/community_files/000/repo_1/tool_wrapper.xml",
"tool_type": "default",
"version": "1.2.3",
@@ -120,11 +110,6 @@
missing_test_components = []
repository = repository_metadata.repository
records_checked += 1
- # Create the tool_test_results_dict dictionary, using the dictionary from the previous test run if available.
- if repository_metadata.tool_test_results:
- tool_test_results_dict = repository_metadata.tool_test_results
- else:
- tool_test_results_dict = {}
# Check the next repository revision.
changeset_revision = str( repository_metadata.changeset_revision )
name = repository.name
@@ -141,7 +126,7 @@
tool_dicts = metadata.get( 'tools', None )
if tool_dicts is not None:
has_test_data = False
- testable_revision_found = False
+ testable_revision = False
# Clone the repository up to the changeset revision we're checking.
repo_dir = repository.repo_path( app )
repo = hg.repository( get_configured_ui(), repo_dir )
@@ -236,7 +221,7 @@
if test_errors not in missing_test_components:
missing_test_components.append( test_errors )
if tool_has_tests and has_test_files:
- testable_revision_found = True
+ testable_revision = True
# Remove the cloned repository path. This has to be done after the check for required test files, for obvious reasons.
if os.path.exists( work_dir ):
shutil.rmtree( work_dir )
@@ -253,6 +238,27 @@
if 'missing_components' in invalid_test:
print '# %s' % invalid_test[ 'missing_components' ]
if not info_only:
+ # Get or create the list of tool_test_results dictionaries.
+ if repository_metadata.tool_test_results is not None:
+ # We'll listify the column value in case it uses the old approach of storing the results of only a single test run.
+ tool_test_results_dicts = listify( repository_metadata.tool_test_results )
+ else:
+ tool_test_results_dicts = []
+ if tool_test_results_dicts:
+ # Inspect the tool_test_results_dict for the last test run in case it contains only a test_environment
+ # entry. This will occur with multiple runs of this script without running the associated
+ # install_and_test_tool_sed_repositories.sh script which will further populate the tool_test_results_dict.
+ tool_test_results_dict = tool_test_results_dicts[ 0 ]
+ if len( tool_test_results_dict ) <= 1:
+ # We can re-use the mostly empty tool_test_results_dict for this run, but we need to eliminate it from
+ # the list of tool_test_results_dicts since it will be re-inserted later.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ else:
+ # The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
+ tool_test_results_dict = {}
+ else:
+ # Create a new dictionary for the most recent test run.
+ tool_test_results_dict = {}
test_environment_dict = tool_test_results_dict.get( 'test_environment', {} )
test_environment_dict[ 'tool_shed_database_version' ] = get_database_version( app )
test_environment_dict[ 'tool_shed_mercurial_version' ] = __version__.version
@@ -270,12 +276,24 @@
# changeset revision will be created, either of which will be automatically checked and flagged as appropriate.
# In the install and test script, this behavior is slightly different, since we do want to always run functional
# tests on the most recent downloadable changeset revision.
- if should_set_do_not_test_flag( app, repository_metadata.repository, changeset_revision ) and not testable_revision_found:
+ if should_set_do_not_test_flag( app, repository, changeset_revision, testable_revision ):
+ print "# Setting do_not_test to True on revision %s of repository %s because it is missing test components" % \
+ ( changeset_revision, name )
+ print "# and it is not the latest downloadable revision."
repository_metadata.do_not_test = True
repository_metadata.tools_functionally_correct = False
repository_metadata.missing_test_components = True
tool_test_results_dict[ 'missing_test_components' ] = missing_test_components
- repository_metadata.tool_test_results = tool_test_results_dict
+ # Store only the configured number of test runs.
+ num_tool_test_results_saved = int( app.config.num_tool_test_results_saved )
+ if len( tool_test_results_dicts ) >= num_tool_test_results_saved:
+ test_results_index = num_tool_test_results_saved - 1
+ new_tool_test_results_dicts = tool_test_results_dicts[ :test_results_index ]
+ else:
+ new_tool_test_results_dicts = [ d for d in tool_test_results_dicts ]
+ # Insert the new element into the first position in the list.
+ new_tool_test_results_dicts.insert( 0, tool_test_results_dict )
+ repository_metadata.tool_test_results = new_tool_test_results_dicts
app.sa_session.add( repository_metadata )
app.sa_session.flush()
stop = time.time()
@@ -313,19 +331,6 @@
changelog_tuples.append( ( ctx.rev(), str( ctx ) ) )
return changelog_tuples
-def is_most_recent_downloadable_revision( app, repository, changeset_revision, downloadable_revisions ):
- # Get a list of ( numeric revision, changeset hash ) tuples from the changelog.
- changelog = get_repo_changelog_tuples( repository.repo_path( app ) )
- latest_downloadable_revision = None
- for ctx_rev, changeset_hash in changelog:
- if changeset_hash in downloadable_revisions:
- # The last changeset hash in the changelog that is present in the list of downloadable revisions will always be the most
- # recent downloadable revision, since the changelog tuples are ordered from earliest to most recent.
- latest_downloadable_revision = changeset_hash
- if latest_downloadable_revision == changeset_revision:
- return True
- return False
-
def main():
'''Script that checks repositories to see if the tools contained within them have functional tests defined.'''
parser = OptionParser()
@@ -345,7 +350,7 @@
config_parser.read( ini_file )
config_dict = {}
for key, value in config_parser.items( "app:main" ):
- config_dict[key] = value
+ config_dict[ key ] = value
config = tool_shed_config.Configuration( **config_dict )
config_section = options.section
now = strftime( "%Y-%m-%d %H:%M:%S" )
@@ -361,34 +366,27 @@
print "# Displaying extra information ( --verbosity = %d )" % options.verbosity
check_and_update_repository_metadata( app, info_only=options.info_only, verbosity=options.verbosity )
-def should_set_do_not_test_flag( app, repository, changeset_revision ):
- '''
- Returns True if:
- a) There are multiple downloadable revisions, and the provided changeset revision is not the most recent downloadable revision. In this case,
- the revision will never be updated with correct data, and re-testing it would be redundant.
- b) There are one or more downloadable revisions, and the provided changeset revision is the most recent downloadable revision. In this case, if
- the repository is updated with test data or functional tests, the downloadable changeset revision that was tested will either be replaced
- with the new changeset revision, or a new downloadable changeset revision will be created, either of which will be automatically checked and
- flagged as appropriate. In the install and test script, this behavior is slightly different, since we do want to always run functional tests
- on the most recent downloadable changeset revision.
- '''
- repository_revisions = app.sa_session.query( app.model.RepositoryMetadata ) \
- .filter( and_( app.model.RepositoryMetadata.table.c.downloadable == True,
- app.model.RepositoryMetadata.table.c.repository_id == repository.id ) ) \
- .all()
- downloadable_revisions = [ repository_metadata.changeset_revision for repository_metadata in repository_revisions ]
- is_latest_revision = is_most_recent_downloadable_revision( app, repository, changeset_revision, downloadable_revisions )
- if len( downloadable_revisions ) == 1:
- return True
- elif len( downloadable_revisions ) > 1 and is_latest_revision:
- return True
- elif len( downloadable_revisions ) > 1 and not is_latest_revision:
- return True
- else:
- return False
+def should_set_do_not_test_flag( app, repository, changeset_revision, testable_revision ):
+ """
+ The received testable_revision is True if the tool has defined tests and test files are in the repository
+ This method returns True if the received repository has multiple downloadable revisions and the received
+ changeset_revision is not the most recent downloadable revision and the received testable_revision is False.
+ In this case, the received changeset_revision will never be updated with correct data, and re-testing it
+ would be redundant.
+ """
+ if not testable_revision:
+ repo_dir = repository.repo_path( app )
+ repo = hg.repository( suc.get_configured_ui(), repo_dir )
+ changeset_revisions = suc.get_ordered_metadata_changeset_revisions( repository, repo, downloadable=True )
+ if len( changeset_revisions ) > 1:
+ latest_downloadable_revision = changeset_revisions[ -1 ]
+ if changeset_revision != latest_downloadable_revision:
+ return True
+ return False
if __name__ == "__main__":
- # The repository_metadata.tool_test_results json value should have the following structure:
+ # The repository_metadata.tool_test_results json value should have the following list structure:
+ # [
# {
# "test_environment":
# {
@@ -467,6 +465,7 @@
# },
# ]
# }
+ # ]
#
# Optionally, "traceback" may be included in a test_errors dict, if it is relevant. No script should overwrite anything other
# than the list relevant to what it is testing.
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 lib/tool_shed/util/container_util.py
--- a/lib/tool_shed/util/container_util.py
+++ b/lib/tool_shed/util/container_util.py
@@ -1,7 +1,7 @@
import logging
import os
import threading
-from galaxy.util import asbool
+import galaxy.util
from galaxy.web.framework.helpers import time_ago
from tool_shed.util import common_util
from tool_shed.util import readme_util
@@ -72,8 +72,8 @@
repository_name=name,
repository_owner=owner,
changeset_revision=changeset_revision,
- prior_installation_required=asbool( prior_installation_required ),
- only_if_compiling_contained_td=asbool( only_if_compiling_contained_td ) )
+ prior_installation_required=galaxy.util.asbool( prior_installation_required ),
+ only_if_compiling_contained_td=galaxy.util.asbool( only_if_compiling_contained_td ) )
class DataManager( object ):
@@ -497,8 +497,8 @@
repository_name=name,
repository_owner=owner,
changeset_revision=changeset_revision,
- prior_installation_required=asbool( prior_installation_required ),
- only_if_compiling_contained_td=asbool( only_if_compiling_contained_td ),
+ prior_installation_required=galaxy.util.asbool( prior_installation_required ),
+ only_if_compiling_contained_td=galaxy.util.asbool( only_if_compiling_contained_td ),
error=error )
folder.invalid_repository_dependencies.append( ird )
invalid_repository_dependencies_folder.folders.append( folder )
@@ -831,16 +831,12 @@
label='Valid tools' )
containers_dict[ 'valid_tools' ] = valid_tools_root_folder
# Tool test results container.
- tool_test_results = repository_metadata.tool_test_results
+ tool_test_results = galaxy.util.listify( repository_metadata.tool_test_results )
# Only create and populate this folder if there are actual tool test results to display.
if can_display_tool_test_results( tool_test_results, exclude=exclude ):
- time_tested = repository_metadata.time_last_tested
- if time_tested is not None:
- time_tested = time_ago( time_tested )
folder_id, tool_test_results_root_folder = build_tool_test_results_folder( trans,
folder_id,
tool_test_results,
- time_tested,
label='Tool test results' )
containers_dict[ 'tool_test_results' ] = tool_test_results_root_folder
# Workflows container.
@@ -1052,153 +1048,165 @@
tool_dependencies_root_folder = None
return folder_id, tool_dependencies_root_folder
-def build_tool_test_results_folder( trans, folder_id, tool_test_results_dict, time_tested, label='Tool test results' ):
+def build_tool_test_results_folder( trans, folder_id, tool_test_results_dicts, label='Tool test results' ):
"""Return a folder hierarchy containing tool dependencies."""
# This container is displayed only in the tool shed.
- if tool_test_results_dict:
+ if tool_test_results_dicts:
+ multiple_tool_test_results_dicts = len( tool_test_results_dicts ) > 1
+ test_results_dict_id = 0
folder_id += 1
tool_test_results_root_folder = Folder( id=folder_id, key='root', label='root', parent=None )
- test_environment_dict = tool_test_results_dict.get( 'test_environment', None )
- if test_environment_dict is not None:
- folder_id += 1
- test_results_folder = Folder( id=folder_id, key='test_results', label=label, parent=tool_test_results_root_folder )
- tool_test_results_root_folder.folders.append( test_results_folder )
- folder_id += 1
- folder = Folder( id=folder_id, key='test_environment', label='Automated test environment', parent=test_results_folder )
- test_results_folder.folders.append( folder )
- architecture = test_environment_dict.get( 'architecture', '' )
- galaxy_database_version = test_environment_dict.get( 'galaxy_database_version', '' )
- galaxy_revision = test_environment_dict.get( 'galaxy_revision', '' )
- python_version = test_environment_dict.get( 'python_version', '' )
- system = test_environment_dict.get( 'system', '' )
- time_tested = time_tested
- tool_shed_database_version = test_environment_dict.get( 'tool_shed_database_version', '' )
- tool_shed_mercurial_version = test_environment_dict.get( 'tool_shed_mercurial_version', '' )
- tool_shed_revision = test_environment_dict.get( 'tool_shed_revision', '' )
- test_environment = TestEnvironment( id=1,
- architecture=architecture,
- galaxy_database_version=galaxy_database_version,
- galaxy_revision=galaxy_revision,
- python_version=python_version,
- system=system,
- time_tested=time_tested,
- tool_shed_database_version=tool_shed_database_version,
- tool_shed_mercurial_version=tool_shed_mercurial_version,
- tool_shed_revision=tool_shed_revision )
- folder.test_environments.append( test_environment )
- not_tested_dict = tool_test_results_dict.get( 'not_tested', {} )
- if not_tested_dict:
- folder_id += 1
- folder = Folder( id=folder_id, key='not_tested', label='Not tested', parent=test_results_folder )
- test_results_folder.folders.append( folder )
- not_tested_id = 0
- not_tested = NotTested( id=not_tested_id,
- reason=not_tested_dict.get( 'reason', '' ) )
- folder.not_tested.append( not_tested )
- passed_tests_dicts = tool_test_results_dict.get( 'passed_tests', [] )
- if passed_tests_dicts:
- folder_id += 1
- folder = Folder( id=folder_id, key='passed_tests', label='Tests that passed successfully', parent=test_results_folder )
- test_results_folder.folders.append( folder )
- passed_test_id = 0
- for passed_tests_dict in passed_tests_dicts:
- passed_test_id += 1
- passed_test = PassedTest( id=passed_test_id,
- test_id=passed_tests_dict.get( 'test_id' '' ),
- tool_id=passed_tests_dict.get( 'tool_id', '' ),
- tool_version=passed_tests_dict.get( 'tool_version', '' ) )
- folder.passed_tests.append( passed_test )
- failed_tests_dicts = tool_test_results_dict.get( 'failed_tests', [] )
- if failed_tests_dicts:
- folder_id += 1
- folder = Folder( id=folder_id, key='failed_tests', label='Tests that failed', parent=test_results_folder )
- test_results_folder.folders.append( folder )
- failed_test_id = 0
- for failed_tests_dict in failed_tests_dicts:
- failed_test_id += 1
- failed_test = FailedTest( id=failed_test_id,
- stderr=failed_tests_dict.get( 'stderr', '' ),
- test_id=failed_tests_dict.get( 'test_id', '' ),
- tool_id=failed_tests_dict.get( 'tool_id', '' ),
- tool_version=failed_tests_dict.get( 'tool_version', '' ),
- traceback=failed_tests_dict.get( 'traceback', '' ) )
- folder.failed_tests.append( failed_test )
- missing_test_components_dicts = tool_test_results_dict.get( 'missing_test_components', [] )
- if missing_test_components_dicts:
- folder_id += 1
- folder = Folder( id=folder_id, key='missing_test_components', label='Tools missing tests or test data', parent=test_results_folder )
- test_results_folder.folders.append( folder )
- missing_test_component_id = 0
- for missing_test_components_dict in missing_test_components_dicts:
- missing_test_component_id += 1
- missing_test_component = MissingTestComponent( id=missing_test_component_id,
- missing_components=missing_test_components_dict.get( 'missing_components', '' ),
- tool_guid=missing_test_components_dict.get( 'tool_guid', '' ),
- tool_id=missing_test_components_dict.get( 'tool_id', '' ),
- tool_version=missing_test_components_dict.get( 'tool_version', '' ) )
- folder.missing_test_components.append( missing_test_component )
- installation_error_dicts = tool_test_results_dict.get( 'installation_errors', {} )
- if installation_error_dicts:
- current_repository_errors = installation_error_dicts.get( 'current_repository', [] )
- repository_dependency_errors = installation_error_dicts.get( 'repository_dependencies', [] )
- tool_dependency_errors = installation_error_dicts.get( 'tool_dependencies', [] )
- if current_repository_errors or repository_dependency_errors or tool_dependency_errors:
+ for index, tool_test_results_dict in enumerate( tool_test_results_dicts ):
+ test_environment_dict = tool_test_results_dict.get( 'test_environment', None )
+ if test_environment_dict is not None:
+ time_tested = test_environment_dict.get( 'time_tested', 'unknown_%d' % index )
+ if multiple_tool_test_results_dicts:
+ folder_id += 1
+ containing_folder = Folder( id=folder_id, key='test_results', label=time_tested, parent=tool_test_results_root_folder )
+ else:
+ containing_folder = tool_test_results_root_folder
+ test_results_dict_id += 1
+ #folder_id += 1
+ #test_results_folder = Folder( id=folder_id, key='test_results', label='Automated test environment', parent=containing_folder )
+ #containing_folder.folders.append( test_results_folder )
folder_id += 1
- installation_error_base_folder = Folder( id=folder_id,
- key='installation_errors',
- label='Installation errors',
- parent=test_results_folder )
- if current_repository_errors:
+ folder = Folder( id=folder_id, key='test_environment', label='Automated test environment', parent=containing_folder )
+ containing_folder.folders.append( folder )
+ architecture = test_environment_dict.get( 'architecture', '' )
+ galaxy_database_version = test_environment_dict.get( 'galaxy_database_version', '' )
+ galaxy_revision = test_environment_dict.get( 'galaxy_revision', '' )
+ python_version = test_environment_dict.get( 'python_version', '' )
+ system = test_environment_dict.get( 'system', '' )
+ tool_shed_database_version = test_environment_dict.get( 'tool_shed_database_version', '' )
+ tool_shed_mercurial_version = test_environment_dict.get( 'tool_shed_mercurial_version', '' )
+ tool_shed_revision = test_environment_dict.get( 'tool_shed_revision', '' )
+ test_environment = TestEnvironment( id=1,
+ architecture=architecture,
+ galaxy_database_version=galaxy_database_version,
+ galaxy_revision=galaxy_revision,
+ python_version=python_version,
+ system=system,
+ time_tested=time_tested,
+ tool_shed_database_version=tool_shed_database_version,
+ tool_shed_mercurial_version=tool_shed_mercurial_version,
+ tool_shed_revision=tool_shed_revision )
+ folder.test_environments.append( test_environment )
+ not_tested_dict = tool_test_results_dict.get( 'not_tested', {} )
+ if not_tested_dict:
+ folder_id += 1
+ folder = Folder( id=folder_id, key='not_tested', label='Not tested', parent=containing_folder )
+ containing_folder.folders.append( folder )
+ not_tested_id = 0
+ not_tested = NotTested( id=not_tested_id,
+ reason=not_tested_dict.get( 'reason', '' ) )
+ folder.not_tested.append( not_tested )
+ passed_tests_dicts = tool_test_results_dict.get( 'passed_tests', [] )
+ if passed_tests_dicts:
+ folder_id += 1
+ folder = Folder( id=folder_id, key='passed_tests', label='Tests that passed successfully', parent=containing_folder )
+ containing_folder.folders.append( folder )
+ passed_test_id = 0
+ for passed_tests_dict in passed_tests_dicts:
+ passed_test_id += 1
+ passed_test = PassedTest( id=passed_test_id,
+ test_id=passed_tests_dict.get( 'test_id' '' ),
+ tool_id=passed_tests_dict.get( 'tool_id', '' ),
+ tool_version=passed_tests_dict.get( 'tool_version', '' ) )
+ folder.passed_tests.append( passed_test )
+ failed_tests_dicts = tool_test_results_dict.get( 'failed_tests', [] )
+ if failed_tests_dicts:
+ folder_id += 1
+ folder = Folder( id=folder_id, key='failed_tests', label='Tests that failed', parent=containing_folder )
+ containing_folder.folders.append( folder )
+ failed_test_id = 0
+ for failed_tests_dict in failed_tests_dicts:
+ failed_test_id += 1
+ failed_test = FailedTest( id=failed_test_id,
+ stderr=failed_tests_dict.get( 'stderr', '' ),
+ test_id=failed_tests_dict.get( 'test_id', '' ),
+ tool_id=failed_tests_dict.get( 'tool_id', '' ),
+ tool_version=failed_tests_dict.get( 'tool_version', '' ),
+ traceback=failed_tests_dict.get( 'traceback', '' ) )
+ folder.failed_tests.append( failed_test )
+ missing_test_components_dicts = tool_test_results_dict.get( 'missing_test_components', [] )
+ if missing_test_components_dicts:
+ folder_id += 1
+ folder = Folder( id=folder_id, key='missing_test_components', label='Tools missing tests or test data', parent=containing_folder )
+ containing_folder.folders.append( folder )
+ missing_test_component_id = 0
+ for missing_test_components_dict in missing_test_components_dicts:
+ missing_test_component_id += 1
+ missing_test_component = MissingTestComponent( id=missing_test_component_id,
+ missing_components=missing_test_components_dict.get( 'missing_components', '' ),
+ tool_guid=missing_test_components_dict.get( 'tool_guid', '' ),
+ tool_id=missing_test_components_dict.get( 'tool_id', '' ),
+ tool_version=missing_test_components_dict.get( 'tool_version', '' ) )
+ folder.missing_test_components.append( missing_test_component )
+ installation_error_dicts = tool_test_results_dict.get( 'installation_errors', {} )
+ if installation_error_dicts:
+ current_repository_errors = installation_error_dicts.get( 'current_repository', [] )
+ repository_dependency_errors = installation_error_dicts.get( 'repository_dependencies', [] )
+ tool_dependency_errors = installation_error_dicts.get( 'tool_dependencies', [] )
+ if current_repository_errors or repository_dependency_errors or tool_dependency_errors:
folder_id += 1
- subfolder = Folder( id=folder_id,
- key='current_repository_errors',
- label='This repository',
- parent=installation_error_base_folder )
- repository_error_id = 0
- for repository_error_dict in current_repository_errors:
- repository_error_id += 1
- repository_installation_error = RepositoryInstallationError( id=repository_error_id,
- tool_shed=repository_error_dict.get( 'tool_shed', '' ),
- name=repository_error_dict.get( 'name', '' ),
- owner=repository_error_dict.get( 'owner', '' ),
- changeset_revision=repository_error_dict.get( 'changeset_revision', '' ),
- error_message=repository_error_dict.get( 'error_message', '' ) )
- subfolder.current_repository_installation_errors.append( repository_installation_error )
- installation_error_base_folder.folders.append( subfolder )
- if repository_dependency_errors:
- folder_id += 1
- subfolder = Folder( id=folder_id,
- key='repository_dependency_errors',
- label='Repository dependencies',
- parent=installation_error_base_folder )
- repository_error_id = 0
- for repository_error_dict in repository_dependency_errors:
- repository_error_id += 1
- repository_installation_error = RepositoryInstallationError( id=repository_error_id,
- tool_shed=repository_error_dict.get( 'tool_shed', '' ),
- name=repository_error_dict.get( 'name', '' ),
- owner=repository_error_dict.get( 'owner', '' ),
- changeset_revision=repository_error_dict.get( 'changeset_revision', '' ),
- error_message=repository_error_dict.get( 'error_message', '' ) )
- subfolder.repository_installation_errors.append( repository_installation_error )
- installation_error_base_folder.folders.append( subfolder )
- if tool_dependency_errors:
- folder_id += 1
- subfolder = Folder( id=folder_id,
- key='tool_dependency_errors',
- label='Tool dependencies',
- parent=installation_error_base_folder )
- tool_dependency_error_id = 0
- for tool_dependency_error_dict in tool_dependency_errors:
- tool_dependency_error_id += 1
- tool_dependency_installation_error = ToolDependencyInstallationError( id=tool_dependency_error_id,
- type=tool_dependency_error_dict.get( 'type', '' ),
- name=tool_dependency_error_dict.get( 'name', '' ),
- version=tool_dependency_error_dict.get( 'version', '' ),
- error_message=tool_dependency_error_dict.get( 'error_message', '' ) )
- subfolder.tool_dependency_installation_errors.append( tool_dependency_installation_error )
- installation_error_base_folder.folders.append( subfolder )
- test_results_folder.installation_errors.append( installation_error_base_folder )
+ installation_error_base_folder = Folder( id=folder_id,
+ key='installation_errors',
+ label='Installation errors',
+ parent=containing_folder )
+ if current_repository_errors:
+ folder_id += 1
+ subfolder = Folder( id=folder_id,
+ key='current_repository_errors',
+ label='This repository',
+ parent=installation_error_base_folder )
+ repository_error_id = 0
+ for repository_error_dict in current_repository_errors:
+ repository_error_id += 1
+ repository_installation_error = RepositoryInstallationError( id=repository_error_id,
+ tool_shed=repository_error_dict.get( 'tool_shed', '' ),
+ name=repository_error_dict.get( 'name', '' ),
+ owner=repository_error_dict.get( 'owner', '' ),
+ changeset_revision=repository_error_dict.get( 'changeset_revision', '' ),
+ error_message=repository_error_dict.get( 'error_message', '' ) )
+ subfolder.current_repository_installation_errors.append( repository_installation_error )
+ installation_error_base_folder.folders.append( subfolder )
+ if repository_dependency_errors:
+ folder_id += 1
+ subfolder = Folder( id=folder_id,
+ key='repository_dependency_errors',
+ label='Repository dependencies',
+ parent=installation_error_base_folder )
+ repository_error_id = 0
+ for repository_error_dict in repository_dependency_errors:
+ repository_error_id += 1
+ repository_installation_error = RepositoryInstallationError( id=repository_error_id,
+ tool_shed=repository_error_dict.get( 'tool_shed', '' ),
+ name=repository_error_dict.get( 'name', '' ),
+ owner=repository_error_dict.get( 'owner', '' ),
+ changeset_revision=repository_error_dict.get( 'changeset_revision', '' ),
+ error_message=repository_error_dict.get( 'error_message', '' ) )
+ subfolder.repository_installation_errors.append( repository_installation_error )
+ installation_error_base_folder.folders.append( subfolder )
+ if tool_dependency_errors:
+ folder_id += 1
+ subfolder = Folder( id=folder_id,
+ key='tool_dependency_errors',
+ label='Tool dependencies',
+ parent=installation_error_base_folder )
+ tool_dependency_error_id = 0
+ for tool_dependency_error_dict in tool_dependency_errors:
+ tool_dependency_error_id += 1
+ tool_dependency_installation_error = ToolDependencyInstallationError( id=tool_dependency_error_id,
+ type=tool_dependency_error_dict.get( 'type', '' ),
+ name=tool_dependency_error_dict.get( 'name', '' ),
+ version=tool_dependency_error_dict.get( 'version', '' ),
+ error_message=tool_dependency_error_dict.get( 'error_message', '' ) )
+ subfolder.tool_dependency_installation_errors.append( tool_dependency_installation_error )
+ installation_error_base_folder.folders.append( subfolder )
+ containing_folder.installation_errors.append( installation_error_base_folder )
+ #containing_folder.folders.append( containing_folder )
+ if multiple_tool_test_results_dicts:
+ tool_test_results_root_folder.folders.append( containing_folder )
else:
tool_test_results_root_folder = None
return folder_id, tool_test_results_root_folder
@@ -1247,13 +1255,13 @@
workflows_root_folder = None
return folder_id, workflows_root_folder
-def can_display_tool_test_results( tool_test_results_dict, exclude=None ):
+def can_display_tool_test_results( tool_test_results_dicts, exclude=None ):
# Only create and populate the tool_test_results container if there are actual tool test results to display.
if exclude is None:
exclude = []
if 'tool_test_results' in exclude:
return False
- if tool_test_results_dict:
+ for tool_test_results_dict in tool_test_results_dicts:
# We check for more than a single entry in the tool_test_results dictionary because it may have
# only the "test_environment" entry, but we want at least 1 of "passed_tests", "failed_tests",
# "installation_errors", "missing_test_components" "skipped_tests", "not_tested" or any other
@@ -1288,7 +1296,7 @@
if key_is_current_repositorys_key( repository_name, repository_owner, changeset_revision, prior_installation_required, only_if_compiling_contained_td, key ):
label = 'Repository dependencies'
else:
- if asbool( prior_installation_required ):
+ if galaxy.util.asbool( prior_installation_required ):
prior_installation_required_str = " <i>(prior install required)</i>"
else:
prior_installation_required_str = ""
@@ -1432,8 +1440,8 @@
repository_name=repository_name,
repository_owner=repository_owner,
changeset_revision=changeset_revision,
- prior_installation_required=asbool( prior_installation_required ),
- only_if_compiling_contained_td=asbool( only_if_compiling_contained_td ),
+ prior_installation_required=galaxy.util.asbool( prior_installation_required ),
+ only_if_compiling_contained_td=galaxy.util.asbool( only_if_compiling_contained_td ),
installation_status=installation_status,
tool_shed_repository_id=tool_shed_repository_id )
# Insert the repository_dependency into the folder.
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 test/install_and_test_tool_shed_repositories/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/functional_tests.py
@@ -58,7 +58,6 @@
from mercurial import __version__
from nose.plugins import Plugin
from paste import httpserver
-from time import strftime
from tool_shed.util import tool_dependency_util
from tool_shed.util.xml_util import parse_xml
@@ -80,6 +79,12 @@
default_galaxy_test_host = '127.0.0.1'
default_galaxy_master_api_key = None
+# This script can be run in such a way that no Tool Shed database records should be changed.
+if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
+ can_update_tool_shed = False
+else:
+ can_update_tool_shed = True
+
# Should this serve static resources (scripts, images, styles, etc.)?
STATIC_ENABLED = True
@@ -157,7 +162,7 @@
galaxy_tool_shed_url = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_URL', None )
tool_shed_api_key = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY', None )
exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'install_test_exclude.xml' )
-
+
if 'GALAXY_INSTALL_TEST_SECRET' not in os.environ:
galaxy_encode_secret = 'changethisinproductiontoo'
os.environ[ 'GALAXY_INSTALL_TEST_SECRET' ] = galaxy_encode_secret
@@ -325,16 +330,16 @@
missing_tool_dependencies.extend( get_missing_tool_dependencies( repository_dependency ) )
return missing_tool_dependencies
-def get_repository_info_from_api( url, repository_dict ):
+def get_repository_dict( url, repository_dict ):
error_message = ''
parts = [ 'api', 'repositories', repository_dict[ 'repository_id' ] ]
api_url = get_api_url( base=url, parts=parts )
extended_dict, error_message = json_from_url( api_url )
if error_message:
return None, error_message
- latest_changeset_revision, error_message = get_latest_downloadable_changeset_revision( url,
- extended_dict[ 'name' ],
- extended_dict[ 'owner' ] )
+ name = str( extended_dict[ 'name' ] )
+ owner = str( extended_dict[ 'owner' ] )
+ latest_changeset_revision, error_message = get_latest_downloadable_changeset_revision( url, name, owner )
if error_message:
return None, error_message
extended_dict[ 'latest_revision' ] = str( latest_changeset_revision )
@@ -361,8 +366,9 @@
repository_dicts = []
params = urllib.urlencode( dict( do_not_test='false',
downloadable='true',
+ includes_tools='true',
malicious='false',
- includes_tools='true',
+ missing_test_components='false',
skip_tool_test='false' ) )
api_url = get_api_url( base=tool_shed_url, parts=[ 'repository_revisions' ], params=params )
baseline_repository_dicts, error_message = json_from_url( api_url )
@@ -372,7 +378,7 @@
for baseline_repository_dict in baseline_repository_dicts:
# We need to get some details from the tool shed API, such as repository name and owner, to pass on to the
# module that will generate the install methods.
- repository_dict, error_message = get_repository_info_from_api( galaxy_tool_shed_url, baseline_repository_dict )
+ repository_dict, error_message = get_repository_dict( galaxy_tool_shed_url, baseline_repository_dict )
if error_message:
log.debug( 'Error getting additional details about repository %s from the API: %s' % ( str( name ), error_message ) )
else:
@@ -447,16 +453,18 @@
tool_id = parts[ -2 ]
return tool_id, tool_version
-def get_tool_test_results_dict( tool_shed_url, encoded_repository_metadata_id ):
+def get_tool_test_results_dicts( tool_shed_url, encoded_repository_metadata_id ):
+ """
+ Return the list of dictionaries contained in the Tool Shed's repository_metadata.tool_test_results
+ column via the Tool Shed API.
+ """
error_message = ''
api_path = [ 'api', 'repository_revisions', encoded_repository_metadata_id ]
api_url = get_api_url( base=tool_shed_url, parts=api_path )
repository_metadata, error_message = json_from_url( api_url )
if error_message:
return None, error_message
- tool_test_results = repository_metadata.get( 'tool_test_results', {} )
- if tool_test_results is None:
- return None, error_message
+ tool_test_results = repository_metadata.get( 'tool_test_results', [] )
return tool_test_results, error_message
def get_webapp_global_conf():
@@ -466,7 +474,8 @@
global_conf.update( get_static_settings() )
return global_conf
-def handle_missing_dependencies( app, repository, missing_tool_dependencies, repository_dict, tool_test_results_dict, results_dict ):
+def handle_missing_dependencies( app, repository, missing_tool_dependencies, repository_dict,
+ tool_test_results_dicts, tool_test_results_dict, results_dict ):
"""Handle missing repository or tool dependencies for an installed repository."""
# If a tool dependency fails to install correctly, this should be considered an installation error,
# and functional tests should be skipped, since the tool dependency needs to be correctly installed
@@ -494,7 +503,8 @@
params = dict( tools_functionally_correct=False,
do_not_test=False,
test_install_error=True )
- register_test_result( galaxy_tool_shed_url, tool_test_results_dict, repository_dict, params )
+ # TODO: do something usefule with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params )
# Since this repository is missing components, we do not want to test it, so deactivate it or uninstall it.
# The deactivate flag is set to True if the environment variable GALAXY_INSTALL_TEST_KEEP_TOOL_DEPENDENCIES
# is set to 'true'.
@@ -608,6 +618,13 @@
name = str( repository_dict[ 'name' ] )
owner = str( repository_dict[ 'owner' ] )
changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ # Populate the tool_test_results_dict.
+ tool_test_results_dicts, error_message = get_tool_test_results_dicts( galaxy_tool_shed_url, encoded_repository_metadata_id )
+ # The preparation script ~/tool_shed/scripts/check_repositories_for_functional_tests.py will have entered
+ # information in the 'test_environment' and possibly the 'missing_test_components' entries of the first
+ # tool_test_results_dict in the list of tool_test_results_dicts. We need to be careful to not lose this
+ # information.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
# See if this repository should be skipped for any reason.
skip_this_repository = False
skip_reason = None
@@ -622,21 +639,20 @@
tool_test_results_dict[ 'not_tested' ] = dict( reason=skip_reason )
params = dict( tools_functionally_correct=False,
do_not_test=False )
- register_test_result( galaxy_tool_shed_url, tool_test_results_dict, repository_dict, params )
+ # TODO: do something usefule with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params )
log.debug( "Not testing revision %s of repository %s owned by %s." % ( changeset_revision, name, owner ) )
else:
- # Populate the tool_test_results_dict.
- tool_test_results_dict, error_message = get_tool_test_results_dict( galaxy_tool_shed_url, encoded_repository_metadata_id )
if error_message:
log.debug( error_message )
else:
- # The preparation script ~/tool_shed/scripts/check_repositories_for_functional_tests.py will have entered
- # information in the 'missing_test_components' entry of the tool_test_results_dict dictionary for repositories
- # that are missing test components. We need to be careful to not lose this information. For all other repositories,
- # no changes will have been made to this dictionary by the preparation script, and tool_test_results_dict will be None.
- # Initialize the tool_test_results_dict dictionary with the information about the current test environment.
test_environment_dict = tool_test_results_dict.get( 'test_environment', None )
test_environment_dict = get_test_environment( test_environment_dict )
+ # Add the current time as the approximate time that this test run occurs. A similar value will also be
+ # set to the repository_metadata.time_last_tested column, but we also store it here because the Tool Shed
+ # may be configured to store multiple test run results, so each must be associated with a time stamp.
+ now = time.strftime( "%Y-%m-%d %H:%M:%S" )
+ test_environment_dict[ 'time_tested' ] = now
test_environment_dict[ 'galaxy_database_version' ] = get_database_version( app )
test_environment_dict[ 'galaxy_revision' ] = get_repository_current_revision( os.getcwd() )
tool_test_results_dict[ 'test_environment' ] = test_environment_dict
@@ -662,8 +678,8 @@
params = dict( tools_functionally_correct=False,
test_install_error=True,
do_not_test=False )
-
- register_test_result( galaxy_tool_shed_url, tool_test_results_dict, repository_dict, params )
+ # TODO: do something usefule with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params )
try:
if deactivate:
# We are deactivating this repository and all of its repository dependencies.
@@ -697,6 +713,7 @@
repository,
missing_tool_dependencies,
repository_dict,
+ tool_test_results_dicts,
tool_test_results_dict,
results_dict )
else:
@@ -705,7 +722,12 @@
file( galaxy_shed_tools_dict, 'w' ).write( to_json_string( shed_tools_dict ) )
log.debug( 'Saved generated shed_tools_dict to %s\nContents: %s' % ( str( galaxy_shed_tools_dict ), str( shed_tools_dict ) ) )
try:
- results_dict = test_repository_tools( app, repository, repository_dict, tool_test_results_dict, results_dict )
+ results_dict = test_repository_tools( app,
+ repository,
+ repository_dict,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ results_dict )
except Exception, e:
exception_message = 'Error executing tests for repository %s: %s' % ( name, str( e ) )
log.exception( exception_message )
@@ -714,7 +736,12 @@
params = dict( tools_functionally_correct=False,
do_not_test=False,
test_install_error=False )
- register_test_result( galaxy_tool_shed_url, tool_test_results_dict, repository_dict, params )
+ # TODO: do something usefule with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params )
results_dict[ 'repositories_failed' ].append( dict( name=name, owner=owner, changeset_revision=changeset_revision ) )
total_repositories_tested += 1
results_dict[ 'total_repositories_tested' ] = total_repositories_tested
@@ -937,7 +964,7 @@
tool_data_path=additional_tool_data_path,
shed_tool_data_table_config=None,
persist=False )
- now = strftime( "%Y-%m-%d %H:%M:%S" )
+ now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
print "# %s - running repository installation and testing script." % now
print "####################################################################################"
@@ -949,12 +976,12 @@
repositories_passed = results_dict[ 'repositories_passed' ]
repositories_failed = results_dict[ 'repositories_failed' ]
repositories_failed_install = results_dict[ 'repositories_failed_install' ]
- now = strftime( "%Y-%m-%d %H:%M:%S" )
+ now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
print "# %s - repository installation and testing script completed." % now
print "# Repository revisions tested: %s" % str( total_repositories_tested )
- if '-info_only' in sys.argv:
- print "# -info_only set, not updating the tool shed."
+ if not can_update_tool_shed:
+ print "# This run will not update the Tool Shed database."
if total_repositories_tested > 0:
if repositories_passed:
print '# ----------------------------------------------------------------------------------'
@@ -1048,26 +1075,26 @@
log.debug( 'Repository %s owned by %s, all revisions.' % ( str( name ), str( owner ) ) )
return exclude_list
-def register_test_result( url, test_results_dict, repository_dict, params ):
+def register_test_result( url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params ):
"""
Update the repository metadata tool_test_results and appropriate flags using the Tool SHed API. This method
updates tool_test_results with the relevant data, sets the do_not_test and tools_functionally correct flags
to the appropriate values and updates the time_last_tested field to the value of the received time_tested.
"""
- if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
- return {}
- else:
+ if can_update_tool_shed:
metadata_revision_id = repository_dict.get( 'id', None )
if metadata_revision_id is not None:
+ tool_test_results_dicts.insert( 0, tool_test_results_dict )
+ params[ 'tool_test_results' ] = tool_test_results_dicts
# Set the time_last_tested entry so that the repository_metadata.time_last_tested will be set in the tool shed.
- time_tested = datetime.utcnow()
- test_results_dict[ 'time_last_tested' ] = time_ago( time_tested )
- params[ 'tool_test_results' ] = test_results_dict
+ params[ 'time_last_tested' ] = 'This entry will result in this value being set via the Tool Shed API.'
url = '%s' % ( suc.url_join( galaxy_tool_shed_url,'api', 'repository_revisions', str( metadata_revision_id ) ) )
try:
return update( tool_shed_api_key, url, params, return_formatted=False )
except Exception, e:
log.exception( 'Error attempting to register test results: %s' % str( e ) )
+ return {}
+ else:
return {}
def remove_generated_tests( app ):
@@ -1138,7 +1165,7 @@
for repository in repositories_by_owner[ owner ]:
print "# %s owned by %s, changeset revision %s" % ( repository[ 'name' ], repository[ 'owner' ], repository[ 'changeset_revision' ] )
-def test_repository_tools( app, repository, repository_dict, tool_test_results_dict, results_dict ):
+def test_repository_tools( app, repository, repository_dict, tool_test_results_dicts, tool_test_results_dict, results_dict ):
"""Test tools contained in the received repository."""
name = str( repository.name )
owner = str( repository.owner )
@@ -1175,7 +1202,8 @@
params = dict( tools_functionally_correct=True,
do_not_test=False,
test_install_error=False )
- register_test_result( galaxy_tool_shed_url, tool_test_results_dict, repository_dict, params )
+ # TODO: do something usefule with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params )
log.debug( 'Revision %s of repository %s installed and passed functional tests.' % ( str( changeset_revision ), str( name ) ) )
else:
tool_test_results_dict[ 'failed_tests' ].append( extract_log_data( result, from_tool_test=True ) )
@@ -1184,7 +1212,8 @@
params = dict( tools_functionally_correct=False,
test_install_error=False,
do_not_test=str( set_do_not_test ) )
- register_test_result( galaxy_tool_shed_url, tool_test_results_dict, repository_dict, params )
+ # TODO: do something usefule with response_dict
+ response_dict = register_test_result( galaxy_tool_shed_url, tool_test_results_dicts, tool_test_results_dict, repository_dict, params )
log.debug( 'Revision %s of repository %s installed successfully but did not pass functional tests.' % \
( str( changeset_revision ), str( name ) ) )
# Run the uninstall method. This removes tool functional test methods from the test_toolbox module and uninstalls the
diff -r ad38b77e96fafc96060f9ee8031094f4a15df278 -r 11028b31d3f024ef920c3168d3f752bc13ba3ad8 tool_shed_wsgi.ini.sample
--- a/tool_shed_wsgi.ini.sample
+++ b/tool_shed_wsgi.ini.sample
@@ -74,6 +74,9 @@
# path to sendmail
sendmail_path = /usr/sbin/sendmail
+# Number of saved tool test results produced by the install and test framework for each repository.
+#num_tool_test_results_saved = 5
+
# For use by email messages sent from the tool shed
#smtp_server = smtp.your_tool_shed_server
#email_from = your_tool_shed_email@server
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ad38b77e96fa/
Changeset: ad38b77e96fa
User: guerler
Date: 2013-11-27 20:06:06
Summary: Pack scripts
Affected #: 1 file
diff -r 9be62566dbb6f0a0bfc20f6a8027832399838861 -r ad38b77e96fafc96060f9ee8031094f4a15df278 static/scripts/packed/galaxy.menu.js
--- a/static/scripts/packed/galaxy.menu.js
+++ b/static/scripts/packed/galaxy.menu.js
@@ -1,1 +1,1 @@
-define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var d=new b.GalaxyMastheadTab({title:"Analyze Data",content:"root/index"});this.masthead.append(d);var c=new b.GalaxyMastheadTab({title:"Workflow",content:"workflow"});this.masthead.append(c);var g=new b.GalaxyMastheadTab({title:"Shared Data",content:"library/index"});g.addMenu({title:"Data Libraries",content:"library/index",divider:true});g.addMenu({title:"Published Histories",content:"history/list_published"});g.addMenu({title:"Published Workflows",content:"workflow/list_published"});g.addMenu({title:"Published Visualizations",content:"visualization/list_published"});g.addMenu({title:"Published Pages",content:"page/list_published"});this.masthead.append(g);if(this.options.user.requests){var h=new b.GalaxyMastheadTab({title:"Lab"});h.addMenu({title:"Sequencing Requests",content:"requests/index"});h.addMenu({title:"Find Samples",content:"requests/find_samples_index"});h.addMenu({title:"Help",content:this.options.lims_doc_url});this.masthead.append(h)}var k=new b.GalaxyMastheadTab({title:"Visualization",content:"visualization/list"});k.addMenu({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});k.addMenu({title:"Saved Visualizations",content:"visualization/list",target:"_frame"});this.masthead.append(k);if(this.options.enable_cloud_launch){var e=new b.GalaxyMastheadTab({title:"Cloud",content:"cloudlaunch/index"});e.addMenu({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(e)}if(this.options.is_admin_user){var f=new b.GalaxyMastheadTab({title:"Admin",content:"admin/index",extra_class:"admin-only"});this.masthead.append(f)}var j=new b.GalaxyMastheadTab({title:"Help"});if(this.options.biostar_url){j.addMenu({title:"Galaxy Q&A Site",content:this.options.biostar_url_redirect,target:"_blank"});j.addMenu({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}j.addMenu({title:"Support",content:this.options.support_url,target:"_blank"});j.addMenu({title:"Search",content:this.options.search_url,target:"_blank"});j.addMenu({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});j.addMenu({title:"Videos",content:this.options.screencasts_url,target:"_blank"});j.addMenu({title:"Wiki",content:this.options.wiki_url,target:"_blank"});j.addMenu({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(!this.options.terms_url){j.addMenu({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(j);if(!this.options.user.valid){var i=new b.GalaxyMastheadTab({title:"User",extra_class:"loggedout-only"});if(this.options.allow_user_creation){i.addMenu({title:"Login",content:"user/login",target:"galaxy_main"})}if(this.options.allow_user_creation){i.addMenu({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(i)}else{var i=new b.GalaxyMastheadTab({title:"User",extra_class:"loggedin-only"});i.addMenu({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){i.addMenu({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{i.addMenu({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});i.addMenu({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});i.addMenu({title:"Logout",content:"user/logout",target:"_top",divider:true})}i.addMenu({title:"Saved Histories",content:"history/list",target:"galaxy_main"});i.addMenu({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});i.addMenu({title:"Saved Pages",content:"page/list",target:"_top"});i.addMenu({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){i.addMenu({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(i)}}});return{GalaxyMenu:a}});
\ No newline at end of file
+define(["galaxy.masthead"],function(b){var a=Backbone.Model.extend({options:null,masthead:null,initialize:function(c){this.options=c.config;this.masthead=c.masthead;this.create()},create:function(){var d=new b.GalaxyMastheadTab({title:"Analyze Data",content:"root/index"});this.masthead.append(d);var c=new b.GalaxyMastheadTab({title:"Workflow",content:"workflow"});this.masthead.append(c);var g=new b.GalaxyMastheadTab({title:"Shared Data",content:"library/index"});g.addMenu({title:"Data Libraries",content:"library/index",divider:true});g.addMenu({title:"Published Histories",content:"history/list_published"});g.addMenu({title:"Published Workflows",content:"workflow/list_published"});g.addMenu({title:"Published Visualizations",content:"visualization/list_published"});g.addMenu({title:"Published Pages",content:"page/list_published"});this.masthead.append(g);if(this.options.user.requests){var h=new b.GalaxyMastheadTab({title:"Lab"});h.addMenu({title:"Sequencing Requests",content:"requests/index"});h.addMenu({title:"Find Samples",content:"requests/find_samples_index"});h.addMenu({title:"Help",content:this.options.lims_doc_url});this.masthead.append(h)}var k=new b.GalaxyMastheadTab({title:"Visualization",content:"visualization/list"});k.addMenu({title:"New Track Browser",content:"visualization/trackster",target:"_frame"});k.addMenu({title:"Saved Visualizations",content:"visualization/list",target:"_frame"});this.masthead.append(k);if(this.options.enable_cloud_launch){var e=new b.GalaxyMastheadTab({title:"Cloud",content:"cloudlaunch/index"});e.addMenu({title:"New Cloud Cluster",content:"cloudlaunch/index"});this.masthead.append(e)}if(this.options.is_admin_user){var f=new b.GalaxyMastheadTab({title:"Admin",content:"admin/index",extra_class:"admin-only"});this.masthead.append(f)}var j=new b.GalaxyMastheadTab({title:"Help"});if(this.options.biostar_url){j.addMenu({title:"Galaxy Q&A Site",content:this.options.biostar_url_redirect,target:"_blank"});j.addMenu({title:"Ask a question",content:"biostar/biostar_question_redirect",target:"_blank"})}j.addMenu({title:"Support",content:this.options.support_url,target:"_blank"});j.addMenu({title:"Search",content:this.options.search_url,target:"_blank"});j.addMenu({title:"Mailing Lists",content:this.options.mailing_lists,target:"_blank"});j.addMenu({title:"Videos",content:this.options.screencasts_url,target:"_blank"});j.addMenu({title:"Wiki",content:this.options.wiki_url,target:"_blank"});j.addMenu({title:"How to Cite Galaxy",content:this.options.citation_url,target:"_blank"});if(!this.options.terms_url){j.addMenu({title:"Terms and Conditions",content:this.options.terms_url,target:"_blank"})}this.masthead.append(j);if(!this.options.user.valid){var i=new b.GalaxyMastheadTab({title:"User",extra_class:"loggedout-only"});i.addMenu({title:"Login",content:"user/login",target:"galaxy_main"});if(this.options.allow_user_creation){i.addMenu({title:"Register",content:"user/create",target:"galaxy_main"})}this.masthead.append(i)}else{var i=new b.GalaxyMastheadTab({title:"User",extra_class:"loggedin-only"});i.addMenu({title:"Logged in as "+this.options.user.email});if(this.options.use_remote_user&&this.options.remote_user_logout_href){i.addMenu({title:"Logout",content:this.options.remote_user_logout_href,target:"_top"})}else{i.addMenu({title:"Preferences",content:"user?cntrller=user",target:"galaxy_main"});i.addMenu({title:"Custom Builds",content:"user/dbkeys",target:"galaxy_main"});i.addMenu({title:"Logout",content:"user/logout",target:"_top",divider:true})}i.addMenu({title:"Saved Histories",content:"history/list",target:"galaxy_main"});i.addMenu({title:"Saved Datasets",content:"dataset/list",target:"galaxy_main"});i.addMenu({title:"Saved Pages",content:"page/list",target:"_top"});i.addMenu({title:"API Keys",content:"user/api_keys?cntrller=user",target:"galaxy_main"});if(this.options.use_remote_user){i.addMenu({title:"Public Name",content:"user/edit_username?cntrller=user",target:"galaxy_main"})}this.masthead.append(i)}}});return{GalaxyMenu:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
27 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9be62566dbb6/
Changeset: 9be62566dbb6
User: guerler
Date: 2013-11-27 19:44:25
Summary: Masthead: Fix login
Affected #: 1 file
diff -r 97940f59772dd8567c68cb8f895d00d99487fdb9 -r 9be62566dbb6f0a0bfc20f6a8027832399838861 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -223,14 +223,11 @@
});
// login
- if (this.options.allow_user_creation)
- {
- tab_user.addMenu({
- title : "Login",
- content : "user/login",
- target : "galaxy_main"
- });
- }
+ tab_user.addMenu({
+ title : "Login",
+ content : "user/login",
+ target : "galaxy_main"
+ });
// register
if (this.options.allow_user_creation)
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jmchilton: Mimic Galaxy functional test handling of GALAXY_TEST_DB_TEMPLATE for tool shed functional tests.
by commits-noreply@bitbucket.org 27 Nov '13
by commits-noreply@bitbucket.org 27 Nov '13
27 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/97940f59772d/
Changeset: 97940f59772d
User: jmchilton
Date: 2013-11-27 15:37:30
Summary: Mimic Galaxy functional test handling of GALAXY_TEST_DB_TEMPLATE for tool shed functional tests.
See 83a95ea606d9fcd15f7554b71804625e203eb1ed for full details (essentially GALAXY_TEST_DB_TEMPLATE can be sqlite file path or URL - will be copied over so original left in pristine state and auto upgraded as needed.
Affected #: 1 file
diff -r 0712180dc77d6df0f19870046562cc34317eb3cc -r 97940f59772dd8567c68cb8f895d00d99487fdb9 test/tool_shed/functional_tests.py
--- a/test/tool_shed/functional_tests.py
+++ b/test/tool_shed/functional_tests.py
@@ -32,6 +32,7 @@
import twill, unittest, time
import sys, threading, random
import httplib, socket
+import urllib
from paste import httpserver
# This is for the tool shed application.
import galaxy.webapps.tool_shed.app
@@ -186,10 +187,20 @@
toolshed_database_connection = os.environ[ 'TOOL_SHED_TEST_DBURI' ]
else:
toolshed_database_connection = 'sqlite:///' + os.path.join( shed_db_path, 'community_test.sqlite' )
+ galaxy_database_auto_migrate = False
if 'GALAXY_TEST_DBURI' in os.environ:
galaxy_database_connection = os.environ[ 'GALAXY_TEST_DBURI' ]
else:
- galaxy_database_connection = 'sqlite:///' + os.path.join( galaxy_db_path, 'universe_test.sqlite' )
+ db_path = os.path.join( galaxy_db_path, 'universe.sqlite' )
+ if 'GALAXY_TEST_DB_TEMPLATE' in os.environ:
+ # Middle ground between recreating a completely new
+ # database and pointing at existing database with
+ # GALAXY_TEST_DBURI. The former requires a lot of setup
+ # time, the latter results in test failures in certain
+ # cases (namely tool shed tests expecting clean database).
+ __copy_database_template(os.environ['GALAXY_TEST_DB_TEMPLATE'], db_path)
+ galaxy_database_auto_migrate = True
+ galaxy_database_connection = 'sqlite:///%s' % db_path
tool_shed_global_conf = get_webapp_global_conf()
tool_shed_global_conf[ '__file__' ] = 'tool_shed_wsgi.ini.sample'
kwargs = dict( admin_users = 'test(a)bx.psu.edu',
@@ -311,6 +322,7 @@
admin_users = 'test(a)bx.psu.edu',
allow_library_path_paste = True,
database_connection = galaxy_database_connection,
+ database_auto_migrate = galaxy_database_auto_migrate,
datatype_converters_config_file = "datatype_converters_conf.xml.sample",
enable_tool_shed_check = True,
file_path = galaxy_file_path,
@@ -449,6 +461,22 @@
else:
return 1
+
+def __copy_database_template( source, db_path ):
+ """
+ Copy a 'clean' sqlite template database (from file or URL) to specified
+ database path.
+ """
+ os.makedirs( os.path.dirname( db_path ) )
+ if os.path.exists( source ):
+ shutil.copy( source, db_path )
+ assert os.path.exists( db_path )
+ elif source.startswith("http"):
+ urllib.urlretrieve( source, db_path )
+ else:
+ raise Exception( "Failed to copy database template from source %s" % source )
+
+
if __name__ == "__main__":
try:
sys.exit( main() )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: UI: temporary workaround for popupmenus v. XSS security
by commits-noreply@bitbucket.org 26 Nov '13
by commits-noreply@bitbucket.org 26 Nov '13
26 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0712180dc77d/
Changeset: 0712180dc77d
User: carlfeberhard
Date: 2013-11-27 00:08:42
Summary: UI: temporary workaround for popupmenus v. XSS security
Affected #: 2 files
diff -r 451af16e671a1be729d0cae6e7ecb1c838af48bd -r 0712180dc77d6df0f19870046562cc34317eb3cc static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -297,18 +297,26 @@
function closePopup( event ){
$( document ).off( 'click.close_popup' );
if( window.parent !== window ){
- $( window.parent.document ).off( "click.close_popup" );
+ try {
+ $( window.parent.document ).off( "click.close_popup" );
+ } catch( err ){}
} else {
- $( 'iframe#galaxy_main' ).contents().off( "click.close_popup" );
+ try {
+ $( 'iframe#galaxy_main' ).contents().off( "click.close_popup" );
+ } catch( err ){}
}
menu.remove();
}
$( 'html' ).one( "click.close_popup", closePopup );
if( window.parent !== window ){
- $( window.parent.document ).find( 'html' ).one( "click.close_popup", closePopup );
+ try {
+ $( window.parent.document ).find( 'html' ).one( "click.close_popup", closePopup );
+ } catch( err ){}
} else {
- $( 'iframe#galaxy_main' ).contents().one( "click.close_popup", closePopup );
+ try {
+ $( 'iframe#galaxy_main' ).contents().one( "click.close_popup", closePopup );
+ } catch( err ){}
}
},
diff -r 451af16e671a1be729d0cae6e7ecb1c838af48bd -r 0712180dc77d6df0f19870046562cc34317eb3cc static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b||$("<div/>");this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var b=this;function a(c){$(document).off("click.close_popup");if(window.parent!==window){$(window.parent.document).off("click.close_popup")}else{$("iframe#galaxy_main").contents().off("click.close_popup")}b.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){$(window.parent.document).find("html").one("click.close_popup",a)}else{$("iframe#galaxy_main").contents().one("click.close_popup",a)}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((a.target)?(' target="'+a.target+'"'):("")),' href="',((a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};var searchInput=function(k){var a=27,h=13,i=$("<div/>"),b={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onsearch:function(l){},minSearchLen:0,escWillClear:true,oninit:function(){}};if(jQuery.type(k)==="object"){k=jQuery.extend(true,b,k)}function d(l){var m=$(this).parent().children("input");m.val("");m.trigger("clear:searchInput");k.onclear()}function j(m,l){$(this).trigger("search:searchInput",l);k.onsearch(l)}function c(){return['<input type="text" name="',k.name,'" placeholder="',k.placeholder,'" ','class="search-query ',k.classes,'" ',"/>"].join("")}function g(){return $(c()).css({width:"100%","padding-right":"24px"}).focus(function(l){$(this).select()}).keyup(function(m){if(m.which===a&&k.escWillClear){d.call(this,m)}else{var l=$(this).val();if((m.which===h)||(k.minSearchLen&&l.length>=k.minSearchLen)){j.call(this,m,l)}else{if(!l.length){d.call(this,m)}}}}).val(k.initialVal)}function f(){return'<span class="search-clear fa fa-times-circle"></span>'}function e(){return $(f()).css({position:"absolute",right:"15px","font-size":"1.4em","line-height":"23px",color:"grey"}).click(function(l){d.call(this,l)})}return i.append([g(),e()])};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b};
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b||$("<div/>");this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((a.target)?(' target="'+a.target+'"'):("")),' href="',((a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};var searchInput=function(k){var a=27,h=13,i=$("<div/>"),b={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onsearch:function(l){},minSearchLen:0,escWillClear:true,oninit:function(){}};if(jQuery.type(k)==="object"){k=jQuery.extend(true,b,k)}function d(l){var m=$(this).parent().children("input");m.val("");m.trigger("clear:searchInput");k.onclear()}function j(m,l){$(this).trigger("search:searchInput",l);k.onsearch(l)}function c(){return['<input type="text" name="',k.name,'" placeholder="',k.placeholder,'" ','class="search-query ',k.classes,'" ',"/>"].join("")}function g(){return $(c()).css({width:"100%","padding-right":"24px"}).focus(function(l){$(this).select()}).keyup(function(m){if(m.which===a&&k.escWillClear){d.call(this,m)}else{var l=$(this).val();if((m.which===h)||(k.minSearchLen&&l.length>=k.minSearchLen)){j.call(this,m,l)}else{if(!l.length){d.call(this,m)}}}}).val(k.initialVal)}function f(){return'<span class="search-clear fa fa-times-circle"></span>'}function e(){return $(f()).css({position:"absolute",right:"15px","font-size":"1.4em","line-height":"23px",color:"grey"}).click(function(l){d.call(this,l)})}return i.append([g(),e()])};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b};
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Add backbone dependency to trackster module; this is needed for Trackster router to function. Pack script.
by commits-noreply@bitbucket.org 26 Nov '13
by commits-noreply@bitbucket.org 26 Nov '13
26 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/451af16e671a/
Changeset: 451af16e671a
User: jgoecks
Date: 2013-11-26 21:46:10
Summary: Add backbone dependency to trackster module; this is needed for Trackster router to function. Pack script.
Affected #: 2 files
diff -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be -r 451af16e671a1be729d0cae6e7ecb1c838af48bd static/scripts/packed/viz/trackster.js
--- a/static/scripts/packed/viz/trackster.js
+++ b/static/scripts/packed/viz/trackster.js
@@ -1,1 +1,1 @@
-var ui=null;var view=null;var browser_router=null;require(["utils/galaxy.utils","libs/jquery/jstorage","libs/jquery/jquery.event.drag","libs/jquery/jquery.event.hover","libs/jquery/jquery.mousewheel","libs/jquery/jquery-ui","libs/jquery/select2","libs/farbtastic","libs/jquery/jquery.form","libs/jquery/jquery.rating","mvc/ui"],function(a){a.cssLoadFile("static/style/jquery.rating.css");a.cssLoadFile("static/style/autocomplete_tagging.css");a.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");a.cssLoadFile("static/style/library.css");a.cssLoadFile("static/style/trackster.css")});define(["viz/visualization","viz/trackster_ui"],function(a,b){var c=Backbone.View.extend({initialize:function(){ui=new b.TracksterUI(galaxy_config.root);ui.createButtonMenu();ui.buttonMenu.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);$("#right .unified-panel-title").append("Bookmarks");$("#right .unified-panel-icons").append("<a id='add-bookmark-button' class='icon-button menu-button plus-button' href='javascript:void(0);' title='Add bookmark'></a>");$("#right-border").click(function(){view.resize_window()});force_right_panel("hide");if(galaxy_config.app.id){this.view_existing()}else{this.view_new()}},set_up_router:function(d){browser_router=new a.TrackBrowserRouter(d);Backbone.history.start()},view_existing:function(){var d=galaxy_config.app.viz_config;view=ui.create_visualization({container:$("#center .unified-panel-body"),name:d.title,vis_id:d.vis_id,dbkey:d.dbkey},d.viewport,d.tracks,d.bookmarks,true);this.init_editor()},view_new:function(){var d=this;$.ajax({url:galaxy_config.root+"api/genomes?chrom_info=True",data:{},error:function(){alert("Couldn't create new browser.")},success:function(e){Galaxy.modal.show({title:"New Visualization",body:d.template_view_new(e),buttons:{Cancel:function(){window.location=galaxy_config.root+"visualization/list"},Create:function(){d.create_browser($("#new-title").val(),$("#new-dbkey").val());Galaxy.modal.hide()}}});if(galaxy_config.app.default_dbkey){$("#new-dbkey").val(galaxy_config.app.default_dbkey)}$("#new-title").focus();$("select[name='dbkey']").select2();$("#overlay").css("overflow","auto")}})},template_view_new:function(d){var f='<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;"><div class="form-row"><label for="new-title">Browser name:</label><div class="form-row-input"><input type="text" name="title" id="new-title" value="Unnamed"></input></div><div style="clear: both;"></div></div><div class="form-row"><label for="new-dbkey">Reference genome build (dbkey): </label><div class="form-row-input"><select name="dbkey" id="new-dbkey">';for(var e=0;e<d.length;e++){f+='<option value="'+d[e][1]+'">'+d[e][0]+"</option>"}f+='</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="'+galaxy_config.root+'user/dbkeys?use_panels=True">Add a Custom Build</a></div></form>';return f},create_browser:function(e,d){$(document).trigger("convert_to_values");view=ui.create_visualization({container:$("#center .unified-panel-body"),name:e,dbkey:d},galaxy_config.app.gene_region);this.init_editor();view.editor=true},init_editor:function(){$("#center .unified-panel-title").text(view.prefs.name+" ("+view.dbkey+")");if(galaxy_config.app.add_dataset){$.ajax({url:galaxy_config.root+"api/datasets/"+galaxy_config.app.add_dataset,data:{hda_ldda:"hda",data_type:"track_config"},dataType:"json",success:function(d){view.add_drawable(b.object_from_template(d,view,view))}})}$("#add-bookmark-button").click(function(){var e=view.chrom+":"+view.low+"-"+view.high,d="Bookmark description";return ui.add_bookmark(e,d,true)});ui.init_keyboard_nav(view);this.set_up_router({view:view})}});return{GalaxyApp:c}});
\ No newline at end of file
+var ui=null;var view=null;var browser_router=null;require(["utils/galaxy.utils","libs/jquery/jstorage","libs/jquery/jquery.event.drag","libs/jquery/jquery.event.hover","libs/jquery/jquery.mousewheel","libs/jquery/jquery-ui","libs/jquery/select2","libs/farbtastic","libs/jquery/jquery.form","libs/jquery/jquery.rating","mvc/ui"],function(a){a.cssLoadFile("static/style/jquery.rating.css");a.cssLoadFile("static/style/autocomplete_tagging.css");a.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");a.cssLoadFile("static/style/library.css");a.cssLoadFile("static/style/trackster.css")});define(["libs/backbone/backbone","viz/visualization","viz/trackster_ui"],function(c,a,b){var d=Backbone.View.extend({initialize:function(){ui=new b.TracksterUI(galaxy_config.root);ui.createButtonMenu();ui.buttonMenu.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);$("#right .unified-panel-title").append("Bookmarks");$("#right .unified-panel-icons").append("<a id='add-bookmark-button' class='icon-button menu-button plus-button' href='javascript:void(0);' title='Add bookmark'></a>");$("#right-border").click(function(){view.resize_window()});force_right_panel("hide");if(galaxy_config.app.id){this.view_existing()}else{this.view_new()}},set_up_router:function(e){browser_router=new a.TrackBrowserRouter(e);Backbone.history.start()},view_existing:function(){var e=galaxy_config.app.viz_config;view=ui.create_visualization({container:$("#center .unified-panel-body"),name:e.title,vis_id:e.vis_id,dbkey:e.dbkey},e.viewport,e.tracks,e.bookmarks,true);this.init_editor()},view_new:function(){var e=this;$.ajax({url:galaxy_config.root+"api/genomes?chrom_info=True",data:{},error:function(){alert("Couldn't create new browser.")},success:function(f){Galaxy.modal.show({title:"New Visualization",body:e.template_view_new(f),buttons:{Cancel:function(){window.location=galaxy_config.root+"visualization/list"},Create:function(){e.create_browser($("#new-title").val(),$("#new-dbkey").val());Galaxy.modal.hide()}}});if(galaxy_config.app.default_dbkey){$("#new-dbkey").val(galaxy_config.app.default_dbkey)}$("#new-title").focus();$("select[name='dbkey']").select2();$("#overlay").css("overflow","auto")}})},template_view_new:function(e){var g='<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;"><div class="form-row"><label for="new-title">Browser name:</label><div class="form-row-input"><input type="text" name="title" id="new-title" value="Unnamed"></input></div><div style="clear: both;"></div></div><div class="form-row"><label for="new-dbkey">Reference genome build (dbkey): </label><div class="form-row-input"><select name="dbkey" id="new-dbkey">';for(var f=0;f<e.length;f++){g+='<option value="'+e[f][1]+'">'+e[f][0]+"</option>"}g+='</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="'+galaxy_config.root+'user/dbkeys?use_panels=True">Add a Custom Build</a></div></form>';return g},create_browser:function(f,e){$(document).trigger("convert_to_values");view=ui.create_visualization({container:$("#center .unified-panel-body"),name:f,dbkey:e},galaxy_config.app.gene_region);this.init_editor();view.editor=true},init_editor:function(){$("#center .unified-panel-title").text(view.prefs.name+" ("+view.dbkey+")");if(galaxy_config.app.add_dataset){$.ajax({url:galaxy_config.root+"api/datasets/"+galaxy_config.app.add_dataset,data:{hda_ldda:"hda",data_type:"track_config"},dataType:"json",success:function(e){view.add_drawable(b.object_from_template(e,view,view))}})}$("#add-bookmark-button").click(function(){var f=view.chrom+":"+view.low+"-"+view.high,e="Bookmark description";return ui.add_bookmark(f,e,true)});ui.init_keyboard_nav(view);this.set_up_router({view:view})}});return{GalaxyApp:d}});
\ No newline at end of file
diff -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be -r 451af16e671a1be729d0cae6e7ecb1c838af48bd static/scripts/viz/trackster.js
--- a/static/scripts/viz/trackster.js
+++ b/static/scripts/viz/trackster.js
@@ -29,8 +29,8 @@
});
// trackster viewer
-define( ["viz/visualization", "viz/trackster_ui"],
- function(visualization, trackster_ui)
+define( ["libs/backbone/backbone", "viz/visualization", "viz/trackster_ui"],
+ function(backbone, visualization, trackster_ui)
{
var TracksterView = Backbone.View.extend(
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Replace Backbone-relational usage with manually model chaining and remove backbone-relational scripts. Fix default colors bug in Trackster. Pack scripts.
by commits-noreply@bitbucket.org 26 Nov '13
by commits-noreply@bitbucket.org 26 Nov '13
26 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/f4832c4ace99/
Changeset: f4832c4ace99
User: jgoecks
Date: 2013-11-26 20:50:41
Summary: Replace Backbone-relational usage with manually model chaining and remove backbone-relational scripts. Fix default colors bug in Trackster. Pack scripts.
Affected #: 33 files
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/galaxy.frame.js
--- a/static/scripts/galaxy.frame.js
+++ b/static/scripts/galaxy.frame.js
@@ -3,7 +3,7 @@
*/
// dependencies
-define(["galaxy.masthead", "libs/backbone/backbone-relational"], function(mod_masthead) {
+define(["galaxy.masthead"], function(mod_masthead) {
// frame manager
var GalaxyFrame = Backbone.View.extend(
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/galaxy.masthead.js
--- a/static/scripts/galaxy.masthead.js
+++ b/static/scripts/galaxy.masthead.js
@@ -3,7 +3,7 @@
*/
// dependencies
-define(["utils/galaxy.utils", "libs/backbone/backbone-relational"], function(mod_utils) {
+define(["utils/galaxy.utils"], function(mod_utils) {
// masthead
var GalaxyMasthead = Backbone.View.extend(
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -3,7 +3,7 @@
*/
// dependencies
-define(["galaxy.masthead", "libs/backbone/backbone-relational"], function(mod_masthead) {
+define(["galaxy.masthead"], function(mod_masthead) {
// frame manager
var GalaxyMenu = Backbone.Model.extend(
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -3,7 +3,7 @@
*/
// dependencies
-define(["libs/backbone/backbone-relational"], function() {
+define([], function() {
// frame manager
var GalaxyModal = Backbone.View.extend(
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/galaxy.upload.js
--- a/static/scripts/galaxy.upload.js
+++ b/static/scripts/galaxy.upload.js
@@ -3,7 +3,7 @@
*/
// dependencies
-define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils", "utils/galaxy.uploadbox", "libs/backbone/backbone-relational"], function(mod_modal, mod_masthead, mod_utils) {
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils", "utils/galaxy.uploadbox"], function(mod_modal, mod_masthead, mod_utils) {
// galaxy upload
var GalaxyUpload = Backbone.View.extend(
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/libs/backbone/backbone-relational.js
--- a/static/scripts/libs/backbone/backbone-relational.js
+++ /dev/null
@@ -1,1919 +0,0 @@
-/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
-/**
- * Backbone-relational.js 0.8.5
- * (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
- *
- * Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
- * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
- * Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
- */
-( function( undefined ) {
- "use strict";
-
- /**
- * CommonJS shim
- **/
- var _, Backbone, exports;
- if ( typeof window === 'undefined' ) {
- _ = require( 'underscore' );
- Backbone = require( 'backbone' );
- exports = Backbone;
- typeof module === 'undefined' || ( module.exports = exports );
- }
- else {
- _ = window._;
- Backbone = window.Backbone;
- exports = window;
- }
-
- Backbone.Relational = {
- showWarnings: true
- };
-
- /**
- * Semaphore mixin; can be used as both binary and counting.
- **/
- Backbone.Semaphore = {
- _permitsAvailable: null,
- _permitsUsed: 0,
-
- acquire: function() {
- if ( this._permitsAvailable && this._permitsUsed >= this._permitsAvailable ) {
- throw new Error( 'Max permits acquired' );
- }
- else {
- this._permitsUsed++;
- }
- },
-
- release: function() {
- if ( this._permitsUsed === 0 ) {
- throw new Error( 'All permits released' );
- }
- else {
- this._permitsUsed--;
- }
- },
-
- isLocked: function() {
- return this._permitsUsed > 0;
- },
-
- setAvailablePermits: function( amount ) {
- if ( this._permitsUsed > amount ) {
- throw new Error( 'Available permits cannot be less than used permits' );
- }
- this._permitsAvailable = amount;
- }
- };
-
- /**
- * A BlockingQueue that accumulates items while blocked (via 'block'),
- * and processes them when unblocked (via 'unblock').
- * Process can also be called manually (via 'process').
- */
- Backbone.BlockingQueue = function() {
- this._queue = [];
- };
- _.extend( Backbone.BlockingQueue.prototype, Backbone.Semaphore, {
- _queue: null,
-
- add: function( func ) {
- if ( this.isBlocked() ) {
- this._queue.push( func );
- }
- else {
- func();
- }
- },
-
- // Some of the queued events may trigger other blocking events. By
- // copying the queue here it allows queued events to process closer to
- // the natural order.
- //
- // queue events [ 'A', 'B', 'C' ]
- // A handler of 'B' triggers 'D' and 'E'
- // By copying `this._queue` this executes:
- // [ 'A', 'B', 'D', 'E', 'C' ]
- // The same order the would have executed if they didn't have to be
- // delayed and queued.
- process: function() {
- var queue = this._queue;
- this._queue = [];
- while ( queue && queue.length ) {
- queue.shift()();
- }
- },
-
- block: function() {
- this.acquire();
- },
-
- unblock: function() {
- this.release();
- if ( !this.isBlocked() ) {
- this.process();
- }
- },
-
- isBlocked: function() {
- return this.isLocked();
- }
- });
- /**
- * Global event queue. Accumulates external events ('add:<key>', 'remove:<key>' and 'change:<key>')
- * until the top-level object is fully initialized (see 'Backbone.RelationalModel').
- */
- Backbone.Relational.eventQueue = new Backbone.BlockingQueue();
-
- /**
- * Backbone.Store keeps track of all created (and destruction of) Backbone.RelationalModel.
- * Handles lookup for relations.
- */
- Backbone.Store = function() {
- this._collections = [];
- this._reverseRelations = [];
- this._orphanRelations = [];
- this._subModels = [];
- this._modelScopes = [ exports ];
- };
- _.extend( Backbone.Store.prototype, Backbone.Events, {
- /**
- * Create a new `Relation`.
- * @param {Backbone.RelationalModel} [model]
- * @param {Object} relation
- * @param {Object} [options]
- */
- initializeRelation: function( model, relation, options ) {
- var type = !_.isString( relation.type ) ? relation.type : Backbone[ relation.type ] || this.getObjectByName( relation.type );
- if ( type && type.prototype instanceof Backbone.Relation ) {
- new type( model, relation, options ); // Also pushes the new Relation into `model._relations`
- }
- else {
- Backbone.Relational.showWarnings && typeof console !== 'undefined' && console.warn( 'Relation=%o; missing or invalid relation type!', relation );
- }
- },
-
- /**
- * Add a scope for `getObjectByName` to look for model types by name.
- * @param {Object} scope
- */
- addModelScope: function( scope ) {
- this._modelScopes.push( scope );
- },
-
- /**
- * Remove a scope.
- * @param {Object} scope
- */
- removeModelScope: function( scope ) {
- this._modelScopes = _.without( this._modelScopes, scope );
- },
-
- /**
- * Add a set of subModelTypes to the store, that can be used to resolve the '_superModel'
- * for a model later in 'setupSuperModel'.
- *
- * @param {Backbone.RelationalModel} subModelTypes
- * @param {Backbone.RelationalModel} superModelType
- */
- addSubModels: function( subModelTypes, superModelType ) {
- this._subModels.push({
- 'superModelType': superModelType,
- 'subModels': subModelTypes
- });
- },
-
- /**
- * Check if the given modelType is registered as another model's subModel. If so, add it to the super model's
- * '_subModels', and set the modelType's '_superModel', '_subModelTypeName', and '_subModelTypeAttribute'.
- *
- * @param {Backbone.RelationalModel} modelType
- */
- setupSuperModel: function( modelType ) {
- _.find( this._subModels, function( subModelDef ) {
- return _.find( subModelDef.subModels || [], function( subModelTypeName, typeValue ) {
- var subModelType = this.getObjectByName( subModelTypeName );
-
- if ( modelType === subModelType ) {
- // Set 'modelType' as a child of the found superModel
- subModelDef.superModelType._subModels[ typeValue ] = modelType;
-
- // Set '_superModel', '_subModelTypeValue', and '_subModelTypeAttribute' on 'modelType'.
- modelType._superModel = subModelDef.superModelType;
- modelType._subModelTypeValue = typeValue;
- modelType._subModelTypeAttribute = subModelDef.superModelType.prototype.subModelTypeAttribute;
- return true;
- }
- }, this );
- }, this );
- },
-
- /**
- * Add a reverse relation. Is added to the 'relations' property on model's prototype, and to
- * existing instances of 'model' in the store as well.
- * @param {Object} relation
- * @param {Backbone.RelationalModel} relation.model
- * @param {String} relation.type
- * @param {String} relation.key
- * @param {String|Object} relation.relatedModel
- */
- addReverseRelation: function( relation ) {
- var exists = _.any( this._reverseRelations, function( rel ) {
- return _.all( relation || [], function( val, key ) {
- return val === rel[ key ];
- });
- });
-
- if ( !exists && relation.model && relation.type ) {
- this._reverseRelations.push( relation );
- this._addRelation( relation.model, relation );
- this.retroFitRelation( relation );
- }
- },
-
- /**
- * Deposit a `relation` for which the `relatedModel` can't be resolved at the moment.
- *
- * @param {Object} relation
- */
- addOrphanRelation: function( relation ) {
- var exists = _.any( this._orphanRelations, function( rel ) {
- return _.all( relation || [], function( val, key ) {
- return val === rel[ key ];
- });
- });
-
- if ( !exists && relation.model && relation.type ) {
- this._orphanRelations.push( relation );
- }
- },
-
- /**
- * Try to initialize any `_orphanRelation`s
- */
- processOrphanRelations: function() {
- // Make sure to operate on a copy since we're removing while iterating
- _.each( this._orphanRelations.slice( 0 ), function( rel ) {
- var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
- if ( relatedModel ) {
- this.initializeRelation( null, rel );
- this._orphanRelations = _.without( this._orphanRelations, rel );
- }
- }, this );
- },
-
- /**
- *
- * @param {Backbone.RelationalModel.constructor} type
- * @param {Object} relation
- * @private
- */
- _addRelation: function( type, relation ) {
- if ( !type.prototype.relations ) {
- type.prototype.relations = [];
- }
- type.prototype.relations.push( relation );
-
- _.each( type._subModels || [], function( subModel ) {
- this._addRelation( subModel, relation );
- }, this );
- },
-
- /**
- * Add a 'relation' to all existing instances of 'relation.model' in the store
- * @param {Object} relation
- */
- retroFitRelation: function( relation ) {
- var coll = this.getCollection( relation.model, false );
- coll && coll.each( function( model ) {
- if ( !( model instanceof relation.model ) ) {
- return;
- }
-
- new relation.type( model, relation );
- }, this );
- },
-
- /**
- * Find the Store's collection for a certain type of model.
- * @param {Backbone.RelationalModel} type
- * @param {Boolean} [create=true] Should a collection be created if none is found?
- * @return {Backbone.Collection} A collection if found (or applicable for 'model'), or null
- */
- getCollection: function( type, create ) {
- if ( type instanceof Backbone.RelationalModel ) {
- type = type.constructor;
- }
-
- var rootModel = type;
- while ( rootModel._superModel ) {
- rootModel = rootModel._superModel;
- }
-
- var coll = _.find( this._collections, function(item) {
- return item.model === rootModel;
- });
-
- if ( !coll && create !== false ) {
- coll = this._createCollection( rootModel );
- }
-
- return coll;
- },
-
- /**
- * Find a model type on one of the modelScopes by name. Names are split on dots.
- * @param {String} name
- * @return {Object}
- */
- getObjectByName: function( name ) {
- var parts = name.split( '.' ),
- type = null;
-
- _.find( this._modelScopes, function( scope ) {
- type = _.reduce( parts || [], function( memo, val ) {
- return memo ? memo[ val ] : undefined;
- }, scope );
-
- if ( type && type !== scope ) {
- return true;
- }
- }, this );
-
- return type;
- },
-
- _createCollection: function( type ) {
- var coll;
-
- // If 'type' is an instance, take its constructor
- if ( type instanceof Backbone.RelationalModel ) {
- type = type.constructor;
- }
-
- // Type should inherit from Backbone.RelationalModel.
- if ( type.prototype instanceof Backbone.RelationalModel ) {
- coll = new Backbone.Collection();
- coll.model = type;
-
- this._collections.push( coll );
- }
-
- return coll;
- },
-
- /**
- * Find the attribute that is to be used as the `id` on a given object
- * @param type
- * @param {String|Number|Object|Backbone.RelationalModel} item
- * @return {String|Number}
- */
- resolveIdForItem: function( type, item ) {
- var id = _.isString( item ) || _.isNumber( item ) ? item : null;
-
- if ( id === null ) {
- if ( item instanceof Backbone.RelationalModel ) {
- id = item.id;
- }
- else if ( _.isObject( item ) ) {
- id = item[ type.prototype.idAttribute ];
- }
- }
-
- // Make all falsy values `null` (except for 0, which could be an id.. see '/issues/179')
- if ( !id && id !== 0 ) {
- id = null;
- }
-
- return id;
- },
-
- /**
- * Find a specific model of a certain `type` in the store
- * @param type
- * @param {String|Number|Object|Backbone.RelationalModel} item
- */
- find: function( type, item ) {
- var id = this.resolveIdForItem( type, item );
- var coll = this.getCollection( type );
-
- // Because the found object could be of any of the type's superModel
- // types, only return it if it's actually of the type asked for.
- if ( coll ) {
- var obj = coll.get( id );
-
- if ( obj instanceof type ) {
- return obj;
- }
- }
-
- return null;
- },
-
- /**
- * Add a 'model' to its appropriate collection. Retain the original contents of 'model.collection'.
- * @param {Backbone.RelationalModel} model
- */
- register: function( model ) {
- var coll = this.getCollection( model );
-
- if ( coll ) {
- var modelColl = model.collection;
- coll.add( model );
- this.listenTo( model, 'destroy', this.unregister, this );
- this.listenTo( model, 'relational:unregister', this.unregister, this );
- model.collection = modelColl;
- }
- },
-
- /**
- * Check if the given model may use the given `id`
- * @param model
- * @param [id]
- */
- checkId: function( model, id ) {
- var coll = this.getCollection( model ),
- duplicate = coll && coll.get( id );
-
- if ( duplicate && model !== duplicate ) {
- if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
- console.warn( 'Duplicate id! Old RelationalModel=%o, new RelationalModel=%o', duplicate, model );
- }
-
- throw new Error( "Cannot instantiate more than one Backbone.RelationalModel with the same id per type!" );
- }
- },
-
- /**
- * Explicitly update a model's id in its store collection
- * @param {Backbone.RelationalModel} model
- */
- update: function( model ) {
- var coll = this.getCollection( model );
- // This triggers updating the lookup indices kept in a collection
- coll._onModelEvent( 'change:' + model.idAttribute, model, coll );
-
- // Trigger an event on model so related models (having the model's new id in their keyContents) can add it.
- model.trigger( 'relational:change:id', model, coll );
- },
-
- /**
- * Remove a 'model' from the store.
- * @param {Backbone.RelationalModel} model
- */
- unregister: function( model, collection, options ) {
- this.stopListening( model );
- var coll = this.getCollection( model );
- coll && coll.remove( model, options );
- },
-
- /**
- * Reset the `store` to it's original state. The `reverseRelations` are kept though, since attempting to
- * re-initialize these on models would lead to a large amount of warnings.
- */
- reset: function() {
- this.stopListening();
- this._collections = [];
- this._subModels = [];
- this._modelScopes = [ exports ];
- }
- });
- Backbone.Relational.store = new Backbone.Store();
-
- /**
- * The main Relation class, from which 'HasOne' and 'HasMany' inherit. Internally, 'relational:<key>' events
- * are used to regulate addition and removal of models from relations.
- *
- * @param {Backbone.RelationalModel} [instance] Model that this relation is created for. If no model is supplied,
- * Relation just tries to instantiate it's `reverseRelation` if specified, and bails out after that.
- * @param {Object} options
- * @param {string} options.key
- * @param {Backbone.RelationalModel.constructor} options.relatedModel
- * @param {Boolean|String} [options.includeInJSON=true] Serialize the given attribute for related model(s)' in toJSON, or just their ids.
- * @param {Boolean} [options.createModels=true] Create objects from the contents of keys if the object is not found in Backbone.store.
- * @param {Object} [options.reverseRelation] Specify a bi-directional relation. If provided, Relation will reciprocate
- * the relation to the 'relatedModel'. Required and optional properties match 'options', except that it also needs
- * {Backbone.Relation|String} type ('HasOne' or 'HasMany').
- * @param {Object} opts
- */
- Backbone.Relation = function( instance, options, opts ) {
- this.instance = instance;
- // Make sure 'options' is sane, and fill with defaults from subclasses and this object's prototype
- options = _.isObject( options ) ? options : {};
- this.reverseRelation = _.defaults( options.reverseRelation || {}, this.options.reverseRelation );
- this.options = _.defaults( options, this.options, Backbone.Relation.prototype.options );
-
- this.reverseRelation.type = !_.isString( this.reverseRelation.type ) ? this.reverseRelation.type :
- Backbone[ this.reverseRelation.type ] || Backbone.Relational.store.getObjectByName( this.reverseRelation.type );
-
- this.key = this.options.key;
- this.keySource = this.options.keySource || this.key;
- this.keyDestination = this.options.keyDestination || this.keySource || this.key;
-
- this.model = this.options.model || this.instance.constructor;
- this.relatedModel = this.options.relatedModel;
- if ( _.isString( this.relatedModel ) ) {
- this.relatedModel = Backbone.Relational.store.getObjectByName( this.relatedModel );
- }
-
- if ( !this.checkPreconditions() ) {
- return;
- }
-
- // Add the reverse relation on 'relatedModel' to the store's reverseRelations
- if ( !this.options.isAutoRelation && this.reverseRelation.type && this.reverseRelation.key ) {
- Backbone.Relational.store.addReverseRelation( _.defaults( {
- isAutoRelation: true,
- model: this.relatedModel,
- relatedModel: this.model,
- reverseRelation: this.options // current relation is the 'reverseRelation' for its own reverseRelation
- },
- this.reverseRelation // Take further properties from this.reverseRelation (type, key, etc.)
- ) );
- }
-
- if ( instance ) {
- var contentKey = this.keySource;
- if ( contentKey !== this.key && typeof this.instance.get( this.key ) === 'object' ) {
- contentKey = this.key;
- }
-
- this.setKeyContents( this.instance.get( contentKey ) );
- this.relatedCollection = Backbone.Relational.store.getCollection( this.relatedModel );
-
- // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
- if ( this.keySource !== this.key ) {
- delete this.instance.attributes[ this.keySource ];
- }
-
- // Add this Relation to instance._relations
- this.instance._relations[ this.key ] = this;
-
- this.initialize( opts );
-
- if ( this.options.autoFetch ) {
- this.instance.fetchRelated( this.key, _.isObject( this.options.autoFetch ) ? this.options.autoFetch : {} );
- }
-
- // When 'relatedModel' are created or destroyed, check if it affects this relation.
- this.listenTo( this.instance, 'destroy', this.destroy )
- .listenTo( this.relatedCollection, 'relational:add relational:change:id', this.tryAddRelated )
- .listenTo( this.relatedCollection, 'relational:remove', this.removeRelated )
- }
- };
- // Fix inheritance :\
- Backbone.Relation.extend = Backbone.Model.extend;
- // Set up all inheritable **Backbone.Relation** properties and methods.
- _.extend( Backbone.Relation.prototype, Backbone.Events, Backbone.Semaphore, {
- options: {
- createModels: true,
- includeInJSON: true,
- isAutoRelation: false,
- autoFetch: false,
- parse: false
- },
-
- instance: null,
- key: null,
- keyContents: null,
- relatedModel: null,
- relatedCollection: null,
- reverseRelation: null,
- related: null,
-
- /**
- * Check several pre-conditions.
- * @return {Boolean} True if pre-conditions are satisfied, false if they're not.
- */
- checkPreconditions: function() {
- var i = this.instance,
- k = this.key,
- m = this.model,
- rm = this.relatedModel,
- warn = Backbone.Relational.showWarnings && typeof console !== 'undefined';
-
- if ( !m || !k || !rm ) {
- warn && console.warn( 'Relation=%o: missing model, key or relatedModel (%o, %o, %o).', this, m, k, rm );
- return false;
- }
- // Check if the type in 'model' inherits from Backbone.RelationalModel
- if ( !( m.prototype instanceof Backbone.RelationalModel ) ) {
- warn && console.warn( 'Relation=%o: model does not inherit from Backbone.RelationalModel (%o).', this, i );
- return false;
- }
- // Check if the type in 'relatedModel' inherits from Backbone.RelationalModel
- if ( !( rm.prototype instanceof Backbone.RelationalModel ) ) {
- warn && console.warn( 'Relation=%o: relatedModel does not inherit from Backbone.RelationalModel (%o).', this, rm );
- return false;
- }
- // Check if this is not a HasMany, and the reverse relation is HasMany as well
- if ( this instanceof Backbone.HasMany && this.reverseRelation.type === Backbone.HasMany ) {
- warn && console.warn( 'Relation=%o: relation is a HasMany, and the reverseRelation is HasMany as well.', this );
- return false;
- }
- // Check if we're not attempting to create a relationship on a `key` that's already used.
- if ( i && _.keys( i._relations ).length ) {
- var existing = _.find( i._relations, function( rel ) {
- return rel.key === k;
- }, this );
-
- if ( existing ) {
- warn && console.warn( 'Cannot create relation=%o on %o for model=%o: already taken by relation=%o.',
- this, k, i, existing );
- return false;
- }
- }
-
- return true;
- },
-
- /**
- * Set the related model(s) for this relation
- * @param {Backbone.Model|Backbone.Collection} related
- */
- setRelated: function( related ) {
- this.related = related;
-
- this.instance.acquire();
- this.instance.attributes[ this.key ] = related;
- this.instance.release();
- },
-
- /**
- * Determine if a relation (on a different RelationalModel) is the reverse
- * relation of the current one.
- * @param {Backbone.Relation} relation
- * @return {Boolean}
- */
- _isReverseRelation: function( relation ) {
- return relation.instance instanceof this.relatedModel && this.reverseRelation.key === relation.key &&
- this.key === relation.reverseRelation.key;
- },
-
- /**
- * Get the reverse relations (pointing back to 'this.key' on 'this.instance') for the currently related model(s).
- * @param {Backbone.RelationalModel} [model] Get the reverse relations for a specific model.
- * If not specified, 'this.related' is used.
- * @return {Backbone.Relation[]}
- */
- getReverseRelations: function( model ) {
- var reverseRelations = [];
- // Iterate over 'model', 'this.related.models' (if this.related is a Backbone.Collection), or wrap 'this.related' in an array.
- var models = !_.isUndefined( model ) ? [ model ] : this.related && ( this.related.models || [ this.related ] );
- _.each( models || [], function( related ) {
- _.each( related.getRelations() || [], function( relation ) {
- if ( this._isReverseRelation( relation ) ) {
- reverseRelations.push( relation );
- }
- }, this );
- }, this );
-
- return reverseRelations;
- },
-
- /**
- * When `this.instance` is destroyed, cleanup our relations.
- * Get reverse relation, call removeRelated on each.
- */
- destroy: function() {
- this.stopListening();
-
- if ( this instanceof Backbone.HasOne ) {
- this.setRelated( null );
- }
- else if ( this instanceof Backbone.HasMany ) {
- this.setRelated( this._prepareCollection() );
- }
-
- _.each( this.getReverseRelations(), function( relation ) {
- relation.removeRelated( this.instance );
- }, this );
- }
- });
-
- Backbone.HasOne = Backbone.Relation.extend({
- options: {
- reverseRelation: { type: 'HasMany' }
- },
-
- initialize: function( opts ) {
- this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
-
- var related = this.findRelated( opts );
- this.setRelated( related );
-
- // Notify new 'related' object of the new relation.
- _.each( this.getReverseRelations(), function( relation ) {
- relation.addRelated( this.instance, opts );
- }, this );
- },
-
- /**
- * Find related Models.
- * @param {Object} [options]
- * @return {Backbone.Model}
- */
- findRelated: function( options ) {
- var related = null;
-
- options = _.defaults( { parse: this.options.parse }, options );
-
- if ( this.keyContents instanceof this.relatedModel ) {
- related = this.keyContents;
- }
- else if ( this.keyContents || this.keyContents === 0 ) { // since 0 can be a valid `id` as well
- var opts = _.defaults( { create: this.options.createModels }, options );
- related = this.relatedModel.findOrCreate( this.keyContents, opts );
- }
-
- // Nullify `keyId` if we have a related model; in case it was already part of the relation
- if ( this.related ) {
- this.keyId = null;
- }
-
- return related;
- },
-
- /**
- * Normalize and reduce `keyContents` to an `id`, for easier comparison
- * @param {String|Number|Backbone.Model} keyContents
- */
- setKeyContents: function( keyContents ) {
- this.keyContents = keyContents;
- this.keyId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, this.keyContents );
- },
-
- /**
- * Event handler for `change:<key>`.
- * If the key is changed, notify old & new reverse relations and initialize the new relation.
- */
- onChange: function( model, attr, options ) {
- // Don't accept recursive calls to onChange (like onChange->findRelated->findOrCreate->initializeRelations->addRelated->onChange)
- if ( this.isLocked() ) {
- return;
- }
- this.acquire();
- options = options ? _.clone( options ) : {};
-
- // 'options.__related' is set by 'addRelated'/'removeRelated'. If it is set, the change
- // is the result of a call from a relation. If it's not, the change is the result of
- // a 'set' call on this.instance.
- var changed = _.isUndefined( options.__related ),
- oldRelated = changed ? this.related : options.__related;
-
- if ( changed ) {
- this.setKeyContents( attr );
- var related = this.findRelated( options );
- this.setRelated( related );
- }
-
- // Notify old 'related' object of the terminated relation
- if ( oldRelated && this.related !== oldRelated ) {
- _.each( this.getReverseRelations( oldRelated ), function( relation ) {
- relation.removeRelated( this.instance, null, options );
- }, this );
- }
-
- // Notify new 'related' object of the new relation. Note we do re-apply even if this.related is oldRelated;
- // that can be necessary for bi-directional relations if 'this.instance' was created after 'this.related'.
- // In that case, 'this.instance' will already know 'this.related', but the reverse might not exist yet.
- _.each( this.getReverseRelations(), function( relation ) {
- relation.addRelated( this.instance, options );
- }, this );
-
- // Fire the 'change:<key>' event if 'related' was updated
- if ( !options.silent && this.related !== oldRelated ) {
- var dit = this;
- this.changed = true;
- Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
- dit.changed = false;
- });
- }
- this.release();
- },
-
- /**
- * If a new 'this.relatedModel' appears in the 'store', try to match it to the last set 'keyContents'
- */
- tryAddRelated: function( model, coll, options ) {
- if ( ( this.keyId || this.keyId === 0 ) && model.id === this.keyId ) { // since 0 can be a valid `id` as well
- this.addRelated( model, options );
- this.keyId = null;
- }
- },
-
- addRelated: function( model, options ) {
- // Allow 'model' to set up its relations before proceeding.
- // (which can result in a call to 'addRelated' from a relation of 'model')
- var dit = this;
- model.queue( function() {
- if ( model !== dit.related ) {
- var oldRelated = dit.related || null;
- dit.setRelated( model );
- dit.onChange( dit.instance, model, _.defaults( { __related: oldRelated }, options ) );
- }
- });
- },
-
- removeRelated: function( model, coll, options ) {
- if ( !this.related ) {
- return;
- }
-
- if ( model === this.related ) {
- var oldRelated = this.related || null;
- this.setRelated( null );
- this.onChange( this.instance, model, _.defaults( { __related: oldRelated }, options ) );
- }
- }
- });
-
- Backbone.HasMany = Backbone.Relation.extend({
- collectionType: null,
-
- options: {
- reverseRelation: { type: 'HasOne' },
- collectionType: Backbone.Collection,
- collectionKey: true,
- collectionOptions: {}
- },
-
- initialize: function( opts ) {
- this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
-
- // Handle a custom 'collectionType'
- this.collectionType = this.options.collectionType;
- if ( _.isString( this.collectionType ) ) {
- this.collectionType = Backbone.Relational.store.getObjectByName( this.collectionType );
- }
- if ( this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
- throw new Error( '`collectionType` must inherit from Backbone.Collection' );
- }
-
- var related = this.findRelated( opts );
- this.setRelated( related );
- },
-
- /**
- * Bind events and setup collectionKeys for a collection that is to be used as the backing store for a HasMany.
- * If no 'collection' is supplied, a new collection will be created of the specified 'collectionType' option.
- * @param {Backbone.Collection} [collection]
- * @return {Backbone.Collection}
- */
- _prepareCollection: function( collection ) {
- if ( this.related ) {
- this.stopListening( this.related );
- }
-
- if ( !collection || !( collection instanceof Backbone.Collection ) ) {
- var options = _.isFunction( this.options.collectionOptions ) ?
- this.options.collectionOptions( this.instance ) : this.options.collectionOptions;
-
- collection = new this.collectionType( null, options );
- }
-
- collection.model = this.relatedModel;
-
- if ( this.options.collectionKey ) {
- var key = this.options.collectionKey === true ? this.options.reverseRelation.key : this.options.collectionKey;
-
- if ( collection[ key ] && collection[ key ] !== this.instance ) {
- if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
- console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, this.options.collectionKey );
- }
- }
- else if ( key ) {
- collection[ key ] = this.instance;
- }
- }
-
- this.listenTo( collection, 'relational:add', this.handleAddition )
- .listenTo( collection, 'relational:remove', this.handleRemoval )
- .listenTo( collection, 'relational:reset', this.handleReset );
-
- return collection;
- },
-
- /**
- * Find related Models.
- * @param {Object} [options]
- * @return {Backbone.Collection}
- */
- findRelated: function( options ) {
- var related = null;
-
- options = _.defaults( { parse: this.options.parse }, options );
-
- // Replace 'this.related' by 'this.keyContents' if it is a Backbone.Collection
- if ( this.keyContents instanceof Backbone.Collection ) {
- this._prepareCollection( this.keyContents );
- related = this.keyContents;
- }
- // Otherwise, 'this.keyContents' should be an array of related object ids.
- // Re-use the current 'this.related' if it is a Backbone.Collection; otherwise, create a new collection.
- else {
- var toAdd = [];
-
- _.each( this.keyContents, function( attributes ) {
- if ( attributes instanceof this.relatedModel ) {
- var model = attributes;
- }
- else {
- // If `merge` is true, update models here, instead of during update.
- model = this.relatedModel.findOrCreate( attributes,
- _.extend( { merge: true }, options, { create: this.options.createModels } )
- );
- }
-
- model && toAdd.push( model );
- }, this );
-
- if ( this.related instanceof Backbone.Collection ) {
- related = this.related;
- }
- else {
- related = this._prepareCollection();
- }
-
- // By now, both `merge` and `parse` will already have been executed for models if they were specified.
- // Disable them to prevent additional calls.
- related.set( toAdd, _.defaults( { merge: false, parse: false }, options ) );
- }
-
- // Remove entries from `keyIds` that were already part of the relation (and are thus 'unchanged')
- this.keyIds = _.difference( this.keyIds, _.pluck( related.models, 'id' ) );
-
- return related;
- },
-
- /**
- * Normalize and reduce `keyContents` to a list of `ids`, for easier comparison
- * @param {String|Number|String[]|Number[]|Backbone.Collection} keyContents
- */
- setKeyContents: function( keyContents ) {
- this.keyContents = keyContents instanceof Backbone.Collection ? keyContents : null;
- this.keyIds = [];
-
- if ( !this.keyContents && ( keyContents || keyContents === 0 ) ) { // since 0 can be a valid `id` as well
- // Handle cases the an API/user supplies just an Object/id instead of an Array
- this.keyContents = _.isArray( keyContents ) ? keyContents : [ keyContents ];
-
- _.each( this.keyContents, function( item ) {
- var itemId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, item );
- if ( itemId || itemId === 0 ) {
- this.keyIds.push( itemId );
- }
- }, this );
- }
- },
-
- /**
- * Event handler for `change:<key>`.
- * If the contents of the key are changed, notify old & new reverse relations and initialize the new relation.
- */
- onChange: function( model, attr, options ) {
- options = options ? _.clone( options ) : {};
- this.setKeyContents( attr );
- this.changed = false;
-
- var related = this.findRelated( options );
- this.setRelated( related );
-
- if ( !options.silent ) {
- var dit = this;
- Backbone.Relational.eventQueue.add( function() {
- // The `changed` flag can be set in `handleAddition` or `handleRemoval`
- if ( dit.changed ) {
- dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
- dit.changed = false;
- }
- });
- }
- },
-
- /**
- * When a model is added to a 'HasMany', trigger 'add' on 'this.instance' and notify reverse relations.
- * (should be 'HasOne', must set 'this.instance' as their related).
- */
- handleAddition: function( model, coll, options ) {
- //console.debug('handleAddition called; args=%o', arguments);
- options = options ? _.clone( options ) : {};
- this.changed = true;
-
- _.each( this.getReverseRelations( model ), function( relation ) {
- relation.addRelated( this.instance, options );
- }, this );
-
- // Only trigger 'add' once the newly added model is initialized (so, has its relations set up)
- var dit = this;
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'add:' + dit.key, model, dit.related, options );
- });
- },
-
- /**
- * When a model is removed from a 'HasMany', trigger 'remove' on 'this.instance' and notify reverse relations.
- * (should be 'HasOne', which should be nullified)
- */
- handleRemoval: function( model, coll, options ) {
- //console.debug('handleRemoval called; args=%o', arguments);
- options = options ? _.clone( options ) : {};
- this.changed = true;
-
- _.each( this.getReverseRelations( model ), function( relation ) {
- relation.removeRelated( this.instance, null, options );
- }, this );
-
- var dit = this;
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'remove:' + dit.key, model, dit.related, options );
- });
- },
-
- handleReset: function( coll, options ) {
- var dit = this;
- options = options ? _.clone( options ) : {};
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'reset:' + dit.key, dit.related, options );
- });
- },
-
- tryAddRelated: function( model, coll, options ) {
- var item = _.contains( this.keyIds, model.id );
-
- if ( item ) {
- this.addRelated( model, options );
- this.keyIds = _.without( this.keyIds, model.id );
- }
- },
-
- addRelated: function( model, options ) {
- // Allow 'model' to set up its relations before proceeding.
- // (which can result in a call to 'addRelated' from a relation of 'model')
- var dit = this;
- model.queue( function() {
- if ( dit.related && !dit.related.get( model ) ) {
- dit.related.add( model, _.defaults( { parse: false }, options ) );
- }
- });
- },
-
- removeRelated: function( model, coll, options ) {
- if ( this.related.get( model ) ) {
- this.related.remove( model, options );
- }
- }
- });
-
- /**
- * A type of Backbone.Model that also maintains relations to other models and collections.
- * New events when compared to the original:
- * - 'add:<key>' (model, related collection, options)
- * - 'remove:<key>' (model, related collection, options)
- * - 'change:<key>' (model, related model or collection, options)
- */
- Backbone.RelationalModel = Backbone.Model.extend({
- relations: null, // Relation descriptions on the prototype
- _relations: null, // Relation instances
- _isInitialized: false,
- _deferProcessing: false,
- _queue: null,
- _attributeChangeFired: false, // Keeps track of `change` event firing under some conditions (like nested `set`s)
-
- subModelTypeAttribute: 'type',
- subModelTypes: null,
-
- constructor: function( attributes, options ) {
- // Nasty hack, for cases like 'model.get( <HasMany key> ).add( item )'.
- // Defer 'processQueue', so that when 'Relation.createModels' is used we trigger 'HasMany'
- // collection events only after the model is really fully set up.
- // Example: event for "p.on( 'add:jobs' )" -> "p.get('jobs').add( { company: c.id, person: p.id } )".
- if ( options && options.collection ) {
- var dit = this,
- collection = this.collection = options.collection;
-
- // Prevent `collection` from cascading down to nested models; they shouldn't go into this `if` clause.
- delete options.collection;
-
- this._deferProcessing = true;
-
- var processQueue = function( model ) {
- if ( model === dit ) {
- dit._deferProcessing = false;
- dit.processQueue();
- collection.off( 'relational:add', processQueue );
- }
- };
- collection.on( 'relational:add', processQueue );
-
- // So we do process the queue eventually, regardless of whether this model actually gets added to 'options.collection'.
- _.defer( function() {
- processQueue( dit );
- });
- }
-
- Backbone.Relational.store.processOrphanRelations();
-
- this._queue = new Backbone.BlockingQueue();
- this._queue.block();
- Backbone.Relational.eventQueue.block();
-
- try {
- Backbone.Model.apply( this, arguments );
- }
- finally {
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
- }
- },
-
- /**
- * Override 'trigger' to queue 'change' and 'change:*' events
- */
- trigger: function( eventName ) {
- if ( eventName.length > 5 && eventName.indexOf( 'change' ) === 0 ) {
- var dit = this,
- args = arguments;
-
- Backbone.Relational.eventQueue.add( function() {
- if ( !dit._isInitialized ) {
- return;
- }
-
- // Determine if the `change` event is still valid, now that all relations are populated
- var changed = true;
- if ( eventName === 'change' ) {
- // `hasChanged` may have gotten reset by nested calls to `set`.
- changed = dit.hasChanged() || dit._attributeChangeFired;
- dit._attributeChangeFired = false;
- }
- else {
- var attr = eventName.slice( 7 ),
- rel = dit.getRelation( attr );
-
- if ( rel ) {
- // If `attr` is a relation, `change:attr` get triggered from `Relation.onChange`.
- // These take precedence over `change:attr` events triggered by `Model.set`.
- // The relation sets a fourth attribute to `true`. If this attribute is present,
- // continue triggering this event; otherwise, it's from `Model.set` and should be stopped.
- changed = ( args[ 4 ] === true );
-
- // If this event was triggered by a relation, set the right value in `this.changed`
- // (a Collection or Model instead of raw data).
- if ( changed ) {
- dit.changed[ attr ] = args[ 2 ];
- }
- // Otherwise, this event is from `Model.set`. If the relation doesn't report a change,
- // remove attr from `dit.changed` so `hasChanged` doesn't take it into account.
- else if ( !rel.changed ) {
- delete dit.changed[ attr ];
- }
- }
- else if ( changed ) {
- dit._attributeChangeFired = true;
- }
- }
-
- changed && Backbone.Model.prototype.trigger.apply( dit, args );
- });
- }
- else {
- Backbone.Model.prototype.trigger.apply( this, arguments );
- }
-
- return this;
- },
-
- /**
- * Initialize Relations present in this.relations; determine the type (HasOne/HasMany), then creates a new instance.
- * Invoked in the first call so 'set' (which is made from the Backbone.Model constructor).
- */
- initializeRelations: function( options ) {
- this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
- this._relations = {};
-
- _.each( _.result(this, 'relations') || [], function( rel ) {
- Backbone.Relational.store.initializeRelation( this, rel, options );
- }, this );
-
- this._isInitialized = true;
- this.release();
- this.processQueue();
- },
-
- /**
- * When new values are set, notify this model's relations (also if options.silent is set).
- * (Relation.setRelated locks this model before calling 'set' on it to prevent loops)
- */
- updateRelations: function( options ) {
- if ( this._isInitialized && !this.isLocked() ) {
- _.each( this._relations, function( rel ) {
- // Update from data in `rel.keySource` if data got set in there, or `rel.key` otherwise
- var val = this.attributes[ rel.keySource ] || this.attributes[ rel.key ];
- if ( rel.related !== val ) {
- this.trigger( 'relational:change:' + rel.key, this, val, options || {} );
- }
-
- // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
- if ( rel.keySource !== rel.key ) {
- delete rel.instance.attributes[ rel.keySource ];
- }
- }, this );
- }
- },
-
- /**
- * Either add to the queue (if we're not initialized yet), or execute right away.
- */
- queue: function( func ) {
- this._queue.add( func );
- },
-
- /**
- * Process _queue
- */
- processQueue: function() {
- if ( this._isInitialized && !this._deferProcessing && this._queue.isBlocked() ) {
- this._queue.unblock();
- }
- },
-
- /**
- * Get a specific relation.
- * @param key {string} The relation key to look for.
- * @return {Backbone.Relation} An instance of 'Backbone.Relation', if a relation was found for 'key', or null.
- */
- getRelation: function( key ) {
- return this._relations[ key ];
- },
-
- /**
- * Get all of the created relations.
- * @return {Backbone.Relation[]}
- */
- getRelations: function() {
- return _.values( this._relations );
- },
-
- /**
- * Retrieve related objects.
- * @param key {string} The relation key to fetch models for.
- * @param [options] {Object} Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
- * @param [refresh=false] {boolean} Fetch existing models from the server as well (in order to update them).
- * @return {jQuery.when[]} An array of request objects
- */
- fetchRelated: function( key, options, refresh ) {
- // Set default `options` for fetch
- options = _.extend( { update: true, remove: false }, options );
-
- var setUrl,
- requests = [],
- rel = this.getRelation( key ),
- idsToFetch = rel && ( ( rel.keyIds && rel.keyIds.slice( 0 ) ) || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
-
- // On `refresh`, add the ids for current models in the relation to `idsToFetch`
- if ( refresh ) {
- var models = rel.related instanceof Backbone.Collection ? rel.related.models : [ rel.related ];
- _.each( models, function( model ) {
- if ( model.id || model.id === 0 ) {
- idsToFetch.push( model.id );
- }
- });
- }
-
- if ( idsToFetch && idsToFetch.length ) {
- // Find (or create) a model for each one that is to be fetched
- var created = [],
- models = _.map( idsToFetch, function( id ) {
- var model = Backbone.Relational.store.find( rel.relatedModel, id );
-
- if ( !model ) {
- var attrs = {};
- attrs[ rel.relatedModel.prototype.idAttribute ] = id;
- model = rel.relatedModel.findOrCreate( attrs, options );
- created.push( model );
- }
-
- return model;
- }, this );
-
- // Try if the 'collection' can provide a url to fetch a set of models in one request.
- if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
- setUrl = rel.related.url( models );
- }
-
- // An assumption is that when 'Backbone.Collection.url' is a function, it can handle building of set urls.
- // To make sure it can, test if the url we got by supplying a list of models to fetch is different from
- // the one supplied for the default fetch action (without args to 'url').
- if ( setUrl && setUrl !== rel.related.url() ) {
- var opts = _.defaults(
- {
- error: function() {
- var args = arguments;
- _.each( created, function( model ) {
- model.trigger( 'destroy', model, model.collection, options );
- options.error && options.error.apply( model, args );
- });
- },
- url: setUrl
- },
- options
- );
-
- requests = [ rel.related.fetch( opts ) ];
- }
- else {
- requests = _.map( models, function( model ) {
- var opts = _.defaults(
- {
- error: function() {
- if ( _.contains( created, model ) ) {
- model.trigger( 'destroy', model, model.collection, options );
- options.error && options.error.apply( model, arguments );
- }
- }
- },
- options
- );
- return model.fetch( opts );
- }, this );
- }
- }
-
- return requests;
- },
-
- get: function( attr ) {
- var originalResult = Backbone.Model.prototype.get.call( this, attr );
-
- // Use `originalResult` get if dotNotation not enabled or not required because no dot is in `attr`
- if ( !this.dotNotation || attr.indexOf( '.' ) === -1 ) {
- return originalResult;
- }
-
- // Go through all splits and return the final result
- var splits = attr.split( '.' );
- var result = _.reduce(splits, function( model, split ) {
- if ( _.isNull(model) || _.isUndefined( model ) ) {
- // Return undefined if the path cannot be expanded
- return undefined;
- }
- if ( !( model instanceof Backbone.Model ) ) {
- throw new Error( 'Attribute must be an instanceof Backbone.Model. Is: ' + model + ', currentSplit: ' + split );
- }
-
- return Backbone.Model.prototype.get.call( model, split );
- }, this );
-
- if ( originalResult !== undefined && result !== undefined ) {
- throw new Error( "Ambiguous result for '" + attr + "'. direct result: " + originalResult + ", dotNotation: " + result );
- }
-
- return originalResult || result;
- },
-
- set: function( key, value, options ) {
- Backbone.Relational.eventQueue.block();
-
- // Duplicate backbone's behavior to allow separate key/value parameters, instead of a single 'attributes' object
- var attributes;
- if ( _.isObject( key ) || key == null ) {
- attributes = key;
- options = value;
- }
- else {
- attributes = {};
- attributes[ key ] = value;
- }
-
- try {
- var id = this.id,
- newId = attributes && this.idAttribute in attributes && attributes[ this.idAttribute ];
-
- // Check if we're not setting a duplicate id before actually calling `set`.
- Backbone.Relational.store.checkId( this, newId );
-
- var result = Backbone.Model.prototype.set.apply( this, arguments );
-
- // Ideal place to set up relations, if this is the first time we're here for this model
- if ( !this._isInitialized && !this.isLocked() ) {
- this.constructor.initializeModelHierarchy();
- Backbone.Relational.store.register( this );
- this.initializeRelations( options );
- }
- // The store should know about an `id` update asap
- else if ( newId && newId !== id ) {
- Backbone.Relational.store.update( this );
- }
-
- if ( attributes ) {
- this.updateRelations( options );
- }
- }
- finally {
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
- }
-
- return result;
- },
-
- clone: function() {
- var attributes = _.clone( this.attributes );
- if ( !_.isUndefined( attributes[ this.idAttribute ] ) ) {
- attributes[ this.idAttribute ] = null;
- }
-
- _.each( this.getRelations(), function( rel ) {
- delete attributes[ rel.key ];
- });
-
- return new this.constructor( attributes );
- },
-
- /**
- * Convert relations to JSON, omits them when required
- */
- toJSON: function( options ) {
- // If this Model has already been fully serialized in this branch once, return to avoid loops
- if ( this.isLocked() ) {
- return this.id;
- }
-
- this.acquire();
- var json = Backbone.Model.prototype.toJSON.call( this, options );
-
- if ( this.constructor._superModel && !( this.constructor._subModelTypeAttribute in json ) ) {
- json[ this.constructor._subModelTypeAttribute ] = this.constructor._subModelTypeValue;
- }
-
- _.each( this._relations, function( rel ) {
- var related = json[ rel.key ],
- includeInJSON = rel.options.includeInJSON,
- value = null;
-
- if ( includeInJSON === true ) {
- if ( related && _.isFunction( related.toJSON ) ) {
- value = related.toJSON( options );
- }
- }
- else if ( _.isString( includeInJSON ) ) {
- if ( related instanceof Backbone.Collection ) {
- value = related.pluck( includeInJSON );
- }
- else if ( related instanceof Backbone.Model ) {
- value = related.get( includeInJSON );
- }
-
- // Add ids for 'unfound' models if includeInJSON is equal to (only) the relatedModel's `idAttribute`
- if ( includeInJSON === rel.relatedModel.prototype.idAttribute ) {
- if ( rel instanceof Backbone.HasMany ) {
- value = value.concat( rel.keyIds );
- }
- else if ( rel instanceof Backbone.HasOne ) {
- value = value || rel.keyId;
- }
- }
- }
- else if ( _.isArray( includeInJSON ) ) {
- if ( related instanceof Backbone.Collection ) {
- value = [];
- related.each( function( model ) {
- var curJson = {};
- _.each( includeInJSON, function( key ) {
- curJson[ key ] = model.get( key );
- });
- value.push( curJson );
- });
- }
- else if ( related instanceof Backbone.Model ) {
- value = {};
- _.each( includeInJSON, function( key ) {
- value[ key ] = related.get( key );
- });
- }
- }
- else {
- delete json[ rel.key ];
- }
-
- if ( includeInJSON ) {
- json[ rel.keyDestination ] = value;
- }
-
- if ( rel.keyDestination !== rel.key ) {
- delete json[ rel.key ];
- }
- });
-
- this.release();
- return json;
- }
- },
- {
- /**
- *
- * @param superModel
- * @returns {Backbone.RelationalModel.constructor}
- */
- setup: function( superModel ) {
- // We don't want to share a relations array with a parent, as this will cause problems with
- // reverse relations.
- this.prototype.relations = ( this.prototype.relations || [] ).slice( 0 );
-
- this._subModels = {};
- this._superModel = null;
-
- // If this model has 'subModelTypes' itself, remember them in the store
- if ( this.prototype.hasOwnProperty( 'subModelTypes' ) ) {
- Backbone.Relational.store.addSubModels( this.prototype.subModelTypes, this );
- }
- // The 'subModelTypes' property should not be inherited, so reset it.
- else {
- this.prototype.subModelTypes = null;
- }
-
- // Initialize all reverseRelations that belong to this new model.
- _.each( this.prototype.relations || [], function( rel ) {
- if ( !rel.model ) {
- rel.model = this;
- }
-
- if ( rel.reverseRelation && rel.model === this ) {
- var preInitialize = true;
- if ( _.isString( rel.relatedModel ) ) {
- /**
- * The related model might not be defined for two reasons
- * 1. it is related to itself
- * 2. it never gets defined, e.g. a typo
- * 3. the model hasn't been defined yet, but will be later
- * In neither of these cases do we need to pre-initialize reverse relations.
- * However, for 3. (which is, to us, indistinguishable from 2.), we do need to attempt
- * setting up this relation again later, in case the related model is defined later.
- */
- var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
- preInitialize = relatedModel && ( relatedModel.prototype instanceof Backbone.RelationalModel );
- }
-
- if ( preInitialize ) {
- Backbone.Relational.store.initializeRelation( null, rel );
- }
- else if ( _.isString( rel.relatedModel ) ) {
- Backbone.Relational.store.addOrphanRelation( rel );
- }
- }
- }, this );
-
- return this;
- },
-
- /**
- * Create a 'Backbone.Model' instance based on 'attributes'.
- * @param {Object} attributes
- * @param {Object} [options]
- * @return {Backbone.Model}
- */
- build: function( attributes, options ) {
- // 'build' is a possible entrypoint; it's possible no model hierarchy has been determined yet.
- this.initializeModelHierarchy();
-
- // Determine what type of (sub)model should be built if applicable.
- var model = this._findSubModelType(this, attributes) || this;
-
- return new model( attributes, options );
- },
-
- /**
- * Determines what type of (sub)model should be built if applicable.
- * Looks up the proper subModelType in 'this._subModels', recursing into
- * types until a match is found. Returns the applicable 'Backbone.Model'
- * or null if no match is found.
- * @param {Backbone.Model} type
- * @param {Object} attributes
- * @return {Backbone.Model}
- */
- _findSubModelType: function (type, attributes) {
- if ( type._subModels && type.prototype.subModelTypeAttribute in attributes ) {
- var subModelTypeAttribute = attributes[type.prototype.subModelTypeAttribute];
- var subModelType = type._subModels[subModelTypeAttribute];
- if ( subModelType ) {
- return subModelType;
- } else {
- // Recurse into subModelTypes to find a match
- for ( subModelTypeAttribute in type._subModels ) {
- subModelType = this._findSubModelType(type._subModels[subModelTypeAttribute], attributes);
- if ( subModelType ) {
- return subModelType;
- }
- }
- }
- }
- return null;
- },
-
- /**
- *
- */
- initializeModelHierarchy: function() {
- // Inherit any relations that have been defined in the parent model.
- this.inheritRelations();
-
- // If we came here through 'build' for a model that has 'subModelTypes' then try to initialize the ones that
- // haven't been resolved yet.
- if ( this.prototype.subModelTypes ) {
- var resolvedSubModels = _.keys(this._subModels);
- var unresolvedSubModels = _.omit(this.prototype.subModelTypes, resolvedSubModels);
- _.each( unresolvedSubModels, function( subModelTypeName ) {
- var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
- subModelType && subModelType.initializeModelHierarchy();
- });
- }
- },
-
- inheritRelations: function() {
- // Bail out if we've been here before.
- if (!_.isUndefined( this._superModel ) && !_.isNull( this._superModel )) {
- return;
- }
- // Try to initialize the _superModel.
- Backbone.Relational.store.setupSuperModel( this );
-
- // If a superModel has been found, copy relations from the _superModel if they haven't been inherited automatically
- // (due to a redefinition of 'relations').
- if ( this._superModel ) {
- // The _superModel needs a chance to initialize its own inherited relations before we attempt to inherit relations
- // from the _superModel. You don't want to call 'initializeModelHierarchy' because that could cause sub-models of
- // this class to inherit their relations before this class has had chance to inherit it's relations.
- this._superModel.inheritRelations();
- if ( this._superModel.prototype.relations ) {
- // Find relations that exist on the '_superModel', but not yet on this model.
- var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
- return !_.any( this.prototype.relations || [], function( rel ) {
- return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
- }, this );
- }, this );
-
- this.prototype.relations = inheritedRelations.concat( this.prototype.relations );
- }
- }
- // Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail the
- // isUndefined/isNull check next time.
- else {
- this._superModel = false;
- }
- },
-
- /**
- * Find an instance of `this` type in 'Backbone.Relational.store'.
- * - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
- * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
- * Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
- * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
- * @param {Object} [options]
- * @param {Boolean} [options.create=true]
- * @param {Boolean} [options.merge=true]
- * @param {Boolean} [options.parse=false]
- * @return {Backbone.RelationalModel}
- */
- findOrCreate: function( attributes, options ) {
- options || ( options = {} );
- var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
- this.prototype.parse( _.clone( attributes ) ) : attributes;
-
- // Try to find an instance of 'this' model type in the store
- var model = Backbone.Relational.store.find( this, parsedAttributes );
-
- // If we found an instance, update it with the data in 'item' (unless 'options.merge' is false).
- // If not, create an instance (unless 'options.create' is false).
- if ( _.isObject( attributes ) ) {
- if ( model && options.merge !== false ) {
- // Make sure `options.collection` and `options.url` doesn't cascade to nested models
- delete options.collection;
- delete options.url;
-
- model.set( parsedAttributes, options );
- }
- else if ( !model && options.create !== false ) {
- model = this.build( attributes, options );
- }
- }
-
- return model;
- },
-
- /**
- * Find an instance of `this` type in 'Backbone.Relational.store'.
- * - If `attributes` is a string or a number, `find` will just query the `store` and return a model if found.
- * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
- * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
- * @param {Object} [options]
- * @param {Boolean} [options.merge=true]
- * @param {Boolean} [options.parse=false]
- * @return {Backbone.RelationalModel}
- */
- find: function( attributes, options ) {
- options || ( options = {} );
- options.create = false;
- return this.findOrCreate( attributes, options );
- }
- });
- _.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
-
- /**
- * Override Backbone.Collection._prepareModel, so objects will be built using the correct type
- * if the collection.model has subModels.
- * Attempts to find a model for `attrs` in Backbone.store through `findOrCreate`
- * (which sets the new properties on it if found), or instantiates a new model.
- */
- Backbone.Collection.prototype.__prepareModel = Backbone.Collection.prototype._prepareModel;
- Backbone.Collection.prototype._prepareModel = function ( attrs, options ) {
- var model;
-
- if ( attrs instanceof Backbone.Model ) {
- if ( !attrs.collection ) {
- attrs.collection = this;
- }
- model = attrs;
- }
- else {
- options || ( options = {} );
- options.collection = this;
-
- if ( typeof this.model.findOrCreate !== 'undefined' ) {
- model = this.model.findOrCreate( attrs, options );
- }
- else {
- model = new this.model( attrs, options );
- }
-
- if ( model && model.isNew() && !model._validate( attrs, options ) ) {
- this.trigger( 'invalid', this, attrs, options );
- model = false;
- }
- }
-
- return model;
- };
-
-
- /**
- * Override Backbone.Collection.set, so we'll create objects from attributes where required,
- * and update the existing models. Also, trigger 'relational:add'.
- */
- var set = Backbone.Collection.prototype.__set = Backbone.Collection.prototype.set;
- Backbone.Collection.prototype.set = function( models, options ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return set.apply( this, arguments );
- }
-
- if ( options && options.parse ) {
- models = this.parse( models, options );
- }
-
- if ( !_.isArray( models ) ) {
- models = models ? [ models ] : [];
- }
-
- var newModels = [],
- toAdd = [];
-
- //console.debug( 'calling add on coll=%o; model=%o, options=%o', this, models, options );
- _.each( models, function( model ) {
- if ( !( model instanceof Backbone.Model ) ) {
- model = Backbone.Collection.prototype._prepareModel.call( this, model, options );
- }
-
- if ( model ) {
- toAdd.push( model );
-
- if ( !( this.get( model ) || this.get( model.cid ) ) ) {
- newModels.push( model );
- }
- // If we arrive in `add` while performing a `set` (after a create, so the model gains an `id`),
- // we may get here before `_onModelEvent` has had the chance to update `_byId`.
- else if ( model.id != null ) {
- this._byId[ model.id ] = model;
- }
- }
- }, this );
-
- // Add 'models' in a single batch, so the original add will only be called once (and thus 'sort', etc).
- // If `parse` was specified, the collection and contained models have been parsed now.
- set.call( this, toAdd, _.defaults( { parse: false }, options ) );
-
- _.each( newModels, function( model ) {
- // Fire a `relational:add` event for any model in `newModels` that has actually been added to the collection.
- if ( this.get( model ) || this.get( model.cid ) ) {
- this.trigger( 'relational:add', model, this, options );
- }
- }, this );
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.remove' to trigger 'relational:remove'.
- */
- var remove = Backbone.Collection.prototype.__remove = Backbone.Collection.prototype.remove;
- Backbone.Collection.prototype.remove = function( models, options ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return remove.apply( this, arguments );
- }
-
- models = _.isArray( models ) ? models.slice( 0 ) : [ models ];
- options || ( options = {} );
-
- var toRemove = [];
-
- //console.debug('calling remove on coll=%o; models=%o, options=%o', this, models, options );
- _.each( models, function( model ) {
- model = this.get( model ) || ( model && this.get( model.cid ) );
- model && toRemove.push( model );
- }, this );
-
- if ( toRemove.length ) {
- remove.call( this, toRemove, options );
-
- _.each( toRemove, function( model ) {
- this.trigger('relational:remove', model, this, options);
- }, this );
- }
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.reset' to trigger 'relational:reset'.
- */
- var reset = Backbone.Collection.prototype.__reset = Backbone.Collection.prototype.reset;
- Backbone.Collection.prototype.reset = function( models, options ) {
- options = _.extend( { merge: true }, options );
- reset.call( this, models, options );
-
- if ( this.model.prototype instanceof Backbone.RelationalModel ) {
- this.trigger( 'relational:reset', this, options );
- }
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.sort' to trigger 'relational:reset'.
- */
- var sort = Backbone.Collection.prototype.__sort = Backbone.Collection.prototype.sort;
- Backbone.Collection.prototype.sort = function( options ) {
- sort.call( this, options );
-
- if ( this.model.prototype instanceof Backbone.RelationalModel ) {
- this.trigger( 'relational:reset', this, options );
- }
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.trigger' so 'add', 'remove' and 'reset' events are queued until relations
- * are ready.
- */
- var trigger = Backbone.Collection.prototype.__trigger = Backbone.Collection.prototype.trigger;
- Backbone.Collection.prototype.trigger = function( eventName ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return trigger.apply( this, arguments );
- }
-
- if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' || eventName === 'sort' ) {
- var dit = this,
- args = arguments;
-
- if ( _.isObject( args[ 3 ] ) ) {
- args = _.toArray( args );
- // the fourth argument is the option object.
- // we need to clone it, as it could be modified while we wait on the eventQueue to be unblocked
- args[ 3 ] = _.clone( args[ 3 ] );
- }
-
- Backbone.Relational.eventQueue.add( function() {
- trigger.apply( dit, args );
- });
- }
- else {
- trigger.apply( this, arguments );
- }
-
- return this;
- };
-
- // Override .extend() to automatically call .setup()
- Backbone.RelationalModel.extend = function( protoProps, classProps ) {
- var child = Backbone.Model.extend.apply( this, arguments );
-
- child.setup( this );
-
- return child;
- };
-})();
diff -r a58f6ca10843c91727807e7150b549ecd9b13ec5 -r f4832c4ace99f24bc42dcf275b55c0b2671fb6be static/scripts/mvc/base-mvc.js
--- a/static/scripts/mvc/base-mvc.js
+++ b/static/scripts/mvc/base-mvc.js
@@ -2,7 +2,7 @@
* Simple base model for any visible element. Includes useful attributes and ability
* to set and track visibility.
*/
-var BaseModel = Backbone.RelationalModel.extend({
+var BaseModel = Backbone.Model.extend({
defaults: {
name: null,
hidden: false
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: Upload: Backup id of current history
by commits-noreply@bitbucket.org 26 Nov '13
by commits-noreply@bitbucket.org 26 Nov '13
26 Nov '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a58f6ca10843/
Changeset: a58f6ca10843
User: guerler
Date: 2013-11-26 18:52:51
Summary: Upload: Backup id of current history
Affected #: 1 file
diff -r 9f31145b33ceb83e539bcdd571b48ff440b4c1eb -r a58f6ca10843c91727807e7150b549ecd9b13ec5 static/scripts/galaxy.upload.js
--- a/static/scripts/galaxy.upload.js
+++ b/static/scripts/galaxy.upload.js
@@ -17,6 +17,9 @@
// upload mod
uploadbox: null,
+ // current history
+ current_history: null,
+
// extension types
select_extension :[['Auto-detect', 'auto']],
@@ -227,7 +230,6 @@
sy.addClass(this.state.running);
// get configuration
- var current_history = Galaxy.currHistoryPanel.model.get('id');
var file_type = it.find('#extension').val();
var genome = it.find('#genome').val();
var url_paste = it.find('#text-content').val();
@@ -251,7 +253,7 @@
// setup data
data = {};
- data['history_id'] = current_history;
+ data['history_id'] = this.current_history;
data['tool_id'] = 'upload1';
data['inputs'] = JSON.stringify(tool_input);
@@ -375,6 +377,9 @@
}
});
+ // backup current history
+ this.current_history = Galaxy.currHistoryPanel.model.get('id');
+
// update running
this.counter.running = this.counter.announce;
this.update_screen();
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0