├── dummy.sln ├── Procfile ├── paket.dependencies ├── README.md ├── app.json └── app.fsx /dummy.sln: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: fsharpi-heroku app.fsx 2 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://nuget.org/api/v2 2 | nuget Suave 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample Single-Script Suave Web App, Deployable to Heroku 2 | 3 | This sample is a [single-script](app.fsx) F# web app and API using [Suave](http://suave.io) web server framework. 4 | 5 | You can deploy this to Heroku by following the instructions on [Suave](http://suave.io) or by clicking this button: 6 | 7 | [![Deploy to Heroku](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 8 | 9 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample Suave web app", 3 | "description": "Sample single-script Suave web app, deployable to Heroku.", 4 | "website": "http://suave.io/", 5 | "repository": "https://github.com/SuaveIO/heroku-getting-started", 6 | "logo": "https://raw.githubusercontent.com/SuaveIO/suave/gh-pages/images/logo.gif", 7 | "env": { 8 | "BUILDPACK_URL": "https://github.com/SuaveIO/mono-script-buildpack.git" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app.fsx: -------------------------------------------------------------------------------- 1 | #if BOOTSTRAP 2 | System.Environment.CurrentDirectory <- __SOURCE_DIRECTORY__ 3 | if not (System.IO.File.Exists "paket.exe") then let url = "https://github.com/fsprojects/Paket/releases/download/0.27.2/paket.exe" in use wc = new System.Net.WebClient() in let tmp = System.IO.Path.GetTempFileName() in wc.DownloadFile(url, tmp); System.IO.File.Move(tmp,System.IO.Path.GetFileName url);; 4 | #r "paket.exe" 5 | Paket.Dependencies.Install (System.IO.File.ReadAllText "paket.dependencies") 6 | #endif 7 | 8 | //--------------------------------------------------------------------- 9 | 10 | #I "packages/Suave/lib/net40" 11 | #r "packages/Suave/lib/net40/Suave.dll" 12 | 13 | open System 14 | open Suave // always open suave 15 | open Suave.Http 16 | open Suave.Http.Applicatives 17 | open Suave.Http.Successful // for OK-result 18 | open Suave.Web // for config 19 | open Suave.Types 20 | 21 | printfn "initializing script..." 22 | 23 | let species = 24 | [("Plant (tree)", "Baishan Fir"); ("Insect (butterfly)", ""); 25 | ("Reptile", "Leaf scaled sea-snake"); 26 | ("Insect (damselfly)", "Amani flatwing"); ("Bird", "Araripe manakin"); 27 | ("Insect", "(earwig)"); ("Fish", "Aci Göl toothcarp"); 28 | ("Mammal (bat)", "Bulmer’s fruit bat"); ("Bird", "White bellied heron"); 29 | ("Bird", "Great Indian bustard"); 30 | ("Reptile (tortoise)", "Ploughshare tortoise Angonoka"); 31 | ("Amphibian (toad)", "Rio Pescado stubfoot toad"); 32 | ("Bird", "Madagascar pochard"); ("Fish", "Galapagos damsel fish"); 33 | ("Fish", "Giant yellow croaker"); 34 | ("Reptile (turtle)", "Common batagur Four-toed terrapin"); 35 | ("Plant", "(liverwort)"); ("Mammal", "Hirola (antelope)"); 36 | ("Insect (bee)", "Franklin’s bumblebee"); 37 | ("Mammal (primate)", "Northern muriqui woolly spider monkey"); 38 | ("Mammal", "Pygmy three-toed sloth"); 39 | ("Plant (freshwater)", "(water-starwort)"); 40 | ("Reptile", "Tarzan’s chameleon"); 41 | ("Mammal (rodent)", "Santa Catarina’s guinea pig"); 42 | ("Mammal (primate)", "Roloway guenon (monkey)"); 43 | ("Mammal (bat)", "Seychelles sheath-tailed bat"); 44 | ("Fungi", "Willow blister"); 45 | ("Mammal (shrew)", "Nelson’s small-eared shrew"); 46 | ("Reptile", "Jamaican iguana Jamaican rock iguana"); 47 | ("Plant (orchid)", "Cayman Islands ghost orchid"); 48 | ("Mammal (rhino)", "Sumatran rhino"); ("Bird", "Amsterdam albatross"); 49 | ("Plant", "Wild yam"); ("Plant (tree)", ""); ("Plant (tree)", ""); 50 | ("Amphibian (frog)", "Hula painted frog"); ("Plant", ""); 51 | ("Plant (tree)", ""); ("Amphibian (frog)", "La Hotte glanded frog"); 52 | ("Amphibian (frog)", "Macaya breast-spot frog"); 53 | ("Plant", "Chilenito (cactus)"); ("Plant (tree)", "Coral tree"); 54 | ("Plant (tree)", ""); ("Bird", "Spoon-billed sandpiper"); ("Plant", ""); 55 | ("Bird", "Northern bald ibis"); 56 | ("Plant", "(flowering plant in legume family)"); 57 | ("Mollusc", "(type of gastropod)"); 58 | ("Amphibian (frog)", "Table mountain ghost frog"); 59 | ("Mollusc", "(type of land snail)"); ("Bird", "Liben lark"); 60 | ("Plant (small tree)", ""); ("Fish", "Sakhalin taimen"); 61 | ("Crustacean", "Singapore freshwater crab"); 62 | ("Plant", 63 | "Belin vetchling (flowering plant related to Lathyrus odoratus)"); 64 | ("Amphibian (frog)", "Archey’s frog"); 65 | ("Amphibian (frog)", "Dusky gopher frog"); ("Bird", "Edwards’s pheasant"); 66 | ("Plant", "(type of Magnolia tree)"); 67 | ("Mollusc", "(type of freshwater mussel)"); ("Mollusc", "(snail)"); 68 | ("Mammal (bat)", "Cuban greater funnel eared bat"); 69 | ("Plant", "Attenborough’s pitcher plant"); 70 | ("Mammal (primate)", "Hainan gibbon"); ("Amphibian", "Luristan newt"); 71 | ("Insect (damselfly)", "Mulanje red damsel (damselfly)"); 72 | ("Fish", "Pangasid catfish"); ("Insect (butterfly)", "(butterfly)"); 73 | ("Mammal (cetacean)", "Vaquita (porpoise)"); 74 | ("Plant (tree)", "Type of spruce tree"); ("Plant (tree)", "Qiaojia pine"); 75 | ("Spider","Gooty tarantula, metallic tarantula, peacock parachute spider)"); 76 | ("Bird", "Fatuhiva monarch"); ("Fish", "Common sawfish"); 77 | ("Mammal (primate)", "Greater bamboo lemur"); 78 | ("Mammal (primate)", "Silky sifaka"); 79 | ("Reptile (tortoise)", "Geometric tortoise"); ("Mammal", "Saola"); 80 | ("Plant", ""); ("Insect", "Beydaglari bush-cricket"); 81 | ("Reptile (turtle)", "Red River giant softshell turtle"); 82 | ("Mammal (rhino)", "Javan rhino"); 83 | ("Mammal (primate)", "Tonkin snub-nosed monkey"); 84 | ("Plant (orchid)", "West Australian underground orchid"); 85 | ("Mammal (shrew)", "Boni giant sengi"); 86 | ("Insect (damselfly)", "Cebu frill-wing (damselfly)"); ("Plant", ""); 87 | ("Mammal", "Durrell’s vontsira (type of mongoose)"); 88 | ("Mammal (rodent)", "Red crested tree rat"); 89 | ("Fish", "Red-finned Blue-eye"); ("Fish (shark)", "Angel shark"); 90 | ("Bird", "Chinese crested tern"); ("Fish", "Estuarine pipefish"); 91 | ("Plant", "Suicide Palm Dimaka"); 92 | ("Amphibian (frog)", "Bullock’s false toad"); 93 | ("Mammal (rodent)", "Okinawa spiny rat"); ("Fish", "Somphongs’s rasbora"); 94 | ("Fish", ""); ("Plant", "Forest coconut"); 95 | ("Mammal", "Attenborough’s echidna")] 96 | 97 | let speciesSorted = 98 | species 99 | |> Seq.countBy fst 100 | |> Seq.sortBy (snd >> (~-)) 101 | |> Seq.toList 102 | 103 | 104 | let config = 105 | let port = System.Environment.GetEnvironmentVariable("PORT") 106 | { defaultConfig with 107 | logger = Logging.Loggers.saneDefaultsFor Logging.LogLevel.Verbose 108 | bindings=[ (if port = null then HttpBinding.mk' HTTP "127.0.0.1" 8080 109 | else HttpBinding.mk' HTTP "0.0.0.0" (int32 port)) ] } 110 | 111 | let text = 112 | [ yield "" ] 116 | |> String.concat "\n" 117 | 118 | let angularHeader = """ 119 | 120 | 121 | """ 122 | 123 | let animalsText = 124 | [ yield """""" 125 | yield angularHeader 126 | yield """ """ 127 | yield """

