Using F# Fake SquirrelHelper to create Squirrel.Windows packages - f#-fake

Attempting to create a SquirrelPack target however it does not seem to be working for me using the docs:
// line 84
Target "CreateSquirrelPackage" (fun _ ->
SquirrelPack (fun p -> {p with WorkingDir = "./tmp"}) "./my.nuget"
)
// line 88
Output
build.fsx(86,49): error FS0001: This expression was expected to have type
string option
but here has type
string
Has anyone been able to get SquirrelPack working in F#Fake?

WorkingDir has type string option, but you're trying to make it just a string.
This should work:
Target "CreateSquirrelPackage" (fun _ ->
SquirrelPack (fun p -> {p with WorkingDir = Some "./tmp"}) "./my.nuget"
)

Related

Reading a FileReader type with a Maybe in elm

(This is related to Initializing an empty file value in Elm )
I am using Elm (0.18) and imported simonh1000's FileReader library. To store a file value, we use the following json type:
type alias FileContentArrayBuffer =
Value
and I structure my model thusly:
type alias Model =
{
username : String
, filecontent: Maybe FileContentArrayBuffer
}
initialModel : Model
initialModel =
{
username = "mark"
, filecontent = Nothing
}
When a file is dropped into place, getFileContents is called. The relevant functions and msg's are as follows:
getFileContents : NativeFile -> Cmd Msg
getFileContents nf =
FileReader.readAsArrayBuffer nf.blob
|> Task.attempt OnFileContent
type Msg
...
| OnFileContent (Result FileReader.Error (Maybe FileContentArrayBuffer))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
...
OnFileContent res ->
case res of
Ok (Just filecontent) ->
( { model | filecontent = filecontent }, Cmd.none )
Ok Nothing ->
Debug.crash "No Content"
Err err ->
Debug.crash (toString err)
When I compile I get this error:
The right side of (|>) is causing a type mismatch.
56| FileReader.readAsArrayBuffer nf.blob
57|> |> Task.attempt OnFileContent
(|>) is expecting the right side to be a:
Task.Task FileReader.Error FileReader.FileContentArrayBuffer -> a
But the right side is:
Task.Task FileReader.Error (Maybe FileReader.FileContentArrayBuffer)
-> Cmd Msg
Not sure why, given that I have included the Maybe in my Type and provided cases. Any ideas?
If you define Msg like this instead:
type Msg
...
| OnFileContent (Result FileReader.Error FileContentArrayBuffer)
Then your update case can set the file value when successful or set to Nothing in failure:
OnFileContent res ->
case res of
Ok filecontent ->
( { model | filecontent = Just filecontent }, Cmd.none )
Err err ->
( { model | filecontent = Nothing }, Cmd.none )
Note that Debug.crash should be avoided at all costs. It really is just there for temporary debugging. It would be better perhaps to add an error message property on your model to notify the user of a problem.

F# Make - Restore certain Nuget packages with PreRelease option

In order to specify additional internal company Nuget feeds, I am currently using RestorePackages like this:
let RestorePackages() =
!! "./**/packages.config"
|> Seq.iter (RestorePackage(fun p -> {p with Sources = "http://internal.example.com/nuget" :: p.Sources}))
Which works great. How can I get the script to restore certain packages with the IncludePreRelease option set?
I have tried matching on the package like this:
let RestorePackages() =
!! "./**/packages.config"
|> Seq.iter (fun item ->
match item with
| "Example" -> RestorePackageId(fun p -> {p with IncludePreRelease = true}) "Example"
| item -> RestorePackage(fun p -> {p with Sources = "http://internal.example.com/nuget" :: p.Sources}))
But that doesn't work. The default match to call RestorePackage says "This expression was expected to have type unit but here has type string -> unit".
Looks like you're missing a string at the end of your last code line. I think you should pass item variable there.
let RestorePackages() =
!! "./**/packages.config"
|> Seq.iter (fun item ->
match item with
| "Example" -> RestorePackageId(fun p -> {p with IncludePreRelease = true}) "Example"
| item -> RestorePackage(fun p -> {p with Sources = "http://internal.example.com/nuget" :: p.Sources}) item)

What local-storage tooling does WebSharper provide?

