Simple version control system for Mathematica projects hosted on Github via Github gists
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"author"-> | |
{ | |
"name" -> "Leonid Shifrin", | |
"email" -> "lshifr@gmail.com", | |
"url" -> "http://www.mathprogramming-intro.org" | |
}, | |
"name" -> "RawVCS", | |
"mavcthematica_version" -> "8.0+", | |
"description" -> "Simple version control system for Mathematica projects hosted on Github via Github gists", | |
"url" -> "https://gist.github.com/4333909" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* Mathematica Package *) | |
BeginPackage["RawVCS`", { "RuleTreeInfo`", "GithubGistClient`", "FileInfo`", "GithubGistProjectInfo`", "OO`", "OO`Methods`", "OO`Errors`"}] | |
(* Exported symbols added here with SymbolName::usage *) | |
RawVCS; | |
Begin["`Private`"] (* Begin Private Context *) | |
join = Function[{dir, file}, FileNameJoin[{dir, file}]]; | |
SetAttributes[CleanUp, HoldAll]; | |
CleanUp[expr_, cleanup_] := | |
Module[{exprFn, result, abort = False, rethrow = True, seq}, | |
exprFn[] := expr; | |
result = | |
CheckAbort[ | |
Catch[Catch[result = exprFn[]; rethrow = False; result], _, | |
seq[##] &], abort = True]; | |
cleanup; | |
If[abort, Abort[]]; | |
If[rethrow, Throw[result /. seq -> Sequence]]; | |
result]; | |
ClearAll[createProject]; | |
createProject[finfo_?(TypeQ[FileInfo]), files : {___String}, | |
opts___?OptionQ] := | |
createProject[finfo, GithubGistProjectInfo[{}]@new[finfo, opts], | |
files, opts]; | |
createProject[finfo_?(TypeQ[FileInfo]), | |
prinfo_?(TypeQ[GithubGistProjectInfo]), files : {___String}] /; | |
prinfo@newProjectQ[] := | |
Module[{ gist, result}, | |
If[MemberQ[Map[finfo@get[#] &, files], $Failed], | |
ThrowError[createProject, "some_files_do_not_exist"] | |
]; | |
(* Update the project.m in finfo with possible changes *) | |
finfo@replace["project.m" -> prinfo@toString[]]; | |
gist = | |
createGist@makeGistCreateInfo[ | |
makeFileCreateInfo @@@ | |
Thread[files -> (files /. finfo@normal[])], | |
prinfo@get["description"] | |
]; | |
If[gist === $Failed, | |
ThrowError[createProject, "error_create_gist"] | |
]; | |
prinfo@replace["url" -> getGistHtmlURL[gist]]; | |
CleanUp[ | |
result = commitProject[finfo, prinfo, {"project.m"}]; | |
gist = result, | |
(* Error second part of the commit *) | |
If[! ValueQ[result] || result === $Failed, | |
(* Delete a gist - commit wasn't successful *) | |
GithubGistClient`deleteGist[getGistID[gist]]; | |
ThrowError[createProject, | |
"error_during_project_descriptor_commit"] | |
]]; | |
gist | |
]; | |
createProject[finfo_?(TypeQ[FileInfo]), | |
prinfo_?(TypeQ[GithubGistProjectInfo]), files_, opts___?OptionQ] := | |
ThrowError[createProject, "need_new_project"]; | |
createProject[___] := ThrowError[createProject]; | |
ClearAll[commitProject]; | |
Options[commitProject] = { | |
RenameFilesOnDisk -> True, | |
DeleteRenamedFiles -> True | |
}; | |
commitProject[ | |
finfo_?(TypeQ[FileInfo]), | |
files : {___String}, | |
renamings : {(_String -> _String) ...} : {}, | |
removals : {___String} : {}, | |
opts___?OptionQ | |
] := | |
commitProject[finfo, GithubGistProjectInfo[{}]@new[finfo, opts], | |
files, renamings, removals, opts]; | |
commitProject[ | |
finfo_?(TypeQ[FileInfo]), | |
prinfo_?(TypeQ[GithubGistProjectInfo]), | |
files : {___String}, | |
renamings : {(_String -> _String) ...} : {}, | |
removals : {___String} : {}, | |
opts___?OptionQ | |
] := | |
Module[{ gist, gistID, nonExistent, renameOnDisk, deleteRenamed}, | |
renameOnDisk = | |
RenameFilesOnDisk /. Flatten[{opts}] /. Options[commitProject]; | |
deleteRenamed = | |
DeleteRenamedFiles /. Flatten[{opts}] /. Options[commitProject]; | |
If[prinfo@newProjectQ[], | |
ThrowError[commitProject, "need_project_url"] | |
]; | |
nonExistent = Pick[#, Map[finfo@get[#] &, #], $Failed] &[ | |
files~Join~renamings[[All, 1]]~Join~removals | |
]; | |
If[nonExistent =!= {}, | |
ThrowError[commitProject, "some_files_do_not_exist", nonExistent] | |
]; | |
(* Always commit the project file *) | |
finfo@replace["project.m" -> prinfo@toString[]]; | |
gistID = prinfo@get["gistID"]; | |
Assert[gistID =!= $Failed]; | |
gist = | |
editGist[gistID, | |
makeGistEditInfo[ | |
Thread[# -> (# /. finfo@normal[])] &[ | |
Append[files~Join~renamings[[All, 1]], "project.m"] | |
], | |
renamings, | |
removals, | |
prinfo@get["description"] | |
] | |
]; | |
prinfo@save[]; | |
finfo@replace[finfo@normal[] /. renamings]; | |
finfo@delete[#] & /@ renamings[[All, 1]]; | |
If[renamings =!= {} && TrueQ@renameOnDisk, | |
finfo@save[]; | |
If[TrueQ@deleteRenamed, | |
With[{dir = finfo@getDir[]}, | |
Map[DeleteFile[dir~join~#] &, renamings[[All, 1]]]; | |
] | |
]]; | |
gist | |
]; | |
commitProject[___] := ThrowError[commitProject]; | |
DeclareType[RawVCS][ | |
OO`Methods`bindToDir[dir_String] := | |
$self@AddMethods[ | |
OO`Methods`getDir[] := dir | |
, | |
OO`Methods`create[files : {___String}, opts___?OptionQ] := | |
OOBlock[ | |
createProject[FileInfo[{}]@new[dir] , files, opts] | |
] | |
, | |
OO`Methods`getGistId[] := | |
Module[{finfo, pinfo, gistID}, | |
OOBlock[ | |
finfo = FileInfo[{}]@new[dir]; | |
pinfo = GithubGistProjectInfo[{}]@new[finfo]; | |
gistID = pinfo@OO`Methods`get["gistID"]; | |
If[gistID === $Failed, | |
ThrowError[RawVCS, OO`Methods`getGistId, "invalid_gist_id"] | |
]]; | |
gistID] | |
, | |
(* Delete the gist associated with the project *) | |
OO`Methods`deleteGistForProject[] := | |
Module[{finfo, pinfo}, | |
OOBlock[ | |
finfo = FileInfo[{}]@OO`Methods`new[dir]; | |
GithubGistClient`deleteGist[$self@OO`Methods`getGistId[]]; | |
pinfo = GithubGistProjectInfo[{}]@new[finfo]; | |
pinfo@OO`Methods`delete["url"]; | |
pinfo@OO`Methods`save[]; | |
]] | |
, | |
OO`Methods`remove[files : {__String}] := | |
OOBlock[ | |
commitProject[FileInfo[{}]@new[dir ], {}, {}, files] | |
] | |
, | |
OO`Methods`rename[renamings : {(_String -> _String) ..}] := | |
OOBlock[ | |
commitProject[FileInfo[{}]@new[dir ], {}, renamings, {}] | |
] | |
, | |
OO`Methods`commit[files : {__String}] := | |
OOBlock[ | |
commitProject[FileInfo[{}]@new[dir ], files] | |
] | |
] | |
] | |
End[] (* End Private Context *) | |
EndPackage[] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment