PageRenderTime 23ms CodeModel.GetById 13ms app.highlight 3ms RepoModel.GetById 2ms app.codeStats 0ms

/plugins/dbmigrate/dbmigrate.cfc

http://raihan.googlecode.com/
ColdFusion CFScript | 242 lines | 231 code | 11 blank | 0 comment | 0 complexity | 9fb68b631f00ada21eff5bd17449ee3c MD5 | raw file
  1<cfcomponent output="false" mixin="none" environment="design,development,maintenance">
  2	
  3	<cffunction name="init">
  4		<cfset this.version = "1.0,1.0.1,1.0.2,1.0.3,1.0.4,1.0.5,1.1,1.1.3,1.1.4,1.1.5">
  5		<cfreturn this>
  6	</cffunction>
  7	
  8	<cffunction name="migrateTo" access="public" returntype="string" hint="migrates database to a specified version">
  9		<cfargument name="version" type="string" required="false" default="">
 10		<cfset var loc = {}>
 11		<cfset loc.feedback = "">
 12		<cfset loc.versionsCurrentlyMigrated = $getVersionsPreviouslyMigrated()>
 13		<cfset loc.currentVersion = ListLast(loc.versionsCurrentlyMigrated)>
 14		<cfif loc.currentVersion eq arguments.version>
 15			<cfset loc.feedback = "Database is currently at version #arguments.version#. No migration required.#chr(13)#">
 16		<cfelse>
 17			<cfset loc.sqlPath = expandPath("db/sql")>
 18			<cfif not DirectoryExists(loc.sqlPath)>
 19				<cfdirectory action="create" directory="#loc.sqlPath#">
 20			</cfif>
 21			<cfset loc.migrations = getAvailableMigrations()>
 22			<cfif loc.currentVersion gt arguments.version>
 23				<cfset loc.feedback = "Migrating from #loc.currentVersion# down to #arguments.version#.#chr(13)#">
 24				<cfloop index="loc.i" from="#ArrayLen(loc.migrations)#" to="1" step="-1">
 25					<cfset loc.migration = loc.migrations[loc.i]>
 26					<cfif loc.migration.version lte arguments.version><cfbreak></cfif>
 27					<cfif loc.migration.status eq "migrated">
 28            <!--- <cftransaction action="begin"> --->
 29  						<cftry>
 30  							<cfset loc.feedback = loc.feedback & "#chr(13)#------- " & loc.migration.cfcfile & " #RepeatString("-",Max(5,50-Len(loc.migration.cfcfile)))##chr(13)#">
 31  							<cfset Request.migrationOutput = "">
 32  							<cfset Request.migrationSQLFile = "#loc.sqlPath#/#loc.migration.cfcfile#_down.sql">
 33  							<cffile action="write" file="#Request.migrationSQLFile#" output="">
 34  							<cfset loc.migration.cfc.down()>
 35  							<cfset loc.feedback = loc.feedback & Request.migrationOutput>
 36  							<cfset $removeVersionAsMigrated(loc.migration.version)>
 37  							<cfcatch type="any">
 38  								<cfset loc.feedback = loc.feedback & "Error migrating to #loc.migration.version#.#chr(13)##CFCATCH.Message##chr(13)##CFCATCH.Detail##chr(13)#">
 39                  <!--- <cftransaction action="rollback" /> --->
 40  								<cfbreak>
 41  							</cfcatch>
 42  						</cftry>
 43              <!--- <cftransaction action="commit" />
 44            </cftransaction> --->
 45					</cfif>
 46				</cfloop>
 47			<cfelse>
 48				<cfset loc.feedback = "Migrating from #loc.currentVersion# up to #arguments.version#.#chr(13)#">
 49				<cfloop index="loc.i" from="1" to="#ArrayLen(loc.migrations)#">
 50					<cfset loc.migration = loc.migrations[loc.i]>
 51					<cfif loc.migration.version lte arguments.version and loc.migration.status neq "migrated">
 52            <!--- <cftransaction action="begin"> --->
 53  						<cftry>
 54  							<cfset loc.feedback = loc.feedback & "#chr(13)#-------- " & loc.migration.cfcfile & " #RepeatString("-",Max(5,50-Len(loc.migration.cfcfile)))##chr(13)#">
 55  							<cfset Request.migrationOutput = "">
 56  							<cfset Request.migrationSQLFile = "#loc.sqlPath#/#loc.migration.cfcfile#_up.sql">
 57  							<cffile action="write" file="#Request.migrationSQLFile#" output="">
 58  							<cfset loc.migration.cfc.up()>
 59  							<cfset loc.feedback = loc.feedback & Request.migrationOutput>
 60  							<cfset $setVersionAsMigrated(loc.migration.version)>
 61  							<cfcatch type="any">
 62  								<cfset loc.feedback = loc.feedback & "Error migrating to #loc.migration.version#.#chr(13)##CFCATCH.Message##chr(13)##CFCATCH.Detail##chr(13)#">
 63                  <!--- <cftransaction action="rollback" /> --->
 64                  <cfbreak>
 65  							</cfcatch>
 66  						</cftry>
 67              <!--- <cftransaction action="commit" />
 68            </cftransaction> --->
 69					<cfelseif loc.migration.version gt arguments.version>
 70						<cfbreak>			
 71					</cfif>
 72				</cfloop>
 73			</cfif>
 74		</cfif>
 75		<cfreturn loc.feedback>
 76	</cffunction>
 77	
 78	<cffunction name="getCurrentMigrationVersion" access="public" returntype="string" hint="returns current database version">
 79		<cfset var loc = {}>
 80		<cfset loc.listPreviouslyMigratedVersions = $getVersionsPreviouslyMigrated()>
 81		<cfreturn ListLast(loc.listPreviouslyMigratedVersions)>
 82	</cffunction>
 83	
 84	<cffunction name="getAvailableMigrations" access="public" returntype="array" hint="searches db/migrate folder for migrations">
 85		<cfset var loc = {}>
 86		<cfset loc.listVersionsPreviouslyMigrated = $getVersionsPreviouslyMigrated()>
 87		<cfset loc.migrations = ArrayNew(1)>
 88		<cfset loc.migrationRE = "^([\d]{3,14})_([^\.]*)\.cfc$">
 89		<cfset loc.migrationsPath = expandPath("db/migrate")>
 90		<cfif not DirectoryExists(loc.migrationsPath)>
 91			<cfdirectory action="create" directory="#loc.migrationsPath#">
 92		</cfif>
 93		<cfdirectory action="list" name="qMigrationFiles" directory="#loc.migrationsPath#" sort="Name" filter="*.cfc" type="file" />
 94		<cfloop query="qMigrationFiles">
 95			<cfif REFind(loc.migrationRE,Name)>
 96				<cfset loc.migration = {}>
 97				<cfset loc.migration.version = REReplace(Name,loc.migrationRE,"\1")>
 98				<cfset loc.migration.name = REReplace(Name,loc.migrationRE,"\2")>
 99				<cfset loc.migration.cfcfile = REReplace(Name,loc.migrationRE,"\1_\2")>