Endangered Animals

""" 128 | yield """ """ 129 | yield """ """ 130 | yield """ """ 131 | for (category,count) in speciesSorted do 132 | yield sprintf "" category count 133 | yield """ """ 134 | yield """
CategoryCount
%s%d
""" 135 | yield """ """ 136 | yield """""" ] 137 | |> String.concat "\n" 138 | 139 | let thingsText n = 140 | [ yield """""" 141 | yield angularHeader 142 | yield """ """ 143 | yield """

Endangered Animals

""" 144 | yield """ """ 145 | yield """ """ 146 | yield """ """ 147 | for i in 1 .. n do 148 | yield sprintf "" i i 149 | yield """ """ 150 | yield """
ThingValue
Thing %d%d
""" 151 | yield """ """ 152 | yield """""" ] 153 | |> String.concat "\n" 154 | 155 | let homePage = 156 | [ yield """""" 157 | yield angularHeader 158 | yield """ """ 159 | yield """

Sample Web App

""" 160 | yield """ """ 161 | yield """ """ 162 | yield """ """ 163 | yield """ """ 164 | yield """ """ 165 | yield """ """ 166 | yield """ """ 167 | yield """ """ 168 | yield """ """ 169 | yield """ """ 170 | yield """ """ 171 | yield """ """ 172 | yield """
PageLink
Endangered AnimalsLink to animals
ThingsLink to things (10)
ThingsLink to things (100)
API JSONLink to result (100)
API XMLLink to result (100)
API JSONLink to result (10)
API XMLLink to result (10)
GoodbyeLink
""" 173 | yield """ """ 174 | yield """""" ] 175 | |> String.concat "\n" 176 | 177 | printfn "starting web server..." 178 | 179 | let jsonText n = 180 | """ 181 | {"menu": { 182 | "id": "file", 183 | "value": "File", 184 | "popup": { 185 | "result": [ 186 | """ + String.concat "\n" 187 | [ for i in 1 .. n -> sprintf """{"value": "%d"},""" i ] + """ 188 | ] 189 | } 190 | }}""" 191 | 192 | let xmlText n = 193 | """ 194 | 195 | 196 | """ + String.concat "\n" 197 | [ for i in 1 .. n -> sprintf """""" i ] + """ 198 | 199 | 200 | 201 | """ 202 | 203 | let xmlMime = Writers.setMimeType "application/xml" 204 | let jsonMime = Writers.setMimeType "application/json" 205 | let app = 206 | choose 207 | [ GET >>= choose 208 | [ path "/" >>= OK homePage 209 | path "/animals" >>= OK animalsText 210 | pathScan "/things/%d" (fun n -> OK (thingsText n)) 211 | path "/api/json" >>= jsonMime >>= OK (jsonText 100) 212 | pathScan "/api/json/%d" (fun n -> jsonMime >>= OK (jsonText n)) 213 | path "/api/xml" >>= xmlMime >>= OK (xmlText 100) 214 | pathScan "/api/xml/%d" (fun n -> xmlMime >>= OK (xmlText n)) 215 | path "/goodbye" >>= OK "Good bye GET" ] 216 | POST >>= choose 217 | [ path "/hello" >>= OK "Hello POST" 218 | path "/goodbye" >>= OK "Good bye POST" ] ] 219 | 220 | 221 | startWebServer config app 222 | printfn "exiting server..." 223 | 224 | 225 | --------------------------------------------------------------------------------