Documentation.st 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. Smalltalk current createPackage: 'Documentation' properties: #{}!
  2. Object subclass: #DocumentationBuilder
  3. instanceVariableNames: 'chapters announcer widget'
  4. category: 'Documentation'!
  5. !DocumentationBuilder methodsFor: 'accessing'!
  6. chapters
  7. ^chapters ifNil: [chapters := self buildChapters]
  8. !
  9. announcer
  10. ^announcer ifNil: [announcer := Announcer new]
  11. !
  12. widget
  13. ^widget ifNil: [widget := DocumentationWidget on: self]
  14. ! !
  15. !DocumentationBuilder methodsFor: 'building'!
  16. buildChapters
  17. ^((self class methodDictionary values sorted: [:a :b | a selector < b selector])
  18. select: [:each | each category = 'chapters'])
  19. collect: [:each | self perform: each selector]
  20. !
  21. buildOn: aCanvas
  22. aCanvas with: self widget.
  23. self
  24. checkHashChange;
  25. checkHash
  26. !
  27. buildOnJQuery: aJQuery
  28. self buildOn: (HTMLCanvas onJQuery: aJQuery)
  29. !
  30. build
  31. self buildOnJQuery: ('body' asJQuery)
  32. ! !
  33. !DocumentationBuilder methodsFor: 'chapters'!
  34. ch1introduction
  35. ^DocChapter new
  36. title: 'Introduction';
  37. contents: '
  38. ##Amber Smalltalk in a nutshell.
  39. Amber is an implementation of the Smalltalk-80 language.
  40. It allows developers to write client-side heavy web applications in Smalltalk. Amber includes an integrated development environment with a class browser, workspace and transcript.
  41. Amber includes the following features:
  42. - It is semantically and syntactically equivalent to Pharo Smalltalk (the implementation considered as the reference)
  43. - It is written in itself and compiles into efficient JavaScript
  44. - A canvas API similar to Seaside to generate HTML
  45. - A jQuery binding.
  46. ## Disclaimer
  47. This documentation doesn''t aim to teach Smalltalk.
  48. Knowledge of Smalltalk is needed to understand the topics covered in this documentation.
  49. If you want to learn the Smalltalk language, you can read the excellent [Pharo By Example](http://www.pharobyexample.org) book.
  50. '
  51. !
  52. ch2differencesWithOtherSmalltalks
  53. ^DocChapter new
  54. title: 'Differences with other Smalltalks';
  55. contents: '
  56. Amber has some differences with other Smalltalk implementations.
  57. Because it maps Smalltalk constructs one-to-one with the JavaScript equivalent, including Smalltalk classes to JavaScript constructors, the core class library is simplified compared to Pharo Smalltalk.
  58. The following list explains the main differences:
  59. - The collection class hierarchy is simpler compared to most Smalltalk implementations. As of today, there is no SortedCollection. The size of arrays is dynamic, and they behave like an ordered collection.
  60. They can also be sorted with the #sort* methods.
  61. - The Date class behaves like the Date and TimeStamp classes in Pharo Smalltalk. Therefore both Date today and Date now are valid in Amber.
  62. '
  63. !
  64. ch3GettingStarted
  65. ^DocChapter new
  66. title: 'Getting started';
  67. contents: '
  68. To get started hacking in Amber you can basically take three routes, independent of your platform:
  69. 1. Just try it out at http://www.amber-lang.net (click the "Class browser" button) - but you will **not be able to save any code you write**!!
  70. Still, it works fine for looking at the IDE and playing around. Just don''t press F5/reload - it will bring you back to zero.
  71. (Well, if you still want to develop and save code online someone has set up this site seems for free use: https://www.screwtopdb.com/amberstore/topics?name=amberstore/amber.html )
  72. 2. Download an Amber zip-ball, install Nodejs, fire up the Amber server and then open Amber from localhost - then you **can save code**. Detailed instructions are below!!
  73. 3. Same as above but install git first and get a proper clone from http://github.com/NicolasPetton/amber instead of a zip/tar-ball.
  74. If you want to **contribute to Amber itself** this is really what you want to do. It requires installing git first, but it is quite simple - although we leave this bit as an "exercise to the reader" :)
  75. **PLEASE NOTE:** Amber core developers use Linux.
  76. We do not want to introduce dependencies that aren''t cross platform - but currently amberc (the command line compiler) is a bash script and we also use Makefiles
  77. (for building Amber itself and server side examples) written on Linux/Unix. So using Windows is currently a bit limited - you can''t run "make" in the .st directory to rebuild whole of Amber for example.
  78. BUT... if you only want to use Amber to build web client apps and not really get involved in hacking Amber itself - then you should be fine!!
  79. ## Downloading Amber
  80. Currently you can download in zip or tar-ball format, either cutting edge or a release. [Downloads are available here](https://github.com/NicolasPetton/amber/archives/amber).
  81. At the moment of writing you have release [0.9 as zip](https://github.com/NicolasPetton/amber/zipball/0.9) or [tar](https://github.com/NicolasPetton/amber/tarball/0.9),
  82. or [cutting edge as zip](https://github.com/NicolasPetton/amber/zipball/amber) or [tar](https://github.com/NicolasPetton/amber/tarball/amber).
  83. At the moment this is just a **1.5Mb download**, so its very small. Unpack wherever you like, but I would rename the directory that is unpacked to something slightly shorter - like say "amber-0.9" or just "amber".
  84. And yes, at this point you can double click the index.html file in the amber directory to get the IDE up, but again, **you will not be able to save code**. So please continue below :)
  85. ## Installing Node.js
  86. [Node](http://www.nodejs.org) (for short) is simply the V8 Javascript VM from Google (used in Chrome) hooked together with some hard core C-libraries for doing "evented I/O".
  87. Basically it''s Javascript for the server - on asynch steroids. Amber runs fine in Node and we use it for several Amber tools, like amberc (the command line Amber compiler) or the Amber server (see below).
  88. There are also several Amber-Node example to look at if you want to play with running Amber programs server side. **In short - you really want to install Nodejs. :)**
  89. - Installing Node on Linux can be done using your package tool of choice ("apt-get install nodejs" for example) or any other way described at [the download page](http://nodejs.org/#download).
  90. - Installing Node on MacOS seems to be easiest by getting it from [here](https://sites.google.com/site/nodejsmacosx/).
  91. - Installing Node on Windows is probably done best by using the [download from Nodejs.org](http://nodejs.org/#download). This is not an installer, it is instead simply the node executable - **node.exe**.
  92. - Put node.exe somewhere in your path. In Windows7 I can run a command prompt "As administrator" (right click the command prompt in Start menu) and then just "copy node.exe c:\windows\" or such.
  93. ## Starting Amber server
  94. Nicolas has written a minimal webDAV server that is the easiest way to get up and running Amber with the ability to save code. This little server is written in... Amber!!
  95. And it runs on top of Node. So to start it up serving your brand new directory tree of sweet Amber you do:
  96. cd amber (or whatever you called the directory you unpackaged)
  97. ./bin/server (in windows you type "node server\server.js" instead)
  98. It should say it is listening on port 4000. If it does, hooray!! That means both Node and Amber are good. In Windows you might get a question about opening that port in the local firewall - yep, do it!!
  99. ## Firing up Amber
  100. The Amber IDE is written in... Amber. It uses JQuery and runs right in your browser as a ... well, a web page.
  101. We could open it up just using a file url - but the reason we performed the previous steps is so that we can load the IDE web page from a server that can handle PUTs (webDAV) of source code.
  102. According to web security Amber can only do PUT back to the same server it was loaded from. Thus we instead want to open it through our little server now listening on port 4000:
  103. http://localhost:4000/index.html
  104. Clicking the above link should get your Amber running.
  105. To verify that you can indeed commit - just select a Package in the browser, like say "Examples" and press "Commit package" button. **If all goes well nothing happens :)**.
  106. So in order to really know if it worked we can check the modified date on the files **amber/st/Examples.st**, **amber/js/Examples.js** and **amber/js/Examples.deploy.js** - they should be brand new.
  107. NOTE: We can use any webDAV server and Apache2 has been used earlier and works fine. But the Amber server is smaller and simpler to start.
  108. '
  109. !
  110. ch4FirstApp
  111. ^DocChapter new
  112. title: 'A first application';
  113. contents: '
  114. Let''s make Hello World in Amber.
  115. First, you need a place for your new project. I made a new directory under amber:
  116. amber/projects/hello
  117. This will store your project files. To get started, add a new index.html file to this folder, as well as empty js and st folders.
  118. Your index.html can be really basic. The most important thing it does is include amber.js and run loadAmber. Here is a basic index.html you can use:
  119. <!!DOCTYPE html>
  120. <html>
  121. <head>
  122. <title>My First Amber Project</title>
  123. <script src="../../js/amber.js" type="text/javascript"></script>
  124. <script type="text/javascript">
  125. loadAmber({
  126. files: [],
  127. prefix: ''projects/hello/js'',
  128. ready: function() {
  129. }});
  130. </script>
  131. </head>
  132. <body>
  133. <article>
  134. <h1>My First Amber Project</h1>
  135. <button onclick="smalltalk.Browser._open()">class browser</button>
  136. <button id="sayHello">say hello</button>
  137. </article>
  138. </body>
  139. </html>
  140. Now start up amber with node.js and navigate to http://localhost:4000/projects/hello/index.html
  141. It''s boring so far, so lets write some code. Click the button to open the class browser. Find an existing class and change its name to Hello and its package to HelloApp.
  142. Then click save. This creates a new class and leaves the old one intact, it doesn''t overwrite it. Your class will look like this:
  143. Object subclass: #Hello
  144. instanceVariableNames: ''''
  145. package: ''HelloApp''
  146. Now click save and navigate to your new class in its new package.
  147. Then click ''commit package''. You just created a new class and saved your work.
  148. On your file system check out your js and st folders. Your new class is now saved in both JavaScript and Smalltalk.
  149. Now, refresh your browser page and reopen the class browser. Oh no, your new class is gone!! To load your new class automatically, you have to add it in index.html. Make your JavaScript look like this:
  150. loadAmber({
  151. files: [''HelloApp.js''],
  152. prefix: ''projects/hello/js'',
  153. ready: function() {
  154. }});
  155. Save and refresh again. Now your class is loaded and shows up in the class browser.
  156. Now, let''s make this class do something. Create a new message in the class browser by navigating to your class, then clicking ''not yet classified'' and fill in a simple message. Try this for example:
  157. begin
  158. "Makes me say hello to the user."
  159. | msg button |
  160. msg := ''Hello world!!''.
  161. button := ''#sayHello'' asJQuery.
  162. button click: [button after: ''<p>'' , msg , ''</p>''].
  163. Your message isn''t too helpful if it doesn''t get called. Save it, commit the package, then edit index.html again. You can write JavaScript code that sends a message to Smalltalk:
  164. loadAmber({
  165. files: [''HelloApp.js''],
  166. prefix: ''projects/hello/js'', // path for js files i think
  167. ready: function() {
  168. $(function() {
  169. smalltalk.Hello._new()._begin();
  170. });
  171. }});
  172. From there, you can create new Smalltalk classes and messages to build up your app. Enjoy!!
  173. '
  174. !
  175. ch5Index
  176. ^ClassesIndexChapter new
  177. !
  178. ch6KernelObjects
  179. ^PackageDocChapter on: (Package named: 'Kernel-Objects')
  180. !
  181. ch7KernelClasses
  182. ^PackageDocChapter on: (Package named: 'Kernel-Classes')
  183. ! !
  184. !DocumentationBuilder methodsFor: 'routing'!
  185. checkHashChange
  186. (window jQuery: window) bind: 'hashchange' do: [self checkHash]
  187. !
  188. checkHash
  189. | hash presentation |
  190. hash := document location hash replace: '^#' with: ''.
  191. self announcer announce: (ChapterSelectionAnnouncement new
  192. id: hash;
  193. yourself)
  194. ! !
  195. !DocumentationBuilder methodsFor: 'updating'!
  196. update
  197. chapters := nil.
  198. announcer := nil.
  199. widget := nil.
  200. (window jQuery: '.documentation') remove.
  201. self build
  202. ! !
  203. DocumentationBuilder class instanceVariableNames: 'current'!
  204. !DocumentationBuilder class methodsFor: 'accessing'!
  205. current
  206. ^current ifNil: [current := self new]
  207. ! !
  208. !DocumentationBuilder class methodsFor: 'initialization'!
  209. initialize
  210. self current build
  211. ! !
  212. Widget subclass: #DocChapter
  213. instanceVariableNames: 'title contents parent'
  214. category: 'Documentation'!
  215. !DocChapter methodsFor: 'accessing'!
  216. title
  217. ^title ifNil: ['']
  218. !
  219. title: aString
  220. title := aString
  221. !
  222. contents
  223. ^contents ifNil: ['']
  224. !
  225. contents: aString
  226. contents := aString
  227. !
  228. htmlContents
  229. ^(Showdown at: #converter) new makeHtml: self contents
  230. !
  231. chapters
  232. "A doc chapter can contain sub chapters"
  233. ^#()
  234. !
  235. cssClass
  236. ^'doc_chapter'
  237. !
  238. level
  239. ^self parent ifNil: [1] ifNotNil: [self parent level +1]
  240. !
  241. level: anInteger
  242. level := anInteger
  243. !
  244. parent
  245. ^parent
  246. !
  247. parent: aChapter
  248. parent := aChapter
  249. !
  250. id
  251. "The id is used in url fragments.
  252. It must be unique amoung all chapters"
  253. ^self title replace: ' ' with: '-'
  254. !
  255. announcer
  256. ^DocumentationBuilder current announcer
  257. ! !
  258. !DocChapter methodsFor: 'actions'!
  259. selectClass: aClass
  260. DocumentationBuilder current announcer announce: (ClassSelectionAnnouncement on: aClass)
  261. !
  262. selectChapter: aChapter
  263. document location hash: aChapter id
  264. !
  265. displayChapter: aChapter
  266. DocumentationBuilder current widget displayChapter: aChapter
  267. ! !
  268. !DocChapter methodsFor: 'initialization'!
  269. initialize
  270. super initialize.
  271. self subscribe
  272. ! !
  273. !DocChapter methodsFor: 'rendering'!
  274. renderOn: html
  275. html div
  276. class: self cssClass;
  277. with: [
  278. self renderDocOn: html.
  279. self renderLinksOn: html]
  280. !
  281. renderDocOn: html
  282. | div |
  283. html h1 with: self title.
  284. self renderNavigationOn: html.
  285. div := html div class: 'contents'.
  286. div asJQuery html: self htmlContents
  287. !
  288. renderNavigationOn: html
  289. self parent ifNotNil: [
  290. html div
  291. class: 'navigation'; with: [
  292. html a
  293. with: '← back to ', self parent title;
  294. onClick: [self selectChapter: self parent]]]
  295. !
  296. renderLinksOn: html
  297. html ul
  298. class: 'links';
  299. with: [
  300. self chapters do: [:each |
  301. html li with: [
  302. html a
  303. with: each title;
  304. onClick: [self selectChapter: each]]]]
  305. ! !
  306. !DocChapter methodsFor: 'subscriptions'!
  307. subscribe
  308. self announcer on: ChapterSelectionAnnouncement do: [:ann |
  309. ann id = self id ifTrue: [self displayChapter: self]]
  310. ! !
  311. DocChapter subclass: #PackageDocChapter
  312. instanceVariableNames: 'package chapters'
  313. category: 'Documentation'!
  314. !PackageDocChapter methodsFor: 'accessing'!
  315. package
  316. ^package
  317. !
  318. title
  319. ^'Package ', self package name
  320. !
  321. chapters
  322. ^chapters
  323. !
  324. contents
  325. ^'Classes in package ', self package name, ':'
  326. ! !
  327. !PackageDocChapter methodsFor: 'initialization'!
  328. initializeWithPackage: aPackage
  329. package := aPackage.
  330. chapters := (aPackage classes sorted: [:a :b | a name < b name]) collect: [:each |
  331. (ClassDocChapter on: each)
  332. parent: self;
  333. yourself]
  334. ! !
  335. !PackageDocChapter class methodsFor: 'instance creation'!
  336. on: aPackage
  337. ^self basicNew
  338. initializeWithPackage: aPackage;
  339. initialize;
  340. yourself
  341. ! !
  342. DocChapter subclass: #ClassDocChapter
  343. instanceVariableNames: 'theClass'
  344. category: 'Documentation'!
  345. !ClassDocChapter methodsFor: 'accessing'!
  346. theClass
  347. ^theClass
  348. !
  349. contents
  350. ^self theClass comment isEmpty
  351. ifTrue: [self theClass name, ' is not documented yet.']
  352. ifFalse: [self theClass comment]
  353. !
  354. cssClass
  355. ^'doc_class ', super cssClass
  356. !
  357. title
  358. ^self theClass name
  359. !
  360. initializeWithClass: aClass
  361. theClass := aClass
  362. ! !
  363. !ClassDocChapter methodsFor: 'rendering'!
  364. renderLinksOn: html
  365. html ul
  366. class: 'links';
  367. with: [
  368. html li with: [html a
  369. with: 'Browse this class';
  370. onClick: [Browser openOn: self theClass]]]
  371. ! !
  372. !ClassDocChapter methodsFor: 'subscriptions'!
  373. subscribe
  374. super subscribe.
  375. self announcer
  376. on: ClassSelectionAnnouncement do: [:ann |
  377. ann theClass = self theClass ifTrue: [
  378. self selectChapter: self]]
  379. ! !
  380. !ClassDocChapter class methodsFor: 'accessing'!
  381. on: aClass
  382. ^self basicNew
  383. initializeWithClass: aClass;
  384. initialize;
  385. yourself
  386. ! !
  387. Widget subclass: #DocumentationWidget
  388. instanceVariableNames: 'builder selectedChapter chapterDiv'
  389. category: 'Documentation'!
  390. !DocumentationWidget methodsFor: 'accessing'!
  391. builder
  392. ^builder
  393. !
  394. builder: aDocumentationBuilder
  395. builder := aDocumentationBuilder
  396. !
  397. chapters
  398. ^self builder chapters
  399. !
  400. selectedChapter
  401. ^selectedChapter ifNil: [selectedChapter := self chapters first]
  402. !
  403. selectedChapter: aChapter
  404. ^selectedChapter := aChapter
  405. ! !
  406. !DocumentationWidget methodsFor: 'actions'!
  407. displayChapter: aChapter
  408. self selectedChapter: aChapter.
  409. self updateChapterDiv
  410. !
  411. selectChapter: aChapter
  412. document location hash: aChapter id
  413. ! !
  414. !DocumentationWidget methodsFor: 'rendering'!
  415. renderOn: html
  416. html div
  417. class: 'documentation';
  418. with: [
  419. self renderMenuOn: html.
  420. chapterDiv := html div.
  421. self updateChapterDiv]
  422. !
  423. renderMenuOn: html
  424. html div
  425. class: 'menu';
  426. with: [
  427. html ol with: [
  428. self chapters do: [:each |
  429. html li with: [
  430. self renderChapterMenu: each on: html]]]]
  431. !
  432. renderChapterMenu: aChapter on: html
  433. html a
  434. with: aChapter title;
  435. onClick: [
  436. self selectChapter: aChapter].
  437. html ol with: [
  438. aChapter chapters do: [:each |
  439. html li with: [
  440. self renderChapterMenu: each on: html]]]
  441. ! !
  442. !DocumentationWidget methodsFor: 'updating'!
  443. updateChapterDiv
  444. chapterDiv contents: [:html |
  445. html with: self selectedChapter]
  446. ! !
  447. !DocumentationWidget class methodsFor: 'instance creation'!
  448. on: aBuilder
  449. ^self new
  450. builder: aBuilder;
  451. yourself
  452. ! !
  453. DocChapter subclass: #ClassesIndexChapter
  454. instanceVariableNames: ''
  455. category: 'Documentation'!
  456. !ClassesIndexChapter methodsFor: 'accessing'!
  457. cssClass
  458. ^'index_doc ', super cssClass
  459. !
  460. title
  461. ^'Smalltalk classes by index'
  462. !
  463. alphabet
  464. ^'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  465. ! !
  466. !ClassesIndexChapter methodsFor: 'rendering'!
  467. renderDocOn: html
  468. html h1 with: self title.
  469. self alphabet do: [:letter || classes |
  470. classes := Smalltalk current classes select: [:each | each name first = letter].
  471. classes ifNotEmpty: [html h2 with: letter].
  472. html ul with: [
  473. (classes sorted: [:a :b | a name < b name])
  474. do: [:each |
  475. html li with: [html a
  476. with: each name;
  477. onClick: [self selectClass: each]]]]]
  478. ! !
  479. Object subclass: #ClassSelectionAnnouncement
  480. instanceVariableNames: 'theClass'
  481. category: 'Documentation'!
  482. !ClassSelectionAnnouncement methodsFor: 'accessing'!
  483. theClass
  484. ^theClass
  485. !
  486. theClass: aClass
  487. theClass := aClass
  488. ! !
  489. !ClassSelectionAnnouncement class methodsFor: 'instance creation'!
  490. on: aClass
  491. ^self new
  492. theClass: aClass;
  493. yourself
  494. ! !
  495. Object subclass: #ChapterSelectionAnnouncement
  496. instanceVariableNames: 'id'
  497. category: 'Documentation'!
  498. !ChapterSelectionAnnouncement methodsFor: 'accessing'!
  499. id
  500. ^id
  501. !
  502. id: aString
  503. id := aString
  504. ! !