├── 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 | [](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 "
"
113 | for (category,count) in speciesSorted do
114 | yield sprintf "- Category %s: %d
" category count
115 | 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 """ | Category | Count |
"""
130 | yield """ """
131 | for (category,count) in speciesSorted do
132 | yield sprintf "| %s | %d |
" category count
133 | yield """ """
134 | yield """
"""
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 """ | Thing | Value |
"""
146 | yield """ """
147 | for i in 1 .. n do
148 | yield sprintf "| Thing %d | %d |
" i i
149 | yield """ """
150 | yield """
"""
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 """ """
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 | """
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 |
--------------------------------------------------------------------------------