In looking at the documentation for WebSharper's local storage, the SetItem item is string * string -> unit (and GetItem is string -> string).
This means that I'll need to convert anything I want to store into strings and do the reverse to retrieve them. Or, to put it in another way, I'll need to serialize and de-serialize them. Is there a way to use the behind-the-scenes conversion that WebSharper already does for RPC calls, or am I stuck with using a server-side library like FsPicker?
Not built in yet, I have been using this helper module to make local storage usable the same way as a ref cell:
open IntelliFactory.WebSharper
// Helper for handling localstorage, making a stored value work like a ref cell.
[<JavaScript; AutoOpen>]
module LocalStorage =
open IntelliFactory.WebSharper.Html5
let localStorage = Window.Self.LocalStorage
type IValue<'T> =
abstract Value: 'T with get, set
let [<Inline>] ( ! ) (x: IValue<_>) = x.Value
let [<Inline>] ( := ) (x: IValue<_>) v = x.Value <- v
// Redefining Ref to use IValue
type Ref<'T> (value: 'T) =
let mutable v = value
interface IValue<'T> with
member this.Value
with get() = v
and set value = v <- value
let [<Inline>] ref v = Ref v
let incr i = i := !i + 1
let decr i = i := !i - 1
type IStorageItem<'T> =
inherit IValue<'T>
abstract Save: unit -> unit
abstract Delete: unit -> unit
type JSONStorageItem<'T>(key, defaultVal) =
let mutable value = None
let getValue() =
match value with
| Some v -> v
| _ ->
let v =
match localStorage.GetItem key with
| null -> defaultVal
| s ->
Json.Parse s :?> _
value <- Some v
v
interface IStorageItem<'T> with
member this.Value
with get() = getValue()
and set v =
try localStorage.SetItem(key, Json.Stringify v)
value <- Some v
with _ -> JavaScript.Alert "Saving data to storage failed."
member this.Save() =
try localStorage.SetItem(key, Json.Stringify (getValue()))
with _ -> JavaScript.Alert "Saving data to storage failed."
member this.Delete() =
localStorage.RemoveItem key
value <- None
let [<Inline>] getJSONStorage key defaultVal = JSONStorageItem<_>(key, defaultVal) :> IStorageItem<_>
However this can currently only stringify/parse straight data objects: record, list, array, tuple and union types are ok, but no prototypes are restored.

F# insert a list into SQL Server

