]> info9.net Git - wiki.git/blob - tmarble/posts/Real_World_Clojure/real-world-clojure.org
Copy over old blog
[wiki.git] / tmarble / posts / Real_World_Clojure / real-world-clojure.org
1 #+TITLE:        Real World Clojure
2 #+AUTHOR:       Tom Marble
3 #+EMAIL:        tmarble@info9.net
4 #+STARTUP:      content
5
6 * Overview
7
8 Real World Clojure
9
10 [[file:~/src/software-passion/Clojure-glyph.svg]]
11
12 My journey in using Clojure for a client that is
13 developing a multiplayer game server hosting service.
14
15
16 ** Overview: Topics to cover
17
18 Press *f9* to see the list of topics
19
20 **** Note's on what I put in my .emacs.d/custom.el
21
22 (require 'org-tree-slide)
23
24 (global-set-key (kbd "<f1>") 'show-all)
25 (global-set-key (kbd "<f5>") 'text-scale-decrease)
26 (global-set-key (kbd "<f6>") 'text-scale-increase)
27 (global-set-key (kbd "<f8>") 'org-tree-slide-mode)
28 (global-set-key (kbd "<f9>") 'org-tree-slide-content)
29 (global-set-key (kbd "<f10>") 'hide-sublevels)
30
31 ** What is that presentation tool?
32
33 Emacs!
34
35 This is *org-tree-slide* from https://github.com/takaxp/org-tree-slide
36
37 For more on org mode see http://orgmode.org/org.html
38
39 Yes I will share my "slides" on my website http://tmarble.info9.net
40
41 [[file:~/src/software-passion/org-mode.png]]
42
43 ** Tack!
44
45 My great grandmother immigrated to the USA from Sweden around 1900
46
47 [[file:~/src/software-passion/asimn.png]]
48
49
50 * Background 
51
52 ** About Tom
53
54 tmarble
55 [[file://home/tmarble/Pictures/Tom/Mugshot2011/Tom-2011-200.jpg]]
56
57 *** Sun: technical presales during the dot.com era
58
59 [[file:~/src/software-passion/e10k.jpg]]
60
61
62
63 *** Sun: Java Performance
64
65 [[file:~/src/software-passion/specjvm2008.png]]
66
67 *** Sun: DLJ
68
69 Early 2006: DLJ with Debian and Canonical (Ubuntu)
70
71 [[file:~/src/software-passion/dlj.png]]
72
73 *** Sun: OpenJDK
74
75 JavaOne 2006: Rich Green announces that Sun will open source Java
76
77 Core Strategy Team
78 - How organize community governance
79 - Copyright, Patent and Trademark licensing
80 - Infrastructure tools
81 - Pick license
82
83 First OpenJDK Ambassador (I went to a lot of conferences)
84 - FOSDEM
85 - FISL
86 - OSCON
87
88 ApacheCon 2006: Sun unBOF/Party
89 [[file:~/src/software-passion/apachecon2006.jpg]]
90 Copyright 2006 Ted Leung: https://secure.flickr.com/photos/twleung/268116213/
91
92 *** Left Sun to do a startup
93
94 [[file:~/src/software-passion/tom-think.jpg]]
95
96 *** But
97
98 The global financial meltdown of 2008 happened (etc.)
99
100 Didn't work out :(
101
102 And so I... got into consulting!
103
104 *** Consulting
105
106 Cybersecurity
107
108 Probabilistic Model Verification (Electrical Engineering)
109
110 Software for Smart Grid + Renewable Energy
111
112 Clojure
113
114 *** Tom and Debian
115
116 Using Linux since 1996
117
118 Using Debian since 2003
119
120 Helped Debian Java Packaging Team since DLJ in 2006
121
122 World with Debian and Oracle on meshing Jigsaw with *apt*
123
124 [[file:~/src/software-passion/gsoc11.jpg]]
125
126 *** FOSDEM
127
128 Java track
129 - Oracle
130 - Distros
131 - Developers
132 - Users
133
134 Legal Issues Track
135 - Organized by Karen Sandler, Bradley Kuhn, Richard Fontana and myself
136 - Check out the Wiki    http://info9.net/wiki/fosdem/LegalIssuesDevRoom/
137 - Check out the oggcast http://faif.us/
138
139 ** About my client
140
141 The client
142 - Developing a multiplayer game server hosting service
143 - Comprised of very young developers
144 - Is in stealth mode (sorry!)
145
146 I have been given the authority to
147 - Make significant choices about architecture
148 - Green light to open source generic bits
149
150 (this is why i like consulting :)
151
152 I'm not the only one (especially in Nordic countries)!
153
154 * Why Lisp?
155
156 John McCarthy is old school:  
157 [[file:~/src/software-passion/John_McCarthy.jpg]]
158
159
160 ** homoiconic
161
162 *code is data*
163
164 List 
165
166      (def mylist '(1 2 3))
167
168 Function
169
170      (defn myadd [a b] (+ a b))
171
172 Clojure is defined in terms of the evaluation of data structures
173 and not in terms of the syntax of character streams/files.
174
175 ** macros: code transformations at compile time
176
177 Macros offer hooks for syntactic abstraction
178 and there is very little syntax.
179
180 (defmacro and
181   ([] true)
182   ([x] x)
183   ([x & rest]
184     `(let [and# ~x]
185        (if and# (and ~@rest) and#))))
186
187 Allows code transformation *before* the reader does evaluation
188 defn is a macro that makes defining functions a little simpler.
189
190 *** defining functions uses the defn macro
191
192 Clojure supports arity overloading in a single function object, 
193 self-reference, and variable-arity functions using &:
194
195 (defn argcount
196   ([] 0)
197   ([x] 1)
198   ([x y] 2)
199   ([x y & more] (+ (argcount x y) (count more))))
200
201 -> #'user/argcount
202 (argcount)
203 -> 0
204 (argcount 1)
205 -> 1
206 (argcount 1 2)
207 -> 2
208 (argcount 1 2 3 4 5)
209 -> 5
210
211 ** Very easy to work with code (because it's data)
212
213 LISP is the language of choice when writing
214 Domain Specific Languages (DSL's).
215 - Mentioned by Theo (JRuby) and Morton (APL) today!
216
217 Example from ILC '09 at MIT
218 - Alex Fukunaga (Tokyo University) spoke on The Satisfyability Problem
219 - A DSL for SAT algorithms
220 - Used a biological evolution inspired algorithm
221
222 ** REPL
223
224 The Read Eval Print Loop
225
226 Interactive code development
227
228 Instead of just dump a stack trace and die on an error...
229 you can edit data and functions (they look the same)
230 and continue your program!
231
232 ** Lisp successes
233
234 Artificial Intelligence
235
236 Scientific Computing Lisp
237
238   SciCL augments Common Lisp with an extensive library of aggregate-wise (“AG-wise”)
239   operations on arrays, providing the essential functionality of languages such as APL,  
240   Fortran 90, IDL and Matlab. 
241
242   http://www.siginf.com/
243
244 * Why Clojure?
245
246 ** Many enterprise deployments already use Java
247
248 Clojure adds a jar to the CLASSPATH
249 (lowers the barrier to customer approval)
250
251 Embraces the power of the JVM
252
253 (defn #^Properties as-properties
254   "Convert any seq of pairs to a java.utils.Properties instance.
255    Uses as-str to convert both keys and values into strings."
256   {:tag Properties}
257   [m]
258   (let [p (Properties.)]
259     (doseq [[k v] m]
260       (.setProperty p (as-str k) (as-str v)))
261     p))
262
263 Leverages the wealth of existing Java libraries
264
265 ** Need the benefits of LISP and
266
267 Need to deal with concurrency using native threads and locking.
268
269 Without the downsides of Java
270 - Skip the boilerplate (to not fetishize complexity)
271 - Multi-methods instead of the "Kingdom of Nouns (OOP)"
272 - Unmoderated mutation simply "has to go"
273   (makes concurrency very difficult)
274
275 ** Functional Programming
276
277 Immutable data + first-class functions, supporting recursion
278
279 Dynamic polymorphism
280
281 Emphasizes recursive iteration instead of side-effect based looping
282
283 user> (let [my-vector [1 2 3 4]
284            my-map {:fred "ethel"}
285            my-list (list 4 3 2 1)]
286        (list
287          (conj my-vector 5)
288          (assoc my-map :ricky "lucy")
289          (conj my-list 5)
290          my-vector
291          my-map
292          my-list))
293 -> ([1 2 3 4 5] {:ricky "lucy", :fred "ethel"} (5 4 3 2 1) [1 2 3 4] {:fred "ethel"} (4 3 2 1))
294
295 ** Software Transactional Memory
296
297 Core data structures are immutable and can easily be shared between threads
298
299 Mutation *is* possible using locks to avoid conflicts
300
301 - dosync, ref, set, alter, et al, supports sharing changing 
302   state between threads in a synchronous and coordinated manner. 
303
304 - The agent system supports sharing changing state between threads 
305   in an asynchronous and independent manner. 
306
307 - The atoms system supports sharing changing state between threads 
308   in a synchronous and independent manner. 
309
310 - The dynamic var system supports isolating changing state within 
311   threads.
312
313 ** No spec, one implementation
314
315 Disadvantages: All eggs in one basket
316
317    Advantages: Clojure works *everywhere*
318                Innovation happens quickly
319                Core data structures are extensible abstractions
320
321 [[file:~/src/software-passion/rich.jpg]]
322
323 ** Java 
324
325 Embraces the power of the JVM
326 - Multiplatform
327 - Performance
328
329 Note: also runs on the CLR and on JavaScript (*)
330
331 *** Java - multiplatform
332
333 Sun originally wanted Java to enable customers to use SPARC
334
335 Today many Enterprises run on Intel architectures
336
337 But what about tomorrow?
338
339 *** ARM looks very good for size, cost, heat
340
341 Maybe we will see ARM in the data center?
342
343 [[file:~/src/software-passion/arm-datacenter.png]] 
344
345 http://news.softpedia.com/news/Ubuntu-and-HP-Will-Power-ARM-Data-Centers-231827.shtml
346
347 *** We are seeing ARM everywhere in embedded devices
348
349 Raspberry Pi = $25
350 - SoC is a Broadcom BCM2835. This contains an ARM1176JZFS, with floating point, running at 700Mhz
351 - Videocore 4 GPU. The GPU is capable of BluRay quality playback, using H.264 at 40MBits/s. 
352 - It has a fast 3D core accessed using the supplied OpenGL ES2.0 and OpenVG libraries.
353 - 256 MB RAM
354 - One USB port
355 - (Model B adds a 2nd USB port, Ethernet)
356
357 [[file:~/src/software-passion/raspi_blue_white.png]]
358
359 http://www.raspberrypi.org/
360
361 *** Java for the "Internet of Things"
362
363 Tiny ARM chip
364 IPv6
365
366 [[file:~/src/software-passion/arm-1mm.png]]
367
368 *** Java as assembly language
369
370 For these reasons Clojure is one of many vibrant,
371 alternative languages on the JVM which include:
372
373 - JRuby
374 - Scala
375 - Jython
376 - IKVM.NET
377 - Gosu
378 - Smalltalk
379 - JavaScript
380
381 ** Bleeding Edge OpenJDK features
382
383 NOT yet truly being used by Clojure
384
385 *** Fork/Join
386
387 Bring Doug Lea's Fork/Join framework into Clojure
388
389 Primary example *pmap* 
390 - using the shortest map/reduce tutorial ever
391 - WAIT, Morton did this in one line in APL :)
392
393     user> (def mylist '(1 2 3 4 5 6))
394     #'user/mylist
395     user> (map even? mylist)
396     (false true false true false true)
397     user> (reduce 'or (map even? mylist))
398     true
399
400 David Liebke: "From Concurrency to Parallelism"
401 http://incanter.org/downloads/fjclj.pdf
402
403 *** Tail Call Optimization
404
405 Save space on the stack:
406
407   call factorial (3)
408    call fact (3 1)
409     call fact (2 3)
410      call fact (1 6)
411       call fact (0 6)
412       return 6
413      return 6
414     return 6
415    return 6
416   return 6
417
418   call factorial (3)
419    call fact (3 1)
420    replace arguments with (2 3), jump to "fact"
421    replace arguments with (1 6), jump to "fact"
422    replace arguments with (0 6), jump to "fact"
423    return 6
424   return 6
425
426 NOTE: Clojure does have *recur* and *trampoline* but
427 the JVM itself lacks a generic optimization for TCO
428 (but there is an older patch in the MVLM repo).
429
430 https://en.wikipedia.org/wiki/Tail_call
431
432 *** Invoke Dynamic
433
434 JSR 292
435
436 Enables the HotSpot VM to *see* into your "JVM Language" code
437 and optimize it!
438
439 Why Clojure Doesn't Need Invokedynamic (Unless You Want It to be More Awesome) 
440 http://blog.headius.com/2011/10/why-clojure-doesnt-need-invokedynamic.html
441
442 *** Modularization (Jigsaw)
443
444 Better startup time
445 Finer grained dependencies
446 Smaller footprint (embedded)
447
448 [[file:~/src/software-passion/graph.png]]
449
450 * The Tools I am using
451
452 ** Maven
453
454 Finding dependencies: =mvn dependency:tree -DoutputFile=dependency.txt=
455
456 my-website:my-website:jar:0.1.0-SNAPSHOT
457 +- org.clojure:clojure:jar:1.3.0:compile
458 \- noir:noir:jar:1.2.2-SNAPSHOT:compile
459    +- compojure:compojure:jar:1.0.0-RC2:compile
460    |  +- org.clojure:core.incubator:jar:0.1.0:compile
461    |  +- org.clojure:tools.macro:jar:0.1.0:compile
462    |  +- clout:clout:jar:1.0.0:compile
463    |  \- ring:ring-core:jar:1.0.1:compile
464    |     +- commons-io:commons-io:jar:1.4:compile
465    |     +- commons-fileupload:commons-fileupload:jar:1.2.1:compile
466    |     \- javax.servlet:servlet-api:jar:2.5:compile
467    +- org.clojure:tools.namespace:jar:0.1.0:compile
468    |  \- org.clojure:java.classpath:jar:0.1.0:compile
469    +- clj-json:clj-json:jar:0.4.3:compile
470    |  \- org.codehaus.jackson:jackson-core-asl:jar:1.5.0:compile
471    +- ring:ring:jar:1.0.1:compile
472    |  +- ring:ring-devel:jar:1.0.1:compile
473    |  |  \- ns-tracker:ns-tracker:jar:0.1.1:compile
474    |  +- ring:ring-jetty-adapter:jar:1.0.1:compile
475    |  |  +- org.mortbay.jetty:jetty:jar:6.1.25:compile
476    |  |  |  \- org.mortbay.jetty:servlet-api:jar:2.5-20081211:compile
477    |  |  \- org.mortbay.jetty:jetty-util:jar:6.1.25:compile
478    |  \- ring:ring-servlet:jar:1.0.1:compile
479    +- hiccup:hiccup:jar:0.3.7:compile
480    +- clj-stacktrace:clj-stacktrace:jar:0.2.3:compile
481    +- ring-reload-modified:ring-reload-modified:jar:0.1.1:compile
482    +- net.java.dev.jets3t:jets3t:jar:0.8.1:compile
483    |  +- commons-codec:commons-codec:jar:1.3:compile
484    |  +- commons-logging:commons-logging:jar:1.1.1:compile
485    |  +- commons-httpclient:commons-httpclient:jar:3.1:compile
486    |  \- com.jamesmurty.utils:java-xmlbuilder:jar:0.4:compile
487    \- org.mindrot:jbcrypt:jar:0.3m:compile
488
489 ** Leiningen
490
491 Leiningen is awesome
492   https://github.com/technomancy/leiningen
493   
494 Use the REPL *swank-clojure*
495   https://github.com/technomancy/swank-clojure
496
497 Get...
498 $ lein plugin install swank-clojure 1.4.0
499 $ lein plugin install lein-localrepo 0.3
500 $ lein plugin install lein-noir 1.2.1
501
502 =lein localrepo help=
503
504 Public Repos: http://clojars.org/
505
506 Private Repos:  https://github.com/technomancy/s3-wagon-private
507
508 Lein directly from git: https://github.com/tobyhede/lein-git-deps
509
510 ** Redis
511
512 Amazing NoSQL Database: http://redis.io
513
514 With a Clojure binding! https://github.com/mmcgrana/clj-redis
515
516 Redis utterly killed it in 2010 – check out the growth in share of developer conversation
517 http://www.redmonk.com/jgovernor/2012/03/15/redis-utterly-killed-it-in-2010-check-out-the-growth-in-share-of-developer-conversation/ 
518
519
520
521 ** Jenkins
522
523 Continuous Integration Server: http://jenkins-ci.org/
524
525 Amazing Plugins: https://wiki.jenkins-ci.org/display/JENKINS/Plugins
526
527 The ones that I use:
528 - Trac Publisher
529 - Dependency Graph Viewer
530 - IM
531 - Pathignore (essential for big git repo)
532 - SSH Slaves
533 - Thin Backup
534 - Build Result Trigger
535
536 Fun ones
537 - Gravatar
538 - Emotional Jenkins
539
540 KK slides from February at MonkiGras in London
541   http://www.slideshare.net/kohsuke/building-developer-community
542   
543 ** Using Jenkins
544
545 Git push triggers Jenkins
546 Updates the one (master) workspace
547 Projects started based on updated paths
548
549 Java Client
550 - Builds on Linux
551 - Triggers native Mac OS X build on Mac slave
552 - Triggers native Windows build on Windows slave
553
554 Deploying Noir application
555 - shuts down dev website
556 - updates code
557 - restarts website
558
559 ** Trac
560
561 http://trac.edgewall.org/
562
563 - Tickets (bugs, tasks), Reports, Browse code, Timeline, Wiki
564 - Can now use git (yeah!)
565 - Integration with Jenkins
566   http://trac-hacks.org/wiki/XmlRpcPlugin 
567
568 ** Noir
569
570 Let's talk about Noir http://webnoir.org
571
572 * Why Open Source Matters
573
574 Free as in Free Beer
575
576 Free as in Free Speech
577
578 Knowing the shape of the solutions: Ease of integration
579
580 No marketing: just code (extra credit: build in tests and Jenkins)
581
582 Fewer bugs (recent Coverity study)
583
584 Education, credentials and employment
585 - Employers *will* google you
586 - Many directly ask for pointers to FLOSS contributions
587
588 ** Where are you going to deploy that code?
589
590 The "cloud".
591
592 Are you really going to deploy to Windows?
593 - you have to name your machines                    #FAIL
594 - you have to Remote Desktop in and click-to-admin  #FAIL
595 - no anticipated downtime until 2016 :)
596
597 You can't deploy to Mac OS X
598 - X Serve died a long time ago
599
600 You want to deploy to Linux
601 - Cost effective
602 - Legal
603 - More reliable
604 - More automatable
605
606 ** permissive vs. restrictive licensing
607
608 BSD (MIT AL2) vs. GPL (MPL)
609
610 Permissive is necessary, but sometimes not enough to
611 hold a community together.
612
613 Jeremy Allison: Why Samba Switched to GPLv3
614 2011 Linux Collaboration Summit
615   http://faif.us/cast/2011/may/10/0x0F/
616
617 NOTE: proprietary (dual) licensing with contributor 
618 license agreements is now considered harmful
619
620 ** Open Source and Web Services
621
622 What if you want to build a strong community 
623 around a web service?
624
625 In the "cloud" the GPL is just like BSD.
626
627 The answer? The AGPL (Affero General Public License)
628
629 From the FSF
630   The GNU Affero General Public License is a modified version 
631   of the ordinary GNU GPL version 3. It has one added requirement: 
632   if you run the program on a server and let other users communicate 
633   with it there, your server must also allow them to download 
634   the source code corresponding to the program that it's running. 
635
636 What? I'm going to build a business on AGPL? Is that CRAZY?
637
638 It is being done now: http://status.net
639 "Enterprise Social Software is OPEN for business."
640
641 ** Where is the value?
642
643 Productivity!
644
645 Right Now
646 - Hardware is effectively free
647 - The best software in life is Free
648 - Savoir Faire (brainpower) is expensive
649 - Data are like diamonds: they vary in clarity, quality and value
650
651 New business models need to maximize productivity
652 around managing and improving quality of data.
653
654 (NOTE: China doesn't care about intellectual property anyway)
655
656 ** Why Debian
657
658 Commitment to quality and building everything from source
659
660 Package inter-dependencies are core to the system
661 - Windows needs Maven, Gems, cygwin, etc.
662 - Mac needs MacPorts, etc.
663
664 Very predictable, easy to administer & automate, secure, stable
665
666 One of the two major Linux families (.deb and .rpm)
667 and the foundation of many derivatives (e.g. Ubuntu)
668
669 [[file:~/src/software-passion/debconf11.jpg]]
670
671 http://wiki.debconf.org/wiki/DebConf11/Pictures/GroupPhoto
672
673 * Challenges and Next Steps
674
675 ** The state of Clojure Contrib (is a challenge)
676
677 "Modularization of Contrib"
678
679    http://dev.clojure.org/display/doc/Clojure+Contrib
680
681 Wait, why isn't there a project.clj (for lein)?
682 - officially must use mvn (!) (lein originally could not deploy
683   to remote mvn repos)
684
685 The idea is that everything that hasn't been modularized yet 
686 is supposedly either low quality or in low demand
687
688 Using clojars: change groupID to highlight it's non-canonical
689
690 Also it's tricky to find out what the *real* disposition of
691 stuff is.. I wanted java-utils (moved to clojure.java.io)
692
693 ** My client will expand capacity from private to public cloud
694
695 Expand service from customer hosted into EC2
696 With auto provisioning of resources (up/down)
697
698 ** Websockets
699
700 jQuery
701 Atmosphere
702 Jetty
703 Noir
704
705 Fully bi-directional pipes (no more AJAX polling)!
706
707 ** Redis binding change
708
709 clj-redis / Jedis / Apache connection library
710 - missing functionality
711 - times out
712
713 My be replaced with redis-clojure
714
715 ** Keeping an eye on Datomic  
716
717 [[file:~/src/software-passion/datomic.png]]
718
719 ** Keeping an eye on ClojureScript One
720
721 ClojureScript
722 ClojureScript One
723 ClojureScript One + the remote REPL + browser testing + no CSS reloads
724
725 Connect With Your Creation Through a Real-Time Editor
726 http://www.webmonkey.com/2012/03/connect-with-your-creation-through-a-real-time-editor/
727 http://www.chris-granger.com/2012/02/20/overtone-and-clojurescript/ 
728
729 ** Experiment with exposing bleeding edge JVM features in the Clojure
730
731 It only takes about 30 min to build the JDK on an 8 core machine
732
733 Tighter Debian Clojure packaging (Jigsaw)
734
735 * Conclusion
736
737 LISP is incredibly powerful (don't be afraid of the parens)
738
739 Clojure is the best LISP now (because of the JVM)
740
741 Java means future proof for platforms in the cloud
742 and the "Internet of Things".
743
744 Open Source isn't just free, it's key to a
745 strong business model (and probably saving the planet).
746
747 Software "best practice" tools are available for Clojure now
748
749 There are *still* many optimizations waiting to be made
750
751 The #1 reason to use Clojure: productivity.
752
753
754 This presentation: Copyright @ 2012 Informatique, Inc.
755 under a Creative Commons Share Alike USA 3.0 license
756 https://creativecommons.org/licenses/by-sa/3.0/us/
757
758 [[file:~/src/software-passion/by-sa-3.0.png]]
759
760
761 Clojure: Copyright 2008-2012 Rich Hickey http://clojure.org
762
763 NOTE: Tom will code in Clojure / Jigsaw / Debian / ARM for food!
764 http://tmarble.info9.net
765
766 * Q/A + Live Hacking
767
768 file:~/src/software-passion
769
770 ** Command line processing and configuration files
771
772 tools.cli
773    https://github.com/clojure/tools.cli 
774    awesome, right?
775 connected to SSH agent (has at least one identity)
776 tmarble@noir 102$ lein search tools.cli
777  == Results from central - Showing page 1 / 1 total
778 [org.clojure/tools.cli "0.1.0"]
779 [org.clojure/tools.cli "0.1.0"]
780 tmarble@noir 103$ 
781
782 ** Pretty Print HTML and XML
783
784 I created a future-contrib package:
785 file:~/src/maas/clojure/future-contrib/project.clj
786
787 See file:~/src/maas/clojure/future-contrib/src/future_contrib/core.clj
788
789 Demonstrate example with file:~/src/clojuremn/example.xml
790
791 ** redis2xml
792
793 Demonstrates command line processing and configuration files
794
795 see file:~/src/maas/clojure/redis2xml/project.clj
796
797 see: file:~/.redis2xml
798
799 also try command line:
800
801 =redis-cli -a NoOneWillEverGuess -n 3=
802
803 ./bin/redis2xml -v -n 3 -f -i ~/src/clojuremn/example.xml
804
805 ** Example Noir site
806
807 See file:~/src/noir-examples/my-website
808
809
810