100				<cfset loc.migration.loadError = "">
101				<cfset loc.migration.details = "description unavailable">
102				<cfset loc.migration.status = "">
103				<cftry>
104					<cfset loc.migration.cfc = $createObjectFromRoot(path="db.migrate",fileName=loc.migration.cfcfile, method="init")>
105					<cfset loc.metaData = GetMetaData(loc.migration.cfc)>
106					<cfif structKeyExists(loc.metaData,"hint")>
107						<cfset loc.migration.details = loc.metaData.hint>
108					</cfif>
109					<cfif ListFind(loc.listVersionsPreviouslyMigrated, loc.migration.version) neq 0>
110						<cfset loc.migration.status = "migrated">
111					</cfif>
112					<cfcatch type="any"><cfset loc.migration.loadError = CFCATCH.Message></cfcatch>
113				</cftry>
114				<cfset ArrayAppend(loc.migrations,loc.migration)>
115			</cfif>
116		</cfloop>
117		<cfreturn loc.migrations>
118	</cffunction>
119	
120	<cffunction name="createMigration" access="public" returntype="string">
121		<cfargument name="migrationName" type="string" required="true" />
122		<cfargument name="templateName" type="string" required="false" default="blank" />
123		<cfargument name="migrationPrefix" type="string" required="false" default="timestamp" />
124		<cfif len(trim(arguments.migrationName)) gt 0>
125			<cfreturn $copyTemplateMigrationAndRename(argumentCollection=arguments)>
126		<cfelse>
127			<cfreturn "You must supply a migration name (e.g. 'creates member table')">
128		</cfif>
129	</cffunction>
130	
131	<cffunction name="$setVersionAsMigrated" access="private">
132		<cfargument name="version" required="true" type="string">
133		<cfquery datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
134		INSERT INTO schemainfo (version) VALUES (<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.version#">)
135		</cfquery>
136	</cffunction>
137	
138	<cffunction name="$removeVersionAsMigrated" access="private">
139		<cfargument name="version" required="true" type="string">
140		<cfquery datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
141		DELETE FROM schemainfo WHERE version = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.version#">
142		</cfquery>
143	</cffunction>	
144	
145	<!--- this function copied from /wheels/global/internal.cfm  --->
146	<cffunction name="$createObjectFromRoot" returntype="any" access="private" output="false">
147		<cfargument name="path" type="string" required="true">
148		<cfargument name="fileName" type="string" required="true">
149		<cfargument name="method" type="string" required="true">
150		<cfscript>
151			var loc = {};
152			arguments.returnVariable = "loc.returnValue";
153			arguments.component = arguments.path & "." & arguments.fileName;
154			StructDelete(arguments, "path");
155			StructDelete(arguments, "fileName");
156		</cfscript>
157		<cfinclude template="../../root.cfm">
158		<cfreturn loc.returnValue>
159	</cffunction>
160
161	<cffunction name="$copyTemplateMigrationAndRename" displayname="$copyTemplateMigrationAndRename" access="private" returntype="string">
162		<cfargument name="migrationName" type="string" required="true" />
163		<cfargument name="templateName" type="string" required="true" />
164		<cfargument name="migrationPrefix" type="string" required="false" default="" />
165		<cfset var loc = {}/>
166		<cfset loc.migrationsPath = expandPath("db/migrate")/>
167		<cfset loc.templateFile = expandPath("plugins/dbmigrate/templates") & "/" & arguments.templateName & ".cfc"/>
168		<cfset loc.extendsPath = "plugins.dbmigrate.Migration"/>
169		<cfif not FileExists(loc.templateFile)>
170			<cfreturn "Template #arguments.templateName# could not be found">
171		</cfif>
172		<cfif not DirectoryExists(loc.migrationsPath)>
173			<cfdirectory action="create" directory="#loc.migrationsPath#">
174		</cfif>
175		<cftry>
176			<cffile action="read" file="#loc.templateFile#" variable="loc.templateContent">
177
178			<cfif Len(Trim(application.wheels.rootcomponentpath)) GT 0>
179			  <cfset loc.extendsPath = application.wheels.rootcomponentpath & ".plugins.dbmigrate.Migration"/>
180			</cfif>
181			
182			<cfset loc.templateContent = replace(loc.templateContent, "[extends]", loc.extendsPath)>
183			<cfset loc.templateContent = replace(loc.templateContent, "[description]", replace(arguments.migrationName,'"','&quot;','ALL'))>
184			
185			<cfset loc.migrationFile = REREplace(arguments.migrationName,"[^A-z0-9]+"," ","ALL")>
186			<cfset loc.migrationFile = REREplace(Trim(loc.migrationFile),"[\s]+","_","ALL")>
187			<cfset loc.migrationFile = $getNextMigrationNumber(arguments.migrationPrefix) & "_#loc.migrationFile#.cfc">
188			
189			<cffile action="write" file="#loc.migrationsPath#/#loc.migrationFile#" output="#loc.templateContent#">
190			
191			<cfcatch type="any">
192				<cfreturn "There was an error when creating the migration: #cfcatch.message#">
193			</cfcatch>
194		</cftry>
195		<cfreturn "The migration #loc.migrationFile# file was created" />
196	</cffunction>
197
198	<cffunction name="$getNextMigrationNumber" returntype="string">
199		<cfargument name="migrationPrefix" type="string" required="false" default="" />
200		<cfscript>
201			var loc = {};
202			loc.migrationNumber = dateformat(now(),'yyyymmdd') & timeformat(now(),'HHMMSS');
203			if(arguments.migrationPrefix != "timestamp") {
204				loc.migrations = getAvailableMigrations();
205				if(ArrayLen(loc.migrations) eq 0) {
206					if(arguments.migrationPrefix == "numeric") {
207						loc.migrationNumber = "001";
208					}
209				} else {
210					// determine current numbering system
211					loc.lastMigration = loc.migrations[ArrayLen(loc.migrations)];
212					if(Len(loc.lastMigration.version) eq 3) {
213						// use numeric numbering
214						loc.migrationNumber = NumberFormat(Val(loc.lastMigration.version)+1,"009");
215					}
216				}
217			}
218		</cfscript>
219		<cfreturn loc.migrationNumber>
220	</cffunction>
221	
222	<cffunction name="$getVersionsPreviouslyMigrated" access="private" returntype="string">
223		<cfset var loc = {}>
224		<cftry>
225			<cfquery name="loc.qMigratedVersions" datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
226			SELECT version FROM schemainfo ORDER BY version ASC
227			</cfquery>
228			<cfcatch type="database">
229				<cfquery datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
230				CREATE TABLE schemainfo (version VARCHAR(25))
231				</cfquery>
232				<cfreturn "0">
233			</cfcatch>
234		</cftry>
235		<cfif loc.qMigratedVersions.recordcount eq 0>
236			<cfreturn "0">
237		<cfelse>
238			<cfreturn ValueList(loc.qMigratedVersions.version)>
239		</cfif>
240	</cffunction>
241
242</cfcomponent>