I'm stuck at the moment trying to figure out a method of inserting into SQL Server from F#.
I have an F# function that iterates through all files inside a folder following a user-defined pattern. Then I can use the returned data to put in a list or (ideally) insert into a database.
I already have a working insert-into-sql function that works properly:
let execNonQuery conn s =
let comm =
new SqlCeCommand(s, conn)
try
comm.ExecuteNonQuery() |> ignore
with e ->
printf "Error : %A\n" e
let string = "insert into MyTable (MyColumn) values ('test .. again')"
execNonQuery conn string; // works
I'm trying to get this method to work properly:
let rec getAllFiles dir pattern =
seq { yield! Directory.EnumerateFiles(dir, pattern)
for d in Directory.EnumerateDirectories(dir) do
yield! getAllFiles d pattern }
let getApplications (dir : string) (extension : string) =
getAllFiles dir extension
//|> Seq.toList // If I need to create a list of returned values
|> Seq.iter (fun s -> SQLInsertString s) // This does not work as it complains about the function not being of type unit
If I use Seq.toList only and call the function as per below, it works:
getApplications "C:\Admin" "*.txt" // works
The other thing I don't understand is how can you create a working insert command that takes in a string for Value. For example:
let SQLInsertString s = "insert into MyTable (MyColumn) values (%s)" //does not work
You're almost there. The problem is sqlInsertString returns string which is not legal to use in Seq.iter.
What you're doing with sqlInsertString is to create a string using string formats. It fits nicely with sprintf function:
let sqlInsertString s =
sprintf "insert into MyTable (MyColumn) values (%s)" s
Now you can use execNonQuery on results of sqlInsertString to actually insert data into database. Since execNonQuery returns unit, it could be easily used in Seq.iter:
// Assuming conn is a global and already defined variable.
let getApplications (dir : string) (extension : string) =
getAllFiles dir extension
|> Seq.iter (fun s -> execNonQuery conn (sqlInsertString s))
Since type annotation is redundant, your code could be rewritten in a more idiomatic way:
let getApplications dir extension conn =
getAllFiles dir extension
|> Seq.iter (sqlInsertString >> execNonQuery conn)
The best way to pass parameters to a query is to use SqlCeParameter. This is easier than composing strings (because you don't need to encode strings and escape quotes) and it is also safer, because you avoid SQL injection attack. Here is a basic sample:
let sqlInsertString value =
// Create and open connection ('use' makes sure it gets closed at the end)
use conn = new SqlCeConnection("...");
conn.Open()
// Create a command with a parameter named '#str'
let cmd = new SqlCeCommand("INSERT INTO MyTable (MyColumn) values (#str)", conn)
// Create parameter '#str' with string value 'value' and add it to the command
let param = new SqlCeParameter("#str", SqlDbType.NVarChar, value)
cmd.Parameters.Add(param)
// Now run the command (exception handling omitted)
cmd.ExecuteNonQuery() |> ignore
Using this function, you should now be able to use Seq.iter. The function takes a string to be inserted and returns unit (no value), so it can be passed to Seq.iter:
let getApplications (dir : string) (extension : string) =
getAllFiles dir extension
|> Seq.iter (fun s -> sqlInsertString s)
Alternatively, you can write the last line just as |> Seq.iter sqlInsertString. If you do that, you're basically saying that the argument s should be directly passed to the sqlInsertString function.

More FP-correct way to create an update sql query

I am working on access a database using F# and my initial attempt at creating a function to create the update query is flawed.
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) =
let buf = new System.Text.StringBuilder("UPDATE users SET ");
if (oldUser.FirstName.Equals(newUser.FirstName) = false) then buf.Append("SET first_name='").Append(newUser.FirstName).Append("'" ) |> ignore
if (oldUser.LastName.Equals(newUser.LastName) = false) then buf.Append("SET last_name='").Append(newUser.LastName).Append("'" ) |> ignore
if (oldUser.UserName.Equals(newUser.UserName) = false) then buf.Append("SET username='").Append(newUser.UserName).Append("'" ) |> ignore
buf.Append(" WHERE id=").Append(newUser.Id).ToString()
This doesn't properly put a , between any update parts after the first, for example:
UPDATE users SET first_name='Firstname', last_name='lastname' WHERE id=...
I could put in a mutable variable to keep track when the first part of the set clause is appended, but that seems wrong.
I could just create an list of tuples, where each tuple is oldtext, newtext, columnname, so that I could then loop through the list and build up the query, but it seems that I should be passing in a StringBuilder to a recursive function, returning back a boolean which is then passed as a parameter to the recursive function.
Does this seem to be the best approach, or is there a better one?
UPDATE:
Here is what I am using as my current solution, as I wanted to make it more generalized, so I just need to write an abstract class for my entities to derive from and they can use the same function. I chose to split up how I do the function so I can pass in how to create the SET part of the update so I can test with different ideas.
let BuildUserUpdateQuery3 (oldUser:UserType) (newUser:UserType) =
let properties = List.zip3 oldUser.ToSqlValuesList newUser.ToSqlValuesList oldUser.ToSqlColumnList
let init = false, new StringBuilder()
let anyChange, (formatted:StringBuilder) =
properties |> Seq.fold (fun (anyChange, sb) (oldVal, newVal, name) ->
match(oldVal=newVal) with
| true -> anyChange, sb
| _ ->
match(anyChange) with
| true -> true, sb.AppendFormat(",{0} = '{1}'", name, newVal)
| _ -> true, sb.AppendFormat("{0} = '{1}'", name, newVal)
) init
formatted.ToString()
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) (updatequery:UserType->UserType->String) =
let buf = StringBuilder("UPDATE users SET ");
buf.AppendFormat(" {0} WHERE id={1}", (updatequery oldUser newUser), newUser.Id)
let UpdateUser conn (oldUser:UserType) (newUser:UserType) =
let query = BuildUserUpdateQuery oldUser newUser BuildUserUpdateQuery3
execNonQuery conn (query.ToString())
Is this the tuple solution you had in mind?
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) =
let buf = StringBuilder("UPDATE users set ")
let properties =
[(oldUser.FirstName, newUser.FirstName, "first_name")
(oldUser.LastName, newUser.LastName, "last_name")
(oldUser.UserName, newUser.UserName, "username")]
|> Seq.map (fun (oldV, newV, field) ->
if oldV <> newV
then sprintf "%s='%s'" field newV
else null)
|> Seq.filter (fun p -> p <> null)
|> Seq.toArray
if properties.Length = 0
then None
else
bprintf buf "%s" (String.Join(", ", properties))
bprintf buf " where id=%d" newUser.Id
Some <| buf.ToString()
I don't see how the recursive solution could be simpler than this...
BTW I would strongly advise to use proper SQL parameters instead of just concatenating the values, you might become vulnerable to injection attacks...
Just for completeness, here is a version that does the same thing directly using the fold function. This can be done quite elegantly, because methods of StringBuilder return the StringBuilder (which allows you to chain them in C#). This can be also used nicely for folding.
Let's assume that we have the list of tuples from the solution by Mauricio:
let properties =
[ (oldUser.FirstName, newUser.FirstName, "first_name")
(oldUser.LastName, newUser.LastName, "last_name")
(oldUser.UserName, newUser.UserName, "username") ]
Now you can write the following code (it also returns a flag whether anything has changed):
let init = false, new StringBuilder()
let anyChange, formatted =
properties |> Seq.fold (fun (anyChange, sb) (oldVal, newVal, name) ->
if (oldVal = newVal) anyChange, sb
else true, sb.AppendFormat("{0} = '{1}'", name, newVal)) init
The state kept during folding has type bool * StringBuilder and we start with an initial value containing empty string builder and false. In each step, we either return the original state (if value is the same as previous) or a new state containing true and a new version of the StringBuilder returned by AppendFormat.
Using recursion explicitly would also work, but when you can use some built-in F# function, it is usually easier to use this approach. If you needed to process nested entities of each entity, you could use the Seq.collect function together with recursion to get a list of properties that you need to process using fold. Pseudo-code might look like this:
let rec processEntities list names =
// Pair matching entity with the name from the list of names
List.zip list names
|> List.collect (fun (entity, name) ->
// Current element containing old value, new value and property name
let current = (entity.OldValue, entity.NewValue, name)
// Recursively proces nested entitites
let nested = processEntities entity.Nested
current::nested)
This can be more elegantly written using sequence expressions:
let rec processEntities list =
seq { for entity, name in List.zip list names do
yield (entity.OldValue, entity.NewValue, name)
yield! processEntities entity.Nested }
Then you could simply call processEntities which returns a flat list of entities and process the entities using fold as in the first case.
I like both Mauricio's and Tomas's solutions, but perhaps this is more like what you originally envisioned?
let sqlFormat (value:'a) = //'
match box value with
| :? int | :? float -> value.ToString()
| _ -> sprintf "'%A'" value // this should actually use database specific escaping logic to make it safe
let appendToQuery getProp (sqlName:string) (oldEntity,newEntity,statements) =
let newStatements =
if (getProp oldEntity <> getProp newEntity) then (sprintf "%s=%s" sqlName (sqlFormat (getProp newEntity)))::statements
else statements
(oldEntity, newEntity, newStatements)
let createUserUpdate (oldUser:UserType) newUser =
let (_,_,statements) =
(oldUser,newUser,[])
|> appendToQuery (fun u -> u.FirstName) "first_name"
|> appendToQuery (fun u -> u.LastName) "last_name"
|> appendToQuery (fun u -> u.UserName) "username"
// ...
let statementArr = statements |> List.toArray
if (statementArr.Length > 0) then
let joinedStatements = System.String.Join(", ", statementArr)
Some(sprintf "UPDATE users SET %s WHERE ID=%i" joinedStatements newUser.ID)
else
None
If you have lots of properties to check, this may be a bit more concise. One benefit to this approach is that it works even if you're checking properties of multiple types, whereas the other approaches require all properties to have the same type (since they're stored in a list).