Created
July 16, 2015 13:29
-
-
Save Thorium/1bc4e3c925d17850136a to your computer and use it in GitHub Desktop.
Simple CRUD -application. What you will need: 1) a Mac, Windows or Linux computer with FSharp (F#) installed. 2) MariaDB or MySQL installed. 3) Paket package manager. Run "paket.exe install" or "mono paket.exe install". 4) If on Mono, you have to add Program.fs to a project to compile it (or SignalR won't work).
This file contains hidden or 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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>My CRUD-Form</title> | |
<script type='text/javascript' src='paket-files/ajax.aspnetcdn.com/jquery.min.js'></script> | |
<script type='text/javascript' src='paket-files/SignalR/bower-signalr/jquery.signalR.js'></script> | |
<script type='text/javascript' src='paket-files/underscorejs.org/underscore-min.js'></script> | |
<script type='text/javascript' src='/signalr/hubs'></script> <!-- Generated SignalR Hub! --> | |
<script> | |
//UrlParameters: /index.html?customerId=1 | |
// In production, replace this with client side router like Crossroads.js: | |
var customerId = location.search.replace(/^.*?\=/, ''); | |
$(document).ready(function () { | |
$.connection.hub.url = "/signalr"; | |
myHub = $.connection.MyHub; | |
var connection = !myHub ? $.hubConnection() : $.connection.hub; | |
var conn = connection.start({ transport: 'longPolling' }); | |
conn.done(function () { | |
function readForm() { | |
var nonbuttons = _.filter($(":input"), function (i) { return i.type != "button"; }); | |
return _.map(nonbuttons, function (k) { return { Item1: k.id, Item2: k.value }; }); | |
} | |
if (customerId == "") { | |
$("#updatebtn").css({ display: "none", visibility: "hidden" }); | |
$("#deletebtn").css({ display: "none", visibility: "hidden" }); | |
$("#createbtn").click(function () { | |
myHub.server.create(readForm()).done( | |
function(data){ | |
alert("Created!"); | |
var id = _.filter(data, function(i){return i.Item1=="Id";}); | |
var idval = _.map(id, function(i){return i.Item2;}); | |
document.location.href = "index.html?customerId=" + idval[0]; | |
}); | |
return false; | |
}); | |
}else { | |
function setData(data) { | |
_.each(data, function (c) { $('#' + c.Item1).val(c.Item2); }); | |
} | |
myHub.server.read(customerId).done(setData); | |
$("#updatebtn").click(function () { | |
myHub.server.update(customerId, readForm()).done(function (data) { | |
alert("Updated!"); setData(data); | |
}); | |
return false; | |
}); | |
$("#deletebtn").click(function () { | |
if(confirm("Are you sure?")){ | |
myHub.server.delete(customerId).done(function (d) { alert("Deleted!"); document.location.href = "index.html"; }); | |
} | |
return false; | |
}); | |
} | |
}); | |
}); | |
</script> | |
<style> | |
.bgFrame { | |
background-color: #ffffff; | |
border: thin solid #000000; | |
padding: 30px; | |
margin: 30px; | |
width: 600px; | |
height: 200px; | |
vertical-align: middle; | |
text-align: center; | |
} | |
</style> | |
</head> | |
<body style="background-color: #688856"> | |
<div class="bgFrame"><h2>Customer management</h2> | |
<form> | |
Customer login: <input type="text" id="Login" /><br /> | |
Customer password: <input type="password" id="Password" /><br /> | |
Customer name: <input type="text" id="Name" /><br /> | |
Customer birthdate: <input type="text" id="BirthDate" /><br /> | |
<input type="button" class = "button" id="createbtn" value="Create new" /> | |
<input type="button" class = "button" id="updatebtn" value="Update current" /> | |
<input type="button" class = "button" id="deletebtn" value="Delete current" /> | |
</form> | |
</div> | |
</body> | |
</html> |
This file contains hidden or 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
// This shows NuGet references. You may also get paket.exe | |
// and run "paket install" or "mono paket.exe install" with Mono | |
source https://www.nuget.org/api/v2 | |
// Server side, F# .NET: | |
nuget FSharp.Data | |
nuget Microsoft.AspNet.SignalR.Core | |
nuget Microsoft.AspNet.WebApi.OwinSelfHost | |
nuget Microsoft.Owin.StaticFiles | |
nuget Microsoft.Owin.Diagnostics | |
nuget Microsoft.Owin.Security | |
nuget MySql.Data | |
nuget SQLProvider | |
// Client side, Javascripts, could be refereced through paket-files folder... | |
github SignalR/bower-signalr jquery.signalR.js | |
http http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.4.min.js jquery.min.js | |
http http://underscorejs.org/underscore-min.js |
This file contains hidden or 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
// In MONO, won't work in interactive, you need to make a project and compile a dll. | |
#if INTERACTIVE | |
#r @"./packages/FSharp.Data/lib/net40/FSharp.Data.dll" | |
#r @"./packages/SQLProvider/lib/net40/FSharp.Data.SqlProvider.dll" | |
// OWIN and SignalR-packages: | |
#I @"./packages/Microsoft.AspNet.SignalR.Core/lib/net45" | |
#r @"./packages/Microsoft.AspNet.SignalR.Core/lib/net45/Microsoft.AspNet.SignalR.Core.dll" | |
#I @"./packages/Microsoft.Owin/lib/net45" | |
#r @"./packages/Microsoft.Owin/lib/net45/Microsoft.Owin.dll" | |
#I @"./packages/Microsoft.Owin.Security/lib/net45" | |
#r @"./packages/Microsoft.Owin.Security/lib/net45/Microsoft.Owin.Security.dll" | |
#r @"./packages/Microsoft.AspNet.WebApi.Core/lib/net45/System.Web.Http.dll" | |
#r @"./packages/Microsoft.AspNet.WebApi.Owin/lib/net45/System.Web.Http.Owin.dll" | |
#r @"./packages/Microsoft.Net.Http/lib/net40/System.Net.Http.dll" | |
#I @"./packages/Microsoft.Owin.Hosting/lib/net45" | |
#r @"./packages/Microsoft.Owin.Hosting/lib/net45/Microsoft.Owin.Hosting.dll" | |
#I @"./packages/Microsoft.Owin.Host.HttpListener/lib/net45" | |
#r @"./packages/Microsoft.Owin.Host.HttpListener/lib/net45/Microsoft.Owin.Host.HttpListener.dll" | |
#I @"./packages/Microsoft.Owin.StaticFiles/lib/net45" | |
#r @"./packages/Microsoft.Owin.StaticFiles/lib/net45/Microsoft.Owin.StaticFiles.dll" | |
#r @"./packages/Microsoft.Owin.FileSystems/lib/net45/Microsoft.Owin.FileSystems.dll" | |
#I @"./packages/Microsoft.Owin.Diagnostics/lib/net45/" | |
#r @"./packages/Microsoft.Owin.Diagnostics/lib/net45/Microsoft.Owin.Diagnostics.dll" | |
#r @"./packages/Microsoft.AspNet.WebApi.Client/lib/net45/System.Net.Http.Formatting.dll" | |
#I @"./packages/Newtonsoft.Json/lib/net45" | |
#r @"./packages/Newtonsoft.Json/lib/net45/Newtonsoft.Json.dll" | |
#I @"./packages/Owin/lib/net40" | |
#r @"./packages/Owin/lib/net40/Owin.dll" | |
#endif | |
open System | |
open FSharp.Data | |
open FSharp.Data.Sql | |
[<Literal>] | |
let mysqldatapath = __SOURCE_DIRECTORY__ + @"/packages/MySql.Data/lib/net45/" | |
type SqlConnection = | |
SqlDataProvider< | |
ConnectionString = @"server = localhost; database = mydatabase; uid = webuser; pwd = v3rySecret", | |
DatabaseVendor = Common.DatabaseProviderTypes.MYSQL, | |
IndividualsAmount=1000, | |
UseOptionTypes=true, | |
ResolutionPath=mysqldatapath> | |
//Runtime connection string, move to configuration file: | |
let cstr = @"server = localhost; database = mydatabase; uid = webuser; pwd = v3rySecret" | |
open Microsoft.AspNet.SignalR.Hubs | |
open System.Threading.Tasks | |
// -------- This is the SignalR-Hub for CRUD-operations (could be more) | |
[<HubName("MyHub")>] | |
type MyHub() = // as this = | |
inherit Microsoft.AspNet.SignalR.Hub() | |
let executeCrud itemId actionToEntity = | |
let context = SqlConnection.GetDataContext cstr | |
let entity = | |
query { | |
for u in context.``[mydatabase].[customer]`` do | |
where (u.Id = itemId) | |
head | |
} | |
entity |> actionToEntity | |
context.SubmitUpdates () | |
entity.ColumnValues | |
let ``map data from form to database format`` (formData: seq<string*obj>) = | |
formData // Add missing fields | |
|> Seq.append [| "LastLogin", DateTime.Now |> box ; | |
// ... | |
|] | |
|> Seq.map ( | |
fun (key, value) -> // Convert some fields | |
match key with | |
| "Password" -> | |
"PasswordHash", | |
value.ToString() | |
|> System.Text.Encoding.UTF8.GetBytes | |
|> System.Security.Cryptography.SHA384Managed.Create().ComputeHash | |
|> Convert.ToBase64String |> box | |
| "BirthDate" -> "BirthDate", value.ToString() |> DateTime.Parse |> box | |
| _ -> key,value) | |
member __.Create (data: seq<string*obj>) = | |
let context = SqlConnection.GetDataContext cstr | |
let entity = data | |
|> ``map data from form to database format`` | |
|> context.``[mydatabase].[customer]``.Create | |
context.SubmitUpdates() | |
entity.ColumnValues | |
member __.Read itemId = executeCrud itemId (fun e -> ()) | |
member __.Update itemId data = executeCrud itemId (fun e -> data |> ``map data from form to database format`` |> e.SetData) | |
member __.Delete itemId = executeCrud itemId (fun e -> e.Delete()) | |
// -------- Just some infra code to setup a web-server: | |
let myHubConfig = Microsoft.AspNet.SignalR.HubConfiguration(EnableDetailedErrors = true, EnableJavaScriptProxies = true) | |
open System.Net.Http | |
open Microsoft.Owin.Diagnostics | |
open Owin | |
open System.Web.Http | |
open Microsoft.Owin | |
type MyWebStartup() = | |
member __.Configuration(ap:Owin.IAppBuilder) = | |
let fileServerOptions = Microsoft.Owin.StaticFiles.FileServerOptions() | |
fileServerOptions.DefaultFilesOptions.DefaultFileNames.Add "index.html" | |
fileServerOptions.FileSystem <- (Microsoft.Owin.FileSystems.PhysicalFileSystem (__SOURCE_DIRECTORY__ (* + "webroot/" *))) | |
ap.UseErrorPage(new ErrorPageOptions(ShowExceptionDetails = true)) | |
|> fun app -> app.MapSignalR(myHubConfig) | |
|> fun app -> app.UseFileServer(fileServerOptions) | |
|> ignore | |
() | |
[<assembly: Microsoft.Owin.OwinStartup(typeof<MyWebStartup>)>] | |
do() | |
#if INTERACTIVE | |
#else | |
[<EntryPoint>] | |
#endif | |
let main args = | |
let url = "http://localhost:9090" // "http://*:8080" | |
let server = Microsoft.Owin.Hosting.WebApp.Start<MyWebStartup> url | |
Console.WriteLine ("Server started at: " + url) | |
Console.WriteLine "Press Enter to stop." | |
let key = Console.ReadLine() | |
server.Dispose() | |
0 | |
#if INTERACTIVE | |
main [||] |> ignore | |
#endif |
This file contains hidden or 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
# execute like this: | |
# mysql -u root -p -e "source setupDatabase.sql" | |
# Create database and user: | |
CREATE DATABASE mydatabase; | |
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'v3rySecret'; | |
GRANT USAGE ON mydatabase.* TO 'webuser'@'localhost'WITH GRANT OPTION; | |
GRANT ALL PRIVILEGES ON *.* TO 'webuser'@'localhost'WITH GRANT OPTION; | |
USE mydatabase; | |
# table names: lower case for MySQL/MaridaDb compability (Win+Mac/Linux) | |
CREATE TABLE customer( | |
Id int auto_increment primary key NOT NULL, | |
Name nvarchar(255) NOT NULL, | |
Login nvarchar(255) NULL, | |
PasswordHash nvarchar(255) NULL, | |
BirthDate date NULL, | |
LastLogin datetime NULL); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment