From 2464b333c307d5029e18ce816629fbe27e429736 Mon Sep 17 00:00:00 2001 From: Jesse House Date: Sat, 28 May 2011 15:38:34 -0700 Subject: [PATCH 001/267] new recipe: create object literal if it does not exist --- ...create-object-literal-if-not-exist.textile | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 chapters/objects/create-object-literal-if-not-exist.textile diff --git a/chapters/objects/create-object-literal-if-not-exist.textile b/chapters/objects/create-object-literal-if-not-exist.textile new file mode 100644 index 0000000..e642240 --- /dev/null +++ b/chapters/objects/create-object-literal-if-not-exist.textile @@ -0,0 +1,29 @@ +--- +layout: recipe +title: Create an object literal if it does not already exist +chapter: Objects +--- + +h2. Problem + +You want to initialize an object literal, but you do not want to overwrite the object if it already exists. + + +h2. Solution + +Use the Existential operator + +{% highlight coffeescript %} +window.MY_NAMESPACE ?= {} +{% endhighlight %} + + +h2. Discussion + +This is equivalent to the following JavaScript: + +{% highlight javascript %} +window.MY_NAMESPACE = window.MY_NAMESPACE || {}; +{% endhighlight %} + +Common JavaScript technique, using object literal to define a namespace. This saves us from clobbering the namespace if it already exists. From f2d2d7c2844ef9a439f50f6291ea18957f55aae8 Mon Sep 17 00:00:00 2001 From: Jesse House Date: Sat, 28 May 2011 15:40:04 -0700 Subject: [PATCH 002/267] new recipe: jquery ajax --- chapters/jquery/ajax.textile | 33 +++++++++++++++++++++++++++++++++ wanted-recipes.textile | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 chapters/jquery/ajax.textile diff --git a/chapters/jquery/ajax.textile b/chapters/jquery/ajax.textile new file mode 100644 index 0000000..4fb319d --- /dev/null +++ b/chapters/jquery/ajax.textile @@ -0,0 +1,33 @@ +--- +layout: recipe +title: Ajax +chapter: jQuery +--- + +h2. Problem + +You need to make an ajax request using jQuery. + + +h2. Solution + +{% highlight coffeescript %} +jQuery -> + console.log "document loaded" + $('form').submit (e) -> + console.log "form submit" + e.preventDefault() + form = this + + $.ajax + type: "POST" + url: $(form).attr('action') + data: $(form).serialize() + success: -> + console.log("success") +{% endhighlight %} + + +h2. Discussion + +The jQuery and $ variables can be used interchangeably. See also "Callback bindings":../jquery/callback-bindings-jquery. \ No newline at end of file diff --git a/wanted-recipes.textile b/wanted-recipes.textile index a0c37de..61c8e81 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -108,7 +108,7 @@ console.log [1,2,3,4,5].map (x) -> h2. jQuery -* jQuery AJAX from CS +* h2. Regular Expressions From 11bb583f210f113876b8a487c138c8492759b4e7 Mon Sep 17 00:00:00 2001 From: James Holder Date: Fri, 3 Jun 2011 22:52:12 -0400 Subject: [PATCH 003/267] Add the complete list of GoF design patterns to the corresponding section of Wanted Recipes (excluding the recipe added below). Add Design Patterns/Builder Pattern recipe. Add name to Authors file. --- authors.textile | 1 + chapters/design_patterns/builder.textile | 54 ++++++++++++++++++++++++ chapters/design_patterns/index.textile | 17 ++++++++ chapters/index.textile | 1 + wanted-recipes.textile | 30 ++++++++++++- 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/builder.textile create mode 100644 chapters/design_patterns/index.textile diff --git a/authors.textile b/authors.textile index 6a0895c..f6e75aa 100644 --- a/authors.textile +++ b/authors.textile @@ -13,6 +13,7 @@ _The following people are totally rad and awesome because they have contributed * David Moulton _dave@themoultons.net_ * Sebastian Slomski _sebastian@simple-systems.org_ * Aaron Weinberger _aw9994@cs.ship.edu_ +* James C. Holder _cs_cookbook@thirdtruck.org_ * ...You! What are you waiting for? Check out the contributing section and get cracking! diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile new file mode 100644 index 0000000..d1536ac --- /dev/null +++ b/chapters/design_patterns/builder.textile @@ -0,0 +1,54 @@ +--- +layout: recipe +title: Builder Pattern +chapter: Design Patterns +--- + +h2. Problem + +You need to prepare a complicated, multi-part object, more than once or with varying configurations. + +h2. Solution + +Create a Builder to encapsulate the production process. +{% highlight coffeescript %} +TodoTxtBuilder = (defaultParameters={ }) -> + date: new Date(defaultParameters.date) or new Date + contexts: defaultParameters.contexts or [ ] + projects: defaultParameters.projects or [ ] + priority: defaultParameters.priority or undefined + + newTodo: (description, parameters={ }) -> + date = (parameters.date and new Date(parameters.date)) or this.date + contexts = this.contexts.concat(parameters.contexts or [ ]) + projects = this.projects.concat(parameters.projects or [ ]) + priorityLevel = parameters.priority or this.priority + + createdAt = [date.getFullYear(), date.getMonth()+1, date.getDate()].join("-") + contextNames = ("@" + context for context in contexts).join(" ") + projectNames = ("+" + project for project in projects).join(" ") + priority = if priorityLevel then "(" + priorityLevel + ")" else "" + + [priority, createdAt, description, contextNames, projectNames].reduce (whole, part) -> + if part then (whole and whole + " ") + part else whole + +builder = new TodoTxtBuilder({date: "10/13/2011"}) + +builder.newTodo "Wash laundry" + +# => 2011-10-13 Wash laundry + +builder = new TodoTxtBuilder({date: "10/13/2011", contexts: ["work"]}) + +builder.newTodo "Show the new design pattern to Sean", {contexts: ["desk", "xpSession"]} + +# => '2011-10-13 Show the new design pattern to Sean @work @desk @xpSession' + +builder.newTodo "Remind Lucy about the failing unit tests", {contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A'} + +# => '(A) 2011-10-13 Remind Lucy about the failing unit tests @work @meeting +compilerRefactor' +{% endhighlight %} + +h2. Discussion +Based on the Todo.txt format, the TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each todo item. A command line tool tool or GUI could plug into this code and retain support for later, more advanced versions of the format with ease. + diff --git a/chapters/design_patterns/index.textile b/chapters/design_patterns/index.textile new file mode 100644 index 0000000..dc44fbd --- /dev/null +++ b/chapters/design_patterns/index.textile @@ -0,0 +1,17 @@ +--- +layout: chapter +title: Design Patterns +chapter: Design Patterns +--- + +{% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} +{% capture indexurl %}{{ url }}/index.html{% endcapture %} + +{% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} + * {{ page.title }} + {% endunless %} + {% endif %} +{% endfor %} + diff --git a/chapters/index.textile b/chapters/index.textile index d26d135..bd759b5 100644 --- a/chapters/index.textile +++ b/chapters/index.textile @@ -13,6 +13,7 @@ chapters: - jQuery - Regular Expressions - AJAX +- Design Patterns --- diff --git a/wanted-recipes.textile b/wanted-recipes.textile index a0c37de..f370475 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -123,4 +123,32 @@ h2. AJAX h2. Design patterns -* Singleton pattern +* Creational Patterns +** Abstract Factory +** Factory Method +** Prototype +** Singleton + +* Structural Patterns +** Adapter +** Bridge +** Composite +** Decorator +** Facade +** Flyweight +** Proxy + +* Behavioral Patterns +** Chain of Responsibility +** Command +** Interpreter +** Iterator +** Mediator +** Memento +** Observer +** State +** Strategy +** Template Method +** Visitor + + From 35610919e40688791fe3fd700254dd127b5c5a35 Mon Sep 17 00:00:00 2001 From: James Holder Date: Fri, 3 Jun 2011 23:27:20 -0400 Subject: [PATCH 004/267] Adjust spacing to avoid code format issue. --- chapters/design_patterns/builder.textile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index d1536ac..e83dfcc 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -11,24 +11,22 @@ You need to prepare a complicated, multi-part object, more than once or with var h2. Solution Create a Builder to encapsulate the production process. + {% highlight coffeescript %} TodoTxtBuilder = (defaultParameters={ }) -> date: new Date(defaultParameters.date) or new Date contexts: defaultParameters.contexts or [ ] projects: defaultParameters.projects or [ ] priority: defaultParameters.priority or undefined - newTodo: (description, parameters={ }) -> date = (parameters.date and new Date(parameters.date)) or this.date contexts = this.contexts.concat(parameters.contexts or [ ]) projects = this.projects.concat(parameters.projects or [ ]) priorityLevel = parameters.priority or this.priority - createdAt = [date.getFullYear(), date.getMonth()+1, date.getDate()].join("-") contextNames = ("@" + context for context in contexts).join(" ") projectNames = ("+" + project for project in projects).join(" ") priority = if priorityLevel then "(" + priorityLevel + ")" else "" - [priority, createdAt, description, contextNames, projectNames].reduce (whole, part) -> if part then (whole and whole + " ") + part else whole @@ -47,8 +45,9 @@ builder.newTodo "Show the new design pattern to Sean", {contexts: ["desk", "xpSe builder.newTodo "Remind Lucy about the failing unit tests", {contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A'} # => '(A) 2011-10-13 Remind Lucy about the failing unit tests @work @meeting +compilerRefactor' + {% endhighlight %} h2. Discussion -Based on the Todo.txt format, the TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each todo item. A command line tool tool or GUI could plug into this code and retain support for later, more advanced versions of the format with ease. +Based on the Todo.txt format, the TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each todo item. A command line tool tool or GUI could plug into this code and retain support for later, more advanced versions of the format with ease. From 119e099c98e21721776e2be613f119f81411a4f5 Mon Sep 17 00:00:00 2001 From: James Holder Date: Sat, 4 Jun 2011 10:07:30 -0400 Subject: [PATCH 005/267] Remove extraneous curly brackets. --- chapters/design_patterns/builder.textile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index e83dfcc..1b51d84 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -30,19 +30,19 @@ TodoTxtBuilder = (defaultParameters={ }) -> [priority, createdAt, description, contextNames, projectNames].reduce (whole, part) -> if part then (whole and whole + " ") + part else whole -builder = new TodoTxtBuilder({date: "10/13/2011"}) +builder = new TodoTxtBuilder(date: "10/13/2011") builder.newTodo "Wash laundry" # => 2011-10-13 Wash laundry -builder = new TodoTxtBuilder({date: "10/13/2011", contexts: ["work"]}) +builder = new TodoTxtBuilder(date: "10/13/2011", contexts: ["work"]) -builder.newTodo "Show the new design pattern to Sean", {contexts: ["desk", "xpSession"]} +builder.newTodo "Show the new design pattern to Sean", contexts: ["desk", "xpSession"] # => '2011-10-13 Show the new design pattern to Sean @work @desk @xpSession' -builder.newTodo "Remind Lucy about the failing unit tests", {contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A'} +builder.newTodo "Remind Lucy about the failing unit tests", contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A' # => '(A) 2011-10-13 Remind Lucy about the failing unit tests @work @meeting +compilerRefactor' From f56ea921f43f1c9e030eda4b6e63053fa25d88be Mon Sep 17 00:00:00 2001 From: James Holder Date: Sat, 4 Jun 2011 10:41:54 -0400 Subject: [PATCH 006/267] Add "Pre-Construction" section to the Discussion to demonstrate more of the pattern's value. Replace the variable re-initialization in the Solution with a new variable to avoid confusion. --- chapters/design_patterns/builder.textile | 37 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index 1b51d84..f3d5598 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -36,13 +36,13 @@ builder.newTodo "Wash laundry" # => 2011-10-13 Wash laundry -builder = new TodoTxtBuilder(date: "10/13/2011", contexts: ["work"]) +workBuilder = new TodoTxtBuilder(date: "10/13/2011", contexts: ["work"]) -builder.newTodo "Show the new design pattern to Sean", contexts: ["desk", "xpSession"] +workBuilder.newTodo "Show the new design pattern to Sean", contexts: ["desk", "xpSession"] # => '2011-10-13 Show the new design pattern to Sean @work @desk @xpSession' -builder.newTodo "Remind Lucy about the failing unit tests", contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A' +workBuilder.newTodo "Remind Lucy about the failing unit tests", contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A' # => '(A) 2011-10-13 Remind Lucy about the failing unit tests @work @meeting +compilerRefactor' @@ -51,3 +51,34 @@ builder.newTodo "Remind Lucy about the failing unit tests", contexts: ["meeting" h2. Discussion Based on the Todo.txt format, the TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each todo item. A command line tool tool or GUI could plug into this code and retain support for later, more advanced versions of the format with ease. + +h3. Pre-Construction + +Instead of creating a new instance of the needed object from scratch every time, we shift the burden to a separate object that we can then tweak during the object creation process. + +{% highlight coffeescript %} +builder = new TodoTxtBuilder(date: "10/13/2011") + +builder.newTodo "Order new netbook" + +# => '2011-10-13 Order new netbook' + +builder.projects.push "summerVacation" + +builder.newTodo "Buy suntan lotion" + +# => '2011-10-13 Buy suntan lotion +summerVacation' + +builder.contexts.push "phone" + +builder.newTodo "Order tickets" + +# => '2011-10-13 Order tickets @phone +summerVacation' + +delete builder.contexts[0] + +builder.newTodo "Fill gas tank" + +# => '2011-10-13 Fill gas tank +summerVacation' +{% endhighlight %} + From 518919911efb5d5f1b49910c93d8e32d0ae4cca7 Mon Sep 17 00:00:00 2001 From: James Holder Date: Sat, 4 Jun 2011 22:07:05 -0400 Subject: [PATCH 007/267] Add Decorator Pattern recipe. Add vim swap files to .gitignore. --- .gitignore | 5 +- .../design_patterns/.decorator.textile.swp | Bin 0 -> 12288 bytes chapters/design_patterns/decorator.textile | 81 ++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/.decorator.textile.swp create mode 100644 chapters/design_patterns/decorator.textile diff --git a/.gitignore b/.gitignore index 0cdcda3..3042ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .rvmrc -_site \ No newline at end of file +_site + +*.swp +*.swo diff --git a/chapters/design_patterns/.decorator.textile.swp b/chapters/design_patterns/.decorator.textile.swp new file mode 100644 index 0000000000000000000000000000000000000000..e62d5867edb93f8fdd7d4d48766bb4ae9d491a63 GIT binary patch literal 12288 zcmeHNy>A>v6rTj~O$bRu5Z!Q>aCgDJvrPdy97_>NBqSRGPKZ!!tnuF5?!WW)aMx56O27|>zAb~UmRvDhZwskI#Sd4U0M>SMv1#D!t{GqVZ&EuEtN^E zm5Ea&b;HxsUL0?%0}U?_iIJ&pw4_#kw3Y~iZbW@AP6Z_DMs6Dw%DeC9RyYP60|f(_ zX-~hnk3By(J5w$_!w(%i=csWEI0hU8jseGjW56-s7;p?Y2L5LZm}C$88$Ry^UI+FR zz>jN=0mp!2z%k$$a11yG90QI4$ADwNG2j?*4BR6Oh!$hl?`LfPgK!A{|4)Ad_;WvF zw}G3$=fHK~8gLc(6gUT*1(t!+z$suaum`yHIAh-cmw_Iz3Y37Sfxq@K_7iXu_zJiI zTm>!x?*l%t2s{fM0DgUpu^)l2fzN<VT@FZ{_@bjb41zZI#0RngpcojGZ8~|=V z!q{)XB_IO~Ab}RJ_hHx#+<1twFM&^hkAM$=6j%e6fJI;yI12m#uD%1l1ug?01Ixf0 zKnWl&|9F70-+}Le>%cYO3h*KD4&Vc;fNPEc$ADwto@AhRl*_0UC_flJh3FZ6bcanC z;%F0wcMtV;4>1y^Haa!>Y`2{xSmwtUcx67AT{tl&T-Z4}9Z1oVDK~*IydyTG=G{2s zJ>9{5w4M0|R8$yC(%6$){4piH4+E~d`+lh!^7h~jqs3K_}s8gjLKW+!>xO5i^PjcPutjFOLFZIejx=gpT zQ>yWHoDL>vkjL#py|&s^JJ&qEB~;rjYJ7Sj-@fuvt-_nUw56hrl9jA9t9GQF##>Q; z=%gJAGGDIwV5dKv%UV85EZ<7=;}k0tMS1Laal0+0_EMGDKw{@5_e7M_)`?rH-Ngw> zF1M97MEuGA)j=MwUz8qPi|m4S*ljX)UYWpl&%*07{j=*YO>yE-7xS%DM1Cl_2t%TV zHn21ePFrJ@FEO4@+1ZYY)RIUyTJU;`Ys5hEa@^_2$ml7K^8&iOotZ3^_=!BD;z(OZ z+Od~unD2#|R+}>HlEDE)aF{-iViVBsdS&OX3A~RNkT~hFp_YH?MQC%@vyi+A|nU6G;+$Tc?MA4 z<*ibwu>N!Y3qvU(k?dO(c7l6(NZhfkOr>Jnr8`zY!@miS({zX2P;sAMHsU>8CYuK` zRfL-Em$5}yUL-pe3+o6SHHDl`LS?F@m8ev!)i1^>%Ac*GA`Wt{o4iT_wqEd}jc!8K zEtRbdWy%ic&y-ShI3!T0BWorpqb2>!XVjr4Lp`jCG%$$ZYJV-l7{1pS%~C_X8!Tcp zP*{RJ`yx}r6$5_;GP*gO)oeV7P&9|@hNY7j9Te%tiaxf|SZUOc9cfI#GBy@?yX!8Q zNNptKc6nVwLX>;74PI815mo~K6ve$B%E(7pF7O#UHKqW`S?=YpuSH7G9jMB?I z`u$>hsD8K%c6WsFf(vu2gUv^D4yI>U3tQ|em}jLhZ8^$2jkl}i=W3v-T0d4h(qN;h zIL6O^zdg=r3N<w)gxc(qQEW3VMa6fl6wmu z1-90rm`eL5Z3!c=3qBj@gCUZ#McRuXpn}=SLZeWF5K}R# + if match = line.match /^(#+)\s*(.*)$/ + headerLevel = match[1].length + headerText = match[2] + "#{headerText}" + else + if line.length > 0 + "

#{line}

" + else + '' + +StripComments = (line) -> + line.replace /\s*\/\/.*$/, '' + +TextProcessor = (processors) -> + processors: processors + reducer: (existing, processor) -> + if processor + processor(existing or '') + else + existing + processLine: (text) -> + this.processors.reduce this.reducer, text + processString: (text) -> + (this.processLine(line) for line in text.split("\n")).join("\n") + +exampleText = ''' + # A level 1 header + A regular line + // a comment + ## A level 2 header + A line // with a comment + ''' + +processor = new TextProcessor [StripComments, MiniMarkdown] + +processor.processString exampleText + +# => "

A level 1 header

\n

A regular line

\n\n

A level 2 header

\n

A line

" +{% endhighlight %} + +h2. Discussion + +With the TextProcessor object ready to wrangle all of the text processors together, the miniMarkdown, stripComments, and any future functions can focus exclusively on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors in order to partake in the process. + +We can even modify the existing Decorator object on the fly: + +{% highlight coffeescript %} +smilies = + ':)' : "smile" + ':D' : "huge_grin" + ':(' : "frown" + ';)' : "wink" + +smilieExpander = (line) -> + if line + (line = line.replace symbol, "{#{text}}") for symbol, text of smilies + line + +processor.processors.unshift smilieExpander + +processor.processString "# A header that makes you :) // you may even laugh" + +# => '

A header that makes you {smile}

' +{% endhighlight %} From f5732330f86c2e59778da3e10ba0c94bbc03872d Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 5 Jun 2011 11:09:14 -0400 Subject: [PATCH 008/267] Tweak the Decorator Discussion to better explain the utility value of the pattern. Delete an unintentionally-tracked temporary file. Rename the Decorator functions to use more function-like capitalization. --- .../design_patterns/.decorator.textile.swp | Bin 12288 -> 0 bytes chapters/design_patterns/decorator.textile | 18 +++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) delete mode 100644 chapters/design_patterns/.decorator.textile.swp diff --git a/chapters/design_patterns/.decorator.textile.swp b/chapters/design_patterns/.decorator.textile.swp deleted file mode 100644 index e62d5867edb93f8fdd7d4d48766bb4ae9d491a63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeHNy>A>v6rTj~O$bRu5Z!Q>aCgDJvrPdy97_>NBqSRGPKZ!!tnuF5?!WW)aMx56O27|>zAb~UmRvDhZwskI#Sd4U0M>SMv1#D!t{GqVZ&EuEtN^E zm5Ea&b;HxsUL0?%0}U?_iIJ&pw4_#kw3Y~iZbW@AP6Z_DMs6Dw%DeC9RyYP60|f(_ zX-~hnk3By(J5w$_!w(%i=csWEI0hU8jseGjW56-s7;p?Y2L5LZm}C$88$Ry^UI+FR zz>jN=0mp!2z%k$$a11yG90QI4$ADwNG2j?*4BR6Oh!$hl?`LfPgK!A{|4)Ad_;WvF zw}G3$=fHK~8gLc(6gUT*1(t!+z$suaum`yHIAh-cmw_Iz3Y37Sfxq@K_7iXu_zJiI zTm>!x?*l%t2s{fM0DgUpu^)l2fzN<VT@FZ{_@bjb41zZI#0RngpcojGZ8~|=V z!q{)XB_IO~Ab}RJ_hHx#+<1twFM&^hkAM$=6j%e6fJI;yI12m#uD%1l1ug?01Ixf0 zKnWl&|9F70-+}Le>%cYO3h*KD4&Vc;fNPEc$ADwto@AhRl*_0UC_flJh3FZ6bcanC z;%F0wcMtV;4>1y^Haa!>Y`2{xSmwtUcx67AT{tl&T-Z4}9Z1oVDK~*IydyTG=G{2s zJ>9{5w4M0|R8$yC(%6$){4piH4+E~d`+lh!^7h~jqs3K_}s8gjLKW+!>xO5i^PjcPutjFOLFZIejx=gpT zQ>yWHoDL>vkjL#py|&s^JJ&qEB~;rjYJ7Sj-@fuvt-_nUw56hrl9jA9t9GQF##>Q; z=%gJAGGDIwV5dKv%UV85EZ<7=;}k0tMS1Laal0+0_EMGDKw{@5_e7M_)`?rH-Ngw> zF1M97MEuGA)j=MwUz8qPi|m4S*ljX)UYWpl&%*07{j=*YO>yE-7xS%DM1Cl_2t%TV zHn21ePFrJ@FEO4@+1ZYY)RIUyTJU;`Ys5hEa@^_2$ml7K^8&iOotZ3^_=!BD;z(OZ z+Od~unD2#|R+}>HlEDE)aF{-iViVBsdS&OX3A~RNkT~hFp_YH?MQC%@vyi+A|nU6G;+$Tc?MA4 z<*ibwu>N!Y3qvU(k?dO(c7l6(NZhfkOr>Jnr8`zY!@miS({zX2P;sAMHsU>8CYuK` zRfL-Em$5}yUL-pe3+o6SHHDl`LS?F@m8ev!)i1^>%Ac*GA`Wt{o4iT_wqEd}jc!8K zEtRbdWy%ic&y-ShI3!T0BWorpqb2>!XVjr4Lp`jCG%$$ZYJV-l7{1pS%~C_X8!Tcp zP*{RJ`yx}r6$5_;GP*gO)oeV7P&9|@hNY7j9Te%tiaxf|SZUOc9cfI#GBy@?yX!8Q zNNptKc6nVwLX>;74PI815mo~K6ve$B%E(7pF7O#UHKqW`S?=YpuSH7G9jMB?I z`u$>hsD8K%c6WsFf(vu2gUv^D4yI>U3tQ|em}jLhZ8^$2jkl}i=W3v-T0d4h(qN;h zIL6O^zdg=r3N<w)gxc(qQEW3VMa6fl6wmu z1-90rm`eL5Z3!c=3qBj@gCUZ#McRuXpn}=SLZeWF5K}R# +miniMarkdown = (line) -> if match = line.match /^(#+)\s*(.*)$/ headerLevel = match[1].length headerText = match[2] @@ -25,7 +25,7 @@ MiniMarkdown = (line) -> else '' -StripComments = (line) -> +stripComments = (line) -> line.replace /\s*\/\/.*$/, '' TextProcessor = (processors) -> @@ -48,7 +48,7 @@ exampleText = ''' A line // with a comment ''' -processor = new TextProcessor [StripComments, MiniMarkdown] +processor = new TextProcessor [stripComments, miniMarkdown] processor.processString exampleText @@ -57,9 +57,9 @@ processor.processString exampleText h2. Discussion -With the TextProcessor object ready to wrangle all of the text processors together, the miniMarkdown, stripComments, and any future functions can focus exclusively on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors in order to partake in the process. +The TextProcessor serves the role of Decorator and binds the individual, specialized text processors together. This frees up the miniMarkdown, stripComments, and any future components to focus on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors. -We can even modify the existing Decorator object on the fly: +We can even modify the existing Decorator object on the fly. We could add a processor for transforming text smilies into images, for example, but only when the end user enables the feature: {% highlight coffeescript %} smilies = @@ -70,12 +70,16 @@ smilies = smilieExpander = (line) -> if line - (line = line.replace symbol, "{#{text}}") for symbol, text of smilies + (line = line.replace symbol, "#{text}") for symbol, text of smilies line processor.processors.unshift smilieExpander processor.processString "# A header that makes you :) // you may even laugh" -# => '

A header that makes you {smile}

' +# => "

A header that makes you smile

" + +processor.processors.shift() + +# => "

A header that makes you :)

" {% endhighlight %} From fcd2a1afb3ef98002baaa8e7997f915d678d192d Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 5 Jun 2011 12:55:45 -0400 Subject: [PATCH 009/267] Add initial Strategy recipe to the Design Patterns chapter. --- chapters/design_patterns/strategy.textile | 76 +++++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/strategy.textile diff --git a/chapters/design_patterns/strategy.textile b/chapters/design_patterns/strategy.textile new file mode 100644 index 0000000..4ee9673 --- /dev/null +++ b/chapters/design_patterns/strategy.textile @@ -0,0 +1,76 @@ +--- +layout: recipe +title: Strategy Pattern +chapter: Design Patterns +--- + +h2. Problem + +You have more than one way to solve a problem but you need to choose (or even switch) between them at run time. + +h2. Solution + +Encapsulate your algorithms inside of Strategy objects. + +Given an unsorted list, for example, we can change the sorting algorithm under different circumstances. + +{% highlight coffeescript %} +StringSorter = (algorithm) -> + algorithm: algorithm + sort: (list) -> + this.algorithm list + +bubbleSort = (list) -> + anySwaps = false + swapPass = -> + for r in [0..list.length-1] + if list[r] > list[r+1] + anySwaps = true + [list[r], list[r+1]] = [list[r+1], list[r]] + + swapPass() + while anySwaps + anySwaps = false + swapPass() + list + +reverseBubbleSort = (list) -> + anySwaps = false + swapPass = -> + for r in [list.length-1..1] + if list[r] < list[r-1] + anySwaps = true + [list[r], list[r-1]] = [list[r-1], list[r]] + + swapPass() + while anySwaps + anySwaps = false + swapPass() + list + +sorter = new StringSorter bubbleSort + +unsortedList = ['e', 'b', 'd', 'c', 'x', 'a'] + +sorter.sort unsortedList + +# => ['a', 'b', 'c', 'd', 'e', 'x'] + +unsortedList.push 'w' + +# => ['a', 'b', 'c', 'd', 'e', 'x', 'w'] + +sorter.algorithm = reverseBubbleSort + +sorter.sort unsortedList + +# => ['a', 'b', 'c', 'd', 'e', 'w', 'x'] +{% endhighlight %} + +h2. Discussion + +Like a skilled general, we must prepare to change our plans under constantly-changing circumstances. At the end of the example, we know that only the newest item in the array is out of order. We can then speed the sort up by switching to an algorithm that starts from the end of the array. + +h3. Exercises + +* Expand StringSorter into an AlwaysSortedArray class that implements all of the functionality of a regular array but which automatically sorts new items based on the method of insertion (e.g. push vs. shift). diff --git a/wanted-recipes.textile b/wanted-recipes.textile index f370475..8b2d2b6 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -147,7 +147,6 @@ h2. Design patterns ** Memento ** Observer ** State -** Strategy ** Template Method ** Visitor From 50829feae5b06f6e155d1b14502e7e9dfbe122fc Mon Sep 17 00:00:00 2001 From: James Holder Date: Mon, 6 Jun 2011 19:35:33 -0400 Subject: [PATCH 010/267] Editorial changes and clarifications. --- chapters/design_patterns/builder.textile | 26 +++++++++++++--------- chapters/design_patterns/decorator.textile | 19 +++++++++++----- chapters/design_patterns/strategy.textile | 4 ++-- wanted-recipes.textile | 1 - 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index f3d5598..1e71e2c 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -6,11 +6,13 @@ chapter: Design Patterns h2. Problem -You need to prepare a complicated, multi-part object, more than once or with varying configurations. +You need to prepare a complicated, multi-part object, but you expect to do it more than once or with varying configurations. h2. Solution -Create a Builder to encapsulate the production process. +Create a Builder to encapsulate the object production process. + +The Todo.txt format provides an advanced but still plain-text method for maintaining lists of to-do items. Typing out each item by hand would provide exhausting and error-prone, however, so a TodoTxtBuilder class could save us the trouble: {% highlight coffeescript %} TodoTxtBuilder = (defaultParameters={ }) -> @@ -24,9 +26,9 @@ TodoTxtBuilder = (defaultParameters={ }) -> projects = this.projects.concat(parameters.projects or [ ]) priorityLevel = parameters.priority or this.priority createdAt = [date.getFullYear(), date.getMonth()+1, date.getDate()].join("-") - contextNames = ("@" + context for context in contexts).join(" ") - projectNames = ("+" + project for project in projects).join(" ") - priority = if priorityLevel then "(" + priorityLevel + ")" else "" + contextNames = ("@#{context}" for context in contexts).join(" ") + projectNames = ("+#{project}" for project in projects).join(" ") + priority = if priorityLevel then "(#{priorityLevel})" else "" [priority, createdAt, description, contextNames, projectNames].reduce (whole, part) -> if part then (whole and whole + " ") + part else whole @@ -38,19 +40,19 @@ builder.newTodo "Wash laundry" workBuilder = new TodoTxtBuilder(date: "10/13/2011", contexts: ["work"]) -workBuilder.newTodo "Show the new design pattern to Sean", contexts: ["desk", "xpSession"] +workBuilder.newTodo "Show the new design pattern to Lucy", contexts: ["desk", "xpSession"] -# => '2011-10-13 Show the new design pattern to Sean @work @desk @xpSession' +# => '2011-10-13 Show the new design pattern to Lucy @work @desk @xpSession' -workBuilder.newTodo "Remind Lucy about the failing unit tests", contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A' +workBuilder.newTodo "Remind Sean about the failing unit tests", contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A' -# => '(A) 2011-10-13 Remind Lucy about the failing unit tests @work @meeting +compilerRefactor' +# => '(A) 2011-10-13 Remind Sean about the failing unit tests @work @meeting +compilerRefactor' {% endhighlight %} h2. Discussion -Based on the Todo.txt format, the TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each todo item. A command line tool tool or GUI could plug into this code and retain support for later, more advanced versions of the format with ease. +The TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each to-do item. Additionally, a command line tool or GUI could plug into this code and still retain support for later, more advanced versions of the format with ease. h3. Pre-Construction @@ -82,3 +84,7 @@ builder.newTodo "Fill gas tank" # => '2011-10-13 Fill gas tank +summerVacation' {% endhighlight %} +h3. Exercises +* Expand the project- and context-tag generation code to filter out duplicate entries. +** Some Todo.txt users like to insert project and context tags inside the description of their to-do items. Add code to identify these tags and filter them out of the end tags. + diff --git a/chapters/design_patterns/decorator.textile b/chapters/design_patterns/decorator.textile index e80e03f..0359d6d 100644 --- a/chapters/design_patterns/decorator.textile +++ b/chapters/design_patterns/decorator.textile @@ -8,10 +8,9 @@ h2. Problem You have a set of data that you need to process in multiple, possibly varying ways. - h2. Solution -Use the Decorator pattern to structure the application of changes. +Use the Decorator pattern in order to structure how you apply the changes. {% highlight coffeescript %} miniMarkdown = (line) -> @@ -26,7 +25,7 @@ miniMarkdown = (line) -> '' stripComments = (line) -> - line.replace /\s*\/\/.*$/, '' + line.replace /\s*\/\/.*$/, '' # Removes one-line, double-slash C-style comments TextProcessor = (processors) -> processors: processors @@ -55,11 +54,21 @@ processor.processString exampleText # => "

A level 1 header

\n

A regular line

\n\n

A level 2 header

\n

A line

" {% endhighlight %} +h3. Results + +{% highlight html %} +

A level 1 header

+

A regular line

+ +

A level 1 header

+

A line

+{% endhighlight %} + h2. Discussion -The TextProcessor serves the role of Decorator and binds the individual, specialized text processors together. This frees up the miniMarkdown, stripComments, and any future components to focus on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors. +The TextProcessor serves the role of Decorator by binding the individual, specialized text processors together. This frees up the miniMarkdown and stripComments components to focus on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors. -We can even modify the existing Decorator object on the fly. We could add a processor for transforming text smilies into images, for example, but only when the end user enables the feature: +We can even modify the existing Decorator object on the fly: {% highlight coffeescript %} smilies = diff --git a/chapters/design_patterns/strategy.textile b/chapters/design_patterns/strategy.textile index 4ee9673..88cd958 100644 --- a/chapters/design_patterns/strategy.textile +++ b/chapters/design_patterns/strategy.textile @@ -6,7 +6,7 @@ chapter: Design Patterns h2. Problem -You have more than one way to solve a problem but you need to choose (or even switch) between them at run time. +You have more than one way to solve a problem but you need to choose (or even switch) between these methods at run time. h2. Solution @@ -69,7 +69,7 @@ sorter.sort unsortedList h2. Discussion -Like a skilled general, we must prepare to change our plans under constantly-changing circumstances. At the end of the example, we know that only the newest item in the array is out of order. We can then speed the sort up by switching to an algorithm that starts from the end of the array. +"No plan survives first contact with the enemy", nor users, but we can use the knowledge gained from changing circumstances to adapt. Near the end of the example, for instance, the newest item in the array now lies out of order. Knowing that detail, we can then speed the sort up by switching to an algorithm optimized for that exact scenario with nothing but a simple reassignment. h3. Exercises diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 8b2d2b6..b8fa1fc 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -133,7 +133,6 @@ h2. Design patterns ** Adapter ** Bridge ** Composite -** Decorator ** Facade ** Flyweight ** Proxy From 266cae4605af0a17c806dcc3ba7152e688ddee55 Mon Sep 17 00:00:00 2001 From: James Holder Date: Mon, 6 Jun 2011 20:10:51 -0400 Subject: [PATCH 011/267] Replace instances of "this." with "@" shorthand for greater legibility. --- chapters/design_patterns/builder.textile | 8 ++++---- chapters/design_patterns/decorator.textile | 4 ++-- chapters/design_patterns/strategy.textile | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index 1e71e2c..e3bf7e2 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -21,10 +21,10 @@ TodoTxtBuilder = (defaultParameters={ }) -> projects: defaultParameters.projects or [ ] priority: defaultParameters.priority or undefined newTodo: (description, parameters={ }) -> - date = (parameters.date and new Date(parameters.date)) or this.date - contexts = this.contexts.concat(parameters.contexts or [ ]) - projects = this.projects.concat(parameters.projects or [ ]) - priorityLevel = parameters.priority or this.priority + date = (parameters.date and new Date(parameters.date)) or @date + contexts = @contexts.concat(parameters.contexts or [ ]) + projects = @projects.concat(parameters.projects or [ ]) + priorityLevel = parameters.priority or @priority createdAt = [date.getFullYear(), date.getMonth()+1, date.getDate()].join("-") contextNames = ("@#{context}" for context in contexts).join(" ") projectNames = ("+#{project}" for project in projects).join(" ") diff --git a/chapters/design_patterns/decorator.textile b/chapters/design_patterns/decorator.textile index 0359d6d..d40fbdf 100644 --- a/chapters/design_patterns/decorator.textile +++ b/chapters/design_patterns/decorator.textile @@ -35,9 +35,9 @@ TextProcessor = (processors) -> else existing processLine: (text) -> - this.processors.reduce this.reducer, text + @processors.reduce @reducer, text processString: (text) -> - (this.processLine(line) for line in text.split("\n")).join("\n") + (@processLine(line) for line in text.split("\n")).join("\n") exampleText = ''' # A level 1 header diff --git a/chapters/design_patterns/strategy.textile b/chapters/design_patterns/strategy.textile index 88cd958..879a9da 100644 --- a/chapters/design_patterns/strategy.textile +++ b/chapters/design_patterns/strategy.textile @@ -18,7 +18,7 @@ Given an unsorted list, for example, we can change the sorting algorithm under d StringSorter = (algorithm) -> algorithm: algorithm sort: (list) -> - this.algorithm list + @algorithm list bubbleSort = (list) -> anySwaps = false From f74f6b4a618e580485b2839fa16c559c7bbfee3a Mon Sep 17 00:00:00 2001 From: James Holder Date: Mon, 6 Jun 2011 20:23:54 -0400 Subject: [PATCH 012/267] Replace explicity initializer assignments with more concise property arguments. --- chapters/design_patterns/decorator.textile | 3 +-- chapters/design_patterns/strategy.textile | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/chapters/design_patterns/decorator.textile b/chapters/design_patterns/decorator.textile index d40fbdf..89cee57 100644 --- a/chapters/design_patterns/decorator.textile +++ b/chapters/design_patterns/decorator.textile @@ -27,8 +27,7 @@ miniMarkdown = (line) -> stripComments = (line) -> line.replace /\s*\/\/.*$/, '' # Removes one-line, double-slash C-style comments -TextProcessor = (processors) -> - processors: processors +TextProcessor = (@processors) -> reducer: (existing, processor) -> if processor processor(existing or '') diff --git a/chapters/design_patterns/strategy.textile b/chapters/design_patterns/strategy.textile index 879a9da..0ca8402 100644 --- a/chapters/design_patterns/strategy.textile +++ b/chapters/design_patterns/strategy.textile @@ -15,8 +15,7 @@ Encapsulate your algorithms inside of Strategy objects. Given an unsorted list, for example, we can change the sorting algorithm under different circumstances. {% highlight coffeescript %} -StringSorter = (algorithm) -> - algorithm: algorithm +StringSorter = (@algorithm) -> sort: (list) -> @algorithm list From a34e2ae0824c00d226b8ca58e1dd0c7f5ce238b8 Mon Sep 17 00:00:00 2001 From: James Holder Date: Mon, 6 Jun 2011 22:52:25 -0400 Subject: [PATCH 013/267] Incorporate the "when" keyword into the string-building loops for more natural-reading code. --- chapters/design_patterns/builder.textile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index e3bf7e2..8ac936d 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -26,17 +26,17 @@ TodoTxtBuilder = (defaultParameters={ }) -> projects = @projects.concat(parameters.projects or [ ]) priorityLevel = parameters.priority or @priority createdAt = [date.getFullYear(), date.getMonth()+1, date.getDate()].join("-") - contextNames = ("@#{context}" for context in contexts).join(" ") - projectNames = ("+#{project}" for project in projects).join(" ") + contextNames = ("@#{context}" for context in contexts when context).join(" ") + projectNames = ("+#{project}" for project in projects when project).join(" ") priority = if priorityLevel then "(#{priorityLevel})" else "" - [priority, createdAt, description, contextNames, projectNames].reduce (whole, part) -> - if part then (whole and whole + " ") + part else whole + todoParts = [priority, createdAt, description, contextNames, projectNames] + (part for part in todoParts when part.length > 0).join " " builder = new TodoTxtBuilder(date: "10/13/2011") builder.newTodo "Wash laundry" -# => 2011-10-13 Wash laundry +# => '2011-10-13 Wash laundry' workBuilder = new TodoTxtBuilder(date: "10/13/2011", contexts: ["work"]) From ab0394b8acd6a5e69cfc2b5296efc39e3c994740 Mon Sep 17 00:00:00 2001 From: James Holder Date: Tue, 7 Jun 2011 13:21:48 -0400 Subject: [PATCH 014/267] Begin move to using "class" keyword for more explicit code. --- chapters/design_patterns/builder.textile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.textile index 8ac936d..9fc8191 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.textile @@ -15,11 +15,12 @@ Create a Builder to encapsulate the object production process. The Todo.txt format provides an advanced but still plain-text method for maintaining lists of to-do items. Typing out each item by hand would provide exhausting and error-prone, however, so a TodoTxtBuilder class could save us the trouble: {% highlight coffeescript %} -TodoTxtBuilder = (defaultParameters={ }) -> - date: new Date(defaultParameters.date) or new Date - contexts: defaultParameters.contexts or [ ] - projects: defaultParameters.projects or [ ] - priority: defaultParameters.priority or undefined +class TodoTxtBuilder + constructor: (defaultParameters={ }) -> + @date = new Date(defaultParameters.date) or new Date + @contexts = defaultParameters.contexts or [ ] + @projects = defaultParameters.projects or [ ] + @priority = defaultParameters.priority or undefined newTodo: (description, parameters={ }) -> date = (parameters.date and new Date(parameters.date)) or @date contexts = @contexts.concat(parameters.contexts or [ ]) From b4ce259b8312b203521dd34a28dbf44417d97bfa Mon Sep 17 00:00:00 2001 From: Dan Gilbert Date: Thu, 9 Jun 2011 16:56:54 -0500 Subject: [PATCH 015/267] Adding example 'Using Arrays to Swap Variables'. --- .../using-arrays-to-swap-variables.textile | 52 +++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 chapters/arrays/using-arrays-to-swap-variables.textile diff --git a/chapters/arrays/using-arrays-to-swap-variables.textile b/chapters/arrays/using-arrays-to-swap-variables.textile new file mode 100644 index 0000000..1049002 --- /dev/null +++ b/chapters/arrays/using-arrays-to-swap-variables.textile @@ -0,0 +1,52 @@ +--- +layout: recipe +title: Using Arrays to Swap Variables +chapter: Arrays +--- + +h1. Using Arrays to Swap Variables + +h2. Problem + +You want to use an array to swap variables. + +h2. Solution + +Use CoffeeScript's "destructuring assignment":http://jashkenas.github.com/coffee-script/#destructuring syntax: + +{% highlight coffeescript %} +a = 1 +b = 3 + +[a, b] = [b, a] + +a +# => 3 + +b +# => 1 +{% endhighlight %} + + +h2. Discussion + +Destructuring assignment allows swapping two values without the use of a temporary variable. + +This can be useful when traversing arrays and ensuring iteration only happens over the shortest one: + +{% highlight coffeescript %} + +ray1 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] +ray2 = [ 5, 9, 14, 20 ] + +intersection = (a, b) -> + [a, b] = [b, a] if a.length > b.length + value for value in a when value in b + +intersection ray1, ray2 +# => [ 5, 9 ] + +intersection ray2, ray1 +# => [ 5, 9 ] + +{% endhighlight %} diff --git a/wanted-recipes.textile b/wanted-recipes.textile index a0c37de..b63aa26 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -28,7 +28,6 @@ h2. Strings h2. Arrays -* Using Arrays to Swap Variables # [x,y] = [y,x] * Reducing arrays to values (ruby inject) with reduce # [1..10].reduce (a,b) -> a+b # 55 * Reducing arrays in reverse order # JS reduceRight {% highlight coffeescript %} From d8d68a50fe42b7f73be3f8ce65ce90c129dd825d Mon Sep 17 00:00:00 2001 From: Sandro Date: Fri, 10 Jun 2011 10:14:42 -0500 Subject: [PATCH 016/267] Adds a template for creating jQuery plugins. --- chapters/jquery/plugin.textile | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 chapters/jquery/plugin.textile diff --git a/chapters/jquery/plugin.textile b/chapters/jquery/plugin.textile new file mode 100644 index 0000000..6293212 --- /dev/null +++ b/chapters/jquery/plugin.textile @@ -0,0 +1,62 @@ +--- +layout: recipe +title: Create a jQuery plugin +chapter: jQuery +--- + +h2. Problem + +You'd like to create jQuery plugin using CoffeeScript + +h2. Solution + +{% highlight coffeescript %} +# Reference jQuery +$ = jQuery + +# Adds plugin object to jQuery +$.fn.extend + # Change pluginName to your plugin's name. + pluginName: (options) -> + # Default settings + settings = + option1: true + option2: false + debug: false + + # Merge default settings with options. + settings = $.extend settings, options + + # Simple logger. + log = (msg) -> + console?.log msg if settings.debug + + # _Insert magic here._ + return @each ()-> + log "Preparing magic show." + # You can use your settings in here now. + log "Option 1 value: #{settings.option1}" +{% endhighlight %} + +h2. Discussion + +h3. Usage + +Here are a couple of examples of how to use your new plugin. + +h4. JavaScript + +{% highlight javascript %} +$("body").pluginName({ + debug: true +}; + +{% endhighlight %} + +h4. CoffeeScript: + +{% highlight coffeescript %} +$("body").pluginName + debug: true + +{% endhighlight %} From ea1106f5875e2dae28de028ddb96c9f64083ecf4 Mon Sep 17 00:00:00 2001 From: Dan Gilbert Date: Fri, 10 Jun 2011 17:26:18 -0500 Subject: [PATCH 017/267] New recipe: splitting a string. --- chapters/strings/splitting-a-string.textile | 37 +++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 chapters/strings/splitting-a-string.textile diff --git a/chapters/strings/splitting-a-string.textile b/chapters/strings/splitting-a-string.textile new file mode 100644 index 0000000..83c1162 --- /dev/null +++ b/chapters/strings/splitting-a-string.textile @@ -0,0 +1,37 @@ +--- +layout: recipe +title: Splitting a String +chapter: Strings +--- + +h2. Problem + +You want to split a string. + +h2. Solution + +Use JavaScript's String split() method: + +{% highlight coffeescript %} +"foo bar baz".split " " +# => [ 'foo', 'bar', 'baz' ] +{% endhighlight %} + +h2. Discussion + +String's split() is a standard JavaScript method. It can be used to split a string on any delimiter, including regular expressions. It also accepts a second parameter that specifies the number of splits to return. + +{% highlight coffeescript %} +"foo-bar-baz".split "-" +# => [ 'foo', 'bar', 'baz' ] +{% endhighlight %} + +{% highlight coffeescript %} +"foo bar \t baz".split /\s+/ +# => [ 'foo', 'bar', 'baz' ] +{% endhighlight %} + +{% highlight coffeescript %} +"the sun goes down and I sit on the old broken-down river pier".split " ", 2 +# => [ 'the', 'sun' ] +{% endhighlight %} diff --git a/wanted-recipes.textile b/wanted-recipes.textile index b63aa26..ea6427a 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -18,7 +18,6 @@ h2. Objects h2. Strings * HTML methods # JS .sup(), .sub(), .blink(), .link(url), etc. May not exist in your JS impl! -* Splitting a string # JS "foo bar baz".split ' ' # => [ 'foo', 'bar', 'baz' ] * substr # str.substr(x,y) === str[x..x+y-1] === str[x...x+y] * substring # str.substring(x,y) === str.slice(x,y) === str[x..y-1] === str[x...y] * Uppercasing a string # JS toUpperCase() From b5637a19244dca14b4d69c2afe938a317f5ef97e Mon Sep 17 00:00:00 2001 From: James Holder Date: Thu, 9 Jun 2011 18:51:40 -0400 Subject: [PATCH 018/267] Add "Chaining Calls to an Object" recipe to Objects chapter. --- chapters/objects/chaining.textile | 76 +++++++++++++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 chapters/objects/chaining.textile diff --git a/chapters/objects/chaining.textile b/chapters/objects/chaining.textile new file mode 100644 index 0000000..8035604 --- /dev/null +++ b/chapters/objects/chaining.textile @@ -0,0 +1,76 @@ +--- +layout: recipe +title: Chaining Calls to an Object +chapter: Objects +--- + +h2. Problem + +You want to call multiple methods on a single object without having to reference that object each time. + +h2. Solution + +Return the *this* (i.e. *@*) object after every chained method. + +{% highlight coffeescript %} +class CoffeeCup + properties: + strength: 'medium' + cream: false + sugar: false + strength: (newStrength) -> + @properties.strength = newStrength + @ + cream: (newCream) -> + @properties.cream = newCream + @ + sugar: (newSugar) -> + @properties.sugar = newSugar + @ + +morningCup = new CoffeeCup() + +morningCup.properties # => { strength: 'medium', cream: false, sugar: false } + +eveningCup = new CoffeeCup().strength('dark').cream(true).sugar(true) + +eveningCup.properties # => { strength: 'dark', cream: true, sugar: true } + +{% endhighlight %} + +h2. Discussion + +The jQuery library uses a similar approach by returning a selector object from every relevant method, modifying it as subsequent methods tweak the selection: + +{% highlight coffeescript %} +$('p').filter('.topic').first() +{% endhighlight %} + +For your own objects, a touch of metaprogramming can automate the setup process and explicitly state the purpose of returning *this*. + +{% highlight coffeescript %} +addChainedAttributeAccessor = (obj, propertyAttr, attr) -> + obj[attr] = (newValues...) -> + if newValues.length == 0 + obj[propertyAttr][attr] + else + obj[propertyAttr][attr] = newValues[0] + obj + +class TeaCup + properties: + size: 'medium' + type: 'black' + sugar: false + cream: false + +addChainedAttributeAccessor(TeaCup.prototype, 'properties', attr) for attr of TeaCup.prototype.properties + +earlgrey = new TeaCup().size('small').type('Earl Grey').sugar('false') + +earlgrey.properties # => { size: 'small', type: 'Earl Grey', sugar: false } + +earlgrey.sugar true + +earlgrey.sugar() # => true +{% endhighlight %} diff --git a/wanted-recipes.textile b/wanted-recipes.textile index b8fa1fc..b01d195 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -13,7 +13,6 @@ h2. Syntax * Ensuring variables are closed over # with "do" h2. Objects -* Chaning the methods of an object h2. Strings From 1ee6471882ca1c25ac75a1280e9ec23d8cab4782 Mon Sep 17 00:00:00 2001 From: James Holder Date: Thu, 9 Jun 2011 23:05:10 -0400 Subject: [PATCH 019/267] Add "AJAX" recipe to the jQuery chapter. --- chapters/jquery/ajax.textile | 43 ++++++++++++++++++++++++++++++++++++ wanted-recipes.textile | 2 -- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 chapters/jquery/ajax.textile diff --git a/chapters/jquery/ajax.textile b/chapters/jquery/ajax.textile new file mode 100644 index 0000000..3885438 --- /dev/null +++ b/chapters/jquery/ajax.textile @@ -0,0 +1,43 @@ +--- +layout: recipe +title: AJAX +chapter: jQuery +--- + +h2. Problem + +You want to make AJAX calls using jQuery. + +h2. Solution + +{% highlight coffeescript %} +$ ?= require 'jquery' # For Node.js compatibility + +$(document).ready -> + # Basic Examples + $.get '/', (data) -> + $('body').append "Successfully got the page." + + $.post '/', + userName: 'John Doe' + favoriteFlavor: 'Mint' + (data) -> $('body').append "Successfully posted to the page." + + # Advanced Settings + $.ajax '/', + type: 'GET' + dataType: 'html' error: (jqXHR, textStatus, errorThrown) -> + $('body').append "AJAX Error: #{textStatus}" + success: (data, textStatus, jqXHR) -> + $('body').append "Successful AJAX call: #{data}" + + # For jQuery 1.5+ + request = $.get '/' + request.success (data) -> $('body').append "Successfully got the page again." + request.error (jqXHR, textStatus, errorThrown) -> $('body').append "AJAX Error: ${textStatus}." + +{% endhighlight %} + +h2. Discussion + +See the "jQuery AJAX API":http://api.jquery.com/jQuery.ajax/ for additional methods and options. diff --git a/wanted-recipes.textile b/wanted-recipes.textile index b01d195..f619528 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -107,8 +107,6 @@ console.log [1,2,3,4,5].map (x) -> h2. jQuery -* jQuery AJAX from CS - h2. Regular Expressions * Searching for substrings # "foo bar baz".match(/ba./) # => [ 'bar', index: 4, input: 'foo bar baz' ] From 7bc9f67e79304a0e76eb18a075bfb6e0b7ffed49 Mon Sep 17 00:00:00 2001 From: David Brady Date: Fri, 10 Jun 2011 23:34:26 -0600 Subject: [PATCH 020/267] Renamed 'Objects' chapter to 'Classes and Objects'; added class-variables recipes --- .../class-variables.textile | 27 +++++++++++++++++++ .../cloning.textile | 2 +- ...create-object-literal-if-not-exist.textile | 2 +- .../index.textile | 4 +-- chapters/index.textile | 2 +- 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 chapters/classes_and_objects/class-variables.textile rename chapters/{objects => classes_and_objects}/cloning.textile (97%) rename chapters/{objects => classes_and_objects}/create-object-literal-if-not-exist.textile (95%) rename chapters/{objects => classes_and_objects}/index.textile (87%) diff --git a/chapters/classes_and_objects/class-variables.textile b/chapters/classes_and_objects/class-variables.textile new file mode 100644 index 0000000..594f9a9 --- /dev/null +++ b/chapters/classes_and_objects/class-variables.textile @@ -0,0 +1,27 @@ +--- +layout: recipe +title: Class Variables +chapter: Classes and Objects +--- + +h2. Creating Class Variables + +You want to create a class variable. + +h2. Solution + +Use json notation in the class body; use :: to access it outside: + +{% highlight coffeescript %} +class Zoo + MAX_ANIMALS: 50 + +Zoo::MAX_ZOOKEEPERS = 5 + +Zoo::MAX_ANIMALS +# => 50 +{% endhighlight %} + +h2. Discussion + +Coffeescript will store these values on the class prototype (e.g. Zoo.prototype.MAX_ANIMALS) rather than on individual object instances, which conserves memory and gives a central location to store class-level values. diff --git a/chapters/objects/cloning.textile b/chapters/classes_and_objects/cloning.textile similarity index 97% rename from chapters/objects/cloning.textile rename to chapters/classes_and_objects/cloning.textile index 389b43b..49b56d9 100644 --- a/chapters/objects/cloning.textile +++ b/chapters/classes_and_objects/cloning.textile @@ -1,7 +1,7 @@ --- layout: recipe title: Cloning an object (deep copy) -chapter: Objects +chapter: Classes and Objects --- h2. Problem diff --git a/chapters/objects/create-object-literal-if-not-exist.textile b/chapters/classes_and_objects/create-object-literal-if-not-exist.textile similarity index 95% rename from chapters/objects/create-object-literal-if-not-exist.textile rename to chapters/classes_and_objects/create-object-literal-if-not-exist.textile index e642240..3cbb5cc 100644 --- a/chapters/objects/create-object-literal-if-not-exist.textile +++ b/chapters/classes_and_objects/create-object-literal-if-not-exist.textile @@ -1,7 +1,7 @@ --- layout: recipe title: Create an object literal if it does not already exist -chapter: Objects +chapter: Classes and Objects --- h2. Problem diff --git a/chapters/objects/index.textile b/chapters/classes_and_objects/index.textile similarity index 87% rename from chapters/objects/index.textile rename to chapters/classes_and_objects/index.textile index 7d979e4..7a2dcea 100644 --- a/chapters/objects/index.textile +++ b/chapters/classes_and_objects/index.textile @@ -1,7 +1,7 @@ --- layout: chapter -title: Objects -chapter: Objects +title: Classes and Objects +chapter: Classes and Objects --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} diff --git a/chapters/index.textile b/chapters/index.textile index bd759b5..f100a9b 100644 --- a/chapters/index.textile +++ b/chapters/index.textile @@ -3,7 +3,7 @@ layout: default title: Cookbook chapters: - Syntax -- Objects +- Classes and Objects - Strings - Arrays - Dates and Times From 27d409b7e9e2d3f4ec9cfdfa38067b6382a7621b Mon Sep 17 00:00:00 2001 From: David Brady Date: Sat, 11 Jun 2011 00:52:21 -0600 Subject: [PATCH 021/267] Fixed bug in random number generator in Generating Predictable Random Numbers --- ...erating-predictable-random-numbers.textile | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/chapters/math/generating-predictable-random-numbers.textile b/chapters/math/generating-predictable-random-numbers.textile index 2980016..5cf9da9 100644 --- a/chapters/math/generating-predictable-random-numbers.textile +++ b/chapters/math/generating-predictable-random-numbers.textile @@ -28,36 +28,21 @@ class Rand @seed = seed # return a random integer 0 <= n < @modulo - randn: + randn: -> # new_seed = (a * seed + c) % m @seed = (@multiplier*@seed + @offset) % @modulo # return a random float 0 <= f < 1.0 - rand: -> + randf: -> this.randn() / @modulo # return a random int 0 <= f < n - randi: (n) -> - Math.floor(this.rand() * n) + rand: (n) -> + Math.floor(this.randf() * n) - r = new Rand 0 -r.randn().toString(16) -# => "3c6ef35f" - -r.randn().toString(16) -# => "47502932" - -r.randn().toString(16) -# => "d1ccf6e9" - -r.randn().toString(16) -# => "aaf95334" - -r.randn().toString(16) -# => "6252e503" - -r.randn().toString(16) -# => "9f2ec686" + # return a random int min <= f < max + rand2: (min, max) -> + min + this.rand(max-min) {% endhighlight %} h2. Discussion From a96c15965adc16b6615d780764f7f006e0d1f578 Mon Sep 17 00:00:00 2001 From: James Holder Date: Fri, 10 Jun 2011 22:53:23 -0400 Subject: [PATCH 022/267] Add "Networking" chapter and server/client recipes. --- chapters/index.textile | 1 + chapters/networking/basic-client.textile | 44 +++++++++++++ chapters/networking/basic-server.textile | 45 +++++++++++++ .../networking/bi-directional-client.textile | 66 +++++++++++++++++++ .../networking/bi-directional-server.textile | 56 ++++++++++++++++ chapters/networking/index.textile | 17 +++++ wanted-recipes.textile | 5 ++ 7 files changed, 234 insertions(+) create mode 100644 chapters/networking/basic-client.textile create mode 100644 chapters/networking/basic-server.textile create mode 100644 chapters/networking/bi-directional-client.textile create mode 100644 chapters/networking/bi-directional-server.textile create mode 100644 chapters/networking/index.textile diff --git a/chapters/index.textile b/chapters/index.textile index bd759b5..6d5adbc 100644 --- a/chapters/index.textile +++ b/chapters/index.textile @@ -13,6 +13,7 @@ chapters: - jQuery - Regular Expressions - AJAX +- Networking - Design Patterns --- diff --git a/chapters/networking/basic-client.textile b/chapters/networking/basic-client.textile new file mode 100644 index 0000000..2909b6e --- /dev/null +++ b/chapters/networking/basic-client.textile @@ -0,0 +1,44 @@ +--- +layout: recipe +title: Basic Client +chapter: Networking +--- + +h2. Problem + +You want to access a service provided over the network. + + +h2. Solution + +Create a basic TCP client. + +h3. Node.js + +{% highlight coffeescript %} +net = require 'net' + +domain = 'localhost' +port = 9001 + +connection = net.createConnection port, domain + +connection.on 'connect', () -> + console.log "Opened connection to #{domain}:#{port}." + +connection.on 'data', (data) -> + console.log "Received: #{data}" + connection.end() +{% endhighlight %} + +h3. Example Usage + +Accessing the Basic Server: + +*$ coffee basic-client.coffee* +Opened connection to localhost:9001 +Received: Hello, World! + +h2. Discussion + +See also the Basic Server, the Bi-Directional Client, and the Bi-Directional Server recipes. diff --git a/chapters/networking/basic-server.textile b/chapters/networking/basic-server.textile new file mode 100644 index 0000000..ff82545 --- /dev/null +++ b/chapters/networking/basic-server.textile @@ -0,0 +1,45 @@ +--- +layout: recipe +title: Basic Server +chapter: Networking +--- + +h2. Problem + +You want to provide a service over a network. + + +h2. Solution + +Create a basic TCP server. + +h3. Node.js + +{% highlight coffeescript %} +net = require 'net' + +domain = 'localhost' +port = 9001 + +server = net.createServer (socket) -> + console.log "Received connection from #{socket.remoteAddress}" + socket.write "Hello, World!\n" + socket.end() + +console.log "Listening to #{domain}:#{port}" +server.listen port, domain +{% endhighlight %} + +h3. Example Usage + +Accessed by the Basic Client: + +*$ coffee basic-server.coffee* +Listening to localhost:9001 +Received connection from 127.0.0.1 +Received connection from 127.0.0.1 +[...] + +h2. Discussion + +See also the Basic Client, the Bi-Directional Server, and the Bi-Directional Client recipes. diff --git a/chapters/networking/bi-directional-client.textile b/chapters/networking/bi-directional-client.textile new file mode 100644 index 0000000..4ffdd85 --- /dev/null +++ b/chapters/networking/bi-directional-client.textile @@ -0,0 +1,66 @@ +--- +layout: recipe +title: Bi-Directional Client +chapter: Networking +--- + +h2. Problem + +You want to access a service that provides a persistent connection over the network. + + +h2. Solution + +Create a bi-directional TCP client. + +h3. Node.js + +{% highlight coffeescript %} +net = require 'net' + +domain = 'localhost' +port = 9001 + +ping = (socket, delay) -> + console.log "Pinging server" + socket.write "Ping" + nextPing = -> ping(socket, delay) + setTimeout nextPing, delay + +connection = net.createConnection port, domain + +connection.on 'connect', () -> + console.log "Opened connection to #{domain}:#{port}" + ping connection, 2000 + +connection.on 'data', (data) -> + console.log "Received: #{data}" + +connection.on 'end', (data) -> + console.log "Connection closed" + process.exit() +{% endhighlight %} + +h3. Example Usage + +Accessing the Bi-Directional Server: + +*$ coffee bi-directional-client.coffee* +Opened connection to localhost:9001 +Pinging server +Received: You have 0 peers on this server +Pinging server +Received: You have 0 peers on this server +Pinging server +Received: You have 0 peers on this server +[...] +Connection closed + +h2. Discussion + +This particular example initiates contact with the server and starts the conversation in the _on 'connect'_ handler. The bulk of the work in a real client, however, will lie in the _on 'data'_ handler, which processes output from the server. + +See also the Bi-Directional Server, the Basic Client, and the Basic Server recipes. + +h3. Exercises +* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-server.textile b/chapters/networking/bi-directional-server.textile new file mode 100644 index 0000000..c3b1d53 --- /dev/null +++ b/chapters/networking/bi-directional-server.textile @@ -0,0 +1,56 @@ +--- +layout: recipe +title: Bi-Directional Server +chapter: Networking +--- + +h2. Problem + +You want to provide a service over a network while maintaining a connection with clients. + + +h2. Solution + +Create a bi-directional TCP server. + +h3. Node.js + +{% highlight coffeescript %} +net = require 'net' + +domain = 'localhost' +port = 9001 + +server = net.createServer (socket) -> + console.log "New connection from #{socket.remoteAddress}" + + socket.on 'data', (data) -> + console.log "#{socket.remoteAddress} sent: #{data}" + others = server.connections - 1 + socket.write "You have #{others} #{others == 1 and "peer" or "peers"} on this server" + +console.log "Listening to #{domain}:#{port}" +server.listen port, domain +{% endhighlight %} + +h3. Example Usage + +Accessed by the Bi-Directional Client: + +*$ coffee bi-directional-server.coffee* +Listening to localhost:9001 +New connection from 127.0.0.1 +127.0.0.1 sent: Ping +127.0.0.1 sent: Ping +127.0.0.1 sent: Ping +[...] + +h2. Discussion + +The bulk of the work lies in the _on 'data'_ handler, which processes all of the input from the client. + +See also the Bi-Directional Client, the Basic Client, and the Basic Server recipes. + +h3. Exercises +* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. + diff --git a/chapters/networking/index.textile b/chapters/networking/index.textile new file mode 100644 index 0000000..cf90e3a --- /dev/null +++ b/chapters/networking/index.textile @@ -0,0 +1,17 @@ +--- +layout: chapter +title: Networking +chapter: Networking +--- + +{% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} +{% capture indexurl %}{{ url }}/index.html{% endcapture %} + +{% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} + * {{ page.title }} + {% endunless %} + {% endif %} +{% endfor %} + diff --git a/wanted-recipes.textile b/wanted-recipes.textile index f619528..fcf8df4 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -114,6 +114,11 @@ h2. Regular Expressions * Replacing substrings # "foo bar baz".replace( /ba./, 'foo') # => "foo foo baz" * Replace HTML tags with named HTML entities #
=> <br/> +h2. Networking + +* Basic HTTP server +* Basic HTTP client + h2. AJAX * Getting data from a remote server # using raw XHTTPRequest instead of jQuery's $.ajax From c1b9cb952ff6b6d269d36752e869270ae3ca40d2 Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 12 Jun 2011 14:06:23 -0400 Subject: [PATCH 023/267] Add "Bridge" recipe to "Design Patterns" chapter. --- chapters/design_patterns/bridge.textile | 55 +++++++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/bridge.textile diff --git a/chapters/design_patterns/bridge.textile b/chapters/design_patterns/bridge.textile new file mode 100644 index 0000000..80be632 --- /dev/null +++ b/chapters/design_patterns/bridge.textile @@ -0,0 +1,55 @@ +--- +layout: recipe +title: Bridge Pattern +chapter: Design Patterns +--- + +h2. Problem + +You need to maintain a reliable interface for code that can change frequently or change between multiple implementations. + +h2. Solution + +Use the Bridge pattern as an intermediate between the different implementations and the rest of the code. + +Assume that you developed an in-browser text editor that saves to the cloud. Now, however, you need to port it to a stand-alone client that saves locally. + +{% highlight coffeescript %} +class TextSaver + constructor: (@filename, @options) -> + save: (data) -> + +class CloudSaver extends TextSaver + constructor: (@filename, @options) -> + super @filename, @options + save: (data) -> + # Assuming jQuery + # Note the fat arrows + $( => + $.post "#{@options.url}/#{@filename}", data, => + alert "Saved '#{data}' to #{@filename} at #{@options.url}." + ) + +class FileSaver extends TextSaver + constructor: (@filename, @options) -> + super @filename, @options + @fs = require 'fs' + save: (data) -> + @fs.writeFile @filename, data, (err) => # Note the fat arrow + if err? then console.log err + else console.log "Saved '#{data}' to #{@filename} in #{@options.directory}." + +filename = "temp.txt" +data = "Example data" + +saver = if window? + new CloudSaver filename, url: 'http://localhost/' # => Saved "Example data" to temp.txt at http://localhost +else if root? + new FileSaver filename, directory: './' # => Saved "Example data" to temp.txt in ./ + +saver.save data +{% endhighlight %} + +h2. Discussion + +The Bridge pattern helps you to move the implementation-specific code out of sight so that you can focus on your program's specific code. In the above example, the rest of your application can call _saver.save data_ without regard for where the file ultimately ends up. diff --git a/wanted-recipes.textile b/wanted-recipes.textile index fcf8df4..7961923 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -133,7 +133,6 @@ h2. Design patterns * Structural Patterns ** Adapter -** Bridge ** Composite ** Facade ** Flyweight From 97d0f404e5903753b277c98af1a85d5cf4400e92 Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 12 Jun 2011 15:36:53 -0400 Subject: [PATCH 024/267] Add "Memento" recipe to the "Design Patterns" chapter. --- chapters/design_patterns/memento.textile | 45 ++++++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/memento.textile diff --git a/chapters/design_patterns/memento.textile b/chapters/design_patterns/memento.textile new file mode 100644 index 0000000..50d9aa2 --- /dev/null +++ b/chapters/design_patterns/memento.textile @@ -0,0 +1,45 @@ +--- +layout: recipe +title: Memento Pattern +chapter: Design Patterns +--- + +h2. Problem + +You want to anticipate the reversion of changes to an object. + +h2. Solution + +Use the "Memento pattern":http://en.wikipedia.org/wiki/Memento_pattern to track changes to an object. The class using the pattern will export a _memento_ object stored elsewhere. + +If you have application where the user can edit a text file, for example, they may want to undo their last action. You can save the current state of the file before the user changes it and then roll back to that at a later point. + +{% highlight coffeescript %} +class PreserveableText + class Memento + constructor: (@text) -> + + constructor: (@text) -> + save: (newText) -> + memento = new Memento @text + @text = newText + memento + restore: (memento) -> + @text = memento.text + +pt = new PreserveableText "The original string" +pt.text # => "The original string" + +memento = pt.save "A new string" +pt.text # => "A new string" + +pt.save "Yet another string" +pt.text # => "Yet another string" + +pt.restore memento +pt.text # => "The original string" +{% endhighlight %} + +h2. Discussion + +The Memento object returned by _PreserveableText#save_ stores the important state information separately for safe-keeping. You could even serialize this Memento in order to maintain an "undo" buffer on the hard disk or remotely for such data-intensive objects as edited images. diff --git a/wanted-recipes.textile b/wanted-recipes.textile index fcf8df4..de36c40 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -145,7 +145,6 @@ h2. Design patterns ** Interpreter ** Iterator ** Mediator -** Memento ** Observer ** State ** Template Method From 78fb5e076fc125178b5c444e29997300ff1f047c Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 12 Jun 2011 16:41:17 -0400 Subject: [PATCH 025/267] Add "Factory Method" recipe to "Design Patterns" chapter. --- .../design_patterns/factory_method.textile | 50 +++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/factory_method.textile diff --git a/chapters/design_patterns/factory_method.textile b/chapters/design_patterns/factory_method.textile new file mode 100644 index 0000000..bb027df --- /dev/null +++ b/chapters/design_patterns/factory_method.textile @@ -0,0 +1,50 @@ +--- +layout: recipe +title: Factory Method Pattern +chapter: Design Patterns +--- + +h2. Problem + +You don't know what kind of object you will need until runtime. + +h2. Solution + +Use the "Factory Method":http://en.wikipedia.org/wiki/Factory_method_pattern pattern and choose the object to be generated dynamically. + +Say that you need to load a file into an editor but you don't know its format until the user chooses the file. A class using the "Factory Method":http://en.wikipedia.org/wiki/Factory_method_pattern pattern can serve up different parsers depending on the file's extension. + +{% highlight coffeescript %} +class HTMLParser + constructor: -> + @type = "HTML parser" +class MarkdownParser + constructor: -> + @type = "Markdown parser" +class JSONParser + constructor: -> + @type = "JSON parser" + +class ParserFactory + makeParser: (filename) -> + matches = filename.match /\.(\w*)$/ + extension = matches[1] + switch extension + when "html" then new HTMLParser + when "htm" then new HTMLParser + when "markdown" then new MarkdownParser + when "md" then new MarkdownParser + when "json" then new JSONParser + +factory = new ParserFactory + +factory.makeParser("example.html").type # => "HTML parser" + +factory.makeParser("example.md").type # => "Markdown parser" + +factory.makeParser("example.json").type # => "JSON parser" +{% endhighlight %} + +h2. Discussion + +In the example, you can ignore the specifics of the file's format and focus on the parsed content. A more advanced Factory Method might, for instance, also search for versioning data within the file itself before returning a more precise parser (e.g. an HTML5 parser instead of an HTML v4 parser). diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 2e54ed8..7521dac 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -127,7 +127,6 @@ h2. Design patterns * Creational Patterns ** Abstract Factory -** Factory Method ** Prototype ** Singleton From 0a9999eb13a2e1fa11274148f7dc732acf969aa7 Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Mon, 13 Jun 2011 00:15:57 -0300 Subject: [PATCH 026/267] add days between dates in date_and_times chapter --- .../days-between-two-dates.textile | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 chapters/dates_and_times/days-between-two-dates.textile diff --git a/chapters/dates_and_times/days-between-two-dates.textile b/chapters/dates_and_times/days-between-two-dates.textile new file mode 100644 index 0000000..7649c81 --- /dev/null +++ b/chapters/dates_and_times/days-between-two-dates.textile @@ -0,0 +1,41 @@ +--- +layout: recipe +title: Get Days Between two Dates +chapter: Dates and Times +--- + +h2. Problem + +You need to find how much seconds minutes, hours, days, months or years has passed between two dates. + +h2. Solution + +Use JavaScript's Date function getTime(). Which provides how much time in miliseconds has passed since 01/01/1970: + +{% highlight coffeescript %} +DAY = 1000 * 60 * 60 * 24 + +d1 = new Date('02/01/2011') +d2 = new Date('02/06/2011') + +days_passed = Math.round((d2.getTime() - d1.getTime()) / DAY) +{% endhighlight %} + +h2. Discussion + +Using miliseconds makes the life easier to avoid overflow mistakes with Dates. So we first calculate how much miliseconds has a day. +Then, given two distincit dates, just get the diference in miliseconds betwen two dates and then divide by how much miliseconds has a +day. It will get you the days between two distinct dates. + +If you'd like to calculate the hours between two dates objects you can do that just by dividing the diference in miliseconds by the +convertion of miliseconds to hours. The same goes to minutes and seconds. + +{% highlight coffeescript %} +HOUR = 1000 * 60 * 60 + +d1 = new Date('02/01/2011 02:20') +d2 = new Date('02/06/2011 05:20') + +hour_passed = Math.round((d2.getTime() - d1.getTime()) / HOUR) +{% endhighlight %} + From 98922788776e9ff5afd442790e9195151853ed39 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 15 Jun 2011 18:17:06 -0400 Subject: [PATCH 027/267] Add default style to the and tags in order to distinguish them from regular text. --- css/default.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/css/default.css b/css/default.css index 2ec21ea..d12653a 100644 --- a/css/default.css +++ b/css/default.css @@ -10,6 +10,14 @@ margin: 0; } +em { + font-style: italic; +} + +code { + font-family: courier; +} + article, header, section, footer { display: block; } From a5f160c482bbf0225ac00384d778b16e0fc89fd9 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 15 Jun 2011 18:17:31 -0400 Subject: [PATCH 028/267] Editorial changes to "Network" recipes. --- chapters/networking/basic-client.textile | 13 ++++++++++--- chapters/networking/basic-server.textile | 13 ++++++++++--- chapters/networking/bi-directional-client.textile | 15 ++++++++------- chapters/networking/bi-directional-server.textile | 15 ++++++++------- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/chapters/networking/basic-client.textile b/chapters/networking/basic-client.textile index 2909b6e..34cf594 100644 --- a/chapters/networking/basic-client.textile +++ b/chapters/networking/basic-client.textile @@ -13,7 +13,7 @@ h2. Solution Create a basic TCP client. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -35,10 +35,17 @@ h3. Example Usage Accessing the Basic Server: -*$ coffee basic-client.coffee* +{% highlight console %} +$ coffee basic-client.coffee Opened connection to localhost:9001 Received: Hello, World! +{% endhighlight %} h2. Discussion -See also the Basic Server, the Bi-Directional Client, and the Bi-Directional Server recipes. +The most important work takes place in the _connection.on 'data'_ handler, where the client receives its response from the server and would most likely arrange for responses to it. + +See also the Basic Server, Bi-Directional Client, and Bi-Directional Server recipes. + +h3. Exercises +* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/basic-server.textile b/chapters/networking/basic-server.textile index ff82545..16ae3fd 100644 --- a/chapters/networking/basic-server.textile +++ b/chapters/networking/basic-server.textile @@ -13,7 +13,7 @@ h2. Solution Create a basic TCP server. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -34,12 +34,19 @@ h3. Example Usage Accessed by the Basic Client: -*$ coffee basic-server.coffee* +{% highlight console %} +$ coffee basic-server.coffee Listening to localhost:9001 Received connection from 127.0.0.1 Received connection from 127.0.0.1 [...] +{% endhighlight %} h2. Discussion -See also the Basic Client, the Bi-Directional Server, and the Bi-Directional Client recipes. +The function passed to @net.createServer@ receives the new socket provided for each new connection to a client. This basic server simply socializes with its visitors but a hard-working server would pass this socket along to a dedicated handler and then return to the task of waiting for the next client. + +See also the Basic Client, Bi-Directional Server, and Bi-Directional Client recipes. + +h3. Exercises +* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-client.textile b/chapters/networking/bi-directional-client.textile index 4ffdd85..7b41d4e 100644 --- a/chapters/networking/bi-directional-client.textile +++ b/chapters/networking/bi-directional-client.textile @@ -6,14 +6,13 @@ chapter: Networking h2. Problem -You want to access a service that provides a persistent connection over the network. - +You want to a persistent service over a network, one which maintains an on-going connection with its clients. h2. Solution Create a bi-directional TCP client. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -45,22 +44,24 @@ h3. Example Usage Accessing the Bi-Directional Server: -*$ coffee bi-directional-client.coffee* +{% highlight console %} +$ coffee bi-directional-client.coffee Opened connection to localhost:9001 Pinging server Received: You have 0 peers on this server Pinging server Received: You have 0 peers on this server Pinging server -Received: You have 0 peers on this server +Received: You have 1 peer on this server [...] Connection closed +{% endhighlight %} h2. Discussion -This particular example initiates contact with the server and starts the conversation in the _on 'connect'_ handler. The bulk of the work in a real client, however, will lie in the _on 'data'_ handler, which processes output from the server. +This particular example initiates contact with the server and starts the conversation in the @connection.on 'connect'@ handler. The bulk of the work in a real client, however, will lie in the @connection.on 'data'@ handler, which processes output from the server. The @ping@ function only recurses in order to illustrate continuous communication with the server and can be removed from a real client. -See also the Bi-Directional Server, the Basic Client, and the Basic Server recipes. +See also the Bi-Directional Server, Basic Client, and Basic Server recipes. h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-server.textile b/chapters/networking/bi-directional-server.textile index c3b1d53..e3d9201 100644 --- a/chapters/networking/bi-directional-server.textile +++ b/chapters/networking/bi-directional-server.textile @@ -6,14 +6,13 @@ chapter: Networking h2. Problem -You want to provide a service over a network while maintaining a connection with clients. - +You want to provide a persistent service over a network, one which maintains an on-going connection with a client. h2. Solution Create a bi-directional TCP server. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -37,20 +36,22 @@ h3. Example Usage Accessed by the Bi-Directional Client: -*$ coffee bi-directional-server.coffee* +{% highlight console %} +$ coffee bi-directional-server.coffee Listening to localhost:9001 New connection from 127.0.0.1 127.0.0.1 sent: Ping 127.0.0.1 sent: Ping 127.0.0.1 sent: Ping [...] +{% endhighlight %} h2. Discussion -The bulk of the work lies in the _on 'data'_ handler, which processes all of the input from the client. +The bulk of the work lies in the @socket.on 'data'@ handler, which processes all of the input from the client. A real server would likely pass the data onto another function to process it and generate any responses so that the original handler. -See also the Bi-Directional Client, the Basic Client, and the Basic Server recipes. +See also the Bi-Directional Client, Basic Client, and Basic Server recipes. h3. Exercises -* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. +* Add support for choosing the target domain and port based on command-line arguments or on a configuration file. From 16f4e14cf37d93d7068fe4be23ad685e4d0096b1 Mon Sep 17 00:00:00 2001 From: Jesse House Date: Sat, 28 May 2011 15:38:34 -0700 Subject: [PATCH 029/267] new recipe: create object literal if it does not exist --- ...create-object-literal-if-not-exist.textile | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 chapters/objects/create-object-literal-if-not-exist.textile diff --git a/chapters/objects/create-object-literal-if-not-exist.textile b/chapters/objects/create-object-literal-if-not-exist.textile new file mode 100644 index 0000000..e642240 --- /dev/null +++ b/chapters/objects/create-object-literal-if-not-exist.textile @@ -0,0 +1,29 @@ +--- +layout: recipe +title: Create an object literal if it does not already exist +chapter: Objects +--- + +h2. Problem + +You want to initialize an object literal, but you do not want to overwrite the object if it already exists. + + +h2. Solution + +Use the Existential operator + +{% highlight coffeescript %} +window.MY_NAMESPACE ?= {} +{% endhighlight %} + + +h2. Discussion + +This is equivalent to the following JavaScript: + +{% highlight javascript %} +window.MY_NAMESPACE = window.MY_NAMESPACE || {}; +{% endhighlight %} + +Common JavaScript technique, using object literal to define a namespace. This saves us from clobbering the namespace if it already exists. From b8b1e70cdb74d759025c8431e4d1376c977a4fc7 Mon Sep 17 00:00:00 2001 From: Jesse House Date: Sat, 28 May 2011 15:40:04 -0700 Subject: [PATCH 030/267] new recipe: jquery ajax --- chapters/jquery/ajax.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/jquery/ajax.textile b/chapters/jquery/ajax.textile index 3885438..d9e25b1 100644 --- a/chapters/jquery/ajax.textile +++ b/chapters/jquery/ajax.textile @@ -40,4 +40,4 @@ $(document).ready -> h2. Discussion -See the "jQuery AJAX API":http://api.jquery.com/jQuery.ajax/ for additional methods and options. +The jQuery and $ variables can be used interchangeably. See also "Callback bindings":../jquery/callback-bindings-jquery. From fe7f89861b2843d0249d7bd507988830f71fa90b Mon Sep 17 00:00:00 2001 From: David Brady Date: Fri, 10 Jun 2011 23:34:26 -0600 Subject: [PATCH 031/267] Renamed 'Objects' chapter to 'Classes and Objects'; added class-variables recipes --- .../class-variables.textile | 27 +++++++++++++++++++ .../cloning.textile | 2 +- ...create-object-literal-if-not-exist.textile | 2 +- .../index.textile | 4 +-- chapters/index.textile | 2 +- 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 chapters/classes_and_objects/class-variables.textile rename chapters/{objects => classes_and_objects}/cloning.textile (97%) rename chapters/{objects => classes_and_objects}/create-object-literal-if-not-exist.textile (95%) rename chapters/{objects => classes_and_objects}/index.textile (87%) diff --git a/chapters/classes_and_objects/class-variables.textile b/chapters/classes_and_objects/class-variables.textile new file mode 100644 index 0000000..594f9a9 --- /dev/null +++ b/chapters/classes_and_objects/class-variables.textile @@ -0,0 +1,27 @@ +--- +layout: recipe +title: Class Variables +chapter: Classes and Objects +--- + +h2. Creating Class Variables + +You want to create a class variable. + +h2. Solution + +Use json notation in the class body; use :: to access it outside: + +{% highlight coffeescript %} +class Zoo + MAX_ANIMALS: 50 + +Zoo::MAX_ZOOKEEPERS = 5 + +Zoo::MAX_ANIMALS +# => 50 +{% endhighlight %} + +h2. Discussion + +Coffeescript will store these values on the class prototype (e.g. Zoo.prototype.MAX_ANIMALS) rather than on individual object instances, which conserves memory and gives a central location to store class-level values. diff --git a/chapters/objects/cloning.textile b/chapters/classes_and_objects/cloning.textile similarity index 97% rename from chapters/objects/cloning.textile rename to chapters/classes_and_objects/cloning.textile index 389b43b..49b56d9 100644 --- a/chapters/objects/cloning.textile +++ b/chapters/classes_and_objects/cloning.textile @@ -1,7 +1,7 @@ --- layout: recipe title: Cloning an object (deep copy) -chapter: Objects +chapter: Classes and Objects --- h2. Problem diff --git a/chapters/objects/create-object-literal-if-not-exist.textile b/chapters/classes_and_objects/create-object-literal-if-not-exist.textile similarity index 95% rename from chapters/objects/create-object-literal-if-not-exist.textile rename to chapters/classes_and_objects/create-object-literal-if-not-exist.textile index e642240..3cbb5cc 100644 --- a/chapters/objects/create-object-literal-if-not-exist.textile +++ b/chapters/classes_and_objects/create-object-literal-if-not-exist.textile @@ -1,7 +1,7 @@ --- layout: recipe title: Create an object literal if it does not already exist -chapter: Objects +chapter: Classes and Objects --- h2. Problem diff --git a/chapters/objects/index.textile b/chapters/classes_and_objects/index.textile similarity index 87% rename from chapters/objects/index.textile rename to chapters/classes_and_objects/index.textile index 7d979e4..7a2dcea 100644 --- a/chapters/objects/index.textile +++ b/chapters/classes_and_objects/index.textile @@ -1,7 +1,7 @@ --- layout: chapter -title: Objects -chapter: Objects +title: Classes and Objects +chapter: Classes and Objects --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} diff --git a/chapters/index.textile b/chapters/index.textile index 6d5adbc..8e93be3 100644 --- a/chapters/index.textile +++ b/chapters/index.textile @@ -3,7 +3,7 @@ layout: default title: Cookbook chapters: - Syntax -- Objects +- Classes and Objects - Strings - Arrays - Dates and Times From 0ca1a7fa8f65fe8a4bd680ae2effcd17b77fc3f9 Mon Sep 17 00:00:00 2001 From: David Brady Date: Sat, 11 Jun 2011 00:52:21 -0600 Subject: [PATCH 032/267] Fixed bug in random number generator in Generating Predictable Random Numbers --- ...erating-predictable-random-numbers.textile | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/chapters/math/generating-predictable-random-numbers.textile b/chapters/math/generating-predictable-random-numbers.textile index 2980016..5cf9da9 100644 --- a/chapters/math/generating-predictable-random-numbers.textile +++ b/chapters/math/generating-predictable-random-numbers.textile @@ -28,36 +28,21 @@ class Rand @seed = seed # return a random integer 0 <= n < @modulo - randn: + randn: -> # new_seed = (a * seed + c) % m @seed = (@multiplier*@seed + @offset) % @modulo # return a random float 0 <= f < 1.0 - rand: -> + randf: -> this.randn() / @modulo # return a random int 0 <= f < n - randi: (n) -> - Math.floor(this.rand() * n) + rand: (n) -> + Math.floor(this.randf() * n) - r = new Rand 0 -r.randn().toString(16) -# => "3c6ef35f" - -r.randn().toString(16) -# => "47502932" - -r.randn().toString(16) -# => "d1ccf6e9" - -r.randn().toString(16) -# => "aaf95334" - -r.randn().toString(16) -# => "6252e503" - -r.randn().toString(16) -# => "9f2ec686" + # return a random int min <= f < max + rand2: (min, max) -> + min + this.rand(max-min) {% endhighlight %} h2. Discussion From 9025f57d2fade696bfc9ff6b5cedec47ed5082c3 Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 12 Jun 2011 15:36:53 -0400 Subject: [PATCH 033/267] Add "Memento" recipe to the "Design Patterns" chapter. --- chapters/design_patterns/memento.textile | 45 ++++++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/memento.textile diff --git a/chapters/design_patterns/memento.textile b/chapters/design_patterns/memento.textile new file mode 100644 index 0000000..50d9aa2 --- /dev/null +++ b/chapters/design_patterns/memento.textile @@ -0,0 +1,45 @@ +--- +layout: recipe +title: Memento Pattern +chapter: Design Patterns +--- + +h2. Problem + +You want to anticipate the reversion of changes to an object. + +h2. Solution + +Use the "Memento pattern":http://en.wikipedia.org/wiki/Memento_pattern to track changes to an object. The class using the pattern will export a _memento_ object stored elsewhere. + +If you have application where the user can edit a text file, for example, they may want to undo their last action. You can save the current state of the file before the user changes it and then roll back to that at a later point. + +{% highlight coffeescript %} +class PreserveableText + class Memento + constructor: (@text) -> + + constructor: (@text) -> + save: (newText) -> + memento = new Memento @text + @text = newText + memento + restore: (memento) -> + @text = memento.text + +pt = new PreserveableText "The original string" +pt.text # => "The original string" + +memento = pt.save "A new string" +pt.text # => "A new string" + +pt.save "Yet another string" +pt.text # => "Yet another string" + +pt.restore memento +pt.text # => "The original string" +{% endhighlight %} + +h2. Discussion + +The Memento object returned by _PreserveableText#save_ stores the important state information separately for safe-keeping. You could even serialize this Memento in order to maintain an "undo" buffer on the hard disk or remotely for such data-intensive objects as edited images. diff --git a/wanted-recipes.textile b/wanted-recipes.textile index fcf8df4..de36c40 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -145,7 +145,6 @@ h2. Design patterns ** Interpreter ** Iterator ** Mediator -** Memento ** Observer ** State ** Template Method From f7bb04b8c1d7b59e2e465ac4d6b04f3ba97b311b Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 12 Jun 2011 14:06:23 -0400 Subject: [PATCH 034/267] Add "Bridge" recipe to "Design Patterns" chapter. --- chapters/design_patterns/bridge.textile | 55 +++++++++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/bridge.textile diff --git a/chapters/design_patterns/bridge.textile b/chapters/design_patterns/bridge.textile new file mode 100644 index 0000000..80be632 --- /dev/null +++ b/chapters/design_patterns/bridge.textile @@ -0,0 +1,55 @@ +--- +layout: recipe +title: Bridge Pattern +chapter: Design Patterns +--- + +h2. Problem + +You need to maintain a reliable interface for code that can change frequently or change between multiple implementations. + +h2. Solution + +Use the Bridge pattern as an intermediate between the different implementations and the rest of the code. + +Assume that you developed an in-browser text editor that saves to the cloud. Now, however, you need to port it to a stand-alone client that saves locally. + +{% highlight coffeescript %} +class TextSaver + constructor: (@filename, @options) -> + save: (data) -> + +class CloudSaver extends TextSaver + constructor: (@filename, @options) -> + super @filename, @options + save: (data) -> + # Assuming jQuery + # Note the fat arrows + $( => + $.post "#{@options.url}/#{@filename}", data, => + alert "Saved '#{data}' to #{@filename} at #{@options.url}." + ) + +class FileSaver extends TextSaver + constructor: (@filename, @options) -> + super @filename, @options + @fs = require 'fs' + save: (data) -> + @fs.writeFile @filename, data, (err) => # Note the fat arrow + if err? then console.log err + else console.log "Saved '#{data}' to #{@filename} in #{@options.directory}." + +filename = "temp.txt" +data = "Example data" + +saver = if window? + new CloudSaver filename, url: 'http://localhost/' # => Saved "Example data" to temp.txt at http://localhost +else if root? + new FileSaver filename, directory: './' # => Saved "Example data" to temp.txt in ./ + +saver.save data +{% endhighlight %} + +h2. Discussion + +The Bridge pattern helps you to move the implementation-specific code out of sight so that you can focus on your program's specific code. In the above example, the rest of your application can call _saver.save data_ without regard for where the file ultimately ends up. diff --git a/wanted-recipes.textile b/wanted-recipes.textile index de36c40..2e54ed8 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -133,7 +133,6 @@ h2. Design patterns * Structural Patterns ** Adapter -** Bridge ** Composite ** Facade ** Flyweight From ff8c1ee3c71cd4c05a6b29711b2f1c5bfa30e96d Mon Sep 17 00:00:00 2001 From: James Holder Date: Mon, 13 Jun 2011 23:06:37 -0400 Subject: [PATCH 035/267] Add "Interpreter" recipe to "Design Patterns" chapter. --- chapters/design_patterns/interpreter.textile | 144 +++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/interpreter.textile diff --git a/chapters/design_patterns/interpreter.textile b/chapters/design_patterns/interpreter.textile new file mode 100644 index 0000000..8d4e49e --- /dev/null +++ b/chapters/design_patterns/interpreter.textile @@ -0,0 +1,144 @@ +--- +layout: recipe +title: Interpreter Pattern +chapter: Design Patterns +--- + +h2. Problem + +Someone else needs to run parts of your code in a controlled fashion. Alternately, your language of choice cannot express the problem domain in a concise fashion. + +h2. Solution + +Use the Interpreter pattern to create a domain-specific language that you translate into specific code. + +Assume, for example, that the user wants to perform math inside of your application. You could let them forward code to _eval_ but that would let them run arbitrary code. Instead, you can provide a miniature "stack calculator" language that you parse separately in order to only run mathematical operations while reporting more useful error messages. + +{% highlight coffeescript %} +class StackCalculator + parseString: (string) -> + @stack = [ ] + for token in string.split /\s+/ + @parseToken token + + if @stack.length > 1 + throw "Not enough operators: numbers left over" + else + @stack[0] + + parseToken: (token, lastNumber) -> + if isNaN parseFloat(token) # Assume that anything other than a number is an operator + @parseOperator token + else + @stack.push parseFloat(token) + + parseOperator: (operator) -> + if @stack.length < 2 + throw "Can't operate on a stack without at least 2 items" + + right = @stack.pop() + left = @stack.pop() + + result = switch operator + when "+" then left + right + when "-" then left - right + when "*" then left * right + when "/" + if right is 0 + throw "Can't divide by 0" + else + left / right + else + throw "Unrecognized operator: #{operator}" + + @stack.push result + +calc = new StackCalculator + +calc.parseString "5 5 +" # => { result: 10 } + +calc.parseString "4.0 5.5 +" # => { result: 9.5 } + +calc.parseString "5 5 + 5 5 + *" # => { result: 100 } + +try + calc.parseString "5 0 /" +catch error + error # => "Can't divide by 0" + +try + calc.parseString "5 -" +catch error + error # => "Can't operate on a stack without at least 2 items" + +try + calc.parseString "5 5 5 -" +catch error + error # => "Not enough operators: numbers left over" + +try + calc.parseString "5 5 5 foo" +catch error + error # => "Unrecognized operator: foo" +{% endhighlight %} + +h2. Discussion + +As an alternative to writing our own interpreter, you can co-op the existing CoffeeScript interpreter in a such a way that its normal syntax makes for more natural (and therefore more comprehensible) expressions of your algorithm. + +{% highlight coffeescript %} +class Sandwich + constructor: (@customer, @bread='white', @toppings=[], @toasted=false)-> + +white = (sw) -> + sw.bread = 'white' + sw + +wheat = (sw) -> + sw.bread = 'wheat' + sw + +turkey = (sw) -> + sw.toppings.push 'turkey' + sw + +ham = (sw) -> + sw.toppings.push 'ham' + sw + +swiss = (sw) -> + sw.toppings.push 'swiss' + sw + +mayo = (sw) -> + sw.toppings.push 'mayo' + sw + +toasted = (sw) -> + sw.toasted = true + sw + +sandwich = (customer) -> + new Sandwich customer + +to = (customer) -> + customer + +send = (sw) -> + toastedState = sw.toasted and 'a toasted' or 'an untoasted' + + toppingState = '' + if sw.toppings.length > 0 + if sw.toppings.length > 1 + toppingState = " with #{sw.toppings[0..sw.toppings.length-2].join ', '} and #{sw.toppings[sw.toppings.length-1]}" + else + toppingState = " with #{sw.toppings[0]}" + "#{sw.customer} requested #{toastedState}, #{sw.bread} bread sandwich#{toppingState}" + +send sandwich to 'Charlie' # => "Charlie requested an untoasted, white bread sandwich" +send turkey sandwich to 'Judy' # => "Judy requested an untoasted, white bread sandwich with turkey" +send toasted ham turkey sandwich to 'Rachel' # => "Rachel requested a toasted, white bread sandwich with turkey and ham" +send toasted turkey ham swiss sandwich to 'Matt' # => "Matt requested a toasted, white bread sandwich with swiss, ham and turkey" +{% endhighlight %} + +This example allows for layers of functions by how it returns the modified object so that outer functions can modify it in turn. By borrowing a very and the particle _to_, the example lends natural grammar to the construction and ends up reading like an actual sentence when used correctly. This way, both your CoffeeScript skills and your existing language skills can help catch code problems. diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 2e54ed8..fee724a 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -141,7 +141,6 @@ h2. Design patterns * Behavioral Patterns ** Chain of Responsibility ** Command -** Interpreter ** Iterator ** Mediator ** Observer From bfdb067af97f3fb0bc5e2fc7de28d622a52020d6 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 15 Jun 2011 18:17:06 -0400 Subject: [PATCH 036/267] Add default style to the and tags in order to distinguish them from regular text. --- css/default.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/css/default.css b/css/default.css index 2ec21ea..d12653a 100644 --- a/css/default.css +++ b/css/default.css @@ -10,6 +10,14 @@ margin: 0; } +em { + font-style: italic; +} + +code { + font-family: courier; +} + article, header, section, footer { display: block; } From 5e6a08440fc9569135dcf740742fb6fbde146b03 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 15 Jun 2011 18:17:31 -0400 Subject: [PATCH 037/267] Editorial changes to "Network" recipes. --- chapters/networking/basic-client.textile | 13 ++++++++++--- chapters/networking/basic-server.textile | 13 ++++++++++--- chapters/networking/bi-directional-client.textile | 15 ++++++++------- chapters/networking/bi-directional-server.textile | 15 ++++++++------- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/chapters/networking/basic-client.textile b/chapters/networking/basic-client.textile index 2909b6e..34cf594 100644 --- a/chapters/networking/basic-client.textile +++ b/chapters/networking/basic-client.textile @@ -13,7 +13,7 @@ h2. Solution Create a basic TCP client. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -35,10 +35,17 @@ h3. Example Usage Accessing the Basic Server: -*$ coffee basic-client.coffee* +{% highlight console %} +$ coffee basic-client.coffee Opened connection to localhost:9001 Received: Hello, World! +{% endhighlight %} h2. Discussion -See also the Basic Server, the Bi-Directional Client, and the Bi-Directional Server recipes. +The most important work takes place in the _connection.on 'data'_ handler, where the client receives its response from the server and would most likely arrange for responses to it. + +See also the Basic Server, Bi-Directional Client, and Bi-Directional Server recipes. + +h3. Exercises +* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/basic-server.textile b/chapters/networking/basic-server.textile index ff82545..16ae3fd 100644 --- a/chapters/networking/basic-server.textile +++ b/chapters/networking/basic-server.textile @@ -13,7 +13,7 @@ h2. Solution Create a basic TCP server. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -34,12 +34,19 @@ h3. Example Usage Accessed by the Basic Client: -*$ coffee basic-server.coffee* +{% highlight console %} +$ coffee basic-server.coffee Listening to localhost:9001 Received connection from 127.0.0.1 Received connection from 127.0.0.1 [...] +{% endhighlight %} h2. Discussion -See also the Basic Client, the Bi-Directional Server, and the Bi-Directional Client recipes. +The function passed to @net.createServer@ receives the new socket provided for each new connection to a client. This basic server simply socializes with its visitors but a hard-working server would pass this socket along to a dedicated handler and then return to the task of waiting for the next client. + +See also the Basic Client, Bi-Directional Server, and Bi-Directional Client recipes. + +h3. Exercises +* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-client.textile b/chapters/networking/bi-directional-client.textile index 4ffdd85..7b41d4e 100644 --- a/chapters/networking/bi-directional-client.textile +++ b/chapters/networking/bi-directional-client.textile @@ -6,14 +6,13 @@ chapter: Networking h2. Problem -You want to access a service that provides a persistent connection over the network. - +You want to a persistent service over a network, one which maintains an on-going connection with its clients. h2. Solution Create a bi-directional TCP client. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -45,22 +44,24 @@ h3. Example Usage Accessing the Bi-Directional Server: -*$ coffee bi-directional-client.coffee* +{% highlight console %} +$ coffee bi-directional-client.coffee Opened connection to localhost:9001 Pinging server Received: You have 0 peers on this server Pinging server Received: You have 0 peers on this server Pinging server -Received: You have 0 peers on this server +Received: You have 1 peer on this server [...] Connection closed +{% endhighlight %} h2. Discussion -This particular example initiates contact with the server and starts the conversation in the _on 'connect'_ handler. The bulk of the work in a real client, however, will lie in the _on 'data'_ handler, which processes output from the server. +This particular example initiates contact with the server and starts the conversation in the @connection.on 'connect'@ handler. The bulk of the work in a real client, however, will lie in the @connection.on 'data'@ handler, which processes output from the server. The @ping@ function only recurses in order to illustrate continuous communication with the server and can be removed from a real client. -See also the Bi-Directional Server, the Basic Client, and the Basic Server recipes. +See also the Bi-Directional Server, Basic Client, and Basic Server recipes. h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-server.textile b/chapters/networking/bi-directional-server.textile index c3b1d53..e3d9201 100644 --- a/chapters/networking/bi-directional-server.textile +++ b/chapters/networking/bi-directional-server.textile @@ -6,14 +6,13 @@ chapter: Networking h2. Problem -You want to provide a service over a network while maintaining a connection with clients. - +You want to provide a persistent service over a network, one which maintains an on-going connection with a client. h2. Solution Create a bi-directional TCP server. -h3. Node.js +h3. In Node.js {% highlight coffeescript %} net = require 'net' @@ -37,20 +36,22 @@ h3. Example Usage Accessed by the Bi-Directional Client: -*$ coffee bi-directional-server.coffee* +{% highlight console %} +$ coffee bi-directional-server.coffee Listening to localhost:9001 New connection from 127.0.0.1 127.0.0.1 sent: Ping 127.0.0.1 sent: Ping 127.0.0.1 sent: Ping [...] +{% endhighlight %} h2. Discussion -The bulk of the work lies in the _on 'data'_ handler, which processes all of the input from the client. +The bulk of the work lies in the @socket.on 'data'@ handler, which processes all of the input from the client. A real server would likely pass the data onto another function to process it and generate any responses so that the original handler. -See also the Bi-Directional Client, the Basic Client, and the Basic Server recipes. +See also the Bi-Directional Client, Basic Client, and Basic Server recipes. h3. Exercises -* Add support for choosing the target domain and port based on command-line arguments or from a configuration file. +* Add support for choosing the target domain and port based on command-line arguments or on a configuration file. From a7787f4ee7af80f53e34a4dffcf69d3ceaa3668b Mon Sep 17 00:00:00 2001 From: James Holder Date: Thu, 16 Jun 2011 23:01:15 -0400 Subject: [PATCH 038/267] Fix botched merge of "jQuery/AJAX" recipe. --- chapters/jquery/ajax.textile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chapters/jquery/ajax.textile b/chapters/jquery/ajax.textile index 3a4f622..2f9f561 100644 --- a/chapters/jquery/ajax.textile +++ b/chapters/jquery/ajax.textile @@ -22,10 +22,7 @@ $(document).ready -> userName: 'John Doe' favoriteFlavor: 'Mint' (data) -> $('body').append "Successfully posted to the page." -{% endhighlight %} -jQuery 1.5 and later have added a new, supplimental API for handling different callbacks. - # Advanced Settings $.ajax '/', type: 'GET' @@ -34,11 +31,14 @@ jQuery 1.5 and later have added a new, supplimental API for handling different c success: (data, textStatus, jqXHR) -> $('body').append "Successful AJAX call: #{data}" - # For jQuery 1.5+ +{% endhighlight %} + +jQuery 1.5 and later have added a new, supplimental API for handling different callbacks. + +{% highlight coffeescript %} request = $.get '/' request.success (data) -> $('body').append "Successfully got the page again." request.error (jqXHR, textStatus, errorThrown) -> $('body').append "AJAX Error: ${textStatus}." - {% endhighlight %} h2. Discussion From d115f0cdb120390d6193e4b40e36c105f7f71d05 Mon Sep 17 00:00:00 2001 From: James Holder Date: Fri, 17 Jun 2011 19:36:28 -0400 Subject: [PATCH 039/267] Add "Functions/Recursive Functions" recipe. --- chapters/functions/recursion.textile | 33 ++++++++++++++++++++++++++++ wanted-recipes.textile | 8 ------- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 chapters/functions/recursion.textile diff --git a/chapters/functions/recursion.textile b/chapters/functions/recursion.textile new file mode 100644 index 0000000..3fcad09 --- /dev/null +++ b/chapters/functions/recursion.textile @@ -0,0 +1,33 @@ +--- +layout: recipe +title: Recursive Functions +chapter: Functions +--- + +h2. Problem + +You want to call a function from within that same function. + +h2. Solution + +With a named function: + +{% highlight coffeescript %} +ping = -> + console.log "Pinged" + setTimeout ping, 1000 +{% endhighlight %} + +With an unnamed function, using @arguments.callee@: + +{% highlight coffeescript %} +delay = 1000 + +setTimeout((-> + console.log "Pinged" + setTimeout arguments.callee, delay + ), delay) +{% endhighlight %} + +h2. Discussion + diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 994cf6b..3103232 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -94,14 +94,6 @@ foo 1, 2, 3 {% endhighlight %} * Variable arguments with splats -* Recursion of unnamed functions with arguments.callee - -{% highlight coffeescript %} -console.log [1,2,3,4,5].map (x) -> - if x <= 1 then return 1 - return x * arguments.callee(x-1) -# => [1, 2, 6, 24, 120 ] -{% endhighlight %} h2. jQuery From cbaf2a6714da727e833d1bf70d5f989f93d536a6 Mon Sep 17 00:00:00 2001 From: Jason Giedymin Date: Sun, 19 Jun 2011 10:31:23 -0400 Subject: [PATCH 040/267] - Added section for fast Fibonacci. --- chapters/math/fast-fibonacci.textile | 103 +++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 chapters/math/fast-fibonacci.textile diff --git a/chapters/math/fast-fibonacci.textile b/chapters/math/fast-fibonacci.textile new file mode 100644 index 0000000..3df3240 --- /dev/null +++ b/chapters/math/fast-fibonacci.textile @@ -0,0 +1,103 @@ +--- +layout: recipe +title: Converting Radians and Degrees +chapter: Math +--- + +h2. Problem + +You would like to calculate a number N in the Fibonacci sequence but want +to do it quickly. + +h2. Solution + +The following solution (which can still be improved on) was originally +talked about on Robin Houston's blog. + +Here are a few links talking about the algorithm and ways to improve it: +# http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/ +# http://www.math.rutgers.edu/~erowland/fibonacci.html +# http://jsfromhell.com/classes/bignumber +# http://www.math.rutgers.edu/~erowland/fibonacci.html +# http://bigintegers.blogspot.com/2010/11/square-division-power-square-root.html +# http://bugs.python.org/issue3451 + +This code is in gist form here: +https://gist.github.com/1032685 + +{% highlight coffeescript %} +### +Author: Jason Giedymin + http://www.jasongiedymin.com + https://github.com/JasonGiedymin + +This CoffeeScript Javascript Fast Fibonacci code is +based on the python code from Robin Houston's blog. +See below links. + +A few things I want to introduce in time are implementions of +Newtonian, Burnikel / Ziegler, and Binet's algorithms on top +of a Big Number framework. + +Todo: +- https://github.com/substack/node-bigint +- BZ and Newton mods. +- Timing + + +### + +MAXIMUM_JS_FIB_N = 1476 + +fib_bits = (n) -> + #Represent an integer as an array of binary digits. + + bits = [] + while n > 0 + [n, bit] = divmodBasic(n, 2) + bits.push(bit) + + bits.reverse() + return bits + +fibFast = (n) -> + #Fast Fibonacci + + if n < 0 + console.log "Choose an number >= 0" + return + + [a, b, c] = [1, 0, 1] + + for bit in fib_bits(n) + if bit + [a, b] = [(a+c)*b, b*b + c*c] + else + [a, b] = [a*a + b*b, (a+c)*b] + + c = a + b + return b + +divmodNewton = (x, y) -> + throw new Error "Method not yet implemented yet." + +divmodBZ = () -> + throw new Error "Method not yet implemented yet." + +divmodBasic = (x, y) -> + ### + Absolutely nothing special here. Maybe later versions will be Newtonian or + Burnikel / Ziegler _if_ possible... + ### + + return [(q = Math.floor(x/y)), (r = if x < y then x else x % y)] + +start = (new Date).getTime(); +calc_value = fibFast(MAXIMUM_JS_FIB_N) +diff = (new Date).getTime() - start; +console.log("[#{calc_value}] took #{diff} ms.") +{% endhighlight %} + +h2. Discussion + +Questions? \ No newline at end of file From c2636cc3455f779eed89ca7ab66f9f81b2b1a3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Sun, 19 Jun 2011 20:17:37 +0200 Subject: [PATCH 041/267] Added Reducing Arrays recipe --- chapters/arrays/reducing-arrays.textile | 43 +++++++++++++++++++++++++ wanted-recipes.textile | 6 ---- 2 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 chapters/arrays/reducing-arrays.textile diff --git a/chapters/arrays/reducing-arrays.textile b/chapters/arrays/reducing-arrays.textile new file mode 100644 index 0000000..2f43560 --- /dev/null +++ b/chapters/arrays/reducing-arrays.textile @@ -0,0 +1,43 @@ +--- +layout: recipe +title: Reducing Arrays +chapter: Arrays +--- + +h2. Problem + +You have an array of objects and want to reduce them to a value, similar to Ruby's reduce() and reduceRight(). + +h2. Solution + +You can simply use Array's reduce() and reduceRight() methods along with an anonoymous function, keeping the code clean and readable. The reduction may be something simple such as using the + operator with numbers or strings. + +{% highlight coffeescript %} +[1,2,3,4].reduce (x,y) -> x + y +# => 10 +{% endhighlight %} + +{% highlight coffeescript %} +["words", "of", "bunch", "A"].reduceRight (x, y) -> x + " " + y +# => 'A bunch of words' +{% endhighlight %} + +Or it may be something more complex such as aggregating elements from a list into a combined object. + +{% highlight coffeescript %} +people = + { name: '', age: 10 } + { name: '', age: 16 } + { name: '', age: 17 } + +people.reduce (x, y) -> + x[y.name]= y.age + x +, {} +# => { alec: 10, bert: 16, chad: 17 } +{% endhighlight %} + +h2. Discussion + +Javascript introduced reduce and reduceRight in version 1.8. Coffeescript provides a natural and simple way to express anonymous functions. Both go together cleanly in the problem of merging a collection's items into a combined result. + diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 994cf6b..33a3cd5 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -26,12 +26,6 @@ h2. Strings h2. Arrays -* Reducing arrays to values (ruby inject) with reduce # [1..10].reduce (a,b) -> a+b # 55 -* Reducing arrays in reverse order # JS reduceRight -{% highlight coffeescript %} -["example", "contrived ", "pretty ", "a ", "is ", "here "].reduceRight (x,y) -> x+y -# => 'here is a pretty contrived example' -{% endhighlight %} * Testing every element in an array {% highlight coffeescript %} evens = (x for x in [0..10] by 2) From 762f543ab0feea2bdf570e09355a54fa0ff481ca Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Sun, 19 Jun 2011 14:23:16 -0400 Subject: [PATCH 042/267] Added singleton pattern --- chapters/design_patterns/singleton.textile | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 chapters/design_patterns/singleton.textile diff --git a/chapters/design_patterns/singleton.textile b/chapters/design_patterns/singleton.textile new file mode 100644 index 0000000..026eab7 --- /dev/null +++ b/chapters/design_patterns/singleton.textile @@ -0,0 +1,66 @@ +--- +layout: recipe +title: Singleton Pattern +chapter: Design Patterns +--- + +h2. Problem + +Many times you only want one, and only one, instance of a class. For example, you may only need one class that creates server resources and you want to ensure that the one object can control those resources. Beware, however, because the singleton pattern can be easily abused to mimic unwanted global variables. + +h2. Solution + +The publically available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned + +The actual definition of the singleton class follows. + +Note that I am using the idiomatic module export feature to emphasize the publically accessible portion of the module. Remember coffeescript wraps all files in a function block to protect the global namespace + +{% highlight coffeescript %} +root = exports ? this # http://stackoverflow.com/questions/4214731/coffeescript-global-variables + +# The publically accessible Singleton fetcher +class root.Singleton + _instance = undefined # Must be declared here to force the closure on the class + @get: (args) -> # Must be a static method + _instance ?= new _Singleton args + +# The actual Singleton class +class _Singleton + constructor: (@args) -> + + echo: -> + @args + +a = root.Singleton.get 'Hello A' +a.echo() +# => 'Hello A' + +b = root.Singleton.get 'Hello B' +a.echo() +# => 'Hello A' + +b.echo() +# => 'Hello A' + +root.Singleton._instance +# => undefined + +root.Singleton._instance = 'foo' + +root.Singleton._instance +# => 'foo' + +c = root.Singleton.get 'Hello C' +c.foo() +# => 'Hello A' + +a.foo() +# => 'Hello A' +{% endhighlight %} + +h2. Discussion + +See in the above example how all instances are outputting from the same instance of the Singleton class + +Note how incredibly simple coffeescript makes this design pattern. For reference and discussion on nice javascript implementations, check out http://addyosmani.com/resources/essentialjsdesignpatterns/book/ From 1b6a5b1ba8494b5e50bc549c9c95ce30c75c4cc1 Mon Sep 17 00:00:00 2001 From: Jason Giedymin Date: Sun, 19 Jun 2011 16:50:57 -0400 Subject: [PATCH 043/267] Added Jason Giedymin to list of authors. --- authors.textile | 1 + 1 file changed, 1 insertion(+) diff --git a/authors.textile b/authors.textile index f6e75aa..6a7dc9c 100644 --- a/authors.textile +++ b/authors.textile @@ -14,6 +14,7 @@ _The following people are totally rad and awesome because they have contributed * Sebastian Slomski _sebastian@simple-systems.org_ * Aaron Weinberger _aw9994@cs.ship.edu_ * James C. Holder _cs_cookbook@thirdtruck.org_ +* Jason Giedymin _jasong@apache.org_ * ...You! What are you waiting for? Check out the contributing section and get cracking! From 95c1280704ed2ee9f1131e0cb69399d8f80ab3b6 Mon Sep 17 00:00:00 2001 From: JasonGiedymin Date: Sun, 19 Jun 2011 14:14:24 -0700 Subject: [PATCH 044/267] Corrected title of fast fibonacci page. --- chapters/math/fast-fibonacci.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/math/fast-fibonacci.textile b/chapters/math/fast-fibonacci.textile index 3df3240..0c3e706 100644 --- a/chapters/math/fast-fibonacci.textile +++ b/chapters/math/fast-fibonacci.textile @@ -1,6 +1,6 @@ --- layout: recipe -title: Converting Radians and Degrees +title: Faster Fibonacci algorithm chapter: Math --- From 0184315aae70955e8d5bfb955c6d17478361791c Mon Sep 17 00:00:00 2001 From: James Holder Date: Sun, 19 Jun 2011 19:13:54 -0400 Subject: [PATCH 045/267] Add "Functions/Splat Arguments" recipe. --- chapters/functions/recursion.textile | 2 + chapters/functions/splat_arguments.textile | 50 ++++++++++++++++++++++ wanted-recipes.textile | 2 - 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 chapters/functions/splat_arguments.textile diff --git a/chapters/functions/recursion.textile b/chapters/functions/recursion.textile index 3fcad09..d20f31b 100644 --- a/chapters/functions/recursion.textile +++ b/chapters/functions/recursion.textile @@ -31,3 +31,5 @@ setTimeout((-> h2. Discussion +While @arguments.callee@ allows for the recursion of anonymous functions and might have the advantage in a very memory-intensive application, named functions keep their purpose more explicit and make for more maintainable code. + diff --git a/chapters/functions/splat_arguments.textile b/chapters/functions/splat_arguments.textile new file mode 100644 index 0000000..782bc12 --- /dev/null +++ b/chapters/functions/splat_arguments.textile @@ -0,0 +1,50 @@ +--- +layout: recipe +title: Splat Arguments +chapter: Functions +--- + +h2. Problem + +Your function will be called with a varying number of arguments. + +h2. Solution + +Use _splats_. + +{% highlight coffeescript %} +loadTruck = (firstDibs, secondDibs, tooSlow...) -> + truck: + driversSeat: firstDibs + passengerSeat: secondDibs + trunkBed: tooSlow + +loadTruck("Amanda", "Joel") +# => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: [] } } + +loadTruck("Amanda", "Joel", "Bob", "Mary", "Phillip") +# => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: ["Bob", "Mary", "Phillip"] } } +{% endhighlight %} + +With a trailing argument: + +{% highlight coffeescript %} +loadTruck = (firstDibs, secondDibs, tooSlow..., leftAtHome) -> + truck: + driversSeat: firstDibs + passengerSeat: secondDibs + trunkBed: tooSlow + taxi: + passengerSeat: leftAtHome + +loadTruck("Amanda", "Joel", "Bob", "Mary", "Phillip", "Austin") +# => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: ["Bob", "Mary", "Phillip"] }, taxi: "Austin" } +{% endhighlight %} + +loadTruck("Amanda") +# => { truck: { driversSeat: "Amanda", passengerSeat: undefined, trunkBed: [] }, taxi: undefined } + +h2. Discussion + +By adding an ellipsis ("...") next to no more than one of a function's arguments, CoffeeScript will combine all of the argument values not captured by other named arguments into a list. It will serve up an empty list even if some of the named arguments were not supplied. + diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 3103232..3908852 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -93,8 +93,6 @@ foo 1, 2, 3 # => 6 {% endhighlight %} -* Variable arguments with splats - h2. jQuery h2. Regular Expressions From cd95045d94ef53e9e79cf8aeb4192ecedf07b019 Mon Sep 17 00:00:00 2001 From: Rimantas Liubertas Date: Mon, 20 Jun 2011 11:06:05 +0300 Subject: [PATCH 046/267] Adds calculation of the date of the Easter Sunday --- .../dates_and_times/date-of-easter.textile | 50 +++++++++++++++++++ wanted-recipes.textile | 1 - 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 chapters/dates_and_times/date-of-easter.textile diff --git a/chapters/dates_and_times/date-of-easter.textile b/chapters/dates_and_times/date-of-easter.textile new file mode 100644 index 0000000..bc51f7d --- /dev/null +++ b/chapters/dates_and_times/date-of-easter.textile @@ -0,0 +1,50 @@ +--- +layout: recipe +title: Calculate the date of Easter Sunday +chapter: Dates and Times +--- + +h2. Problem + +You need to find the month and day of the Easter Sunday for given year + +h2. Solution + +The following function returns array with two elements: month (1-12) and day of the Easter Sunday. If no arguments are given +result is for the current year. +This is an implementation of http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm in CoffeeScript + +{% highlight coffeescript %} + +gregorianEaster = (year = (new Date).getFullYear()) -> + a = year % 19 + b = ~~(year / 100) + c = year % 100 + d = ~~(b / 4) + e = b % 4 + f = ~~((b + 8) / 25) + g = ~~((b - f + 1) / 3) + h = (19 * a + b - d - g + 15) % 30 + i = ~~(c / 4) + k = c % 4 + l = (32 + 2 * e + 2 * i - h - k) % 7 + m = ~~((a + 11 * h + 22 * l) / 451) + n = h + l - 7 * m + 114 + month = ~~(n / 31) + day = (n % 31) + 1 + [month, day] + +{% endhighlight %} + +h2. Discussion + +NB! Javascript numbers months from 0 to 11 so .getMonth() for date in March will return 2, this function will return 3. +You can modify the function if you want this to be consistent. + +{% highlight coffeescript %} + +gregorianEaster() # => [4, 24] (April 24th in 2011) +gregorianEaster 1972 # => [4, 2] + +{% endhighlight %} + diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 33a3cd5..7c1f0f2 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -42,7 +42,6 @@ evens.every even h2. Dates and Times * Calculating the phase of the moon -* Holidays: Calculating Easter * Holidays: Calculating US Thanksgiving * Holidays: Calculating CA Thanksgiving * Number of days between two dates From 712c078bf2324ce0050d817488ea34717d8d108d Mon Sep 17 00:00:00 2001 From: Rimantas Liubertas Date: Mon, 20 Jun 2011 11:20:35 +0300 Subject: [PATCH 047/267] Fixes typo, adds link --- chapters/dates_and_times/date-of-easter.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapters/dates_and_times/date-of-easter.textile b/chapters/dates_and_times/date-of-easter.textile index bc51f7d..dfefda6 100644 --- a/chapters/dates_and_times/date-of-easter.textile +++ b/chapters/dates_and_times/date-of-easter.textile @@ -6,13 +6,13 @@ chapter: Dates and Times h2. Problem -You need to find the month and day of the Easter Sunday for given year +You need to find the month and day of the Easter Sunday for given year. h2. Solution The following function returns array with two elements: month (1-12) and day of the Easter Sunday. If no arguments are given result is for the current year. -This is an implementation of http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm in CoffeeScript +This is an implementation of "Anonymous Gregorian algorithm":http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm in CoffeeScript {% highlight coffeescript %} From 9baf0726e328b1963bff05afcc2ba323ed0f8ce6 Mon Sep 17 00:00:00 2001 From: Rimantas Liubertas Date: Mon, 20 Jun 2011 11:38:30 +0300 Subject: [PATCH 048/267] Add note about ~~ trick. Fixes one more typo. --- chapters/dates_and_times/date-of-easter.textile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chapters/dates_and_times/date-of-easter.textile b/chapters/dates_and_times/date-of-easter.textile index dfefda6..020a9e2 100644 --- a/chapters/dates_and_times/date-of-easter.textile +++ b/chapters/dates_and_times/date-of-easter.textile @@ -12,7 +12,7 @@ h2. Solution The following function returns array with two elements: month (1-12) and day of the Easter Sunday. If no arguments are given result is for the current year. -This is an implementation of "Anonymous Gregorian algorithm":http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm in CoffeeScript +This is an implementation of "Anonymous Gregorian algorithm":http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm in CoffeeScript. {% highlight coffeescript %} @@ -41,6 +41,8 @@ h2. Discussion NB! Javascript numbers months from 0 to 11 so .getMonth() for date in March will return 2, this function will return 3. You can modify the function if you want this to be consistent. +The function uses ~~ trick instead of Math.floor(). + {% highlight coffeescript %} gregorianEaster() # => [4, 24] (April 24th in 2011) From 88ae76673944500f542f0b002181dacfed48c4ac Mon Sep 17 00:00:00 2001 From: Rimantas Liubertas Date: Mon, 20 Jun 2011 16:48:37 +0300 Subject: [PATCH 049/267] Adds the calculation of the day of Thanksgiving (USA and CA) --- .../date-of-thanksgiving.textile | 55 +++++++++++++++++++ wanted-recipes.textile | 2 - 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 chapters/dates_and_times/date-of-thanksgiving.textile diff --git a/chapters/dates_and_times/date-of-thanksgiving.textile b/chapters/dates_and_times/date-of-thanksgiving.textile new file mode 100644 index 0000000..991bbfc --- /dev/null +++ b/chapters/dates_and_times/date-of-thanksgiving.textile @@ -0,0 +1,55 @@ +--- +layout: recipe +title: Calculate the date of Thanksgiving (USA and Canada) +chapter: Dates and Times +--- + +h2. Problem + +You need to calculate when is Thanksgivinig in given year. + +h2. Solution + +The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. + +In the USA Thanksgiving is celebrated on the fourth Thursday in November: + +{% highlight coffeescript %} + +thanksgivingDayUSA = (year = (new Date).getFullYear()) -> + first = new Date year, 10, 1 + day_of_week = first.getDay() + 22 + (11 - day_of_week) % 7 + +{% endhighlight %} + +In Canada it is the second Monday in October: + +{% highlight coffeescript %} + +thanksgivingDayCA = (year = (new Date).getFullYear()) -> + first = new Date year, 9, 1 + day_of_week = first.getDay() + 8 + (8 - day_of_week) % 7 + +{% endhighlight %} + +h2. Discussion + + +{% highlight coffeescript %} + +thanksgivingDayUSA() #=> 24 (November 24th, 2011) + +thanksgivingDayCA() # => 10 (October 10th, 2011) + +thanksgivingDayUSA(2012) # => 22 (November 22nd) + +thanksgivingDayCA(2012) # => 8 (October 8th) + +{% endhighlight %} + +The idea is very simple: +# Find out what day of the week is the first day of respective month (November for USA, October for Canada). +# Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). +# Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). diff --git a/wanted-recipes.textile b/wanted-recipes.textile index 7701c84..3d0dd65 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.textile @@ -42,8 +42,6 @@ evens.every even h2. Dates and Times * Calculating the phase of the moon -* Holidays: Calculating US Thanksgiving -* Holidays: Calculating CA Thanksgiving * Number of days between two dates h2. Math From 4e932022a666bec47d14d524c1975ae8076d05a9 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 10:56:40 -0600 Subject: [PATCH 050/267] Move chapter indexes to HTML The textile markup was putting each link in its own list. The result was lots of unnecessary whitespace on those pages. --- chapters/ajax/{index.textile => index.html} | 5 +++-- chapters/arrays/{index.textile => index.html} | 5 +++-- .../{objects => classes_and_objects}/chaining.textile | 0 .../classes_and_objects/{index.textile => index.html} | 5 +++-- .../dates_and_times/{index.textile => index.html} | 5 +++-- .../design_patterns/{index.textile => index.html} | 5 +++-- chapters/functions/{index.textile => index.html} | 5 +++-- chapters/{index.textile => index.html} | 11 ++++++----- chapters/jquery/{index.textile => index.html} | 5 +++-- chapters/math/{index.textile => index.html} | 5 +++-- .../metaprogramming/{index.textile => index.html} | 5 +++-- chapters/networking/{index.textile => index.html} | 5 +++-- .../regular_expressions/{index.textile => index.html} | 5 +++-- chapters/strings/{index.textile => index.html} | 5 +++-- chapters/syntax/{index.textile => index.html} | 5 +++-- 15 files changed, 45 insertions(+), 31 deletions(-) rename chapters/ajax/{index.textile => index.html} (79%) rename chapters/arrays/{index.textile => index.html} (79%) rename chapters/{objects => classes_and_objects}/chaining.textile (100%) rename chapters/classes_and_objects/{index.textile => index.html} (80%) rename chapters/dates_and_times/{index.textile => index.html} (80%) rename chapters/design_patterns/{index.textile => index.html} (80%) rename chapters/functions/{index.textile => index.html} (79%) rename chapters/{index.textile => index.html} (77%) rename chapters/jquery/{index.textile => index.html} (79%) rename chapters/math/{index.textile => index.html} (79%) rename chapters/metaprogramming/{index.textile => index.html} (80%) rename chapters/networking/{index.textile => index.html} (79%) rename chapters/regular_expressions/{index.textile => index.html} (80%) rename chapters/strings/{index.textile => index.html} (79%) rename chapters/syntax/{index.textile => index.html} (79%) diff --git a/chapters/ajax/index.textile b/chapters/ajax/index.html similarity index 79% rename from chapters/ajax/index.textile rename to chapters/ajax/index.html index 004ced5..5ca402b 100644 --- a/chapters/ajax/index.textile +++ b/chapters/ajax/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} - +
diff --git a/chapters/arrays/index.textile b/chapters/arrays/index.html similarity index 79% rename from chapters/arrays/index.textile rename to chapters/arrays/index.html index afa1d87..80b9b41 100644 --- a/chapters/arrays/index.textile +++ b/chapters/arrays/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} - +
diff --git a/chapters/objects/chaining.textile b/chapters/classes_and_objects/chaining.textile similarity index 100% rename from chapters/objects/chaining.textile rename to chapters/classes_and_objects/chaining.textile diff --git a/chapters/classes_and_objects/index.textile b/chapters/classes_and_objects/index.html similarity index 80% rename from chapters/classes_and_objects/index.textile rename to chapters/classes_and_objects/index.html index 7a2dcea..082c0a3 100644 --- a/chapters/classes_and_objects/index.textile +++ b/chapters/classes_and_objects/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} - +
diff --git a/chapters/dates_and_times/index.textile b/chapters/dates_and_times/index.html similarity index 80% rename from chapters/dates_and_times/index.textile rename to chapters/dates_and_times/index.html index 671240e..969aee4 100644 --- a/chapters/dates_and_times/index.textile +++ b/chapters/dates_and_times/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} - +
diff --git a/chapters/design_patterns/index.textile b/chapters/design_patterns/index.html similarity index 80% rename from chapters/design_patterns/index.textile rename to chapters/design_patterns/index.html index dc44fbd..e488a5d 100644 --- a/chapters/design_patterns/index.textile +++ b/chapters/design_patterns/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} - +
diff --git a/chapters/functions/index.textile b/chapters/functions/index.html similarity index 79% rename from chapters/functions/index.textile rename to chapters/functions/index.html index dbeb3c9..be9a60a 100644 --- a/chapters/functions/index.textile +++ b/chapters/functions/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} - +
diff --git a/chapters/index.textile b/chapters/index.html similarity index 77% rename from chapters/index.textile rename to chapters/index.html index 8e93be3..505f157 100644 --- a/chapters/index.textile +++ b/chapters/index.html @@ -22,15 +22,16 @@ {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} -h2. {{ chapter }} +

{{ chapter }}

+ {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %} -{% endfor %} - + +{% endfor %} diff --git a/chapters/jquery/index.textile b/chapters/jquery/index.html similarity index 79% rename from chapters/jquery/index.textile rename to chapters/jquery/index.html index c046341..278cdbe 100644 --- a/chapters/jquery/index.textile +++ b/chapters/jquery/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    diff --git a/chapters/math/index.textile b/chapters/math/index.html similarity index 79% rename from chapters/math/index.textile rename to chapters/math/index.html index 9fb796d..5ad24b7 100644 --- a/chapters/math/index.textile +++ b/chapters/math/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    diff --git a/chapters/metaprogramming/index.textile b/chapters/metaprogramming/index.html similarity index 80% rename from chapters/metaprogramming/index.textile rename to chapters/metaprogramming/index.html index 2e886be..7287ee9 100644 --- a/chapters/metaprogramming/index.textile +++ b/chapters/metaprogramming/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    diff --git a/chapters/networking/index.textile b/chapters/networking/index.html similarity index 79% rename from chapters/networking/index.textile rename to chapters/networking/index.html index cf90e3a..5cc875e 100644 --- a/chapters/networking/index.textile +++ b/chapters/networking/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    diff --git a/chapters/regular_expressions/index.textile b/chapters/regular_expressions/index.html similarity index 80% rename from chapters/regular_expressions/index.textile rename to chapters/regular_expressions/index.html index f90d8a4..bc841bc 100644 --- a/chapters/regular_expressions/index.textile +++ b/chapters/regular_expressions/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    diff --git a/chapters/strings/index.textile b/chapters/strings/index.html similarity index 79% rename from chapters/strings/index.textile rename to chapters/strings/index.html index 6186231..50a3d9f 100644 --- a/chapters/strings/index.textile +++ b/chapters/strings/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    diff --git a/chapters/syntax/index.textile b/chapters/syntax/index.html similarity index 79% rename from chapters/syntax/index.textile rename to chapters/syntax/index.html index 3605ea3..deb6165 100644 --- a/chapters/syntax/index.textile +++ b/chapters/syntax/index.html @@ -7,11 +7,12 @@ {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %} - * {{ page.title }} +
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %} - +
    From c06f29f19452aead12c727c626a970f69a357b5e Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 10:59:06 -0600 Subject: [PATCH 051/267] Remove links to AJAX chapter Just remember to add it back when there is content there. :) --- chapters/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/chapters/index.html b/chapters/index.html index 505f157..f97c720 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -12,7 +12,6 @@ - Metaprogramming - jQuery - Regular Expressions -- AJAX - Networking - Design Patterns --- From 697e3fbae06f35262cc5137cc38b0beb5f259f96 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 11:06:59 -0600 Subject: [PATCH 052/267] Add recipe links to the home page Removes an unnecessary click to get to content. Should make the site friendlier to non-contributors. --- chapters/index.html | 2 +- index.html | 39 +++++++++++++++++++++++++++++++++++++++ index.textile | 17 ----------------- 3 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 index.html delete mode 100644 index.textile diff --git a/chapters/index.html b/chapters/index.html index f97c720..2b79a72 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -21,7 +21,7 @@ {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} -

    {{ chapter }}

    {{ chapter }}
      {% for page in site.pages %} diff --git a/index.html b/index.html new file mode 100644 index 0000000..acfe28d --- /dev/null +++ b/index.html @@ -0,0 +1,39 @@ +--- +layout: default +title: Home +chapters: +- Syntax +- Classes and Objects +- Strings +- Arrays +- Dates and Times +- Math +- Functions +- Metaprogramming +- jQuery +- Regular Expressions +- Networking +- Design Patterns +--- + +

      Welcome

      + +

      Welcome to the CoffeeScript Cookbook! CoffeeScript recipes for the community by the community. Head over to the Contributing page and see what you can do to help out!

      + +{% for chapter in page.chapters %} + {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} + {% capture indexurl %}{{ url }}/index.html{% endcapture %} + +

      {{ chapter }}

      + +
        + {% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} +
      • {{ page.title }}
      • + {% endunless %} + {% endif %} + {% endfor %} +
      + +{% endfor %} diff --git a/index.textile b/index.textile deleted file mode 100644 index cfe32fd..0000000 --- a/index.textile +++ /dev/null @@ -1,17 +0,0 @@ ---- -layout: default -title: Home ---- - -h1. Welcome - -Welcome to the CoffeeScript Cookbook! It's a bit of a mess right now, and VERY lonely and empty in these pages. That's where _you_ come in. Head over to the Contributing page and see what you can do to help out! - -Contributions URGENTLY needed NOW: - -* +NEW!+ Check out the Wanted Recipes page! -* +NEW!+ For authors: we've added a Recipe Template to help get you started. -* Authors: Write recipes! Here's how. -* Developers: Expand the Cookbook site! Here's how. -* Designers: Make this place look like not-crap! Here's how. - From 92508a39391e6c9b6a8b2ab3f0acc8edf3390249 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 11:47:14 -0600 Subject: [PATCH 053/267] Remove ".html" from links --- chapters/math/fast-fibonacci.textile | 6 +++--- chapters/networking/basic-client.textile | 4 ++-- chapters/networking/basic-server.textile | 4 ++-- chapters/networking/bi-directional-client.textile | 4 ++-- chapters/networking/bi-directional-server.textile | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/chapters/math/fast-fibonacci.textile b/chapters/math/fast-fibonacci.textile index 0c3e706..d79537e 100644 --- a/chapters/math/fast-fibonacci.textile +++ b/chapters/math/fast-fibonacci.textile @@ -16,10 +16,10 @@ talked about on Robin Houston's blog. Here are a few links talking about the algorithm and ways to improve it: # http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/ -# http://www.math.rutgers.edu/~erowland/fibonacci.html +# http://www.math.rutgers.edu/~erowland/fibonacci.html # http://jsfromhell.com/classes/bignumber -# http://www.math.rutgers.edu/~erowland/fibonacci.html -# http://bigintegers.blogspot.com/2010/11/square-division-power-square-root.html +# http://www.math.rutgers.edu/~erowland/fibonacci.html +# http://bigintegers.blogspot.com/2010/11/square-division-power-square-root.html # http://bugs.python.org/issue3451 This code is in gist form here: diff --git a/chapters/networking/basic-client.textile b/chapters/networking/basic-client.textile index 34cf594..42b30e2 100644 --- a/chapters/networking/basic-client.textile +++ b/chapters/networking/basic-client.textile @@ -33,7 +33,7 @@ connection.on 'data', (data) -> h3. Example Usage -Accessing the Basic Server: +Accessing the Basic Server: {% highlight console %} $ coffee basic-client.coffee @@ -45,7 +45,7 @@ h2. Discussion The most important work takes place in the _connection.on 'data'_ handler, where the client receives its response from the server and would most likely arrange for responses to it. -See also the Basic Server, Bi-Directional Client, and Bi-Directional Server recipes. +See also the Basic Server, Bi-Directional Client, and Bi-Directional Server recipes. h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/basic-server.textile b/chapters/networking/basic-server.textile index 16ae3fd..bd4a031 100644 --- a/chapters/networking/basic-server.textile +++ b/chapters/networking/basic-server.textile @@ -32,7 +32,7 @@ server.listen port, domain h3. Example Usage -Accessed by the Basic Client: +Accessed by the Basic Client: {% highlight console %} $ coffee basic-server.coffee @@ -46,7 +46,7 @@ h2. Discussion The function passed to @net.createServer@ receives the new socket provided for each new connection to a client. This basic server simply socializes with its visitors but a hard-working server would pass this socket along to a dedicated handler and then return to the task of waiting for the next client. -See also the Basic Client, Bi-Directional Server, and Bi-Directional Client recipes. +See also the Basic Client, Bi-Directional Server, and Bi-Directional Client recipes. h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-client.textile b/chapters/networking/bi-directional-client.textile index 7b41d4e..d8234d8 100644 --- a/chapters/networking/bi-directional-client.textile +++ b/chapters/networking/bi-directional-client.textile @@ -42,7 +42,7 @@ connection.on 'end', (data) -> h3. Example Usage -Accessing the Bi-Directional Server: +Accessing the Bi-Directional Server: {% highlight console %} $ coffee bi-directional-client.coffee @@ -61,7 +61,7 @@ h2. Discussion This particular example initiates contact with the server and starts the conversation in the @connection.on 'connect'@ handler. The bulk of the work in a real client, however, will lie in the @connection.on 'data'@ handler, which processes output from the server. The @ping@ function only recurses in order to illustrate continuous communication with the server and can be removed from a real client. -See also the Bi-Directional Server, Basic Client, and Basic Server recipes. +See also the Bi-Directional Server, Basic Client, and Basic Server recipes. h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-server.textile b/chapters/networking/bi-directional-server.textile index e3d9201..36c0e4c 100644 --- a/chapters/networking/bi-directional-server.textile +++ b/chapters/networking/bi-directional-server.textile @@ -34,7 +34,7 @@ server.listen port, domain h3. Example Usage -Accessed by the Bi-Directional Client: +Accessed by the Bi-Directional Client: {% highlight console %} $ coffee bi-directional-server.coffee @@ -50,7 +50,7 @@ h2. Discussion The bulk of the work lies in the @socket.on 'data'@ handler, which processes all of the input from the client. A real server would likely pass the data onto another function to process it and generate any responses so that the original handler. -See also the Bi-Directional Client, Basic Client, and Basic Server recipes. +See also the Bi-Directional Client, Basic Client, and Basic Server recipes. h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or on a configuration file. From 024180dadb574a1e0fd6b8bc586f19c9154f2f00 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 11:55:37 -0600 Subject: [PATCH 054/267] Update the recipe layout Tighten up the header and make it a bit simpler. Add the page title to the content of each page. --- _layouts/recipe.html | 18 +++++++++++------- chapters/arrays/concatenating-arrays.textile | 2 -- chapters/arrays/reversing-arrays.textile | 2 -- .../using-arrays-to-swap-variables.textile | 2 -- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/_layouts/recipe.html b/_layouts/recipe.html index 42cf547..7405cc7 100644 --- a/_layouts/recipe.html +++ b/_layouts/recipe.html @@ -6,20 +6,24 @@
      -

      - CoffeeScript Cookbook » +

      CoffeeScript Cookbook

      +
      +

      {{ page.title }}

      {{ content }}
      diff --git a/chapters/arrays/concatenating-arrays.textile b/chapters/arrays/concatenating-arrays.textile index 8113b84..7fae568 100644 --- a/chapters/arrays/concatenating-arrays.textile +++ b/chapters/arrays/concatenating-arrays.textile @@ -4,8 +4,6 @@ title: Concatenating Arrays chapter: Arrays --- -h1. Concatenating Arrays - h2. Problem You want to join two arrays together. diff --git a/chapters/arrays/reversing-arrays.textile b/chapters/arrays/reversing-arrays.textile index 60b6070..ae3cdf3 100644 --- a/chapters/arrays/reversing-arrays.textile +++ b/chapters/arrays/reversing-arrays.textile @@ -4,8 +4,6 @@ title: Reversing Arrays chapter: Arrays --- -h1. Reversing an Array - h2. Problem You want to reverse an array. diff --git a/chapters/arrays/using-arrays-to-swap-variables.textile b/chapters/arrays/using-arrays-to-swap-variables.textile index 1049002..162e7e8 100644 --- a/chapters/arrays/using-arrays-to-swap-variables.textile +++ b/chapters/arrays/using-arrays-to-swap-variables.textile @@ -4,8 +4,6 @@ title: Using Arrays to Swap Variables chapter: Arrays --- -h1. Using Arrays to Swap Variables - h2. Problem You want to use an array to swap variables. From 79e8d53b7701d37024fe5e40f3258005e093e80d Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 11:58:31 -0600 Subject: [PATCH 055/267] Fix chapter --- chapters/arrays/filtering-arrays.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/filtering-arrays.textile b/chapters/arrays/filtering-arrays.textile index ce8c253..4ee7e1d 100644 --- a/chapters/arrays/filtering-arrays.textile +++ b/chapters/arrays/filtering-arrays.textile @@ -1,7 +1,7 @@ --- layout: recipe title: Filtering Arrays -chapter: Filtering Arrays +chapter: Arrays --- h2. Problem From 4aabfc45d13d28647f80c5609e243ae610d5365a Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 12:08:30 -0600 Subject: [PATCH 056/267] Update stylesheet Add Solarized theme. Set link colors to solarized colors. --- css/default.css | 140 +++++++++++++++++++++------------------- css/solarized-light.css | 90 ++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 66 deletions(-) create mode 100644 css/solarized-light.css diff --git a/css/default.css b/css/default.css index d12653a..0631591 100644 --- a/css/default.css +++ b/css/default.css @@ -25,13 +25,13 @@ article, header, section, footer { /* Colors */ a, .header a:visited { - color: #26ADE4; + color: #268bd2; text-decoration: none; } a:hover { text-decoration: underline; - text-shadow: 0 0 0 #DCEEF5; + text-shadow: 0 0 0 #2aa198; } footer { @@ -65,7 +65,7 @@ ul, ol { body { text-rendering: optimizeLegibility; - color: #373837; + color: #002b36; font-family: Palatino, "Palatino Linotype", serif; text-shadow: 0 0 0 transparent; font-size: 16px; @@ -87,66 +87,74 @@ h1 { font-size: 30px; line-height: 44px; } h2 { font-size: 22px; line-height: 44px; } h3 { font-size: 18px; line-height: 22px; } -/* Syntax Highlighting */ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #408080; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ -.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #808080 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0040D0 } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #7D9029 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #A0A000 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ +.highlight { background: #fdf6e3; color: #657b83; line-height: 1em; font-size: 13px; } +.highlight .lineno { background-color: #eee8d5; color: #93a1a1; padding-left: .5em; padding-right: .5em; } +.highlight .hll { background-color: #eee8d5; } /* Don't know what this is... */ +.highlight .c { color: #93a1a1; font-style: italic; } /* Comment */ +.highlight .err { color: #dc322f; } /* Error */ +.highlight .g { color: #839496; } /* Generic */ +.highlight .k { color: #859900; } /* Keyword */ +.highlight .l { color: #839496; } /* Literal */ +.highlight .n { color: #839496; } /* Name */ +.highlight .o { color: #859900; } /* Operator */ +.highlight .x { color: #839496; } /* Other */ +.highlight .p { color: #839496; } /* Punctuation */ +.highlight .cm { color: #93a1a1; font-style: italic; } /* Comment.Multiline */ +.highlight .cp { color: #93a1a1; font-style: italic; } /* Comment.Preproc */ +.highlight .c1 { color: #93a1a1; font-style: italic; } /* Comment.Single */ +.highlight .cs { color: #93a1a1; font-style: italic; } /* Comment.Special */ +.highlight .gd { color: #dc322f; } /* Generic.Deleted */ +.highlight .ge { color: #839496; font-style: italic; } /* Generic.Emph */ +.highlight .gr { color: #839496; } /* Generic.Error */ +.highlight .gh { color: #839496; font-weight: bold; } /* Generic.Heading */ +.highlight .gi { color: #859900; } /* Generic.Inserted */ +.highlight .go { color: #839496; } /* Generic.Output */ +.highlight .gp { color: #839496; font-weight: bold; } /* Generic.Prompt */ +.highlight .gs { color: #839496; font-weight: bold; } /* Generic.Strong */ +.highlight .gu { color: #839496; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #839496; } /* Generic.Traceback */ +.highlight .kc { color: #839496; } /* Keyword.Constant */ +.highlight .kd { color: #839496; } /* Keyword.Declaration */ +.highlight .kn { color: #839496; } /* Keyword.Namespace */ +.highlight .kp { color: #2aa198; } /* Keyword.Pseudo */ +.highlight .kr { color: #839496; } /* Keyword.Reserved */ +.highlight .kt { color: #839496; } /* Keyword.Type */ +.highlight .ld { color: #839496; } /* Literal.Date */ +.highlight .m { color: #839496; } /* Literal.Number */ +.highlight .s { color: #2aa198; } /* Literal.String */ +.highlight .na { color: #839496; } /* Name.Attribute */ +.highlight .nb { color: #cb4b16; } /* Name.Builtin */ +.highlight .nc { color: #839496; } /* Name.Class */ +.highlight .no { color: #b58900; } /* Name.Constant */ +.highlight .nd { color: #839496; } /* Name.Decorator */ +.highlight .ni { color: #839496; } /* Name.Entity */ +.highlight .ne { color: #839496; } /* Name.Exception */ +.highlight .nf { color: #268bd2; } /* Name.Function */ +.highlight .nl { color: #839496; } /* Name.Label */ +.highlight .nn { color: #cb4b16; } /* Name.Namespace */ +.highlight .nx { color: #839496; } /* Name.Other */ +.highlight .py { color: #839496; } /* Name.Property */ +.highlight .nt { color: #839496; } /* Name.Tag */ +.highlight .nv { color: #839496; } /* Name.Variable */ +.highlight .ow { color: #839496; } /* Operator.Word */ +.highlight .w { color: #839496; } /* Text.Whitespace */ +.highlight .mf { color: #2aa198; } /* Literal.Number.Float */ +.highlight .mh { color: #2aa198; } /* Literal.Number.Hex */ +.highlight .mi { color: #2aa198; } /* Literal.Number.Integer */ +.highlight .mo { color: #2aa198; } /* Literal.Number.Oct */ +.highlight .sb { color: #2aa198; } /* Literal.String.Backtick */ +.highlight .sc { color: #2aa198; } /* Literal.String.Char */ +.highlight .sd { color: #2aa198; } /* Literal.String.Doc */ +.highlight .s2 { color: #2aa198; } /* Literal.String.Double */ +.highlight .se { color: #dc322f; } /* Literal.String.Escape */ +.highlight .sh { color: #2aa198; } /* Literal.String.Heredoc */ +.highlight .si { color: #2aa198; } /* Literal.String.Interpol */ +.highlight .sx { color: #2aa198; } /* Literal.String.Other */ +.highlight .sr { color: #2aa198; } /* Literal.String.Regex */ +.highlight .s1 { color: #2aa198; } /* Literal.String.Single */ +.highlight .ss { color: #2aa198; } /* Literal.String.Symbol */ +.highlight .bp { color: #839496; } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #839496; } /* Name.Variable.Class */ +.highlight .vg { color: #839496; } /* Name.Variable.Global */ +.highlight .vi { color: #839496; } /* Name.Variable.Instance */ +.highlight .il { color: #839496; } /* Literal.Number.Integer.Long */ diff --git a/css/solarized-light.css b/css/solarized-light.css new file mode 100644 index 0000000..610e1d4 --- /dev/null +++ b/css/solarized-light.css @@ -0,0 +1,90 @@ +/* +base03: #002b36; +base02: #073642; +base01: #586e75; +base00: #657b83; +base0: #839496; +base1: #93a1a1; +base2: #eee8d5; +base3: #fdf6e3; +yellow: #b58900; +orange: #cb4b16; +red: #dc322f; +magenta: #d33682; +violet: #6c71c4; +blue: #268bd2; +cyan: #2aa198; +green: #859900; +*/ + +.highlight { background: #fdf6e3; color: #657b83; line-height: 1em; font-size: 13px; } +.highlight .lineno { background-color: #eee8d5; color: #93a1a1; padding-left: .5em; padding-right: .5em; } +.highlight .hll { background-color: #eee8d5; } /* Don't know what this is... */ +.highlight .c { color: #93a1a1; font-style: italic; } /* Comment */ +.highlight .err { color: #dc322f; } /* Error */ +.highlight .g { color: #839496; } /* Generic */ +.highlight .k { color: #859900; } /* Keyword */ +.highlight .l { color: #839496; } /* Literal */ +.highlight .n { color: #839496; } /* Name */ +.highlight .o { color: #859900; } /* Operator */ +.highlight .x { color: #839496; } /* Other */ +.highlight .p { color: #839496; } /* Punctuation */ +.highlight .cm { color: #93a1a1; font-style: italic; } /* Comment.Multiline */ +.highlight .cp { color: #93a1a1; font-style: italic; } /* Comment.Preproc */ +.highlight .c1 { color: #93a1a1; font-style: italic; } /* Comment.Single */ +.highlight .cs { color: #93a1a1; font-style: italic; } /* Comment.Special */ +.highlight .gd { color: #dc322f; } /* Generic.Deleted */ +.highlight .ge { color: #839496; font-style: italic; } /* Generic.Emph */ +.highlight .gr { color: #839496; } /* Generic.Error */ +.highlight .gh { color: #839496; font-weight: bold; } /* Generic.Heading */ +.highlight .gi { color: #859900; } /* Generic.Inserted */ +.highlight .go { color: #839496; } /* Generic.Output */ +.highlight .gp { color: #839496; font-weight: bold; } /* Generic.Prompt */ +.highlight .gs { color: #839496; font-weight: bold; } /* Generic.Strong */ +.highlight .gu { color: #839496; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #839496; } /* Generic.Traceback */ +.highlight .kc { color: #839496; } /* Keyword.Constant */ +.highlight .kd { color: #839496; } /* Keyword.Declaration */ +.highlight .kn { color: #839496; } /* Keyword.Namespace */ +.highlight .kp { color: #2aa198; } /* Keyword.Pseudo */ +.highlight .kr { color: #839496; } /* Keyword.Reserved */ +.highlight .kt { color: #839496; } /* Keyword.Type */ +.highlight .ld { color: #839496; } /* Literal.Date */ +.highlight .m { color: #839496; } /* Literal.Number */ +.highlight .s { color: #2aa198; } /* Literal.String */ +.highlight .na { color: #839496; } /* Name.Attribute */ +.highlight .nb { color: #cb4b16; } /* Name.Builtin */ +.highlight .nc { color: #839496; } /* Name.Class */ +.highlight .no { color: #b58900; } /* Name.Constant */ +.highlight .nd { color: #839496; } /* Name.Decorator */ +.highlight .ni { color: #839496; } /* Name.Entity */ +.highlight .ne { color: #839496; } /* Name.Exception */ +.highlight .nf { color: #268bd2; } /* Name.Function */ +.highlight .nl { color: #839496; } /* Name.Label */ +.highlight .nn { color: #cb4b16; } /* Name.Namespace */ +.highlight .nx { color: #839496; } /* Name.Other */ +.highlight .py { color: #839496; } /* Name.Property */ +.highlight .nt { color: #839496; } /* Name.Tag */ +.highlight .nv { color: #839496; } /* Name.Variable */ +.highlight .ow { color: #839496; } /* Operator.Word */ +.highlight .w { color: #839496; } /* Text.Whitespace */ +.highlight .mf { color: #2aa198; } /* Literal.Number.Float */ +.highlight .mh { color: #2aa198; } /* Literal.Number.Hex */ +.highlight .mi { color: #2aa198; } /* Literal.Number.Integer */ +.highlight .mo { color: #2aa198; } /* Literal.Number.Oct */ +.highlight .sb { color: #2aa198; } /* Literal.String.Backtick */ +.highlight .sc { color: #2aa198; } /* Literal.String.Char */ +.highlight .sd { color: #2aa198; } /* Literal.String.Doc */ +.highlight .s2 { color: #2aa198; } /* Literal.String.Double */ +.highlight .se { color: #dc322f; } /* Literal.String.Escape */ +.highlight .sh { color: #2aa198; } /* Literal.String.Heredoc */ +.highlight .si { color: #2aa198; } /* Literal.String.Interpol */ +.highlight .sx { color: #2aa198; } /* Literal.String.Other */ +.highlight .sr { color: #2aa198; } /* Literal.String.Regex */ +.highlight .s1 { color: #2aa198; } /* Literal.String.Single */ +.highlight .ss { color: #2aa198; } /* Literal.String.Symbol */ +.highlight .bp { color: #839496; } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #839496; } /* Name.Variable.Class */ +.highlight .vg { color: #839496; } /* Name.Variable.Global */ +.highlight .vi { color: #839496; } /* Name.Variable.Instance */ +.highlight .il { color: #839496; } /* Literal.Number.Integer.Long */ From 305527145e375026a67c1b5c90ae529625dd6faa Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 12:10:51 -0600 Subject: [PATCH 057/267] Fix chapter --- chapters/classes_and_objects/chaining.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/classes_and_objects/chaining.textile b/chapters/classes_and_objects/chaining.textile index 8035604..d8e20b1 100644 --- a/chapters/classes_and_objects/chaining.textile +++ b/chapters/classes_and_objects/chaining.textile @@ -1,7 +1,7 @@ --- layout: recipe title: Chaining Calls to an Object -chapter: Objects +chapter: Classes and Objects --- h2. Problem From 476f88978bd255cc2c88beecdc059885978b9ef8 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 13:44:09 -0600 Subject: [PATCH 058/267] Convert recipes to Markdown JavaScript has Markdown libraries, but no Textile libraries. This will be useful for a future conversion to DocPad or Docco. --- ...arrays.textile => concatenating-arrays.md} | 7 +- ...ing-arrays.textile => filtering-arrays.md} | 9 +- ...ensions.textile => list-comprehensions.md} | 12 +- ...pping-arrays.textile => mapping-arrays.md} | 8 +- chapters/arrays/max-array-value.md | 30 ++++ chapters/arrays/max-array-value.textile | 31 ---- ...cing-arrays.textile => reducing-arrays.md} | 14 +- ...ing-arrays.textile => reversing-arrays.md} | 11 +- ...tile => using-arrays-to-swap-variables.md} | 10 +- .../{chaining.textile => chaining.md} | 9 +- ...s-variables.textile => class-variables.md} | 9 +- .../{cloning.textile => cloning.md} | 7 +- ...if-not-exist.textile => object-literal.md} | 11 +- ...te-of-easter.textile => date-of-easter.md} | 10 +- .../dates_and_times/date-of-thanksgiving.md | 159 ++++++++++++++++++ .../date-of-thanksgiving.textile | 55 ------ ...ates.textile => days-between-two-dates.md} | 10 +- ...xtile => finding-last-day-of-the-month.md} | 9 +- ....textile => finding-last-or-next-month.md} | 8 +- .../{bridge.textile => bridge.md} | 9 +- .../{builder.textile => builder.md} | 17 +- .../{decorator.textile => decorator.md} | 9 +- ...ctory_method.textile => factory_method.md} | 11 +- .../{interpreter.textile => interpreter.md} | 11 +- .../{memento.textile => memento.md} | 13 +- .../{singleton.textile => singleton.md} | 27 ++- .../{strategy.textile => strategy.md} | 15 +- .../{parentheses.textile => parentheses.md} | 11 +- .../{recursion.textile => recursion.md} | 10 +- ...t_arguments.textile => splat_arguments.md} | 14 +- chapters/index.html | 1 - chapters/jquery/{ajax.textile => ajax.md} | 15 +- ...ry.textile => callback-bindings-jquery.md} | 11 +- chapters/jquery/{plugin.textile => plugin.md} | 19 +-- .../math/{constants.textile => constants.md} | 11 +- ...st-fibonacci.textile => fast-fibonacci.md} | 32 ++-- ... generating-predictable-random-numbers.md} | 17 +- ...s.textile => generating-random-numbers.md} | 17 +- ...ans-degrees.textile => radians-degrees.md} | 9 +- ...e => detecting-and-replacing-functions.md} | 12 +- ...g-classes.textile => extending-classes.md} | 12 +- .../{basic-client.textile => basic-client.md} | 19 +-- .../{basic-server.textile => basic-server.md} | 19 +-- ...lient.textile => bi-directional-client.md} | 18 +- ...erver.textile => bi-directional-server.md} | 19 +-- .../{heregexes.textile => heregexes.md} | 14 +- ...ng-words.textile => capitalizing-words.md} | 10 +- ...bstrings.textile => finding-substrings.md} | 9 +- ...interpolation.textile => interpolation.md} | 10 +- .../{repeating.textile => repeating.md} | 9 +- ...a-string.textile => splitting-a-string.md} | 7 +- ...ing_ranges.textile => comparing_ranges.md} | 9 +- ...script.textile => embedding_javascript.md} | 12 +- .../{for_loops.textile => for_loops.md} | 7 +- 54 files changed, 451 insertions(+), 433 deletions(-) rename chapters/arrays/{concatenating-arrays.textile => concatenating-arrays.md} (92%) rename chapters/arrays/{filtering-arrays.textile => filtering-arrays.md} (90%) rename chapters/arrays/{list-comprehensions.textile => list-comprehensions.md} (75%) rename chapters/arrays/{mapping-arrays.textile => mapping-arrays.md} (96%) create mode 100644 chapters/arrays/max-array-value.md delete mode 100644 chapters/arrays/max-array-value.textile rename chapters/arrays/{reducing-arrays.textile => reducing-arrays.md} (51%) rename chapters/arrays/{reversing-arrays.textile => reversing-arrays.md} (68%) rename chapters/arrays/{using-arrays-to-swap-variables.textile => using-arrays-to-swap-variables.md} (82%) rename chapters/classes_and_objects/{chaining.textile => chaining.md} (94%) rename chapters/classes_and_objects/{class-variables.textile => class-variables.md} (78%) rename chapters/classes_and_objects/{cloning.textile => cloning.md} (96%) rename chapters/classes_and_objects/{create-object-literal-if-not-exist.textile => object-literal.md} (89%) rename chapters/dates_and_times/{date-of-easter.textile => date-of-easter.md} (90%) create mode 100644 chapters/dates_and_times/date-of-thanksgiving.md delete mode 100644 chapters/dates_and_times/date-of-thanksgiving.textile rename chapters/dates_and_times/{days-between-two-dates.textile => days-between-two-dates.md} (96%) rename chapters/dates_and_times/{finding-last-day-of-the-month.textile => finding-last-day-of-the-month.md} (95%) rename chapters/dates_and_times/{finding-last-or-next-month.textile => finding-last-or-next-month.md} (97%) rename chapters/design_patterns/{bridge.textile => bridge.md} (94%) rename chapters/design_patterns/{builder.textile => builder.md} (85%) rename chapters/design_patterns/{decorator.textile => decorator.md} (97%) rename chapters/design_patterns/{factory_method.textile => factory_method.md} (80%) rename chapters/design_patterns/{interpreter.textile => interpreter.md} (98%) rename chapters/design_patterns/{memento.textile => memento.md} (76%) rename chapters/design_patterns/{singleton.textile => singleton.md} (67%) rename chapters/design_patterns/{strategy.textile => strategy.md} (86%) rename chapters/functions/{parentheses.textile => parentheses.md} (78%) rename chapters/functions/{recursion.textile => recursion.md} (85%) rename chapters/functions/{splat_arguments.textile => splat_arguments.md} (92%) rename chapters/jquery/{ajax.textile => ajax.md} (85%) rename chapters/jquery/{callback-bindings-jquery.textile => callback-bindings-jquery.md} (69%) rename chapters/jquery/{plugin.textile => plugin.md} (90%) rename chapters/math/{constants.textile => constants.md} (76%) rename chapters/math/{fast-fibonacci.textile => fast-fibonacci.md} (64%) rename chapters/math/{generating-predictable-random-numbers.textile => generating-predictable-random-numbers.md} (81%) rename chapters/math/{generating-random-numbers.textile => generating-random-numbers.md} (80%) rename chapters/math/{radians-degrees.textile => radians-degrees.md} (91%) rename chapters/metaprogramming/{detecting-and-replacing-functions.textile => detecting-and-replacing-functions.md} (79%) rename chapters/metaprogramming/{extending-classes.textile => extending-classes.md} (76%) rename chapters/networking/{basic-client.textile => basic-client.md} (68%) rename chapters/networking/{basic-server.textile => basic-server.md} (72%) rename chapters/networking/{bi-directional-client.textile => bi-directional-client.md} (79%) rename chapters/networking/{bi-directional-server.textile => bi-directional-server.md} (75%) rename chapters/regular_expressions/{heregexes.textile => heregexes.md} (87%) rename chapters/strings/{capitalizing-words.textile => capitalizing-words.md} (92%) rename chapters/strings/{finding-substrings.textile => finding-substrings.md} (92%) rename chapters/strings/{interpolation.textile => interpolation.md} (91%) rename chapters/strings/{repeating.textile => repeating.md} (93%) rename chapters/strings/{splitting-a-string.textile => splitting-a-string.md} (95%) rename chapters/syntax/{comparing_ranges.textile => comparing_ranges.md} (94%) rename chapters/syntax/{embedding_javascript.textile => embedding_javascript.md} (66%) rename chapters/syntax/{for_loops.textile => for_loops.md} (93%) diff --git a/chapters/arrays/concatenating-arrays.textile b/chapters/arrays/concatenating-arrays.md similarity index 92% rename from chapters/arrays/concatenating-arrays.textile rename to chapters/arrays/concatenating-arrays.md index 7fae568..b6fcd13 100644 --- a/chapters/arrays/concatenating-arrays.textile +++ b/chapters/arrays/concatenating-arrays.md @@ -3,12 +3,11 @@ layout: recipe title: Concatenating Arrays chapter: Arrays --- - -h2. Problem +## Problem You want to join two arrays together. -h2. Solution +## Solution Use JavaScript's Array concat() method: @@ -19,7 +18,7 @@ ray3 = ray1.concat(ray2) # => [1, 2, 3, 4, 5, 6] {% endhighlight %} -h2. Discussion +## Discussion CoffeeScript lacks a special syntax for joining arrays, but concat is a standard JavaScript method. diff --git a/chapters/arrays/filtering-arrays.textile b/chapters/arrays/filtering-arrays.md similarity index 90% rename from chapters/arrays/filtering-arrays.textile rename to chapters/arrays/filtering-arrays.md index 4ee7e1d..34e14cb 100644 --- a/chapters/arrays/filtering-arrays.textile +++ b/chapters/arrays/filtering-arrays.md @@ -3,12 +3,11 @@ layout: recipe title: Filtering Arrays chapter: Arrays --- - -h2. Problem +## Problem You want to be able to filter arrays based on a boolean condition. -h2. Solution +## Solution Use Array.filter (ECMAScript 5): @@ -38,6 +37,6 @@ filtered_array = array.filter gt_five # => [6,7,8,9,10] {% endhighlight %} -h2. Discussion +## Discussion -This is similair to using ruby's Array#select method. +This is similar to using ruby's Array#select method. diff --git a/chapters/arrays/list-comprehensions.textile b/chapters/arrays/list-comprehensions.md similarity index 75% rename from chapters/arrays/list-comprehensions.textile rename to chapters/arrays/list-comprehensions.md index 9794c0b..68e7c88 100644 --- a/chapters/arrays/list-comprehensions.textile +++ b/chapters/arrays/list-comprehensions.md @@ -3,14 +3,13 @@ layout: recipe title: List Comprehensions chapter: Arrays --- - -h2. Problem +## Problem You have an array of objects and want to map them to another array, similar to Python's list comprehensions. -h2. Solution +## Solution -Use a list comprehension, but don't forget about mapping arrays. +Use a list comprehension, but don't forget about [mapping arrays](/chapters/arrays/mapping-arrays). {% highlight coffeescript %} electric_mayhem = [ { name: "Doctor Teeth", instrument: "piano" }, @@ -24,7 +23,6 @@ names = (muppet.name for muppet in electric_mayhem) # => [ 'Doctor Teeth', 'Janice', 'Sgt. Floyd Pepper', 'Zoot', 'Lips', 'Animal' ] {% endhighlight %} -h2. Discussion - -Because CoffeeScript directly support list comprehensions, they work pretty much as advertised wherever you would use one in Python. For simple mappings, list comprehensions are much more readable. For complicated transformations or for chained mappings, mapping arrays might be more elegant. +## Discussion +Because CoffeeScript directly support list comprehensions, they work pretty much as advertised wherever you would use one in Python. For simple mappings, list comprehensions are much more readable. For complicated transformations or for chained mappings, [mapping arrays](/chapters/arrays/mapping-arrays) might be more elegant. diff --git a/chapters/arrays/mapping-arrays.textile b/chapters/arrays/mapping-arrays.md similarity index 96% rename from chapters/arrays/mapping-arrays.textile rename to chapters/arrays/mapping-arrays.md index 6bf7e5a..6a54964 100644 --- a/chapters/arrays/mapping-arrays.textile +++ b/chapters/arrays/mapping-arrays.md @@ -3,12 +3,11 @@ layout: recipe title: Mapping Arrays chapter: Arrays --- - -h2. Problem +## Problem You have an array of objects and want to map them to another array, similar to Ruby's map. -h2. Solution +## Solution Use map() with an anonymous function, but don't forget about list comprehensions. @@ -24,9 +23,8 @@ names = electric_mayhem.map (muppet) -> muppet.name # => [ 'Doctor Teeth', 'Janice', 'Sgt. Floyd Pepper', 'Zoot', 'Lips', 'Animal' ] {% endhighlight %} -h2. Discussion +## Discussion Because CoffeeScript has clean support for anonymous functions, mapping an array in CoffeeScript is nearly as easy as it is in Ruby. Maps are are good way to handle complicated transforms and chained mappings in CoffeeScript. If your transformation is as simple as the one above, however, it may read more cleanly as a list comprehension. - diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md new file mode 100644 index 0000000..ab8cb70 --- /dev/null +++ b/chapters/arrays/max-array-value.md @@ -0,0 +1,30 @@ +--- +layout: recipe +title: Max Array Value +chapter: Arrays +--- +## Problem + +You need to find the largest value contained in an array. + +## Solution + +In ECMAScript 5, use `Array#reduce`. In older javascripts, use Math.max over a list comprehension: + +{% highlight coffeescript %} +# ECMAScript 5 +[12,32,11,67,1,3].reduce (a,b) -> Math.max(a,b) +# => 67 + +# Pre-EC5 +max = Math.max(max or= num, num) for num in [12,32,11,67,1,3] +# => [ 12, 32, 32, 67, 67, 67 ] +max +# => 67 +{% endhighlight %} + +## Discussion + +`Math.max` compares two numbers and returns the larger of the two; the rest of this recipe (both versions) is just iterating over the array to find the largest one. It is interesting to note that when assigning from a list comprehension, the last item evaluated will be returned to the assignment but the expression itself (such as in the node.js coffeescript interpreter) evaluates to the list comprehension's complete mapping. This means that the Pre-EC5 version will duplicate the array. + +For very large arrays in ECMAScript 4, a more memory efficient approach might be to initialize `max` to the first element of the array, and then loop over it with a traditional for loop. Since non-idiomatic CoffeeScript is discouraged in the Cookbook, this approach is left as an exercise to the reader. diff --git a/chapters/arrays/max-array-value.textile b/chapters/arrays/max-array-value.textile deleted file mode 100644 index bbad4b7..0000000 --- a/chapters/arrays/max-array-value.textile +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: recipe -title: Max Array Value -chapter: Arrays ---- - -h2. Problem - -You need to find the largest value contained in an array. - -h2. Solution - -In ECMAScript 5, use Array#reduce. In older javascripts, use Math.max over a list comprehension: - -{% highlight coffeescript %} -# ECMAScript 5 -[12,32,11,67,1,3].reduce (a,b) -> Math.max(a,b) -# => 67 - -# Pre-EC5 -max = Math.max(max or= num, num) for num in [12,32,11,67,1,3] -# => [ 12, 32, 32, 67, 67, 67 ] -max -# => 67 -{% endhighlight %} - -h2. Discussion - -Math.max compares two numbers and returns the larger of the two; the rest of this recipe (both versions) is just iterating over the array to find the largest one. It is interesting to note that when assigning from a list comprehension, the last item evaluated will be returned to the assignment but the expression itself (such as in the node.js coffeescript interpreter) evaluates to the list comprehension's complete mapping. This means that the Pre-EC5 version will duplicate the array. - -For very large arrays in ECMAScript 4, a more memory efficient approach might be to initialize max to the first element of the array, and then loop over it with a traditional for loop. Since non-idiomatic CoffeeScript is discouraged in the Cookbook, this approach is left as an exercise to the reader. diff --git a/chapters/arrays/reducing-arrays.textile b/chapters/arrays/reducing-arrays.md similarity index 51% rename from chapters/arrays/reducing-arrays.textile rename to chapters/arrays/reducing-arrays.md index 2f43560..7e85463 100644 --- a/chapters/arrays/reducing-arrays.textile +++ b/chapters/arrays/reducing-arrays.md @@ -3,14 +3,13 @@ layout: recipe title: Reducing Arrays chapter: Arrays --- +## Problem -h2. Problem +You have an array of objects and want to reduce them to a value, similar to Ruby's `reduce()` and `reduceRight()`. -You have an array of objects and want to reduce them to a value, similar to Ruby's reduce() and reduceRight(). +## Solution -h2. Solution - -You can simply use Array's reduce() and reduceRight() methods along with an anonoymous function, keeping the code clean and readable. The reduction may be something simple such as using the + operator with numbers or strings. +You can simply use Array's `reduce()` and `reduceRight()` methods along with an anonoymous function, keeping the code clean and readable. The reduction may be something simple such as using the `+` operator with numbers or strings. {% highlight coffeescript %} [1,2,3,4].reduce (x,y) -> x + y @@ -37,7 +36,6 @@ people.reduce (x, y) -> # => { alec: 10, bert: 16, chad: 17 } {% endhighlight %} -h2. Discussion - -Javascript introduced reduce and reduceRight in version 1.8. Coffeescript provides a natural and simple way to express anonymous functions. Both go together cleanly in the problem of merging a collection's items into a combined result. +## Discussion +Javascript introduced `reduce` and `reduceRight` in version 1.8. Coffeescript provides a natural and simple way to express anonymous functions. Both go together cleanly in the problem of merging a collection's items into a combined result. diff --git a/chapters/arrays/reversing-arrays.textile b/chapters/arrays/reversing-arrays.md similarity index 68% rename from chapters/arrays/reversing-arrays.textile rename to chapters/arrays/reversing-arrays.md index ae3cdf3..ba599ec 100644 --- a/chapters/arrays/reversing-arrays.textile +++ b/chapters/arrays/reversing-arrays.md @@ -3,12 +3,11 @@ layout: recipe title: Reversing Arrays chapter: Arrays --- - -h2. Problem +## Problem You want to reverse an array. -h2. Solution +## Solution Use JavaScript's Array reverse() method: @@ -17,8 +16,6 @@ Use JavaScript's Array reverse() method: # => ["three", "two", "one"] {% endhighlight %} +## Discussion -h2. Discussion - -reverse() is a standard JavaScript method. Don't forget the parentheses. - +`reverse()` is a standard JavaScript method. Don't forget the parentheses. diff --git a/chapters/arrays/using-arrays-to-swap-variables.textile b/chapters/arrays/using-arrays-to-swap-variables.md similarity index 82% rename from chapters/arrays/using-arrays-to-swap-variables.textile rename to chapters/arrays/using-arrays-to-swap-variables.md index 162e7e8..0ff8905 100644 --- a/chapters/arrays/using-arrays-to-swap-variables.textile +++ b/chapters/arrays/using-arrays-to-swap-variables.md @@ -3,14 +3,13 @@ layout: recipe title: Using Arrays to Swap Variables chapter: Arrays --- - -h2. Problem +## Problem You want to use an array to swap variables. -h2. Solution +## Solution -Use CoffeeScript's "destructuring assignment":http://jashkenas.github.com/coffee-script/#destructuring syntax: +Use CoffeeScript's [destructuring assignment](http://jashkenas.github.com/coffee-script/#destructuring) syntax: {% highlight coffeescript %} a = 1 @@ -25,8 +24,7 @@ b # => 1 {% endhighlight %} - -h2. Discussion +## Discussion Destructuring assignment allows swapping two values without the use of a temporary variable. diff --git a/chapters/classes_and_objects/chaining.textile b/chapters/classes_and_objects/chaining.md similarity index 94% rename from chapters/classes_and_objects/chaining.textile rename to chapters/classes_and_objects/chaining.md index d8e20b1..bec3a46 100644 --- a/chapters/classes_and_objects/chaining.textile +++ b/chapters/classes_and_objects/chaining.md @@ -3,14 +3,13 @@ layout: recipe title: Chaining Calls to an Object chapter: Classes and Objects --- - -h2. Problem +## Problem You want to call multiple methods on a single object without having to reference that object each time. -h2. Solution +## Solution -Return the *this* (i.e. *@*) object after every chained method. +Return the `this` (i.e. `@`) object after every chained method. {% highlight coffeescript %} class CoffeeCup @@ -38,7 +37,7 @@ eveningCup.properties # => { strength: 'dark', cream: true, sugar: true } {% endhighlight %} -h2. Discussion +## Discussion The jQuery library uses a similar approach by returning a selector object from every relevant method, modifying it as subsequent methods tweak the selection: diff --git a/chapters/classes_and_objects/class-variables.textile b/chapters/classes_and_objects/class-variables.md similarity index 78% rename from chapters/classes_and_objects/class-variables.textile rename to chapters/classes_and_objects/class-variables.md index 594f9a9..d9f3a9a 100644 --- a/chapters/classes_and_objects/class-variables.textile +++ b/chapters/classes_and_objects/class-variables.md @@ -3,14 +3,13 @@ layout: recipe title: Class Variables chapter: Classes and Objects --- - -h2. Creating Class Variables +## Creating Class Variables You want to create a class variable. -h2. Solution +## Solution -Use json notation in the class body; use :: to access it outside: +Use json notation in the class body; use `::` to access it outside: {% highlight coffeescript %} class Zoo @@ -22,6 +21,6 @@ Zoo::MAX_ANIMALS # => 50 {% endhighlight %} -h2. Discussion +## Discussion Coffeescript will store these values on the class prototype (e.g. Zoo.prototype.MAX_ANIMALS) rather than on individual object instances, which conserves memory and gives a central location to store class-level values. diff --git a/chapters/classes_and_objects/cloning.textile b/chapters/classes_and_objects/cloning.md similarity index 96% rename from chapters/classes_and_objects/cloning.textile rename to chapters/classes_and_objects/cloning.md index 49b56d9..1b52bcc 100644 --- a/chapters/classes_and_objects/cloning.textile +++ b/chapters/classes_and_objects/cloning.md @@ -3,12 +3,11 @@ layout: recipe title: Cloning an object (deep copy) chapter: Classes and Objects --- - -h2. Problem +## Problem You want to clone an object with all its sub-objects. -h2. Solution +## Solution {% highlight coffeescript %} clone = (obj) -> @@ -34,7 +33,7 @@ console.log x.foo isnt y.foo, x.foo, y.foo # => true, bar, test {% endhighlight %} -h2. Discussion +## Discussion The difference between copying an object through assignment and through this clone-function is how they handle references. The assignment only copies the object's reference, whereas the clone-function creates a complete new object by diff --git a/chapters/classes_and_objects/create-object-literal-if-not-exist.textile b/chapters/classes_and_objects/object-literal.md similarity index 89% rename from chapters/classes_and_objects/create-object-literal-if-not-exist.textile rename to chapters/classes_and_objects/object-literal.md index 3cbb5cc..616f7c4 100644 --- a/chapters/classes_and_objects/create-object-literal-if-not-exist.textile +++ b/chapters/classes_and_objects/object-literal.md @@ -3,22 +3,19 @@ layout: recipe title: Create an object literal if it does not already exist chapter: Classes and Objects --- - -h2. Problem +## Problem You want to initialize an object literal, but you do not want to overwrite the object if it already exists. - -h2. Solution +## Solution Use the Existential operator {% highlight coffeescript %} -window.MY_NAMESPACE ?= {} +window.MY_NAMESPACE ?= {} {% endhighlight %} - -h2. Discussion +## Discussion This is equivalent to the following JavaScript: diff --git a/chapters/dates_and_times/date-of-easter.textile b/chapters/dates_and_times/date-of-easter.md similarity index 90% rename from chapters/dates_and_times/date-of-easter.textile rename to chapters/dates_and_times/date-of-easter.md index 020a9e2..1ce890e 100644 --- a/chapters/dates_and_times/date-of-easter.textile +++ b/chapters/dates_and_times/date-of-easter.md @@ -3,16 +3,15 @@ layout: recipe title: Calculate the date of Easter Sunday chapter: Dates and Times --- - -h2. Problem +## Problem You need to find the month and day of the Easter Sunday for given year. -h2. Solution +## Solution The following function returns array with two elements: month (1-12) and day of the Easter Sunday. If no arguments are given result is for the current year. -This is an implementation of "Anonymous Gregorian algorithm":http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm in CoffeeScript. +This is an implementation of [Anonymous Gregorian algorithm](http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm) in CoffeeScript. {% highlight coffeescript %} @@ -36,7 +35,7 @@ gregorianEaster = (year = (new Date).getFullYear()) -> {% endhighlight %} -h2. Discussion +## Discussion NB! Javascript numbers months from 0 to 11 so .getMonth() for date in March will return 2, this function will return 3. You can modify the function if you want this to be consistent. @@ -49,4 +48,3 @@ gregorianEaster() # => [4, 24] (April 24th in 2011) gregorianEaster 1972 # => [4, 2] {% endhighlight %} - diff --git a/chapters/dates_and_times/date-of-thanksgiving.md b/chapters/dates_and_times/date-of-thanksgiving.md new file mode 100644 index 0000000..ea9a3c7 --- /dev/null +++ b/chapters/dates_and_times/date-of-thanksgiving.md @@ -0,0 +1,159 @@ +--- +layout: recipe +title: Calculate the date of Thanksgiving (USA and Canada) +chapter: Dates and Times +--- +## Problem + +You need to calculate when is Thanksgivinig in given year. + +## Solution + +The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. + +In the USA Thanksgiving is celebrated on the fourth Thursday in November: + +{% highlight coffeescript %} + +thanksgivingDayUSA = (year = (new Date).getFullYear()) -> + first = new Date year, 10, 1 + day_of_week = first.getDay() + 22 + (11 - day_of_week) % 7 + +{% endhighlight %} + +In Canada it is the second Monday in October: + +{% highlight coffeescript %} + +thanksgivingDayCA = (year = (new Date).getFullYear()) -> + first = new Date year, 9, 1 + day_of_week = first.getDay() + 8 + (8 - day_of_week) % 7 + +{% endhighlight %} + +## Discussion + +{% highlight coffeescript %} + +thanksgivingDayUSA() #=> 24 (November 24th, 2011) + +thanksgivingDayCA() # => 10 (October 10th, 2011) + +thanksgivingDayUSA(2012) # => 22 (November 22nd) + +thanksgivingDayCA(2012) # => 8 (October 8th) + +{% endhighlight %} + +The idea is very simple: +* Find out what day of the week is the first day of respective month (November for USA, October for Canada). +* Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). +* Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). +--- +layout: recipe +title: Calculate the date of Thanksgiving (USA and Canada) +chapter: Dates and Times +--- +## Problem + +You need to calculate when is Thanksgivinig in given year. + +## Solution + +The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. + +In the USA Thanksgiving is celebrated on the fourth Thursday in November: + +{% highlight coffeescript %} + +thanksgivingDayUSA = (year = (new Date).getFullYear()) -> + first = new Date year, 10, 1 + day_of_week = first.getDay() + 22 + (11 - day_of_week) % 7 + +{% endhighlight %} + +In Canada it is the second Monday in October: + +{% highlight coffeescript %} + +thanksgivingDayCA = (year = (new Date).getFullYear()) -> + first = new Date year, 9, 1 + day_of_week = first.getDay() + 8 + (8 - day_of_week) % 7 + +{% endhighlight %} + +## Discussion + +{% highlight coffeescript %} + +thanksgivingDayUSA() #=> 24 (November 24th, 2011) + +thanksgivingDayCA() # => 10 (October 10th, 2011) + +thanksgivingDayUSA(2012) # => 22 (November 22nd) + +thanksgivingDayCA(2012) # => 8 (October 8th) + +{% endhighlight %} + +The idea is very simple: +* Find out what day of the week is the first day of respective month (November for USA, October for Canada). +* Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). +* Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). +--- +layout: recipe +title: Calculate the date of Thanksgiving (USA and Canada) +chapter: Dates and Times +--- +## Problem + +You need to calculate when is Thanksgivinig in given year. + +## Solution + +The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. + +In the USA Thanksgiving is celebrated on the fourth Thursday in November: + +{% highlight coffeescript %} + +thanksgivingDayUSA = (year = (new Date).getFullYear()) -> + first = new Date year, 10, 1 + day_of_week = first.getDay() + 22 + (11 - day_of_week) % 7 + +{% endhighlight %} + +In Canada it is the second Monday in October: + +{% highlight coffeescript %} + +thanksgivingDayCA = (year = (new Date).getFullYear()) -> + first = new Date year, 9, 1 + day_of_week = first.getDay() + 8 + (8 - day_of_week) % 7 + +{% endhighlight %} + +## Discussion + +{% highlight coffeescript %} + +thanksgivingDayUSA() #=> 24 (November 24th, 2011) + +thanksgivingDayCA() # => 10 (October 10th, 2011) + +thanksgivingDayUSA(2012) # => 22 (November 22nd) + +thanksgivingDayCA(2012) # => 8 (October 8th) + +{% endhighlight %} + +The idea is very simple: +1. Find out what day of the week is the first day of respective month (November for USA, October for Canada). +2. Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). +3. Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). diff --git a/chapters/dates_and_times/date-of-thanksgiving.textile b/chapters/dates_and_times/date-of-thanksgiving.textile deleted file mode 100644 index 991bbfc..0000000 --- a/chapters/dates_and_times/date-of-thanksgiving.textile +++ /dev/null @@ -1,55 +0,0 @@ ---- -layout: recipe -title: Calculate the date of Thanksgiving (USA and Canada) -chapter: Dates and Times ---- - -h2. Problem - -You need to calculate when is Thanksgivinig in given year. - -h2. Solution - -The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. - -In the USA Thanksgiving is celebrated on the fourth Thursday in November: - -{% highlight coffeescript %} - -thanksgivingDayUSA = (year = (new Date).getFullYear()) -> - first = new Date year, 10, 1 - day_of_week = first.getDay() - 22 + (11 - day_of_week) % 7 - -{% endhighlight %} - -In Canada it is the second Monday in October: - -{% highlight coffeescript %} - -thanksgivingDayCA = (year = (new Date).getFullYear()) -> - first = new Date year, 9, 1 - day_of_week = first.getDay() - 8 + (8 - day_of_week) % 7 - -{% endhighlight %} - -h2. Discussion - - -{% highlight coffeescript %} - -thanksgivingDayUSA() #=> 24 (November 24th, 2011) - -thanksgivingDayCA() # => 10 (October 10th, 2011) - -thanksgivingDayUSA(2012) # => 22 (November 22nd) - -thanksgivingDayCA(2012) # => 8 (October 8th) - -{% endhighlight %} - -The idea is very simple: -# Find out what day of the week is the first day of respective month (November for USA, October for Canada). -# Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). -# Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). diff --git a/chapters/dates_and_times/days-between-two-dates.textile b/chapters/dates_and_times/days-between-two-dates.md similarity index 96% rename from chapters/dates_and_times/days-between-two-dates.textile rename to chapters/dates_and_times/days-between-two-dates.md index 7649c81..f89b7f7 100644 --- a/chapters/dates_and_times/days-between-two-dates.textile +++ b/chapters/dates_and_times/days-between-two-dates.md @@ -3,12 +3,11 @@ layout: recipe title: Get Days Between two Dates chapter: Dates and Times --- - -h2. Problem +## Problem You need to find how much seconds minutes, hours, days, months or years has passed between two dates. -h2. Solution +## Solution Use JavaScript's Date function getTime(). Which provides how much time in miliseconds has passed since 01/01/1970: @@ -21,10 +20,10 @@ d2 = new Date('02/06/2011') days_passed = Math.round((d2.getTime() - d1.getTime()) / DAY) {% endhighlight %} -h2. Discussion +## Discussion Using miliseconds makes the life easier to avoid overflow mistakes with Dates. So we first calculate how much miliseconds has a day. -Then, given two distincit dates, just get the diference in miliseconds betwen two dates and then divide by how much miliseconds has a +Then, given two distincit dates, just get the diference in miliseconds betwen two dates and then divide by how much miliseconds has a day. It will get you the days between two distinct dates. If you'd like to calculate the hours between two dates objects you can do that just by dividing the diference in miliseconds by the @@ -38,4 +37,3 @@ d2 = new Date('02/06/2011 05:20') hour_passed = Math.round((d2.getTime() - d1.getTime()) / HOUR) {% endhighlight %} - diff --git a/chapters/dates_and_times/finding-last-day-of-the-month.textile b/chapters/dates_and_times/finding-last-day-of-the-month.md similarity index 95% rename from chapters/dates_and_times/finding-last-day-of-the-month.textile rename to chapters/dates_and_times/finding-last-day-of-the-month.md index 6cd96b2..c054afd 100644 --- a/chapters/dates_and_times/finding-last-day-of-the-month.textile +++ b/chapters/dates_and_times/finding-last-day-of-the-month.md @@ -3,12 +3,11 @@ layout: recipe title: Finding the Last Day of the Month chapter: Dates and Times --- - -h2. Problem +## Problem You need to find the last day of the month, but don't want to keep a lookup table of the number of days in each month of the year. -h2. Solution +## Solution Use JavaScript's Date underflow to find the -1th day of the following month: @@ -17,8 +16,6 @@ now = new Date lastDayOfTheMonth = new Date(1900+now.getYear(), now.getMonth()+1, -1) {% endhighlight %} -h2. Discussion +## Discussion JavaScript's Date constructor cheerfully handles overflow and underflow conditions, which makes date math very easy. Given this ease of manipulation, it doesn't make sense to worry about how many days are in a given month; just nudge the math around. In December, the solution above will actually ask for the -1th day of the 13th month of the current year, which works out to the -1th day of January of NEXT year, which works out to the 31st day of December of the current year. - - diff --git a/chapters/dates_and_times/finding-last-or-next-month.textile b/chapters/dates_and_times/finding-last-or-next-month.md similarity index 97% rename from chapters/dates_and_times/finding-last-or-next-month.textile rename to chapters/dates_and_times/finding-last-or-next-month.md index 19195be..81ea611 100644 --- a/chapters/dates_and_times/finding-last-or-next-month.textile +++ b/chapters/dates_and_times/finding-last-or-next-month.md @@ -3,12 +3,11 @@ layout: recipe title: Finding Last (or Next) Month chapter: Dates and Times --- - -h2. Problem +## Problem You need to calculate a relative date range like "last month" or "next month". -h2. Solution +## Solution Add or subtract from the current month, secure in the knowledge that JavaScript's Date constructor will fix up the math. @@ -25,7 +24,7 @@ lastMonthEnd = new Date 1900+now.getYear(), now.getMonth(), 0 # => "Sat, 30 Apr 2011 06:00:00 GMT" {% endhighlight %} -h2. Discussion +## Discussion JavaScript Date objects will cheerfully handle underflows and overflows in the month and day fields, and will adjust the date object accordingly. You can ask for the 42nd of March, for example, and will get the 11th of April. @@ -42,4 +41,3 @@ The same is true for overflows: thirtyNinthOfFourteember = new Date 1900+now.getYear(), 13, 39 # => "Sat, 10 Mar 2012 07:00:00 GMT" {% endhighlight %} - diff --git a/chapters/design_patterns/bridge.textile b/chapters/design_patterns/bridge.md similarity index 94% rename from chapters/design_patterns/bridge.textile rename to chapters/design_patterns/bridge.md index 80be632..b9d6f56 100644 --- a/chapters/design_patterns/bridge.textile +++ b/chapters/design_patterns/bridge.md @@ -3,12 +3,11 @@ layout: recipe title: Bridge Pattern chapter: Design Patterns --- - -h2. Problem +## Problem You need to maintain a reliable interface for code that can change frequently or change between multiple implementations. -h2. Solution +## Solution Use the Bridge pattern as an intermediate between the different implementations and the rest of the code. @@ -50,6 +49,6 @@ else if root? saver.save data {% endhighlight %} -h2. Discussion +## Discussion -The Bridge pattern helps you to move the implementation-specific code out of sight so that you can focus on your program's specific code. In the above example, the rest of your application can call _saver.save data_ without regard for where the file ultimately ends up. +The Bridge pattern helps you to move the implementation-specific code out of sight so that you can focus on your program's specific code. In the above example, the rest of your application can call `saver.save data` without regard for where the file ultimately ends up. diff --git a/chapters/design_patterns/builder.textile b/chapters/design_patterns/builder.md similarity index 85% rename from chapters/design_patterns/builder.textile rename to chapters/design_patterns/builder.md index 9fc8191..ead9d3f 100644 --- a/chapters/design_patterns/builder.textile +++ b/chapters/design_patterns/builder.md @@ -3,16 +3,15 @@ layout: recipe title: Builder Pattern chapter: Design Patterns --- - -h2. Problem +## Problem You need to prepare a complicated, multi-part object, but you expect to do it more than once or with varying configurations. -h2. Solution +## Solution Create a Builder to encapsulate the object production process. -The Todo.txt format provides an advanced but still plain-text method for maintaining lists of to-do items. Typing out each item by hand would provide exhausting and error-prone, however, so a TodoTxtBuilder class could save us the trouble: +The [Todo.txt](http://todotxt.com) format provides an advanced but still plain-text method for maintaining lists of to-do items. Typing out each item by hand would provide exhausting and error-prone, however, so a TodoTxtBuilder class could save us the trouble: {% highlight coffeescript %} class TodoTxtBuilder @@ -51,11 +50,11 @@ workBuilder.newTodo "Remind Sean about the failing unit tests", contexts: ["meet {% endhighlight %} -h2. Discussion +## Discussion The TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each to-do item. Additionally, a command line tool or GUI could plug into this code and still retain support for later, more advanced versions of the format with ease. -h3. Pre-Construction +### Pre-Construction Instead of creating a new instance of the needed object from scratch every time, we shift the burden to a separate object that we can then tweak during the object creation process. @@ -85,7 +84,7 @@ builder.newTodo "Fill gas tank" # => '2011-10-13 Fill gas tank +summerVacation' {% endhighlight %} -h3. Exercises -* Expand the project- and context-tag generation code to filter out duplicate entries. -** Some Todo.txt users like to insert project and context tags inside the description of their to-do items. Add code to identify these tags and filter them out of the end tags. +### Exercises +* Expand the project- and context-tag generation code to filter out duplicate entries. +* Some Todo.txt users like to insert project and context tags inside the description of their to-do items. * Add code to identify these tags and filter them out of the end tags. diff --git a/chapters/design_patterns/decorator.textile b/chapters/design_patterns/decorator.md similarity index 97% rename from chapters/design_patterns/decorator.textile rename to chapters/design_patterns/decorator.md index 89cee57..0be3d7a 100644 --- a/chapters/design_patterns/decorator.textile +++ b/chapters/design_patterns/decorator.md @@ -3,12 +3,11 @@ layout: recipe title: Decorator Pattern chapter: Design Patterns --- - -h2. Problem +## Problem You have a set of data that you need to process in multiple, possibly varying ways. -h2. Solution +## Solution Use the Decorator pattern in order to structure how you apply the changes. @@ -53,7 +52,7 @@ processor.processString exampleText # => "

      A level 1 header

      \n

      A regular line

      \n\n

      A level 2 header

      \n

      A line

      " {% endhighlight %} -h3. Results +### Results {% highlight html %}

      A level 1 header

      @@ -63,7 +62,7 @@ h3. Results

      A line

      {% endhighlight %} -h2. Discussion +## Discussion The TextProcessor serves the role of Decorator by binding the individual, specialized text processors together. This frees up the miniMarkdown and stripComments components to focus on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors. diff --git a/chapters/design_patterns/factory_method.textile b/chapters/design_patterns/factory_method.md similarity index 80% rename from chapters/design_patterns/factory_method.textile rename to chapters/design_patterns/factory_method.md index bb027df..70e2770 100644 --- a/chapters/design_patterns/factory_method.textile +++ b/chapters/design_patterns/factory_method.md @@ -3,16 +3,15 @@ layout: recipe title: Factory Method Pattern chapter: Design Patterns --- - -h2. Problem +## Problem You don't know what kind of object you will need until runtime. -h2. Solution +## Solution -Use the "Factory Method":http://en.wikipedia.org/wiki/Factory_method_pattern pattern and choose the object to be generated dynamically. +Use the [Factory Method](http://en.wikipedia.org/wiki/Factory_method_pattern) pattern and choose the object to be generated dynamically. -Say that you need to load a file into an editor but you don't know its format until the user chooses the file. A class using the "Factory Method":http://en.wikipedia.org/wiki/Factory_method_pattern pattern can serve up different parsers depending on the file's extension. +Say that you need to load a file into an editor but you don't know its format until the user chooses the file. A class using the [Factory Method](http://en.wikipedia.org/wiki/Factory_method_pattern) pattern can serve up different parsers depending on the file's extension. {% highlight coffeescript %} class HTMLParser @@ -45,6 +44,6 @@ factory.makeParser("example.md").type # => "Markdown parser" factory.makeParser("example.json").type # => "JSON parser" {% endhighlight %} -h2. Discussion +## Discussion In the example, you can ignore the specifics of the file's format and focus on the parsed content. A more advanced Factory Method might, for instance, also search for versioning data within the file itself before returning a more precise parser (e.g. an HTML5 parser instead of an HTML v4 parser). diff --git a/chapters/design_patterns/interpreter.textile b/chapters/design_patterns/interpreter.md similarity index 98% rename from chapters/design_patterns/interpreter.textile rename to chapters/design_patterns/interpreter.md index 8d4e49e..0f6ba9d 100644 --- a/chapters/design_patterns/interpreter.textile +++ b/chapters/design_patterns/interpreter.md @@ -3,12 +3,11 @@ layout: recipe title: Interpreter Pattern chapter: Design Patterns --- - -h2. Problem +## Problem Someone else needs to run parts of your code in a controlled fashion. Alternately, your language of choice cannot express the problem domain in a concise fashion. -h2. Solution +## Solution Use the Interpreter pattern to create a domain-specific language that you translate into specific code. @@ -50,7 +49,7 @@ class StackCalculator left / right else throw "Unrecognized operator: #{operator}" - + @stack.push result calc = new StackCalculator @@ -82,14 +81,14 @@ catch error error # => "Unrecognized operator: foo" {% endhighlight %} -h2. Discussion +## Discussion As an alternative to writing our own interpreter, you can co-op the existing CoffeeScript interpreter in a such a way that its normal syntax makes for more natural (and therefore more comprehensible) expressions of your algorithm. {% highlight coffeescript %} class Sandwich constructor: (@customer, @bread='white', @toppings=[], @toasted=false)-> - + white = (sw) -> sw.bread = 'white' sw diff --git a/chapters/design_patterns/memento.textile b/chapters/design_patterns/memento.md similarity index 76% rename from chapters/design_patterns/memento.textile rename to chapters/design_patterns/memento.md index 50d9aa2..5c897dc 100644 --- a/chapters/design_patterns/memento.textile +++ b/chapters/design_patterns/memento.md @@ -3,16 +3,15 @@ layout: recipe title: Memento Pattern chapter: Design Patterns --- - -h2. Problem +## Problem You want to anticipate the reversion of changes to an object. -h2. Solution +## Solution -Use the "Memento pattern":http://en.wikipedia.org/wiki/Memento_pattern to track changes to an object. The class using the pattern will export a _memento_ object stored elsewhere. +Use the [Memento pattern](http://en.wikipedia.org/wiki/Memento_pattern) to track changes to an object. The class using the pattern will export a `memento` object stored elsewhere. -If you have application where the user can edit a text file, for example, they may want to undo their last action. You can save the current state of the file before the user changes it and then roll back to that at a later point. +If you have application where the user can edit a text file, for example, they may want to undo their last action. You can save the current state of the file before the user changes it and then roll back to that at a later point. {% highlight coffeescript %} class PreserveableText @@ -40,6 +39,6 @@ pt.restore memento pt.text # => "The original string" {% endhighlight %} -h2. Discussion +## Discussion -The Memento object returned by _PreserveableText#save_ stores the important state information separately for safe-keeping. You could even serialize this Memento in order to maintain an "undo" buffer on the hard disk or remotely for such data-intensive objects as edited images. +The Memento object returned by `PreserveableText#save` stores the important state information separately for safe-keeping. You could even serialize this Memento in order to maintain an "undo" buffer on the hard disk or remotely for such data-intensive objects as edited images. diff --git a/chapters/design_patterns/singleton.textile b/chapters/design_patterns/singleton.md similarity index 67% rename from chapters/design_patterns/singleton.textile rename to chapters/design_patterns/singleton.md index 026eab7..4f40859 100644 --- a/chapters/design_patterns/singleton.textile +++ b/chapters/design_patterns/singleton.md @@ -3,23 +3,22 @@ layout: recipe title: Singleton Pattern chapter: Design Patterns --- - -h2. Problem +## Problem Many times you only want one, and only one, instance of a class. For example, you may only need one class that creates server resources and you want to ensure that the one object can control those resources. Beware, however, because the singleton pattern can be easily abused to mimic unwanted global variables. -h2. Solution +## Solution -The publically available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned +The publicly available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned. The actual definition of the singleton class follows. -Note that I am using the idiomatic module export feature to emphasize the publically accessible portion of the module. Remember coffeescript wraps all files in a function block to protect the global namespace +Note that I am using the idiomatic module export feature to emphasize the publicly accessible portion of the module. Remember coffeescript wraps all files in a function block to protect the global namespace. {% highlight coffeescript %} root = exports ? this # http://stackoverflow.com/questions/4214731/coffeescript-global-variables -# The publically accessible Singleton fetcher +# The publicly accessible Singleton fetcher class root.Singleton _instance = undefined # Must be declared here to force the closure on the class @get: (args) -> # Must be a static method @@ -43,24 +42,24 @@ a.echo() b.echo() # => 'Hello A' -root.Singleton._instance +root.Singleton._instance # => undefined -root.Singleton._instance = 'foo' +root.Singleton._instance = 'foo' -root.Singleton._instance +root.Singleton._instance # => 'foo' c = root.Singleton.get 'Hello C' -c.foo() +c.foo() # => 'Hello A' -a.foo() +a.foo() # => 'Hello A' {% endhighlight %} -h2. Discussion +## Discussion -See in the above example how all instances are outputting from the same instance of the Singleton class +See in the above example how all instances are outputting from the same instance of the Singleton class. -Note how incredibly simple coffeescript makes this design pattern. For reference and discussion on nice javascript implementations, check out http://addyosmani.com/resources/essentialjsdesignpatterns/book/ +Note how incredibly simple coffeescript makes this design pattern. For reference and discussion on nice javascript implementations, check out [Essential JavaScript Design Patterns For Beginners](http://addyosmani.com/resources/essentialjsdesignpatterns/book/). diff --git a/chapters/design_patterns/strategy.textile b/chapters/design_patterns/strategy.md similarity index 86% rename from chapters/design_patterns/strategy.textile rename to chapters/design_patterns/strategy.md index 0ca8402..a06c6c2 100644 --- a/chapters/design_patterns/strategy.textile +++ b/chapters/design_patterns/strategy.md @@ -3,12 +3,11 @@ layout: recipe title: Strategy Pattern chapter: Design Patterns --- - -h2. Problem +## Problem You have more than one way to solve a problem but you need to choose (or even switch) between these methods at run time. -h2. Solution +## Solution Encapsulate your algorithms inside of Strategy objects. @@ -26,7 +25,7 @@ bubbleSort = (list) -> if list[r] > list[r+1] anySwaps = true [list[r], list[r+1]] = [list[r+1], list[r]] - + swapPass() while anySwaps anySwaps = false @@ -40,7 +39,7 @@ reverseBubbleSort = (list) -> if list[r] < list[r-1] anySwaps = true [list[r], list[r-1]] = [list[r-1], list[r]] - + swapPass() while anySwaps anySwaps = false @@ -66,10 +65,10 @@ sorter.sort unsortedList # => ['a', 'b', 'c', 'd', 'e', 'w', 'x'] {% endhighlight %} -h2. Discussion +## Discussion "No plan survives first contact with the enemy", nor users, but we can use the knowledge gained from changing circumstances to adapt. Near the end of the example, for instance, the newest item in the array now lies out of order. Knowing that detail, we can then speed the sort up by switching to an algorithm optimized for that exact scenario with nothing but a simple reassignment. -h3. Exercises +### Exercises -* Expand StringSorter into an AlwaysSortedArray class that implements all of the functionality of a regular array but which automatically sorts new items based on the method of insertion (e.g. push vs. shift). +* Expand `StringSorter` into an `AlwaysSortedArray` class that implements all of the functionality of a regular array but which automatically sorts new items based on the method of insertion (e.g. `push` vs. `shift`). diff --git a/chapters/functions/parentheses.textile b/chapters/functions/parentheses.md similarity index 78% rename from chapters/functions/parentheses.textile rename to chapters/functions/parentheses.md index 690ec92..8256056 100644 --- a/chapters/functions/parentheses.textile +++ b/chapters/functions/parentheses.md @@ -3,16 +3,15 @@ layout: recipe title: When Function Parentheses are not Optional chapter: Functions --- - -h2. Problem +## Problem You want to call a function that takes no arguments, but don't want to use parentheses. -h2. Solution +## Solution Use parentheses anyway. -Another alternative is to utilise the do-notation like so: +Another alternative is to utilize the do-notation like so: {% highlight coffeescript %} notify = -> alert "Hello, user!" @@ -31,11 +30,11 @@ if (condition) { } {% endhighlight %} -h2. Discussion +## Discussion Like Ruby, CoffeeScript allows you to drop parentheses to method calls. Unlike Ruby, however, CoffeeScript treats a bare function name as the pointer to the function. The practical upshot of this is that if you give no arguments to a method, CoffeeScript cannot tell if you want to call the function or use it as a reference. -Is this good or bad? It's just different. It creates an unexpected syntax case—parentheses aren't _always_ optional—but in exchange it gives you the ability to pass and receive functions fluently by name, something that's a bit klunky in Ruby. +Is this good or bad? It's just different. It creates an unexpected syntax case -- parentheses aren't _always_ optional -- but in exchange it gives you the ability to pass and receive functions fluently by name, something that's a bit klunky in Ruby. This usage of the do-notation is a neat approach for CoffeeScript with parenphobia. Some people simply prefer to write out the parentheses in the function call, though. diff --git a/chapters/functions/recursion.textile b/chapters/functions/recursion.md similarity index 85% rename from chapters/functions/recursion.textile rename to chapters/functions/recursion.md index d20f31b..3bddb59 100644 --- a/chapters/functions/recursion.textile +++ b/chapters/functions/recursion.md @@ -3,12 +3,11 @@ layout: recipe title: Recursive Functions chapter: Functions --- - -h2. Problem +## Problem You want to call a function from within that same function. -h2. Solution +## Solution With a named function: @@ -29,7 +28,6 @@ setTimeout((-> ), delay) {% endhighlight %} -h2. Discussion - -While @arguments.callee@ allows for the recursion of anonymous functions and might have the advantage in a very memory-intensive application, named functions keep their purpose more explicit and make for more maintainable code. +## Discussion +While `arguments.callee` allows for the recursion of anonymous functions and might have the advantage in a very memory-intensive application, named functions keep their purpose more explicit and make for more maintainable code. diff --git a/chapters/functions/splat_arguments.textile b/chapters/functions/splat_arguments.md similarity index 92% rename from chapters/functions/splat_arguments.textile rename to chapters/functions/splat_arguments.md index 782bc12..f0e39cb 100644 --- a/chapters/functions/splat_arguments.textile +++ b/chapters/functions/splat_arguments.md @@ -3,18 +3,17 @@ layout: recipe title: Splat Arguments chapter: Functions --- - -h2. Problem +## Problem Your function will be called with a varying number of arguments. -h2. Solution +## Solution Use _splats_. {% highlight coffeescript %} loadTruck = (firstDibs, secondDibs, tooSlow...) -> - truck: + truck: driversSeat: firstDibs passengerSeat: secondDibs trunkBed: tooSlow @@ -39,12 +38,11 @@ loadTruck = (firstDibs, secondDibs, tooSlow..., leftAtHome) -> loadTruck("Amanda", "Joel", "Bob", "Mary", "Phillip", "Austin") # => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: ["Bob", "Mary", "Phillip"] }, taxi: "Austin" } -{% endhighlight %} loadTruck("Amanda") # => { truck: { driversSeat: "Amanda", passengerSeat: undefined, trunkBed: [] }, taxi: undefined } +{% endhighlight %} -h2. Discussion - -By adding an ellipsis ("...") next to no more than one of a function's arguments, CoffeeScript will combine all of the argument values not captured by other named arguments into a list. It will serve up an empty list even if some of the named arguments were not supplied. +## Discussion +By adding an ellipsis (`...`) next to no more than one of a function's arguments, CoffeeScript will combine all of the argument values not captured by other named arguments into a list. It will serve up an empty list even if some of the named arguments were not supplied. diff --git a/chapters/index.html b/chapters/index.html index 2b79a72..b8c7695 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -16,7 +16,6 @@ - Design Patterns --- - {% for chapter in page.chapters %} {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} diff --git a/chapters/jquery/ajax.textile b/chapters/jquery/ajax.md similarity index 85% rename from chapters/jquery/ajax.textile rename to chapters/jquery/ajax.md index 2f9f561..e5563fa 100644 --- a/chapters/jquery/ajax.textile +++ b/chapters/jquery/ajax.md @@ -3,12 +3,11 @@ layout: recipe title: AJAX chapter: jQuery --- - -h2. Problem +## Problem You want to make AJAX calls using jQuery. -h2. Solution +## Solution {% highlight coffeescript %} $ ?= require 'jquery' # For Node.js compatibility @@ -30,17 +29,17 @@ $(document).ready -> $('body').append "AJAX Error: #{textStatus}" success: (data, textStatus, jqXHR) -> $('body').append "Successful AJAX call: #{data}" - + {% endhighlight %} -jQuery 1.5 and later have added a new, supplimental API for handling different callbacks. - +jQuery 1.5 and later have added a new, supplemental API for handling different callbacks. + {% highlight coffeescript %} request = $.get '/' request.success (data) -> $('body').append "Successfully got the page again." request.error (jqXHR, textStatus, errorThrown) -> $('body').append "AJAX Error: ${textStatus}." {% endhighlight %} -h2. Discussion +## Discussion -The jQuery and $ variables can be used interchangeably. See also "Callback bindings":../jquery/callback-bindings-jquery. +The jQuery and $ variables can be used interchangeably. See also [Callback bindings](/chapters/jquery/callback-bindings-jquery). diff --git a/chapters/jquery/callback-bindings-jquery.textile b/chapters/jquery/callback-bindings-jquery.md similarity index 69% rename from chapters/jquery/callback-bindings-jquery.textile rename to chapters/jquery/callback-bindings-jquery.md index c656dc3..7d36bea 100644 --- a/chapters/jquery/callback-bindings-jquery.textile +++ b/chapters/jquery/callback-bindings-jquery.md @@ -3,12 +3,11 @@ layout: recipe title: Callback bindings # using => instead of -> chapter: jQuery --- - -h2. Problem +## Problem You want to bind a callback function to an object. -h2. Solution +## Solution {% highlight coffeescript %} $ -> @@ -26,7 +25,7 @@ $ -> new Basket() {% endhighlight %} -h2. Discussion +## Discussion -By using the fat arrow => instead of the normal arrow -> the function gets -automatically bound to the object and can access the @-variable +By using the fat arrow (`=>`) instead of the normal arrow (`->`) the function gets +automatically bound to the object and can access the `@-variable`. diff --git a/chapters/jquery/plugin.textile b/chapters/jquery/plugin.md similarity index 90% rename from chapters/jquery/plugin.textile rename to chapters/jquery/plugin.md index 6293212..bf08b90 100644 --- a/chapters/jquery/plugin.textile +++ b/chapters/jquery/plugin.md @@ -3,12 +3,11 @@ layout: recipe title: Create a jQuery plugin chapter: jQuery --- - -h2. Problem +## Problem You'd like to create jQuery plugin using CoffeeScript -h2. Solution +## Solution {% highlight coffeescript %} # Reference jQuery @@ -23,14 +22,14 @@ $.fn.extend option1: true option2: false debug: false - + # Merge default settings with options. settings = $.extend settings, options - + # Simple logger. log = (msg) -> console?.log msg if settings.debug - + # _Insert magic here._ return @each ()-> log "Preparing magic show." @@ -38,13 +37,11 @@ $.fn.extend log "Option 1 value: #{settings.option1}" {% endhighlight %} -h2. Discussion - -h3. Usage +## Discussion Here are a couple of examples of how to use your new plugin. -h4. JavaScript +### JavaScript {% highlight javascript %} $("body").pluginName({ @@ -53,7 +50,7 @@ $("body").pluginName({ {% endhighlight %} -h4. CoffeeScript: +### CoffeeScript: {% highlight coffeescript %} $("body").pluginName diff --git a/chapters/math/constants.textile b/chapters/math/constants.md similarity index 76% rename from chapters/math/constants.textile rename to chapters/math/constants.md index 18462ba..989012a 100644 --- a/chapters/math/constants.textile +++ b/chapters/math/constants.md @@ -3,12 +3,11 @@ layout: recipe title: Math Constants chapter: Math --- - -h2. Problem +## Problem You need to use common mathematical constants like pi or e. -h2. Solution +## Solution Use Javascript's Math object to provide commonly needed mathematical constants. @@ -18,7 +17,7 @@ Math.PI # Note: Capitalization matters! This produces no output, it's undefined. Math.Pi -# => +# => Math.E # => 2.718281828459045 @@ -44,6 +43,6 @@ Math.LOG10E {% endhighlight %} -h2. Discussion +## Discussion -For another example of how a math constant is used in a real world problem, refer to the 'Converting Radians and Degrees' section of this Math chapter. \ No newline at end of file +For another example of how a math constant is used in a real world problem, refer to the [Converting Radians and Degrees](/chapters/math/radians-degrees) section of this Math chapter. diff --git a/chapters/math/fast-fibonacci.textile b/chapters/math/fast-fibonacci.md similarity index 64% rename from chapters/math/fast-fibonacci.textile rename to chapters/math/fast-fibonacci.md index d79537e..b8f7027 100644 --- a/chapters/math/fast-fibonacci.textile +++ b/chapters/math/fast-fibonacci.md @@ -3,48 +3,46 @@ layout: recipe title: Faster Fibonacci algorithm chapter: Math --- - -h2. Problem +## Problem You would like to calculate a number N in the Fibonacci sequence but want to do it quickly. -h2. Solution +## Solution The following solution (which can still be improved on) was originally talked about on Robin Houston's blog. Here are a few links talking about the algorithm and ways to improve it: -# http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/ -# http://www.math.rutgers.edu/~erowland/fibonacci.html -# http://jsfromhell.com/classes/bignumber -# http://www.math.rutgers.edu/~erowland/fibonacci.html -# http://bigintegers.blogspot.com/2010/11/square-division-power-square-root.html -# http://bugs.python.org/issue3451 +* [http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/](http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/) +* [http://www.math.rutgers.edu/~erowland/fibonacci](http://www.math.rutgers.edu/~erowland/fibonacci.html) +* [http://jsfromhell.com/classes/bignumber](http://jsfromhell.com/classes/bignumber) +* [http://www.math.rutgers.edu/~erowland/fibonacci](http://www.math.rutgers.edu/~erowland/fibonacci.html) +* [http://bigintegers.blogspot.com/2010/11/square-division-power-square-root](http://bigintegers.blogspot.com/2010/11/square-division-power-square-root.html) +* [http://bugs.python.org/issue3451](http://bugs.python.org/issue3451) This code is in gist form here: -https://gist.github.com/1032685 +[https://gist.github.com/1032685](https://gist.github.com/1032685) {% highlight coffeescript %} -### +### Author: Jason Giedymin http://www.jasongiedymin.com https://github.com/JasonGiedymin -This CoffeeScript Javascript Fast Fibonacci code is +This CoffeeScript Javascript Fast Fibonacci code is based on the python code from Robin Houston's blog. See below links. -A few things I want to introduce in time are implementions of +A few things I want to introduce in time are implementions of Newtonian, Burnikel / Ziegler, and Binet's algorithms on top of a Big Number framework. -Todo: +Todo: - https://github.com/substack/node-bigint - BZ and Newton mods. - Timing - ### MAXIMUM_JS_FIB_N = 1476 @@ -98,6 +96,6 @@ diff = (new Date).getTime() - start; console.log("[#{calc_value}] took #{diff} ms.") {% endhighlight %} -h2. Discussion +## Discussion -Questions? \ No newline at end of file +Questions? diff --git a/chapters/math/generating-predictable-random-numbers.textile b/chapters/math/generating-predictable-random-numbers.md similarity index 81% rename from chapters/math/generating-predictable-random-numbers.textile rename to chapters/math/generating-predictable-random-numbers.md index 5cf9da9..a7cc561 100644 --- a/chapters/math/generating-predictable-random-numbers.textile +++ b/chapters/math/generating-predictable-random-numbers.md @@ -3,12 +3,11 @@ layout: recipe title: Generating Predictable Random Numbers chapter: Math --- - -h2. Problem +## Problem You need to generate a random number in a certain range, but you also need to be able to "seed" the generator to deliver predictable values. -h2. Solution +## Solution Write your own random number generator. There are a LOT of ways to do this. Here's a simple one. _This generator is +ABSOLUTELY NOT+ acceptable for cryptographic purposes!_ @@ -45,14 +44,14 @@ class Rand min + this.rand(max-min) {% endhighlight %} -h2. Discussion +## Discussion JavaScript and CoffeeScript do not provide a seedable random number generator. Writing your own will be an exercise in trading off the amount of randomness with the simplicity of the generator. A full discussion of randomness is beyond the scope of this cookbook; for further reading consult Donald Knuth's _The Art of Computer Programming_, Volume II, Chapter 3, "Random Numbers", and _Numerical Recipes in C_, 2nd Edition, Chapter 7, "Random Numbers". -A brief explanation of this random number generator is in order, however. It is a Linear Congruential Pseudorandom Number Generator. LCPRNG's operate on the mathematical formula Ij+1 = (aIj+c) % m, where a is the multiplier, c is the addition offset, and m is the modulus. - Each time a random number is requested, a very large multiplication and addition are performed—"very large" relative to the key space—and the resulting number is modulused back down into the keyspace. +A brief explanation of this random number generator is in order, however. It is a Linear Congruential Pseudorandom Number Generator. LCPRNG's operate on the mathematical formula `Ij+1 = (aIj+c) % m`, where a is the multiplier, c is the addition offset, and m is the modulus. + Each time a random number is requested, a very large multiplication and addition are performed -- "very large" relative to the key space -- and the resulting number is modulused back down into the keyspace. -This generator has a period of 232. It is absolutely unacceptable for cryptographic purposes, but for most simple randomness requirements it is quite adequate. randn() will traverse the entire keyspace before repeating itself, and the next number is determined by the previous one. +This generator has a period of 232. It is absolutely unacceptable for cryptographic purposes, but for most simple randomness requirements it is quite adequate. `randn()` will traverse the entire keyspace before repeating itself, and the next number is determined by the previous one. If you want to tinker with this generator, you are _strongly_ encouraged to read Chapter 3 of Knuth's _The Art of Computer Programming_. Random number generation is VERY easy to screw up, and Knuth explains how to tell a good RNG from a bad one. @@ -63,6 +62,4 @@ Avoid the temptation to modulus the output of this generator. If you need an int r.randn() % 2 {% endhighlight %} -because you will most definitely not get random digits. Use r.randi(2) instead. - - +because you will most definitely not get random digits. Use `r.randi(2)` instead. diff --git a/chapters/math/generating-random-numbers.textile b/chapters/math/generating-random-numbers.md similarity index 80% rename from chapters/math/generating-random-numbers.textile rename to chapters/math/generating-random-numbers.md index 3359205..f181940 100644 --- a/chapters/math/generating-random-numbers.textile +++ b/chapters/math/generating-random-numbers.md @@ -3,12 +3,11 @@ layout: recipe title: Generating Random Numbers chapter: Math --- - -h2. Problem +## Problem You need to generate a random number in a certain range. -h2. Solution +## Solution Use JavaScript's Math.random() to get floating-point numbers from 0 <= x < 1.0. Use multiplication and Math.floor to get a number in a certain range. @@ -27,18 +26,10 @@ dice = Math.floor(Math.random() * 6) + 1 # => true {% endhighlight %} -h2. Discussion +## Discussion This is a straight lift from JavaScript. -Note that JavaScripts's Math.random() does not allow you to seed the random number generator to force certain values. See Generating Predictable Random Numbers for that. +Note that JavaScripts's Math.random() does not allow you to seed the random number generator to force certain values. See [Generating Predictable Random Numbers](/chapters/math/generating-predictable-random-numbers) for that. To generate a number from 0 up to (but not including) n, multiply by n. To generate a number from 1 to n (inclusive), multiply by n and add 1. - - - - - - - - diff --git a/chapters/math/radians-degrees.textile b/chapters/math/radians-degrees.md similarity index 91% rename from chapters/math/radians-degrees.textile rename to chapters/math/radians-degrees.md index c186ddb..8be5400 100644 --- a/chapters/math/radians-degrees.textile +++ b/chapters/math/radians-degrees.md @@ -3,12 +3,11 @@ layout: recipe title: Converting Radians and Degrees chapter: Math --- - -h2. Problem +## Problem You need to convert between radians and degrees. -h2. Solution +## Solution Use Javascript's Math.PI and a simple formula to convert between the two. @@ -28,6 +27,6 @@ degreesToRadians(1) # => 0.017453292519943295 {% endhighlight %} -h2. Discussion +## Discussion -Questions? \ No newline at end of file +Questions? diff --git a/chapters/metaprogramming/detecting-and-replacing-functions.textile b/chapters/metaprogramming/detecting-and-replacing-functions.md similarity index 79% rename from chapters/metaprogramming/detecting-and-replacing-functions.textile rename to chapters/metaprogramming/detecting-and-replacing-functions.md index e87566b..d448a41 100644 --- a/chapters/metaprogramming/detecting-and-replacing-functions.textile +++ b/chapters/metaprogramming/detecting-and-replacing-functions.md @@ -3,14 +3,13 @@ layout: recipe title: Detecting and Creating Missing Functions chapter: Metaprogramming --- - -h2. Problem +## Problem You want to detect if a function exists and create it if it does not (such as an ECMAScript 5 function in Internet Explorer 8). -h2. Solution +## Solution -Use :: to detect the function, and assign to it if it does not exist. +Use `::` to detect the function, and assign to it if it does not exist. {% highlight coffeescript %} unless Array::filter @@ -22,8 +21,7 @@ array = [1..10] array.filter (x) -> x > 5 # => [6,7,8,9,10] {% endhighlight %} - -h2. Discussion -Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the :: operator. +## Discussion +Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the `::` operator. diff --git a/chapters/metaprogramming/extending-classes.textile b/chapters/metaprogramming/extending-classes.md similarity index 76% rename from chapters/metaprogramming/extending-classes.textile rename to chapters/metaprogramming/extending-classes.md index 0038724..fb401eb 100644 --- a/chapters/metaprogramming/extending-classes.textile +++ b/chapters/metaprogramming/extending-classes.md @@ -3,14 +3,13 @@ layout: recipe title: Extending Classes chapter: Metaprogramming --- - -h2. Problem +## Problem You want to extend a class to add new functionality or replace old. -h2. Solution +## Solution -Use :: to assign your new function to the prototype of the object or class. +Use `::` to assign your new function to the prototype of the object or class. {% highlight coffeescript %} String::capitalize = () -> @@ -19,8 +18,7 @@ String::capitalize = () -> "foo bar baz".capitalize() # => 'Foo Bar Baz' {% endhighlight %} - -h2. Discussion -Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the :: operator. +## Discussion +Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the `::` operator. diff --git a/chapters/networking/basic-client.textile b/chapters/networking/basic-client.md similarity index 68% rename from chapters/networking/basic-client.textile rename to chapters/networking/basic-client.md index 42b30e2..64b3452 100644 --- a/chapters/networking/basic-client.textile +++ b/chapters/networking/basic-client.md @@ -3,17 +3,15 @@ layout: recipe title: Basic Client chapter: Networking --- - -h2. Problem +## Problem You want to access a service provided over the network. - -h2. Solution +## Solution Create a basic TCP client. -h3. In Node.js +### In Node.js {% highlight coffeescript %} net = require 'net' @@ -31,9 +29,9 @@ connection.on 'data', (data) -> connection.end() {% endhighlight %} -h3. Example Usage +### Example Usage -Accessing the Basic Server: +Accessing the [Basic Server](/chapters/networking/basic-server): {% highlight console %} $ coffee basic-client.coffee @@ -41,11 +39,12 @@ Opened connection to localhost:9001 Received: Hello, World! {% endhighlight %} -h2. Discussion +## Discussion The most important work takes place in the _connection.on 'data'_ handler, where the client receives its response from the server and would most likely arrange for responses to it. -See also the Basic Server, Bi-Directional Client, and Bi-Directional Server recipes. +See also the [Basic Server](/chapters/networking/basic-server), [Bi-Directional Client](/chapters/networking/bi-directional-client), and [Bi-Directional Server](/chapters/networking/bi-directional-server) recipes. + +### Exercises -h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/basic-server.textile b/chapters/networking/basic-server.md similarity index 72% rename from chapters/networking/basic-server.textile rename to chapters/networking/basic-server.md index bd4a031..9dd55c9 100644 --- a/chapters/networking/basic-server.textile +++ b/chapters/networking/basic-server.md @@ -3,17 +3,15 @@ layout: recipe title: Basic Server chapter: Networking --- - -h2. Problem +## Problem You want to provide a service over a network. - -h2. Solution +## Solution Create a basic TCP server. -h3. In Node.js +### In Node.js {% highlight coffeescript %} net = require 'net' @@ -30,9 +28,9 @@ console.log "Listening to #{domain}:#{port}" server.listen port, domain {% endhighlight %} -h3. Example Usage +### Example Usage -Accessed by the Basic Client: +Accessed by the [Basic Client](/chapters/networking/basic-client): {% highlight console %} $ coffee basic-server.coffee @@ -42,11 +40,12 @@ Received connection from 127.0.0.1 [...] {% endhighlight %} -h2. Discussion +## Discussion The function passed to @net.createServer@ receives the new socket provided for each new connection to a client. This basic server simply socializes with its visitors but a hard-working server would pass this socket along to a dedicated handler and then return to the task of waiting for the next client. -See also the Basic Client, Bi-Directional Server, and Bi-Directional Client recipes. +See also the [Basic Client](/chapters/networking/basic-client), [Bi-Directional Server](/chapters/networking/bi-directional-server), and [Bi-Directional Client](/chapters/networking/bi-directional-client) recipes. + +### Exercises -h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-client.textile b/chapters/networking/bi-directional-client.md similarity index 79% rename from chapters/networking/bi-directional-client.textile rename to chapters/networking/bi-directional-client.md index d8234d8..b7e020e 100644 --- a/chapters/networking/bi-directional-client.textile +++ b/chapters/networking/bi-directional-client.md @@ -3,16 +3,15 @@ layout: recipe title: Bi-Directional Client chapter: Networking --- - -h2. Problem +## Problem You want to a persistent service over a network, one which maintains an on-going connection with its clients. -h2. Solution +## Solution Create a bi-directional TCP client. -h3. In Node.js +### In Node.js {% highlight coffeescript %} net = require 'net' @@ -40,9 +39,9 @@ connection.on 'end', (data) -> process.exit() {% endhighlight %} -h3. Example Usage +### Example Usage -Accessing the Bi-Directional Server: +Accessing the [Bi-Directional Server](/chapters/networking/bi-directional-server): {% highlight console %} $ coffee bi-directional-client.coffee @@ -57,11 +56,12 @@ Received: You have 1 peer on this server Connection closed {% endhighlight %} -h2. Discussion +## Discussion This particular example initiates contact with the server and starts the conversation in the @connection.on 'connect'@ handler. The bulk of the work in a real client, however, will lie in the @connection.on 'data'@ handler, which processes output from the server. The @ping@ function only recurses in order to illustrate continuous communication with the server and can be removed from a real client. -See also the Bi-Directional Server, Basic Client, and Basic Server recipes. +See also the [Bi-Directional Server](/chapters/networking/bi-directional-server), [Basic Client](/chapters/networking/basic-client), and [Basic Server](/chapters/networking/basic-server) recipes. + +### Exercises -h3. Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. diff --git a/chapters/networking/bi-directional-server.textile b/chapters/networking/bi-directional-server.md similarity index 75% rename from chapters/networking/bi-directional-server.textile rename to chapters/networking/bi-directional-server.md index 36c0e4c..d187f92 100644 --- a/chapters/networking/bi-directional-server.textile +++ b/chapters/networking/bi-directional-server.md @@ -3,16 +3,15 @@ layout: recipe title: Bi-Directional Server chapter: Networking --- - -h2. Problem +## Problem You want to provide a persistent service over a network, one which maintains an on-going connection with a client. -h2. Solution +## Solution Create a bi-directional TCP server. -h3. In Node.js +### In Node.js {% highlight coffeescript %} net = require 'net' @@ -32,9 +31,9 @@ console.log "Listening to #{domain}:#{port}" server.listen port, domain {% endhighlight %} -h3. Example Usage +### Example Usage -Accessed by the Bi-Directional Client: +Accessed by the [Bi-Directional Client](/chapters/networking/bi-directional-client): {% highlight console %} $ coffee bi-directional-server.coffee @@ -46,12 +45,12 @@ New connection from 127.0.0.1 [...] {% endhighlight %} -h2. Discussion +## Discussion The bulk of the work lies in the @socket.on 'data'@ handler, which processes all of the input from the client. A real server would likely pass the data onto another function to process it and generate any responses so that the original handler. -See also the Bi-Directional Client, Basic Client, and Basic Server recipes. +See also the [Bi-Directional Client](/chapters/networking/bi-directional-client), [Basic Client](/chapters/networking/basic-client), and [Basic Server](/chapters/networking/basic-server) recipes. -h3. Exercises -* Add support for choosing the target domain and port based on command-line arguments or on a configuration file. +### Exercises +* Add support for choosing the target domain and port based on command-line arguments or on a configuration file. diff --git a/chapters/regular_expressions/heregexes.textile b/chapters/regular_expressions/heregexes.md similarity index 87% rename from chapters/regular_expressions/heregexes.textile rename to chapters/regular_expressions/heregexes.md index cc913c9..31b09d0 100644 --- a/chapters/regular_expressions/heregexes.textile +++ b/chapters/regular_expressions/heregexes.md @@ -3,12 +3,11 @@ layout: recipe title: Using Heregexes chapter: Regular Expressions --- - -h2. Problem +## Problem You need to write a complex regular expression. -h2. Solution +## Solution Use Coffeescript's "heregexes" -- extended regular expressions that ignore internal whitespace and can contain comments. @@ -22,15 +21,14 @@ pattern = /// # => ['555', '123', '4567'] {% endhighlight %} -h2. Discussion +## Discussion Breaking up your complex regular expressions and commenting key sections makes them a lot more decipherable and maintainable. For example, changing this regex to allow an optional space between the prefix and line number would now be fairly obvious. -h3. Whitespace characters in heregexes +### Whitespace characters in heregexes -Whitespace is ignored in heregexes -- so what do you do if you need to match a -literal ASCII space? +Whitespace is ignored in heregexes -- so what do you do if you need to match a literal ASCII space? One solution is to use the @\s@ character class, which will match spaces, tabs and line breaks. If you only want to match a space, though, you'll need to use -@\x20@ to denote a literal ASCII space. +`\x20` to denote a literal ASCII space. diff --git a/chapters/strings/capitalizing-words.textile b/chapters/strings/capitalizing-words.md similarity index 92% rename from chapters/strings/capitalizing-words.textile rename to chapters/strings/capitalizing-words.md index 4074ea1..5df2e96 100644 --- a/chapters/strings/capitalizing-words.textile +++ b/chapters/strings/capitalizing-words.md @@ -3,12 +3,11 @@ layout: recipe title: Capitalizing Words chapter: Strings --- - -h2. Problem +## Problem You want to capitalize the first letter of every word in a string. -h2. Solution +## Solution Use the split, map, join pattern: Split the string into words, then use a map to capitalize the first letter and lowercase all other letters of each word before gluing the string back together with join. @@ -24,9 +23,9 @@ Or do the same thing using a list comprehension: # => 'Foo Bar Baz' {% endhighlight %} -h2. Discussion +## Discussion -Split, map, join is a common scripting pattern dating back to perl. This function may benefit from being placed directly onto the String class by Extending Classes. +Split, map, join is a common scripting pattern dating back to perl. This function may benefit from being placed directly onto the String class by [Extending Classes](/chapters/objects/extending-classes). Be aware that two wrinkles can appear in the split, map, join pattern. The first is that the split text works best when it is constant. If the source string has multiple spaces in it, the split will need to take this into account to prevent getting extra, empty words. One way to do this is with a regular expression to split on runs of whitespace instead of a single space: @@ -38,4 +37,3 @@ Be aware that two wrinkles can appear in the split, map, join pattern. The first ...but this leads us to the second wrinkle: notice that the runs of whitespace are now compressed down to a single character by the join. Quite often one or both of these wrinkles is acceptable, however, so the split, map, join pattern can be a powerful tool. - diff --git a/chapters/strings/finding-substrings.textile b/chapters/strings/finding-substrings.md similarity index 92% rename from chapters/strings/finding-substrings.textile rename to chapters/strings/finding-substrings.md index 713c1fa..1bebca9 100644 --- a/chapters/strings/finding-substrings.textile +++ b/chapters/strings/finding-substrings.md @@ -3,12 +3,11 @@ layout: recipe title: Finding Substrings chapter: Strings --- - -h2. Problem +## Problem You need to find the first or last occurrence of a search string within a message. -h2. Solution +## Solution Use Javascript's indexOf() and lastIndexOf() to find the first and last occurrences of a string, respectively. Syntax: string.indexOf(searchstring, start) @@ -27,6 +26,6 @@ message.lastIndexOf("This") {% endhighlight %} -h2. Discussion +## Discussion -Still need recipe to count occurrences of a given string within a message. \ No newline at end of file +Still need recipe to count occurrences of a given string within a message. diff --git a/chapters/strings/interpolation.textile b/chapters/strings/interpolation.md similarity index 91% rename from chapters/strings/interpolation.textile rename to chapters/strings/interpolation.md index 259f30e..b1e1e9e 100644 --- a/chapters/strings/interpolation.textile +++ b/chapters/strings/interpolation.md @@ -3,13 +3,12 @@ layout: recipe title: String Interpolation chapter: Strings --- - -h2. Problem +## Problem You want to create a string that contains a text representation of a CoffeeScript Variable. -h2. Solution +## Solution Use CoffeeScript's ruby-like string interpolation instead of JavaScript's string addition. @@ -30,10 +29,10 @@ message = "The square of 7 is #{square 7}." # => "The square of 7 is 49." {% endhighlight %} -h2. Discussion +## Discussion CoffeeScript interpolates strings in similar fashion to ruby. Most -expressions are valid inside the #{...} interpolation syntax. +expressions are valid inside the `#{...}` interpolation syntax. CoffeeScript permits multiple expressions inside the interpolation which can have side effects, but this is discouraged. Only the last @@ -47,4 +46,3 @@ message = "The square of 10 is #{muppet='Animal'; square 10}. Oh, and your favor # => "The square of 10 is 100. Oh, and your favorite muppet is now Animal." {% endhighlight %} - diff --git a/chapters/strings/repeating.textile b/chapters/strings/repeating.md similarity index 93% rename from chapters/strings/repeating.textile rename to chapters/strings/repeating.md index 6db88f2..7749444 100644 --- a/chapters/strings/repeating.textile +++ b/chapters/strings/repeating.md @@ -3,12 +3,11 @@ layout: recipe title: Repeating a String chapter: Strings --- - -h2. Problem +## Problem You want to repeat a string. -h2. Solution +## Solution Create an array of n+1 nulls, and then join it with the repetition string as the glue: @@ -19,8 +18,6 @@ Array(11).join('foo') # => "foofoofoofoofoofoofoofoofoofoo" {% endhighlight %} -h2. Discussion +## Discussion JavaScript lacks a string repeat function, as does CoffeeScript. List comprehensions and maps can be pressed into service here, but in the case of a simple string repeat it's easier to simply build an array of n+1 nulls and then glue them together. - - diff --git a/chapters/strings/splitting-a-string.textile b/chapters/strings/splitting-a-string.md similarity index 95% rename from chapters/strings/splitting-a-string.textile rename to chapters/strings/splitting-a-string.md index 83c1162..51f3350 100644 --- a/chapters/strings/splitting-a-string.textile +++ b/chapters/strings/splitting-a-string.md @@ -3,12 +3,11 @@ layout: recipe title: Splitting a String chapter: Strings --- - -h2. Problem +## Problem You want to split a string. -h2. Solution +## Solution Use JavaScript's String split() method: @@ -17,7 +16,7 @@ Use JavaScript's String split() method: # => [ 'foo', 'bar', 'baz' ] {% endhighlight %} -h2. Discussion +## Discussion String's split() is a standard JavaScript method. It can be used to split a string on any delimiter, including regular expressions. It also accepts a second parameter that specifies the number of splits to return. diff --git a/chapters/syntax/comparing_ranges.textile b/chapters/syntax/comparing_ranges.md similarity index 94% rename from chapters/syntax/comparing_ranges.textile rename to chapters/syntax/comparing_ranges.md index 10f8b52..ae2b1df 100644 --- a/chapters/syntax/comparing_ranges.textile +++ b/chapters/syntax/comparing_ranges.md @@ -3,12 +3,11 @@ layout: recipe title: Comparing Ranges chapter: Syntax --- - -h2. Problem +## Problem You want to know if a variable is inside a given range. -h2. Solution +## Solution Use CoffeeScript's chained comparison syntax. @@ -22,7 +21,7 @@ normalHeight = maxDwarfism < height < minAcromegaly # => true {% endhighlight %} -h2. Discussion +## Discussion This is a nice feature lifted from Python. Instead of writing out the full comparison like @@ -31,5 +30,3 @@ normalHeight = height > maxDwarfism && height < minAcromegaly {% endhighlight %} CoffeeScript allows us to chain the two comparisons together in a form that more closely matches the way a mathematician would write it. - - diff --git a/chapters/syntax/embedding_javascript.textile b/chapters/syntax/embedding_javascript.md similarity index 66% rename from chapters/syntax/embedding_javascript.textile rename to chapters/syntax/embedding_javascript.md index e082caf..4b5d446 100644 --- a/chapters/syntax/embedding_javascript.textile +++ b/chapters/syntax/embedding_javascript.md @@ -3,12 +3,11 @@ layout: recipe title: Embedding JavaScript chapter: Syntax --- - -h2. Problem +## Problem You want to include some found/pre-written JavaScript code inline with your CoffeeScript. -h2. Solution +## Solution Wrap the JavaScript with backticks: @@ -22,9 +21,9 @@ greet "Coffee" # => "Hello Coffee" {% endhighlight %} -h2. Discussion +## Discussion -This is a simple way to integrate small snippets of JavaScript code into your CoffeeScript without converting it over to use CoffeeScript syntax. As shown in the "CoffeeScript Language Reference":http://jashkenas.github.com/coffee-script/#embedded you can mix to the two languages to a certain extent: +This is a simple way to integrate small snippets of JavaScript code into your CoffeeScript without converting it over to use CoffeeScript syntax. As shown in the [CoffeeScript Language Reference](http://jashkenas.github.com/coffee-script/#embedded) you can mix to the two languages to a certain extent: {% highlight coffeescript %} hello = `function (name) { @@ -34,4 +33,5 @@ hello "Coffee" # => "Hello Coffee" {% endhighlight %} -Here the "hello" variable is still in CoffeeScript, but is assigned a function written in JavaScript. \ No newline at end of file + +Here the `hello` variable is still in CoffeeScript, but is assigned a function written in JavaScript. diff --git a/chapters/syntax/for_loops.textile b/chapters/syntax/for_loops.md similarity index 93% rename from chapters/syntax/for_loops.textile rename to chapters/syntax/for_loops.md index 80622bb..e56802a 100644 --- a/chapters/syntax/for_loops.textile +++ b/chapters/syntax/for_loops.md @@ -3,12 +3,11 @@ layout: recipe title: For Loops chapter: Syntax --- - -h2. Problem +## Problem You need to iterate over an array, object or range with a for loop. -h2. Solution +## Solution {% highlight coffeescript %} # for(i = 1; i<= 10; i++) @@ -25,6 +24,6 @@ x * x for x in [1..10] # = > [1,4,9,16,25,36,49,64,81,100] {% endhighlight %} -h2. Discussion +## Discussion Comprehensions replace for loops in CoffeeScript, but they simply compile into the traditional javascript equivalent for-loop. \ No newline at end of file From 343fbde7b98a145b037bb9b84c02ec57a2359b13 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 14:33:38 -0600 Subject: [PATCH 059/267] Convert guides to Markdown For the same reasons the recipes were changed. --- LICENSE-CC-BY.textile | 3 + _config.yml | 6 +- authors-guide.textile => authors-guide.md | 94 +++++++--------- authors.md | 33 ++++++ authors.textile | 36 ------ .../dates_and_times/date-of-thanksgiving.md | 106 ------------------ contributing.md | 30 +++++ contributing.textile | 32 ------ designers-guide.textile => designers-guide.md | 5 +- ...opers-guide.textile => developers-guide.md | 34 +++--- license.md | 8 ++ license.textile | 9 -- recipe-template.textile => recipe-template.md | 17 ++- terms-of-use.textile => terms-of-use.md | 1 - wanted-recipes.textile => wanted-recipes.md | 28 +++-- 15 files changed, 154 insertions(+), 288 deletions(-) rename authors-guide.textile => authors-guide.md (50%) create mode 100644 authors.md delete mode 100644 authors.textile create mode 100644 contributing.md delete mode 100644 contributing.textile rename designers-guide.textile => designers-guide.md (61%) rename developers-guide.textile => developers-guide.md (67%) create mode 100644 license.md delete mode 100644 license.textile rename recipe-template.textile => recipe-template.md (51%) rename terms-of-use.textile => terms-of-use.md (97%) rename wanted-recipes.textile => wanted-recipes.md (93%) diff --git a/LICENSE-CC-BY.textile b/LICENSE-CC-BY.textile index d6744e4..df9928b 100644 --- a/LICENSE-CC-BY.textile +++ b/LICENSE-CC-BY.textile @@ -20,6 +20,7 @@ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE B "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. + 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: @@ -40,6 +41,7 @@ The above rights may be exercised in all media and formats whether now known or You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. + 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. @@ -50,6 +52,7 @@ UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS T This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + 8. Miscellaneous Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. diff --git a/_config.yml b/_config.yml index 8434992..f61488e 100644 --- a/_config.yml +++ b/_config.yml @@ -1,12 +1,10 @@ pygments: true lsi: false exclude: - - README + - README.md - CNAME - - TODO.textile + - TODO.md - script - Procfile - Gemfile - Gemfile.lock - - diff --git a/authors-guide.textile b/authors-guide.md similarity index 50% rename from authors-guide.textile rename to authors-guide.md index e0e7f01..a690c79 100644 --- a/authors-guide.textile +++ b/authors-guide.md @@ -2,15 +2,13 @@ layout: default title: Author's Guide --- +# Author's Guide -h1. Author's Guide +## tl;dr: Look at Other Recipes, and Blend In -h2. tl;dr: Look at Other Recipes, and Blend In +Look at the source of other recipe pages and follow that page structure. Start with the [Developer's Guide](/developers-guide) to get a test version of the cookbook up and running on your machine, and get to work! -Look at the source of other recipe pages and follow that page structure. Start with the Developer's Guide to get a test version of the cookbook up and running on your machine, and get to work! - - -h2. General Guidelines +## General Guidelines * Feel free to add new pages, or even chapters. Just keep them organized. _(See "How to Add a Chapter" below)_ * Try to write well-styled, idiomatic CoffeeScript. @@ -18,44 +16,43 @@ h2. General Guidelines * Sharing code that you think might only be used rarely is fine. Sharing esoteric code tricks is less so. There's a difference between sharing a translator for an obscure format and sharing a weird bit-shifting trick that does fast but inaccurate multiplication. * Don't forget to add your name to the authors page! -h2. Use Cookbook Problem/Solution/Discussion Format +## Use Cookbook Problem/Solution/Discussion Format A typical cookbook page will have three sections (four if you count the title): -* +Problem+ A one- or two-sentence description of the problem, such as "You want to access parts of a string" or "You want to format a floating point number as currency, with a leading dollar sign, two digits of precision and comma-separated triples." Where possible, phrase the problem as though speaking directly to the reader: "You want to...", "You have an X but you want a Y", etc. -* +Solution+ State the solution as briefly as possible, ideally as a single sentence that identifies the strategy you would use, and give example code. It's tempting to explain the solution, but don't do it yet. Remember that the reader will look this solution up many times after the first time, and that they will be looking for a quick reference each time. You're going to explain the solution in the +Discussion+, and the first time reader will read your solution, think about it, and then proceed to the discussion if necessary. For example, for "accessing parts of a string", a good Solution sentence might be "Use CoffeeScript's array range subscripts, or JavaScript's slice function." -* +Discussion+ Here you should explain why your solution works, or give further examples such as edge cases. If your solution can break on some edge cases, be sure to note them here. For example, if a percentage function crashes when given a zero, you could note this in the discussion and give a workaround. NOTE: If your solution has really dangerous edge cases, so dangerous that you would include them in the solution, please consider whether your recipe should be included at all. Remember, this Cookbook is for good code! - +* **Problem** A one- or two-sentence description of the problem, such as "You want to access parts of a string" or "You want to format a floating point number as currency, with a leading dollar sign, two digits of precision and comma-separated triples." Where possible, phrase the problem as though speaking directly to the reader: "You want to...", "You have an X but you want a Y", etc. +* **Solution** State the solution as briefly as possible, ideally as a single sentence that identifies the strategy you would use, and give example code. It's tempting to explain the solution, but don't do it yet. Remember that the reader will look this solution up many times after the first time, and that they will be looking for a quick reference each time. You're going to explain the solution in the **Discussion**, and the first time reader will read your solution, think about it, and then proceed to the discussion if necessary. For example, for "accessing parts of a string", a good Solution sentence might be "Use CoffeeScript's array range subscripts, or JavaScript's slice function." +* **Discussion** Here you should explain why your solution works, or give further examples such as edge cases. If your solution can break on some edge cases, be sure to note them here. For example, if a percentage function crashes when given a zero, you could note this in the discussion and give a workaround. NOTE: If your solution has really dangerous edge cases, so dangerous that you would include them in the solution, please consider whether your recipe should be included at all. Remember, this Cookbook is for good code! -h2. Copyright Issues +## Copyright Issues -Do not post code that is copyrighted by another user, unless you have permission to use that code AND to relicense that code under the CC BY 3.0 license. If you DO have permission and the author would like credit, please add them to the authors page. +Do not post code that is copyrighted by another user, unless you have permission to use that code AND to relicense that code under the [CC BY 3.0](/license) license. If you DO have permission and the author would like credit, please add them to the [authors](/authors) page. -Also, just a stylistic note, please do not yank code directly from http://coffeescript.org and post it with little or no discussion. The CoffeeScript Cookbook is not affiliated with them. We think they're awesome and want them to like us, too! Make sure that anything taken from coffeescript.org is permissible use and that it stands alone as a valid recipe. If the recipe is too terse, consider adding more examples and discussion. +Also, just a stylistic note, please do not yank code directly from [http://coffeescript.org/](http://coffeescript.org/) and post it with little or no discussion. The CoffeeScript Cookbook is not affiliated with them. We think they're awesome and want them to like us, too! Make sure that anything taken from [http://coffeescript.org/](http://coffeescript.org/) is permissible use and that it stands alone as a valid recipe. If the recipe is too terse, consider adding more examples and discussion. - -h2. Tag the page with Jekyll frontmatter +## Tag the page with Jekyll frontmatter ...that's a lot of fancy words that mean "don't forget to put the layout, chapter and page title at the top of the file in a YAML block". For example, the string interpolation page begins with ---- +{% highlight text %} +--- layout: recipe title: String Interpolation chapter: Strings ---- - +--- +{% endhighlight %} -h2. Use Liquid highlighting templates +## Use Liquid highlighting templates -Turn on syntax highlighting for CoffeeScript with highlight coffeescript. +Turn on syntax highlighting for CoffeeScript with `highlight coffeescript`. -{% highlight coffeescript %} +
      {% highlight coffeescript %}
       # Calculate the square of a number
       square = (x) -> x * x
       
       square(16)
       # => 256
      -{% endhighlight %}
      +{% endhighlight %}
      This produces the following: @@ -67,10 +64,9 @@ square(16) # => 256 {% endhighlight %} +## Include Output -h2. Include Output - -After writing an important expression, show the reader what the output would be by adding it with a # => on the following line. Once we get automated script testing working up in this joint, we'll actually load up your recipe snippets and evaluate its expressions against the output comment you wrote. (In other words, # => tells the reader what the output will be, but it tells automated checker what the assertEquals should be) +After writing an important expression, show the reader what the output would be by adding it with a `# =>` on the following line. Once we get automated script testing working up in this joint, we'll actually load up your recipe snippets and evaluate its expressions against the output comment you wrote. (In other words, `# =>` tells the reader what the output will be, but it tells automated checker what the assertEquals should be) {% highlight coffeescript %} # right @@ -97,12 +93,11 @@ Not all snippets evaluate to something useful. For example, array.forEach has si When in doubt about what output to show, try evaluating your snippet in the coffee interpreter and see what IT thinks. Ideally your output should match, or at least be machine-comparable. +# Grindy HOWTOs -h1. Grindy HOWTOs +## How to Add a Recipe -h2. How to Add a Recipe - -Create a new textile page (or copy the Recipe Template. The filename should be about the problem, e.g. finding-last-day-of-the-month.textile or reversing-arrays.textile. In your file, start with the following template: +Create a new markdown page (or copy the [Recipe Template](/recipe-template). The filename should be about the problem, e.g. `finding-last-day-of-the-month.md` or `reversing-arrays.md`. In your file, start with the following template: {% highlight text %} --- @@ -111,27 +106,26 @@ title: Title of The Recipe chapter: Chapter Name --- -h2. Problem +## Problem You have a problem. -h2. Solution +## Solution Do this about it. -h2. Discussion +## Discussion Here's why. {% endhighlight %} One fussy little bit, the chapter name should match the directory the chapter is in, otherwise the page won't render correctly. For example, if you're writing a recipe for arrays, make sure the chapter is "Arrays", not "arrays" or "aray" ...or especially not "Chapter Name". +## How to Add a Chapter -h2. How to Add a Chapter - -* Open chapters/index.textile and your chapter's name to the yaml list of chapters. +* Open chapters/index.html and your chapter's name to the yaml list of chapters. * cd into chapters/ and create the directory for the chapter name. Downcase the name and replace spaces with underscores. -* add an index.textile file that uses layout: chapter. For convenience, just copy the index.textile from another chapter and update the yaml frontmatter to reflect the name of your new chapter. +* add an index.html file that uses `layout: chapter`. For convenience, just copy the index.html from another chapter and update the yaml frontmatter to reflect the name of your new chapter. For example, to add a chapter called "Dates and Times", you would add it to the chapters array: @@ -152,13 +146,11 @@ mkdir dates_and_times Now create a new page in that chapter (remember to add its YAML front matter) and once jekyll regenerates the chapter index, your new page should appear. +# FAQ -h1. FAQ - +## I Have a Weird Recipe. Should I Share It? -h2. I Have a Weird Recipe. Should I Share It? - -Maybe! The real question is, is it really useful, or is it just clever? If you have a formatter for a weird Albanian GIS format, that's a real problem that almost nobody would ever have—but when somebody DOES have that problem, they REALLY need a solution. If you have a bit shifting trick that can swap two numbers using three xor-equals operations, that's a really clever solution but it's not very good CoffeeScript. (For one thing, x ^= y ^= x ^= y is not idiomatic, while [x,y]=[y,x] is. For another, there's a bug in that code. And once you fix it, there's another bug caused by extrapolating this register trick to a reference-based language where—look, it's just a bad idea, okay?) +Maybe! The real question is, is it really useful, or is it just clever? If you have a formatter for a weird Albanian GIS format, that's a real problem that almost nobody would ever have -- but when somebody DOES have that problem, they REALLY need a solution. If you have a bit shifting trick that can swap two numbers using three xor-equals operations, that's a really clever solution but it's not very good CoffeeScript. (For one thing, `x ^= y ^= x ^= y` is not idiomatic, while `[x,y]=[y,x]` is. For another, there's a bug in that code. And once you fix it, there's another bug caused by extrapolating this register trick to a reference-based language where -- look, it's just a bad idea, okay?) If you have a cool but weird recipe, ask yourself if a reader would genuinely find it useful. Here are two very good questions to consider: @@ -167,28 +159,22 @@ If you have a cool but weird recipe, ask yourself if a reader would genuinely fi If the answer to either question is no, you might have some code that is a "clever solution in search of a problem". If in doubt, ask. - -h2. What If My Recipe is Inefficient/Too Big/Too Slow? +## What If My Recipe is Inefficient/Too Big/Too Slow? If it solves a problem to which the alternative is to _not_ solve the problem, share it. Let the reader decide if they want to use it. Sometimes we want tight efficient code, other times we want a robust featureset. If the code has abysmal performance characteristics, be sure to warn the reader in the Discussion. - -h2. Can I Edit An Existing Recipe? +## Can I Edit An Existing Recipe? Yes. Please improve anything and everything! Be sure to test your changes and make sure that your solution really is better. - -h2. I Have a Really Efficient Solution, But It's Not As Readable As the Existing Recipe. Should I Add It? +## I Have a Really Efficient Solution, But It's Not As Readable As the Existing Recipe. Should I Add It? See the "Weird Recipe" note above. Do real people in the real world ever hit the performance constraint? If so, then by all means, add your strategy to the existing solution, and be sure to explain why your solution is not idiomatic. If a reader really has that problem, they'll be glad for the extra options. +## I Have A Problem/Solution, But It's Basically Just JavaScript. Should I Add It? -h2. I Have A Problem/Solution, But It's Basically Just JavaScript. Should I Add It? - -Yes! CoffeeScript compiles to JavaScript, and that means that some of its functionality comes straight from JavaScript. (For example, see Reversing Arrays.) But if you're programming in CoffeeScript and you need to reverse an array, this Cookbook should stand ready to tell you it's available to you in CoffeeScript—even if it's just a straight call into a JavaScript library. +Yes! CoffeeScript compiles to JavaScript, and that means that some of its functionality comes straight from JavaScript. (For example, see [Reversing Arrays](/chapters/arrays/reversing-arrays).) But if you're programming in CoffeeScript and you need to reverse an array, this Cookbook should stand ready to tell you it's available to you in CoffeeScript -- even if it's just a straight call into a JavaScript library. - -h2. I Found a Typo. Is That Enough of a Fix? Does That Count? +## I Found a Typo. Is That Enough of a Fix? Does That Count? Absolutely! - diff --git a/authors.md b/authors.md new file mode 100644 index 0000000..6d4a792 --- /dev/null +++ b/authors.md @@ -0,0 +1,33 @@ +--- +layout: default +title: Authors +--- + +# Authors + +The following people are totally rad and awesome because they have contributed recipes! + +* David Brady _ratgeyser@gmail.com_ +* John Ford _jwford@gmail.com_ +* Steven Reid _steven @ reidnorthwest . com_ +* David Moulton _dave@themoultons.net_ +* Sebastian Slomski _sebastian@simple-systems.org_ +* Aaron Weinberger _aw9994@cs.ship.edu_ +* James C. Holder _cs_cookbook@thirdtruck.org_ +* Jason Giedymin _jasong@apache.org_ +* ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! + +# Developers + +_The following people are amazingly rad and awesome because they have helped fix the code for the site!_ + +* David Brady _ratgeyser@gmail.com_ +* Mike Moore _mike@blowmage.com_ +* Peter Hellberg _peter@c7.se_ +* ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! + +# Designers + +The following people are astonishingly rad and awesome because they did great design for the site! + +* Oh please SOMEBODY put their name here! Check out the [contributing](/contributing) section and get cracking! diff --git a/authors.textile b/authors.textile deleted file mode 100644 index 6a7dc9c..0000000 --- a/authors.textile +++ /dev/null @@ -1,36 +0,0 @@ ---- -layout: default -title: Authors ---- - -h1. Authors - -_The following people are totally rad and awesome because they have contributed recipes!_ - -* David Brady _ratgeyser@gmail.com_ -* John Ford _jwford@gmail.com_ -* Steven Reid _steven @ reidnorthwest . com_ -* David Moulton _dave@themoultons.net_ -* Sebastian Slomski _sebastian@simple-systems.org_ -* Aaron Weinberger _aw9994@cs.ship.edu_ -* James C. Holder _cs_cookbook@thirdtruck.org_ -* Jason Giedymin _jasong@apache.org_ -* ...You! What are you waiting for? Check out the contributing section and get cracking! - - -h1. Developers - -_The following people are amazingly rad and awesome because they have helped fix the code for the site!_ - -* David Brady _ratgeyser@gmail.com_ -* Mike Moore _mike@blowmage.com_ -* Peter Hellberg _peter@c7.se_ -* ...You! What are you waiting for? Check out the contributing section and get cracking! - - -h1. Designers - -_The following people are astonishingly rad and awesome because they did great design for the site!_ - -* Oh please SOMEBODY put their name here! Check out the contributing section and get cracking! - diff --git a/chapters/dates_and_times/date-of-thanksgiving.md b/chapters/dates_and_times/date-of-thanksgiving.md index ea9a3c7..7df1584 100644 --- a/chapters/dates_and_times/date-of-thanksgiving.md +++ b/chapters/dates_and_times/date-of-thanksgiving.md @@ -47,112 +47,6 @@ thanksgivingDayCA(2012) # => 8 (October 8th) {% endhighlight %} -The idea is very simple: -* Find out what day of the week is the first day of respective month (November for USA, October for Canada). -* Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). -* Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). ---- -layout: recipe -title: Calculate the date of Thanksgiving (USA and Canada) -chapter: Dates and Times ---- -## Problem - -You need to calculate when is Thanksgivinig in given year. - -## Solution - -The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. - -In the USA Thanksgiving is celebrated on the fourth Thursday in November: - -{% highlight coffeescript %} - -thanksgivingDayUSA = (year = (new Date).getFullYear()) -> - first = new Date year, 10, 1 - day_of_week = first.getDay() - 22 + (11 - day_of_week) % 7 - -{% endhighlight %} - -In Canada it is the second Monday in October: - -{% highlight coffeescript %} - -thanksgivingDayCA = (year = (new Date).getFullYear()) -> - first = new Date year, 9, 1 - day_of_week = first.getDay() - 8 + (8 - day_of_week) % 7 - -{% endhighlight %} - -## Discussion - -{% highlight coffeescript %} - -thanksgivingDayUSA() #=> 24 (November 24th, 2011) - -thanksgivingDayCA() # => 10 (October 10th, 2011) - -thanksgivingDayUSA(2012) # => 22 (November 22nd) - -thanksgivingDayCA(2012) # => 8 (October 8th) - -{% endhighlight %} - -The idea is very simple: -* Find out what day of the week is the first day of respective month (November for USA, October for Canada). -* Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). -* Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). ---- -layout: recipe -title: Calculate the date of Thanksgiving (USA and Canada) -chapter: Dates and Times ---- -## Problem - -You need to calculate when is Thanksgivinig in given year. - -## Solution - -The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. - -In the USA Thanksgiving is celebrated on the fourth Thursday in November: - -{% highlight coffeescript %} - -thanksgivingDayUSA = (year = (new Date).getFullYear()) -> - first = new Date year, 10, 1 - day_of_week = first.getDay() - 22 + (11 - day_of_week) % 7 - -{% endhighlight %} - -In Canada it is the second Monday in October: - -{% highlight coffeescript %} - -thanksgivingDayCA = (year = (new Date).getFullYear()) -> - first = new Date year, 9, 1 - day_of_week = first.getDay() - 8 + (8 - day_of_week) % 7 - -{% endhighlight %} - -## Discussion - -{% highlight coffeescript %} - -thanksgivingDayUSA() #=> 24 (November 24th, 2011) - -thanksgivingDayCA() # => 10 (October 10th, 2011) - -thanksgivingDayUSA(2012) # => 22 (November 22nd) - -thanksgivingDayCA(2012) # => 8 (October 8th) - -{% endhighlight %} - The idea is very simple: 1. Find out what day of the week is the first day of respective month (November for USA, October for Canada). 2. Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..e51cdee --- /dev/null +++ b/contributing.md @@ -0,0 +1,30 @@ +--- +layout: default +title: Contributing +--- +# Contributing + +The Cookbook needs your help! + +Here's the Contribution Recipe: + +1. Fork the repository at [https://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com](https://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com) +2. Do awesomeness! +3. Send a pull request to coffeescript-cookbook +4. If we merge your pull request, you get commit access. BAM. Go back to step 2 and stay there as long as you want. + +## Wanted Recipes + +Want to help, but don't know where to start? Want a recipe, but don't know how to write it? Check out the [Wanted Recipes](/wanted-recipes) page! + +## Authors + +Write recipes! Fork the repository, author some pages, and send us a pull request. For more information read the [Author's Guide](/authors-guide). + +## Developers + +Improve the code! Fork the repository, extend or improve the site coded, and send a pull request. For more information read the [Developer's Guide](/developers-guide). + +## Designers + +Make this site look pretty! Fork the repository, extend, improve or update the design, and send a pull request. For more information read the [Designer's Guide](/designers-guide). diff --git a/contributing.textile b/contributing.textile deleted file mode 100644 index 19ba91d..0000000 --- a/contributing.textile +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: default -title: Contributing ---- -h1. Contributing - -The Cookbook needs your help! - -Here's the Contribution Recipe: - -# Fork the repository at http://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com -# Do awesomeness! -# Send a pull request to coffeescript-cookbook -# If we merge your pull request, you get commit access. BAM. Go back to step 2 and stay there as long as you want. - - -h2. Wanted Recipes - -Want to help, but don't know where to start? Want a recipe, but don't know how to write it? Check out the Wanted Recipes page! - -h2. Authors - -Write recipes! Fork the repository, author some pages, and send us a pull request. For more information read the author's guide. - -h2. Developers - -Improve the code! Fork the repository, extend or improve the site coded, and send a pull request. For more information read the developer's guide. - -h2. Designers - -Make this site look pretty! Fork the repository, extend, improve or update the design, and send a pull request. For more information read the designer's guide. - diff --git a/designers-guide.textile b/designers-guide.md similarity index 61% rename from designers-guide.textile rename to designers-guide.md index 80d0808..bc47757 100644 --- a/designers-guide.textile +++ b/designers-guide.md @@ -3,13 +3,12 @@ layout: default title: Designer's Guide --- -h1. Designer's Guide +# Designer's Guide -Start with the Developer's Guide to get a test version of the cookbook up and running on your machine, and get started! +Start with the [Developer's Guide](/developers-guide) to get a test version of the cookbook up and running on your machine, and get started! First designer to actually DO some design gets to write this page! If you have been waiting for your chance to literally _write the designer's guide_ for a website that is also a cookbook about CoffeeScript... ...welcome home. - diff --git a/developers-guide.textile b/developers-guide.md similarity index 67% rename from developers-guide.textile rename to developers-guide.md index 6347369..484a7e0 100644 --- a/developers-guide.textile +++ b/developers-guide.md @@ -3,23 +3,23 @@ layout: default title: Developer's Guide --- -h1. Developer's Guide +# Developer's Guide _Please help out by updating this page_ -h3. Operating System +### Operating System It works on Mac OSX. Probably works without any changes or issues on linux. A masochist could probably get it working on Windows. -h2. Installation +## Installation -h3. Clone the repo +### Clone the repo {% highlight bash %} git clone git://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com.git {% endhighlight %} -h3. Create a Ruby Gemset +### Create a Ruby Gemset Optional, but highly recommended. @@ -28,16 +28,16 @@ $ rvm gemset create jekyll $ echo 'rvm gemset use jekyll' >> .rvmrc {% endhighlight %} -h3. Install Required Gems +### Install Required Gems {% highlight bash %} gem install jekyll # needed for testing building the site -gem install RedCloth # needed for .textile rendering +gem install RedCloth # needed for .md rendering gem install serve # needed for resolving .html files w/o extension gem install thin # optional; more efficient webserver than Webrick but not strictly necessary {% endhighlight %} -h3. Install pygments +### Install pygments You'll need python installed for this. Macs and most linuces come with it preinstalled. @@ -45,9 +45,9 @@ You'll need python installed for this. Macs and most linuces come with it preins easy_install pygments # for syntax highlighting {% endhighlight %} -h2. Building and Viewing the Website +## Building and Viewing the Website -h3. Run jekyll +### Run jekyll Open a terminal window, cd into the project folder and run jekyll from the project root. @@ -55,20 +55,18 @@ Open a terminal window, cd into the project folder and run jekyll from the proje jekyll --auto {% endhighlight %} -Leave this window running while you work. Any time you change a file, jekyll will rerender it into the _site folder. +Leave this window running while you work. Any time you change a file, jekyll will rerender it into the `_site` folder. -h3. Run serve +### Run serve -Open another terminal window, cd into the project folder, then cd into the _site subfolder, and run +Open another terminal window, cd into the project folder, then cd into the `_site` subfolder, and run {% highlight bash %} serve {% endhighlight %} -This will start a webserver in the _site folder. Open a browser and visit http://localhost:4000/ and you should see the site. - -h2. Minituiae and Other Trivialities - -jekyll can take a second or two to catch up when you save a file. If you edit a file and don't see the changes in your browser, give it a second or two and try again. +This will start a webserver in the `_site` folder. Open a browser and visit `http://localhost:4000/` and you should see the site. +## Minituiae and Other Trivialities +jekyll can take a second or two to catch up when you save a file. If you edit a file and don't see the changes in your browser, give it a second or two and try again. You may also see Maruku warnings, but as long as it prints `Successfully generated site` you should be alright. diff --git a/license.md b/license.md new file mode 100644 index 0000000..f4a9905 --- /dev/null +++ b/license.md @@ -0,0 +1,8 @@ +--- +layout: default +title: License +--- + +coffeescriptcookbook.com is licensed under the [Creative Commons Attribution 3.0 Unported (CC BY 3.0)](http://creativecommons.org/licenses/by/3.0/) license. By submitting information to this site you agree to grant this license to all users of the site, and that your editing of the authors page constitutes satisfactory attribution. + +[LICENSE](/LICENSE-CC-BY) diff --git a/license.textile b/license.textile deleted file mode 100644 index d69c019..0000000 --- a/license.textile +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: default -title: License ---- - -coffeescriptcookbook.com is licensed under the Creative Commons Attribution 3.0 Unported (CC BY 3.0) license. By submitting information to this site you agree to grant this license to all users of the site, and that your editing of the authors page constitutes satisfactory attribution. - -LICENSE - diff --git a/recipe-template.textile b/recipe-template.md similarity index 51% rename from recipe-template.textile rename to recipe-template.md index 8fab668..5ed7232 100644 --- a/recipe-template.textile +++ b/recipe-template.md @@ -3,28 +3,25 @@ layout: default title: Recipe Template --- -h2. Sample recipe template +## Sample recipe template -Create a new *my_recipe.textile* file and use this text as a start. +Create a new _my_recipe.md_ file and use this text as a start. -
      +{% highlight text %}
       ---
       layout: recipe
       title: Title of The Recipe
       chapter: Chapter Name
       ---
      -
      -h2. Problem
      +## Problem
       
       You have a problem.
       
      -
      -h2. Solution
      +## Solution
       
       Do this about it.
       
      -
      -h2. Discussion
      +## Discussion
       
       Here's why.
      -
      +{% endhighlight %} diff --git a/terms-of-use.textile b/terms-of-use.md similarity index 97% rename from terms-of-use.textile rename to terms-of-use.md index c41bd4e..ec07bc7 100644 --- a/terms-of-use.textile +++ b/terms-of-use.md @@ -3,4 +3,3 @@ layout: default title: Terms of Use --- - diff --git a/wanted-recipes.textile b/wanted-recipes.md similarity index 93% rename from wanted-recipes.textile rename to wanted-recipes.md index 3d0dd65..7af4511 100644 --- a/wanted-recipes.textile +++ b/wanted-recipes.md @@ -2,19 +2,19 @@ layout: default title: Wanted Recipes --- -h1. Wanted Recipes +# Wanted Recipes Here's a list of recipes we think we need. Pick one, implement it, and remove it from the page. Alternately, add a quick note here for a recipe you'd like to see so someone else can add it. In the notes below, "JS" means the recipe is just a simple passthrough to an existing JavaScript method. -h2. Syntax +## Syntax * Ensuring variables are closed over # with "do" -h2. Objects +## Objects -h2. Strings +## Strings * HTML methods # JS .sup(), .sub(), .blink(), .link(url), etc. May not exist in your JS impl! * substr # str.substr(x,y) === str[x..x+y-1] === str[x...x+y] @@ -24,7 +24,7 @@ h2. Strings * Replacing substrings * Trimming whitespace from the end of a string -h2. Arrays +## Arrays * Testing every element in an array {% highlight coffeescript %} @@ -39,12 +39,12 @@ evens.every even * Creating a string from an array * Replace all duplicates of an array -h2. Dates and Times +## Dates and Times * Calculating the phase of the moon * Number of days between two dates -h2. Math +## Math * square root # JS Math.sqrt * Constants # JS Math.PI, Math.E @@ -55,7 +55,7 @@ h2. Math * Exponents # Math.exp * Check if a creditcard is valid (checksum, Luhn algorithm) -h2. Functions +## Functions * Nested functions @@ -84,25 +84,25 @@ foo 1, 2, 3 # => 6 {% endhighlight %} -h2. jQuery +## jQuery -h2. Regular Expressions +## Regular Expressions * Searching for substrings # "foo bar baz".match(/ba./) # => [ 'bar', index: 4, input: 'foo bar baz' ] * Searching for substrings # "foo bar baz".search(/ba./) # => 4 * Replacing substrings # "foo bar baz".replace( /ba./, 'foo') # => "foo foo baz" * Replace HTML tags with named HTML entities #
      => <br/> -h2. Networking +## Networking * Basic HTTP server * Basic HTTP client -h2. AJAX +## AJAX * Getting data from a remote server # using raw XHTTPRequest instead of jQuery's $.ajax -h2. Design patterns +## Design patterns * Creational Patterns ** Abstract Factory @@ -125,5 +125,3 @@ h2. Design patterns ** State ** Template Method ** Visitor - - From bab63900d2573979b1a1e30420e08c34ed980c07 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 14:39:21 -0600 Subject: [PATCH 060/267] We use hyphens in file names, not underscores --- recipe-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipe-template.md b/recipe-template.md index 5ed7232..1c53641 100644 --- a/recipe-template.md +++ b/recipe-template.md @@ -5,7 +5,7 @@ title: Recipe Template ## Sample recipe template -Create a new _my_recipe.md_ file and use this text as a start. +Create a new `my-recipe.md` file and use this text as a start. {% highlight text %} --- From 415589a040e0bf64c1935db07cb4e63919c54e0f Mon Sep 17 00:00:00 2001 From: JasonGiedymin Date: Mon, 20 Jun 2011 16:55:26 -0400 Subject: [PATCH 061/267] Added recipe for fast inverse square root. --- chapters/math/fast-inv-square.md | 117 +++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 chapters/math/fast-inv-square.md diff --git a/chapters/math/fast-inv-square.md b/chapters/math/fast-inv-square.md new file mode 100644 index 0000000..211e274 --- /dev/null +++ b/chapters/math/fast-inv-square.md @@ -0,0 +1,117 @@ +--- +layout: recipe +title: Fast Inverse Square Root +chapter: Math +--- +## Problem + +You would like to calculate a the inverse square root[5] of a number quickly. + +## Solution +Appearing in the Quake III Arena source code[1], this strange algorithm uses +integer operations along with a 'magic number' to calculate floating point +approximation values of inverse square roots. + +In this CoffeeScript variant I supply the original classic, and newer optimal +32 bit magic numbers found by Chris Lomont[2]. Also supplied is the 64-bit +sized magic number. + +Another feature included is the ability to alter the level of precision. +This is done by controling the number of iterations for performing Newton's +method[3]. + +Depending on the machine and level of percision this algorithm may still +provide performance increases over the classic. + +To run this, compile the script with coffee: + coffee -c .coffee + +Then copy & paste the compiled js code in to the JavaSript console of your +browser. + +Note: You will need a browser which supports typed-arrays[4]. + +References: +* [1] [ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip](ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip) +* [2] [http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf](http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf) +* [3] [http://en.wikipedia.org/wiki/Newton%27s_method](http://en.wikipedia.org/wiki/Newton%27s_method) +* [4] [https://developer.mozilla.org/en/JavaScript_typed_arrays](https://developer.mozilla.org/en/JavaScript_typed_arrays) +* [5] [http://en.wikipedia.org/wiki/Fast_inverse_square_root](http://en.wikipedia.org/wiki/Fast_inverse_square_root) + +This code is in gist form here: +[https://gist.github.com/1036533](https://gist.github.com/1036533) + +{% highlight coffeescript %} +### + +Author: Jason Giedymin + http://www.jasongiedymin.com + https://github.com/JasonGiedymin + +Appearing in the Quake III Arena source code[1], this strange algorithm uses +integer operations along with a 'magic number' to calculate floating point +approximation values of inverse square roots. + +In this CoffeeScript variant I supply the original classic, and newer optimal +32 bit magic numbers found by Chris Lomont[2]. Also supplied is the 64-bit +sized magic number. + +Another feature included is the ability to alter the level of precision. +This is done by controling the number of iterations for performing Newton's +method[3]. + +Depending on the machine and level of percision this algorithm may still +provide performance increases over the classic. + +To run this, compile the script with coffee: + coffee -c .coffee + +Then copy & paste the compiled js code in to the JavaSript console of your +browser. + +Note: You will need a browser which supports typed-arrays[4]. + +References: +[1] ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip +[2] http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf +[3] http://en.wikipedia.org/wiki/Newton%27s_method +[4] https://developer.mozilla.org/en/JavaScript_typed_arrays + +### + +approx_const_quake_32 = 0x5f3759df # See [1] +approx_const_32 = 0x5f375a86 # See [2] +approx_const_64 = 0x5fe6eb50c7aa19f9 # See [2] + +fastInvSqrt_typed = (n, precision=1) -> + # Using typed arrays. Right now only works in browsers. + # Node.JS version coming soon. + + y = new Float32Array(1) + i = new Int32Array(y.buffer) + + y[0] = n + i[0] = 0x5f375a86 - (i[0] >> 1) + + for iter in [1...precision] + y[0] = y[0] * (1.5 - ((n * 0.5) * y[0] * y[0])) + + return y[0] + +### Sample single runs ### +testSingle = () -> + example_n = 10 + + console.log("Fast InvSqrt of 10, precision 1: #{fastInvSqrt_typed(example_n)}") + console.log("Fast InvSqrt of 10, precision 5: #{fastInvSqrt_typed(example_n, 5)}") + console.log("Fast InvSqrt of 10, precision 10: #{fastInvSqrt_typed(example_n, 10)}") + console.log("Fast InvSqrt of 10, precision 20: #{fastInvSqrt_typed(example_n, 20)}") + console.log("Classic of 10: #{1.0 / Math.sqrt(example_n)}") + +testSingle() + +{% endhighlight %} + +## Discussion + +Questions? From ef8e7c0609fe386a4319b6f08a0c713570286034 Mon Sep 17 00:00:00 2001 From: JasonGiedymin Date: Mon, 20 Jun 2011 16:57:26 -0400 Subject: [PATCH 062/267] Fixed fast-inv-square.md syntax. --- chapters/math/fast-inv-square.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/math/fast-inv-square.md b/chapters/math/fast-inv-square.md index 211e274..79f3b1e 100644 --- a/chapters/math/fast-inv-square.md +++ b/chapters/math/fast-inv-square.md @@ -24,7 +24,7 @@ Depending on the machine and level of percision this algorithm may still provide performance increases over the classic. To run this, compile the script with coffee: - coffee -c .coffee + coffee -c script.coffee Then copy & paste the compiled js code in to the JavaSript console of your browser. From 43defe1a0cce2612cfaa60de0311a3d961b1dc0b Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 20 Jun 2011 15:32:12 -0600 Subject: [PATCH 063/267] Add appropriate whitespace --- chapters/classes_and_objects/cloning.md | 1 + 1 file changed, 1 insertion(+) diff --git a/chapters/classes_and_objects/cloning.md b/chapters/classes_and_objects/cloning.md index 1b52bcc..bdf508c 100644 --- a/chapters/classes_and_objects/cloning.md +++ b/chapters/classes_and_objects/cloning.md @@ -42,6 +42,7 @@ The difference between copying an object through assignment and through this clo * repeating these steps for all sub-objects by calling the clone-function recursively. Example of an assignment copy: + {% highlight coffeescript %} x = foo: 'bar' From 675b0854c8c13c437a692be5b1e6f8df363389a5 Mon Sep 17 00:00:00 2001 From: Allen Goodman Date: Mon, 20 Jun 2011 21:24:08 -0400 Subject: [PATCH 064/267] Included recipe for matching two or more strings (i.e. calculating an edit distance). --- chapters/strings/matching-strings.md | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 chapters/strings/matching-strings.md diff --git a/chapters/strings/matching-strings.md b/chapters/strings/matching-strings.md new file mode 100644 index 0000000..7a65af3 --- /dev/null +++ b/chapters/strings/matching-strings.md @@ -0,0 +1,42 @@ +--- +layout: recipe +title: Matching Strings +chapter: Strings +--- +## Problem + +You want to match two or more strings. + +## Solution + +Calculate the edit distance, or number of operations required to transform one string into the other. + +{% highlight coffeescript %} +Levenshtein = + (str1, str2) -> + + l1 = str1.length + l2 = str2.length + + Math.max l1, l2 if Math.min l1, l2 == 0 + + i = 0; j = 0; distance = [] + + for i in [0...l1 + 1] + distance[i] = [] + distance[i][0] = i + + distance[0][j] = j for j in [0...l2 + 1] + + for i in [1...l1 + 1] + for j in [1...l2 + 1] + distance[i][j] = Math.min distance[i - 1][j] + 1, + distance[i][j - 1] + 1, + distance[i - 1][j - 1] + if str1.charAt(i - 1) == str2.charAt(j - 1) then 0 else 1 + + distance[l1][l2] +{% endhighlight %} + +## Discussion + +You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. \ No newline at end of file From 7bd773d07d4386d92599ffd8f39bcc9624440be5 Mon Sep 17 00:00:00 2001 From: JasonGiedymin Date: Mon, 20 Jun 2011 21:57:08 -0400 Subject: [PATCH 065/267] Fixed formatting on fast-inv-square.md and corrected references. --- chapters/math/fast-inv-square.md | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/chapters/math/fast-inv-square.md b/chapters/math/fast-inv-square.md index 79f3b1e..0aadaae 100644 --- a/chapters/math/fast-inv-square.md +++ b/chapters/math/fast-inv-square.md @@ -5,20 +5,20 @@ chapter: Math --- ## Problem -You would like to calculate a the inverse square root[5] of a number quickly. +You would like to calculate a the inverse square root of a number [quickly][5]. ## Solution -Appearing in the Quake III Arena source code[1], this strange algorithm uses +Appearing in the Quake III Arena [source code][1], this strange algorithm uses integer operations along with a 'magic number' to calculate floating point approximation values of inverse square roots. In this CoffeeScript variant I supply the original classic, and newer optimal -32 bit magic numbers found by Chris Lomont[2]. Also supplied is the 64-bit +32 bit magic numbers found by [Chris Lomont][2]. Also supplied is the 64-bit sized magic number. Another feature included is the ability to alter the level of precision. -This is done by controling the number of iterations for performing Newton's -method[3]. +This is done by controling the number of iterations for performing [Newton's +method][3]. Depending on the machine and level of percision this algorithm may still provide performance increases over the classic. @@ -29,14 +29,20 @@ To run this, compile the script with coffee: Then copy & paste the compiled js code in to the JavaSript console of your browser. -Note: You will need a browser which supports typed-arrays[4]. +Note: You will need a browser which supports [typed-arrays][4]. -References: -* [1] [ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip](ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip) -* [2] [http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf](http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf) -* [3] [http://en.wikipedia.org/wiki/Newton%27s_method](http://en.wikipedia.org/wiki/Newton%27s_method) -* [4] [https://developer.mozilla.org/en/JavaScript_typed_arrays](https://developer.mozilla.org/en/JavaScript_typed_arrays) -* [5] [http://en.wikipedia.org/wiki/Fast_inverse_square_root](http://en.wikipedia.org/wiki/Fast_inverse_square_root) +References: +1. [ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip](ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip) +2. [http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf](http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf) +3. [http://en.wikipedia.org/wiki/Newton%27s_method](http://en.wikipedia.org/wiki/Newton%27s_method) +4. [https://developer.mozilla.org/en/JavaScript_typed_arrays](https://developer.mozilla.org/en/JavaScript_typed_arrays) +5. [http://en.wikipedia.org/wiki/Fast_inverse_square_root](http://en.wikipedia.org/wiki/Fast_inverse_square_root) + +[1]: ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip "ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip" +[2]: http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf "http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf" +[3]: http://en.wikipedia.org/wiki/Newton%27s_method "http://en.wikipedia.org/wiki/Newton%27s_method" +[4]: https://developer.mozilla.org/en/JavaScript_typed_arrays "https://developer.mozilla.org/en/JavaScript_typed_arrays" +[5]: http://en.wikipedia.org/wiki/Fast_inverse_square_root "http://en.wikipedia.org/wiki/Fast_inverse_square_root" This code is in gist form here: [https://gist.github.com/1036533](https://gist.github.com/1036533) @@ -50,7 +56,7 @@ Author: Jason Giedymin Appearing in the Quake III Arena source code[1], this strange algorithm uses integer operations along with a 'magic number' to calculate floating point -approximation values of inverse square roots. +approximation values of inverse square roots[5]. In this CoffeeScript variant I supply the original classic, and newer optimal 32 bit magic numbers found by Chris Lomont[2]. Also supplied is the 64-bit @@ -76,6 +82,7 @@ References: [2] http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf [3] http://en.wikipedia.org/wiki/Newton%27s_method [4] https://developer.mozilla.org/en/JavaScript_typed_arrays +[5] http://en.wikipedia.org/wiki/Fast_inverse_square_root ### From 03a46f38410a0120ef8c36887f4ca2f701e25984 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Mon, 20 Jun 2011 22:39:42 -0700 Subject: [PATCH 066/267] add recipe for uppercasing a string --- chapters/strings/uppercasing-a-string.md | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 chapters/strings/uppercasing-a-string.md diff --git a/chapters/strings/uppercasing-a-string.md b/chapters/strings/uppercasing-a-string.md new file mode 100644 index 0000000..9ee762b --- /dev/null +++ b/chapters/strings/uppercasing-a-string.md @@ -0,0 +1,45 @@ +--- +layout: recipe +title: Uppercasing a String +chapter: Strings +--- +## Problem + +You want to uppercase a string. + +## Solution + +Use JavaScript's String toUpperCase() method: + +{% highlight coffeescript %} +"one two three".toUpperCase() +# => 'ONE TWO THREE' +{% endhighlight %} + +## Discussion + +`toUpperCase()` is a standard JavaScript method. Don't forget the parentheses. + +### Syntax Sugar + +You can add some Ruby-like syntax sugar with the following shortcut: + +{% highlight coffeescript %} +String::upcase = -> @toUpperCase() +"one two three".upcase() +# => 'ONE TWO THREE' +{% endhighlight %} + +The snippet above demonstrates a few features of CoffeeScript: + +* The double-colon `::` is shorthand for saying `.prototype.` +* The "at" sign `@` is shorthand for saying `this.` + +The code above compiles in to the following JavaScript: + +{% highlight javascript %} +String.prototype.upcase = function() { + return this.toUpperCase(); +}; +"one two three".upcase(); +{% endhighlight %} \ No newline at end of file From 8d66fb5f66b077db52b9b5706de6697a4bd8a508 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Mon, 20 Jun 2011 22:39:51 -0700 Subject: [PATCH 067/267] add recipe for lowercasing a string --- chapters/strings/lowercasing-a-string.md | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 chapters/strings/lowercasing-a-string.md diff --git a/chapters/strings/lowercasing-a-string.md b/chapters/strings/lowercasing-a-string.md new file mode 100644 index 0000000..0a7708b --- /dev/null +++ b/chapters/strings/lowercasing-a-string.md @@ -0,0 +1,45 @@ +--- +layout: recipe +title: Lowercasing a String +chapter: Strings +--- +## Problem + +You want to lowercase a string. + +## Solution + +Use JavaScript's String toLowerCase() method: + +{% highlight coffeescript %} +"ONE TWO THREE".toLowerCase() +# => 'one two three' +{% endhighlight %} + +## Discussion + +`toLowerCase()` is a standard JavaScript method. Don't forget the parentheses. + +### Syntax Sugar + +You can add some Ruby-like syntax sugar with the following shortcut: + +{% highlight coffeescript %} +String::downcase = -> @toLowerCase() +"ONE TWO THREE".downcase() +# => 'one two three' +{% endhighlight %} + +The snippet above demonstrates a few features of CoffeeScript: + +* The double-colon `::` is shorthand for saying `.prototype.` +* The "at" sign `@` is shorthand for saying `this.` + +The code above compiles in to the following JavaScript: + +{% highlight javascript %} +String.prototype.downcase = function() { + return this.toLowerCase(); +}; +"ONE TWO THREE".downcase(); +{% endhighlight %} \ No newline at end of file From 9f2c544697e7657105ab431c2473217702f66b27 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Mon, 20 Jun 2011 22:40:26 -0700 Subject: [PATCH 068/267] update wanted recipes --- wanted-recipes.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/wanted-recipes.md b/wanted-recipes.md index 7af4511..a7f0902 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -19,8 +19,6 @@ In the notes below, "JS" means the recipe is just a simple passthrough to an exi * HTML methods # JS .sup(), .sub(), .blink(), .link(url), etc. May not exist in your JS impl! * substr # str.substr(x,y) === str[x..x+y-1] === str[x...x+y] * substring # str.substring(x,y) === str.slice(x,y) === str[x..y-1] === str[x...y] -* Uppercasing a string # JS toUpperCase() -* Lowercasing a string # JS toLowerCase() * Replacing substrings * Trimming whitespace from the end of a string From 81bb6f9141e83582cf2ba72dc6c909b4c23e00ad Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Mon, 20 Jun 2011 23:17:01 -0700 Subject: [PATCH 069/267] add recipe for trimming whitespace from a string --- .../trimming-whitespace-from-a-string.md | 67 +++++++++++++++++++ wanted-recipes.md | 1 - 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 chapters/strings/trimming-whitespace-from-a-string.md diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md new file mode 100644 index 0000000..483f106 --- /dev/null +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -0,0 +1,67 @@ +--- +layout: recipe +title: Trimming whitespace from a String +chapter: Strings +--- +## Problem + +You want to trim whitespace from a string. + +## Solution + +Use JavaScript's Regular Expression support to replace whitespace. + +To trim leading and trailing whitespace, use the following: + +{% highlight coffeescript %} +" padded string ".replace /^\s+|\s+$/g, "" +# => 'padded string' +{% endhighlight %} + +To trim only leading whitespace, use the following: + +{% highlight coffeescript %} +" padded string ".replace /^\s+/g, "" +# => 'padded string ' +{% endhighlight %} + +To trim only trailing whitespace, use the following: + +{% highlight coffeescript %} +" padded string ".replace /\s+$/g, "" +# => ' padded string' +{% endhighlight %} + +## Discussion + +Opera, Firefox and Chrome all have a native string prototype `trim` method, and the other browsers could add one as well. For this particular method, I would use the built-in method where possible: + +{% highlight coffeescript %} +trim = (val) -> + if String::trim? then val.trim() else val.replace /^\s+|\s+$/g, "" + +trim " padded string " +# => 'padded string' +{% endhighlight %} + + +### Syntax Sugar + +You can add some Ruby-like syntax sugar with the following shortcuts: + +{% highlight coffeescript %} +String::strip = -> if String::trim? then @trim() else @replace /^\s+|\s+$/g, "" +String::lstrip = -> @replace /^\s+/g, "" +String::rstrip = -> @replace /\s+$/g, "" + +" padded string ".strip() +# => 'padded string' +" padded string ".lstrip() +# => 'padded string ' +" padded string ".rstrip() +# => ' padded string' +{% endhighlight %} + +For an interesting discussion and benchmarks of JavaScript `trim` performance, see [this blog post][1] by Steve Levithan. + +[1] http://blog.stevenlevithan.com/archives/faster-trim-javascript \ No newline at end of file diff --git a/wanted-recipes.md b/wanted-recipes.md index a7f0902..14a3bf0 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -20,7 +20,6 @@ In the notes below, "JS" means the recipe is just a simple passthrough to an exi * substr # str.substr(x,y) === str[x..x+y-1] === str[x...x+y] * substring # str.substring(x,y) === str.slice(x,y) === str[x..y-1] === str[x...y] * Replacing substrings -* Trimming whitespace from the end of a string ## Arrays From 49838fa54679855f0c4492066a4363ddd3806c5d Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Mon, 20 Jun 2011 23:23:23 -0700 Subject: [PATCH 070/267] fix link to blog post --- chapters/strings/trimming-whitespace-from-a-string.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md index 483f106..5c08bbb 100644 --- a/chapters/strings/trimming-whitespace-from-a-string.md +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -62,6 +62,5 @@ String::rstrip = -> @replace /\s+$/g, "" # => ' padded string' {% endhighlight %} -For an interesting discussion and benchmarks of JavaScript `trim` performance, see [this blog post][1] by Steve Levithan. +For an interesting discussion and benchmarks of JavaScript `trim` performance, see [this blog post](http://blog.stevenlevithan.com/archives/faster-trim-javascript) by Steve Levithan. -[1] http://blog.stevenlevithan.com/archives/faster-trim-javascript \ No newline at end of file From b746ee1cf994d7917e4a5af1e99b7f76f1a56019 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 00:18:25 -0700 Subject: [PATCH 071/267] add recipe Removing duplicate elements from Arrays --- ...removing-duplicate-elements-from-arrays.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 chapters/arrays/removing-duplicate-elements-from-arrays.md diff --git a/chapters/arrays/removing-duplicate-elements-from-arrays.md b/chapters/arrays/removing-duplicate-elements-from-arrays.md new file mode 100644 index 0000000..8cfdbd6 --- /dev/null +++ b/chapters/arrays/removing-duplicate-elements-from-arrays.md @@ -0,0 +1,26 @@ +--- +layout: recipe +title: Removing duplicate elements from Arrays +chapter: Arrays +--- +## Problem + +You want to remove duplicate elements from an array. + +## Solution + +{% highlight coffeescript %} +Array::unique = -> + o = {} + r = [] + o[this[i]] = this[i] for i in [1...@length] + r.push(v) for k,v of o + r + +[1,1,2,2,2,3,4,5,6,6,6,"a","a","b","d","b","c"].unique() +# => [ 1, 2, 3, 4, 5, 6, 'a', 'b', 'd', 'c' ] +{% endhighlight %} + +## Discussion + +There are many implementations of the `unique` method in JavaScript. This one is based on "The fastest method to find unique items in array" found [here](http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/). From e94f5b09bc30d8ae15acfefb9165cffadd956867 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 00:27:08 -0700 Subject: [PATCH 072/267] add recipe Creating a String from an Array --- .../arrays/creating-a-string-from-an-array.md | 21 +++++++++++++++++++ wanted-recipes.md | 1 - 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 chapters/arrays/creating-a-string-from-an-array.md diff --git a/chapters/arrays/creating-a-string-from-an-array.md b/chapters/arrays/creating-a-string-from-an-array.md new file mode 100644 index 0000000..d2b83bb --- /dev/null +++ b/chapters/arrays/creating-a-string-from-an-array.md @@ -0,0 +1,21 @@ +--- +layout: recipe +title: Creating a String from an Array +chapter: Arrays +--- +## Problem + +You want to create a string from an array. + +## Solution + +Use JavaScript's Array toString() method: + +{% highlight coffeescript %} +["one", "two", "three"].toString() +# => 'three,two,one' +{% endhighlight %} + +## Discussion + +`toString()` is a standard JavaScript method. Don't forget the parentheses. diff --git a/wanted-recipes.md b/wanted-recipes.md index 14a3bf0..d082cba 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -33,7 +33,6 @@ evens.every even * Filtering arrays # [1..10.filter (x) -> x % 2 == 0 # => [ 2, 4, 6, 8, 10 ] * Detecting presence of matching items in an array # [1..10].some (x) -> x % 2 == 0 # => true * Processing an array item by item # [10..1].forEach (x) -> console.log x # => nothing;, but a countdown is displayed on the console -* Creating a string from an array * Replace all duplicates of an array ## Dates and Times From 03873e87d98538eb9de94a0131ddd47cee0b7451 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 00:45:44 -0700 Subject: [PATCH 073/267] add Replacing HTML tags with HTML named entities --- ...cing-html-tags-with-html-named-entities.md | 25 +++++++++++++++++++ wanted-recipes.md | 1 - 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md diff --git a/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md b/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md new file mode 100644 index 0000000..8c3a46d --- /dev/null +++ b/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md @@ -0,0 +1,25 @@ +--- +layout: recipe +title: Replacing HTML tags with HTML named entities +chapter: Regular Expressions +--- +## Problem + +You need to replace HTML tags with named entities: + +`
      => <br/>` + +## Solution + +{% highlight coffeescript %} +htmlEncode = (str) -> + str.replace /[&<>"']/g, ($0) -> + "&" + {"&":"amp", "<":"lt", ">":"gt", '"':"quot", "'":"#39"}[$0] + ";" + +htmlEncode('Barnes & Noble') +# => '<a href="http://bn.com">Barnes & Noble</a>' +{% endhighlight %} + +## Discussion + +There are probably better ways to implement the above method. \ No newline at end of file diff --git a/wanted-recipes.md b/wanted-recipes.md index d082cba..df42004 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -87,7 +87,6 @@ foo 1, 2, 3 * Searching for substrings # "foo bar baz".match(/ba./) # => [ 'bar', index: 4, input: 'foo bar baz' ] * Searching for substrings # "foo bar baz".search(/ba./) # => 4 * Replacing substrings # "foo bar baz".replace( /ba./, 'foo') # => "foo foo baz" -* Replace HTML tags with named HTML entities #
      => <br/> ## Networking From 1da06a883615ffa42e221ef7a58ded6079f2e4fc Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 00:47:29 -0700 Subject: [PATCH 074/267] add info to authors page --- authors.md | 1 + 1 file changed, 1 insertion(+) diff --git a/authors.md b/authors.md index 6d4a792..54043fd 100644 --- a/authors.md +++ b/authors.md @@ -15,6 +15,7 @@ The following people are totally rad and awesome because they have contributed r * Aaron Weinberger _aw9994@cs.ship.edu_ * James C. Holder _cs_cookbook@thirdtruck.org_ * Jason Giedymin _jasong@apache.org_ +* Phil Cohen _github@phlippers.net_ * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers From d1824197c303e3239d68ae28938735f65d3129d5 Mon Sep 17 00:00:00 2001 From: Allen Goodman Date: Tue, 21 Jun 2011 03:56:46 -0400 Subject: [PATCH 075/267] added newline; repositioned parentheses to make algorithm slightly more idiomatic --- chapters/strings/matching-strings.md | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/chapters/strings/matching-strings.md b/chapters/strings/matching-strings.md index 7a65af3..100aefa 100644 --- a/chapters/strings/matching-strings.md +++ b/chapters/strings/matching-strings.md @@ -12,31 +12,34 @@ You want to match two or more strings. Calculate the edit distance, or number of operations required to transform one string into the other. {% highlight coffeescript %} + Levenshtein = (str1, str2) -> - + l1 = str1.length l2 = str2.length - + Math.max l1, l2 if Math.min l1, l2 == 0 - + i = 0; j = 0; distance = [] for i in [0...l1 + 1] - distance[i] = [] - distance[i][0] = i + distance[i] = [] + distance[i][0] = i distance[0][j] = j for j in [0...l2 + 1] - + for i in [1...l1 + 1] - for j in [1...l2 + 1] - distance[i][j] = Math.min distance[i - 1][j] + 1, - distance[i][j - 1] + 1, - distance[i - 1][j - 1] + if str1.charAt(i - 1) == str2.charAt(j - 1) then 0 else 1 + for j in [1...l2 + 1] + distance[i][j] = Math.min distance[i - 1][j] + 1, + distance[i][j - 1] + 1, + distance[i - 1][j - 1] + + if (str1.charAt i - 1) == (str2.charAt j - 1) then 0 else 1 distance[l1][l2] + {% endhighlight %} ## Discussion -You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. \ No newline at end of file +You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. From b1866679bdc09438d5e1905fbf4420c973475944 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 01:19:16 -0700 Subject: [PATCH 076/267] refactor Array::unique to be more idiomatic --- .../arrays/removing-duplicate-elements-from-arrays.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/chapters/arrays/removing-duplicate-elements-from-arrays.md b/chapters/arrays/removing-duplicate-elements-from-arrays.md index 8cfdbd6..3842433 100644 --- a/chapters/arrays/removing-duplicate-elements-from-arrays.md +++ b/chapters/arrays/removing-duplicate-elements-from-arrays.md @@ -11,11 +11,9 @@ You want to remove duplicate elements from an array. {% highlight coffeescript %} Array::unique = -> - o = {} - r = [] - o[this[i]] = this[i] for i in [1...@length] - r.push(v) for k,v of o - r + unique = {} + unique[@[key]] = @[key] for key in [1...@length] + value for key, value of unique [1,1,2,2,2,3,4,5,6,6,6,"a","a","b","d","b","c"].unique() # => [ 1, 2, 3, 4, 5, 6, 'a', 'b', 'd', 'c' ] From 55316d065b64eb0e57b87d51c19c36526e6cc1e5 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 01:50:11 -0700 Subject: [PATCH 077/267] rename variables for clarity --- chapters/arrays/removing-duplicate-elements-from-arrays.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/arrays/removing-duplicate-elements-from-arrays.md b/chapters/arrays/removing-duplicate-elements-from-arrays.md index 3842433..fcf8434 100644 --- a/chapters/arrays/removing-duplicate-elements-from-arrays.md +++ b/chapters/arrays/removing-duplicate-elements-from-arrays.md @@ -11,9 +11,9 @@ You want to remove duplicate elements from an array. {% highlight coffeescript %} Array::unique = -> - unique = {} - unique[@[key]] = @[key] for key in [1...@length] - value for key, value of unique + output = {} + output[@[key]] = @[key] for key in [1...@length] + value for key, value of output [1,1,2,2,2,3,4,5,6,6,6,"a","a","b","d","b","c"].unique() # => [ 1, 2, 3, 4, 5, 6, 'a', 'b', 'd', 'c' ] From 56231d325dac2174c37ec208f42079cf7a04b55b Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 01:56:38 -0700 Subject: [PATCH 078/267] fix bug with array index --- chapters/arrays/removing-duplicate-elements-from-arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/removing-duplicate-elements-from-arrays.md b/chapters/arrays/removing-duplicate-elements-from-arrays.md index fcf8434..7ecd731 100644 --- a/chapters/arrays/removing-duplicate-elements-from-arrays.md +++ b/chapters/arrays/removing-duplicate-elements-from-arrays.md @@ -12,7 +12,7 @@ You want to remove duplicate elements from an array. {% highlight coffeescript %} Array::unique = -> output = {} - output[@[key]] = @[key] for key in [1...@length] + output[@[key]] = @[key] for key in [0...@length] value for key, value of output [1,1,2,2,2,3,4,5,6,6,6,"a","a","b","d","b","c"].unique() From d00857d1b28205691675089402adbf0d386ef4c6 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Tue, 21 Jun 2011 03:05:12 -0700 Subject: [PATCH 079/267] make EOF newlines consistent --- .../replacing-html-tags-with-html-named-entities.md | 2 +- chapters/strings/lowercasing-a-string.md | 2 +- chapters/strings/trimming-whitespace-from-a-string.md | 1 - chapters/strings/uppercasing-a-string.md | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md b/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md index 8c3a46d..cbd23a8 100644 --- a/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md +++ b/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md @@ -22,4 +22,4 @@ htmlEncode('Barnes & Noble') ## Discussion -There are probably better ways to implement the above method. \ No newline at end of file +There are probably better ways to implement the above method. diff --git a/chapters/strings/lowercasing-a-string.md b/chapters/strings/lowercasing-a-string.md index 0a7708b..729dafa 100644 --- a/chapters/strings/lowercasing-a-string.md +++ b/chapters/strings/lowercasing-a-string.md @@ -42,4 +42,4 @@ String.prototype.downcase = function() { return this.toLowerCase(); }; "ONE TWO THREE".downcase(); -{% endhighlight %} \ No newline at end of file +{% endhighlight %} diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md index 5c08bbb..b133e7f 100644 --- a/chapters/strings/trimming-whitespace-from-a-string.md +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -63,4 +63,3 @@ String::rstrip = -> @replace /\s+$/g, "" {% endhighlight %} For an interesting discussion and benchmarks of JavaScript `trim` performance, see [this blog post](http://blog.stevenlevithan.com/archives/faster-trim-javascript) by Steve Levithan. - diff --git a/chapters/strings/uppercasing-a-string.md b/chapters/strings/uppercasing-a-string.md index 9ee762b..fd0e103 100644 --- a/chapters/strings/uppercasing-a-string.md +++ b/chapters/strings/uppercasing-a-string.md @@ -42,4 +42,4 @@ String.prototype.upcase = function() { return this.toUpperCase(); }; "one two three".upcase(); -{% endhighlight %} \ No newline at end of file +{% endhighlight %} From 5915613ff00ca94a9bd0ae3f3206ecd98f71ed9b Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 22 Jun 2011 12:59:02 -0400 Subject: [PATCH 080/267] Change author contact info formatting to avoid conflicts with email addresses that have underscores. Updated my email address. Remove extra HTML markup from /authors-guide.md. --- authors-guide.md | 6 ++++-- authors.md | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/authors-guide.md b/authors-guide.md index a690c79..f5de511 100644 --- a/authors-guide.md +++ b/authors-guide.md @@ -46,13 +46,15 @@ chapter: Strings Turn on syntax highlighting for CoffeeScript with `highlight coffeescript`. -
      {% highlight coffeescript %}
      +test2
      +
      +{% highlight coffeescript %}
       # Calculate the square of a number
       square = (x) -> x * x
       
       square(16)
       # => 256
      -{% endhighlight %}
      +{% endhighlight %} This produces the following: diff --git a/authors.md b/authors.md index 54043fd..59dd643 100644 --- a/authors.md +++ b/authors.md @@ -7,24 +7,24 @@ title: Authors The following people are totally rad and awesome because they have contributed recipes! -* David Brady _ratgeyser@gmail.com_ -* John Ford _jwford@gmail.com_ -* Steven Reid _steven @ reidnorthwest . com_ -* David Moulton _dave@themoultons.net_ -* Sebastian Slomski _sebastian@simple-systems.org_ -* Aaron Weinberger _aw9994@cs.ship.edu_ -* James C. Holder _cs_cookbook@thirdtruck.org_ -* Jason Giedymin _jasong@apache.org_ -* Phil Cohen _github@phlippers.net_ +* David Brady *ratgeyser@gmail.com* +* John Ford *jwford@gmail.com* +* Steven Reid *steven @ reidnorthwest . com* +* David Moulton *dave@themoultons.net* +* Sebastian Slomski *sebastian@simple-systems.org* +* Aaron Weinberger *aw9994@cs.ship.edu* +* James C. Holder *coffeescriptcookbook.com@thirdtruck.org* +* Jason Giedymin *jasong@apache.org* +* Phil Cohen *github@phlippers.net* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers -_The following people are amazingly rad and awesome because they have helped fix the code for the site!_ +*The following people are amazingly rad and awesome because they have helped fix the code for the site!* -* David Brady _ratgeyser@gmail.com_ -* Mike Moore _mike@blowmage.com_ -* Peter Hellberg _peter@c7.se_ +* David Brady *ratgeyser@gmail.com* +* Mike Moore *mike@blowmage.com* +* Peter Hellberg *peter@c7.se* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Designers From 3b82a24b7285f0d9f0d55b3fb9d482aa98fae450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o?= Date: Wed, 22 Jun 2011 20:14:58 +0200 Subject: [PATCH 081/267] Added basic HTTP server + client recipes. --- .gitignore | 2 + authors.textile | 1 + chapters/networking/basic-http-client.textile | 63 ++++ chapters/networking/basic-http-server.textile | 269 ++++++++++++++++++ 4 files changed, 335 insertions(+) create mode 100644 chapters/networking/basic-http-client.textile create mode 100644 chapters/networking/basic-http-server.textile diff --git a/.gitignore b/.gitignore index 3042ecd..587c487 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ _site *.swp *.swo + +.DS_Store diff --git a/authors.textile b/authors.textile index f6e75aa..855d1ff 100644 --- a/authors.textile +++ b/authors.textile @@ -14,6 +14,7 @@ _The following people are totally rad and awesome because they have contributed * Sebastian Slomski _sebastian@simple-systems.org_ * Aaron Weinberger _aw9994@cs.ship.edu_ * James C. Holder _cs_cookbook@thirdtruck.org_ +* João Moreno _coffeecb @joaomoreno.com_ * ...You! What are you waiting for? Check out the contributing section and get cracking! diff --git a/chapters/networking/basic-http-client.textile b/chapters/networking/basic-http-client.textile new file mode 100644 index 0000000..9151f14 --- /dev/null +++ b/chapters/networking/basic-http-client.textile @@ -0,0 +1,63 @@ +--- +layout: recipe +title: Basic HTTP Client +chapter: Networking +--- + +h2. Problem + +You want to create a HTTP client. + +h2. Solution + +In this recipe, we'll use "node.js":http://nodejs.org/ 's HTTP library. We'll go from a simple GET request example to a client which returns the external IP of a computer. + +h3. GET something + +{% highlight coffeescript %} +http = require 'http' + +http.get { host: 'www.google.com' }, (res) -> + console.log res.statusCode +{% endhighlight %} + +The @get@ function, from node.js's @http@ module, issues a GET request to a HTTP server. The response comes in the form of a callback, which we can handle in a function. This example merely prints the response status code. Check it out: + +{% highlight console %} +$ coffee http-client.coffee +200 + +{% endhighlight %} + +h3. What's my IP? + +If you are inside a network which relies on "NAT":http://en.wikipedia.org/wiki/Network_address_translation such as a LAN, you probably have faced the issue of finding out what's your external IP address. Let's write a small coffeescript for this. + +{% highlight coffeescript %} +http = require 'http' + +http.get { host: 'checkip.dyndns.org' }, (res) -> + data = '' + res.on 'data', (chunk) -> + data += chunk.toString() + res.on 'end', () -> + console.log data.match(/([0-9]+\.){3}[0-9]+/)[0] +{% endhighlight %} + +We can get the data from the result object by listening on its @'data'@ event; and know that it has come to an end once the @'end'@ event has been fired. When that happens, we can do a simple regular expression match to extract our IP address. Try it: + +{% highlight console %} +$ coffee http-client.coffee +123.123.123.123 +{% endhighlight %} + +h2. Discussion + +Note that @http.get@ is a shortcut of @http.request@. The latter allows you to issue HTTP requests with different methods, such as POST or PUT. + +For API and overall information on this subject, check node.js's "http":http://nodejs.org/docs/latest/api/http.html and "https":http://nodejs.org/docs/latest/api/https.html documentation pages. Also, the "HTTP spec":http://www.ietf.org/rfc/rfc2616.txt might come in handy. + +h3. Exercises + +* Create a client for the key-value store HTTP server, from the "Basic HTTP Server":http://coffeescriptcookbook.com/chapters/networking/basic-http-server recipe. + diff --git a/chapters/networking/basic-http-server.textile b/chapters/networking/basic-http-server.textile new file mode 100644 index 0000000..e59f6f8 --- /dev/null +++ b/chapters/networking/basic-http-server.textile @@ -0,0 +1,269 @@ +--- +layout: recipe +title: Basic HTTP Server +chapter: Networking +--- + +h2. Problem + +You want to create a HTTP server over a network. Over the course of this recipe, we'll go step by step from the smallest server possible to a functional key-value store. + +h2. Solution + +We'll use "node.js":http://nodejs.org/ 's HTTP library to our own selfish purposes and create the simplest web server possible in Coffeescript. + +h3. Say 'hi\n' + +We can start by importing the @http@ module. This module has a nice helper function — @createServer@ — which, given a simple request handler, creates a HTTP server. All that's left to do then is have the server listening on a port. + +{% highlight coffeescript %} +http = require 'http' +server = http.createServer (req, res) -> res.end 'hi\n' +server.listen 8000 +{% endhighlight %} + +To run this example, simply put in a file and run it. You can kill it with @Ctrl-C@. We can test it using the @curl@ command, available on most *nix platforms: + +{% highlight console %} +$ curl -D - http://localhost:8000/ +HTTP/1.1 200 OK +Connection: keep-alive +Transfer-Encoding: chunked + +hi +{% endhighlight %} + +h3. What's going on? + +Let's get a little bit more feedback on what's happening on our server. While we're at it, we could also be friendlier to our clients and provide them some HTTP headers. + +{% highlight coffeescript %} +http = require 'http' + +server = http.createServer (req, res) -> + console.log req.method, req.url + data = 'hi\n' + res.writeHead 200, + 'Content-Type': 'text/plain' + 'Content-Length': data.length + res.end data + +server.listen 8000 +{% endhighlight %} + +Try to access it once again, but this time use different URL paths, such as @http://localhost:8000/coffee@. You'll see something like this on the server console: + +{% highlight console %} +$ coffee http-server.coffee +GET / +GET /coffee +GET /user/1337 +{% endhighlight %} + +h3. GETting stuff + +What if our webserver was able to hold some data? We'll try to come up with a simple key-value store in which elements are retrievable via GET requests. Provide a key on the request path and the server will return the corresponding value - or 404 if it doesn't exist. + +{% highlight coffeescript %} +http = require 'http' + +store = # we'll use a simple object as our store + foo: 'bar' + coffee: 'script' + +server = http.createServer (req, res) -> + console.log req.method, req.url + + value = store[req.url[1..]] + + if not value + res.writeHead 404 + else + res.writeHead 200, + 'Content-Type': 'text/plain' + 'Content-Length': value.length + 1 + res.write value + '\n' + + res.end() + +server.listen 8000 +{% endhighlight %} + +{% highlight console %} +$ curl -D - http://localhost:8000/coffee +HTTP/1.1 200 OK +Content-Type: text/plain +Content-Length: 7 +Connection: keep-alive + +script + +$ curl -D - http://localhost:8000/oops +HTTP/1.1 404 Not Found +Connection: keep-alive +Transfer-Encoding: chunked + +{% endhighlight %} + +h3. Use your head(ers) + +Let's face it, @text/plain@ is kind of lame. How about if we use something hip like @application/json@ or @text/xml@? Also, our store retrieval process could use a bit of refactoring — how about some exception throwing & handling? Let's see what we can come up with: + +{% highlight coffeescript %} +http = require 'http' + +# known mime types +[any, json, xml] = ['*/*', 'application/json', 'text/xml'] + +# gets a value from the db in format [value, contentType] +get = (store, key, format) -> + value = store[key] + throw 'Unknown key' if not value + switch format + when any, json then [JSON.stringify({ key: key, value: value }), json] + when xml then ["#{ key }\n#{ value }", xml] + else throw 'Unknown format' + +store = + foo: 'bar' + coffee: 'script' + +server = http.createServer (req, res) -> + console.log req.method, req.url + + try + key = req.url[1..] + [value, contentType] = get store, key, req.headers.accept + code = 200 + catch error + contentType = 'text/plain' + value = error + code = 404 + + res.writeHead code, + 'Content-Type': contentType + 'Content-Length': value.length + 1 + res.write value + '\n' + res.end() + +server.listen 8000 +{% endhighlight %} + +This server will still return the value which matches a given key, or 404 if non-existent. But it will structure the response either in JSON or XML, according to the @Accept@ header. See for yourself: + +{% highlight console %} +$ curl http://localhost:8000/ +Unknown key + +$ curl http://localhost:8000/coffee +{"key":"coffee","value":"script"} + +$ curl -H "Accept: text/xml" http://localhost:8000/coffee +coffee +script + +$ curl -H "Accept: image/png" http://localhost:8000/coffee +Unknown format +{% endhighlight %} + +h3. You gotta give to get back + +The obvious last step in our adventure is to provide the client the ability to store data. We'll keep our RESTiness by listening to POST requests for this purpose. + +{% highlight coffeescript %} +http = require 'http' + +# known mime types +[any, json, xml] = ['*/*', 'application/json', 'text/xml'] + +# gets a value from the db in format [value, contentType] +get = (store, key, format) -> + value = store[key] + throw 'Unknown key' if not value + switch format + when any, json then [JSON.stringify({ key: key, value: value }), json] + when xml then ["#{ key }\n#{ value }", xml] + else throw 'Unknown format' + +# puts a value in the db +put = (store, key, value) -> + throw 'Invalid key' if not key or key is '' + store[key] = value + +store = + foo: 'bar' + coffee: 'script' + +# helper function that responds to the client +respond = (res, code, contentType, data) -> + res.writeHead code, + 'Content-Type': contentType + 'Content-Length': data.length + res.write data + res.end() + +server = http.createServer (req, res) -> + console.log req.method, req.url + key = req.url[1..] + contentType = 'text/plain' + code = 404 + + switch req.method + when 'GET' + try + [value, contentType] = get store, key, req.headers.accept + code = 200 + catch error + value = error + respond res, code, contentType, value + '\n' + + when 'POST' + value = '' + req.on 'data', (chunk) -> value += chunk + req.on 'end', () -> + try + put store, key, value + value = '' + code = 200 + catch error + value = error + '\n' + respond res, code, contentType, value + +server.listen 8000 +{% endhighlight %} + +Notice how the data is received in a POST request. By attaching some handlers on the @'data'@ and @'end'@ events of the request object, we're able to buffer and finally save the data from the client in the @store@. + +{% highlight console %} +$ curl -D - http://localhost:8000/cookie +HTTP/1.1 404 Not Found # ... +Unknown key + +$ curl -D - -d "monster" http://localhost:8000/cookie +HTTP/1.1 200 OK # ... + +$ curl -D - http://localhost:8000/cookie +HTTP/1.1 200 OK # ... +{"key":"cookie","value":"monster"} +{% endhighlight %} + +h2. Discussion + +Give @http.createServer@ a function in the shape of @(request, response) -> ...@ and it will return a server object, which we can use to listen on a port. Interact with the @request@ and @response@ objects to give the server its behaviour. Listen on port 8000 using @server.listen 8000@. + +For API and overall information on this subject, check node.js's "http":http://nodejs.org/docs/latest/api/http.html and "https":http://nodejs.org/docs/latest/api/https.html documentation pages. Also, the "HTTP spec":http://www.ietf.org/rfc/rfc2616.txt might come in handy. + +h3. Exercises + +* Create a layer in between the server and the developer which would allow the developer to do something like: + +{% highlight coffeescript %} +server = layer.createServer + 'GET /': (req, res) -> + ... + 'GET /page': (req, res) -> + ... + 'PUT /image': (req, res) -> + ... +{% endhighlight %} + From e8adc5a96881f5abe1715dbe1fd0e3d2faae9b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o?= Date: Wed, 22 Jun 2011 23:38:46 +0200 Subject: [PATCH 082/267] Adopted Markdown for Basic HTTP Client & Server --- authors.md | 1 + ...tp-client.textile => basic-http-client.md} | 26 ++++++------ ...tp-server.textile => basic-http-server.md} | 40 ++++++++++--------- 3 files changed, 35 insertions(+), 32 deletions(-) rename chapters/networking/{basic-http-client.textile => basic-http-client.md} (55%) rename chapters/networking/{basic-http-server.textile => basic-http-server.md} (81%) diff --git a/authors.md b/authors.md index 59dd643..faf6555 100644 --- a/authors.md +++ b/authors.md @@ -16,6 +16,7 @@ The following people are totally rad and awesome because they have contributed r * James C. Holder *coffeescriptcookbook.com@thirdtruck.org* * Jason Giedymin *jasong@apache.org* * Phil Cohen *github@phlippers.net* +* João Moreno *coffeecb @joaomoreno .com* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/networking/basic-http-client.textile b/chapters/networking/basic-http-client.md similarity index 55% rename from chapters/networking/basic-http-client.textile rename to chapters/networking/basic-http-client.md index 9151f14..0faef9f 100644 --- a/chapters/networking/basic-http-client.textile +++ b/chapters/networking/basic-http-client.md @@ -4,15 +4,15 @@ title: Basic HTTP Client chapter: Networking --- -h2. Problem +## Problem You want to create a HTTP client. -h2. Solution +## Solution -In this recipe, we'll use "node.js":http://nodejs.org/ 's HTTP library. We'll go from a simple GET request example to a client which returns the external IP of a computer. +In this recipe, we'll use [node.js](http://nodejs.org/)'s HTTP library. We'll go from a simple GET request example to a client which returns the external IP of a computer. -h3. GET something +### GET something {% highlight coffeescript %} http = require 'http' @@ -21,7 +21,7 @@ http.get { host: 'www.google.com' }, (res) -> console.log res.statusCode {% endhighlight %} -The @get@ function, from node.js's @http@ module, issues a GET request to a HTTP server. The response comes in the form of a callback, which we can handle in a function. This example merely prints the response status code. Check it out: +The `get` function, from node.js's `http` module, issues a GET request to a HTTP server. The response comes in the form of a callback, which we can handle in a function. This example merely prints the response status code. Check it out: {% highlight console %} $ coffee http-client.coffee @@ -29,9 +29,9 @@ $ coffee http-client.coffee {% endhighlight %} -h3. What's my IP? +### What's my IP? -If you are inside a network which relies on "NAT":http://en.wikipedia.org/wiki/Network_address_translation such as a LAN, you probably have faced the issue of finding out what's your external IP address. Let's write a small coffeescript for this. +If you are inside a network which relies on [NAT](http://en.wikipedia.org/wiki/Network_address_translation) such as a LAN, you probably have faced the issue of finding out what's your external IP address. Let's write a small coffeescript for this. {% highlight coffeescript %} http = require 'http' @@ -44,20 +44,20 @@ http.get { host: 'checkip.dyndns.org' }, (res) -> console.log data.match(/([0-9]+\.){3}[0-9]+/)[0] {% endhighlight %} -We can get the data from the result object by listening on its @'data'@ event; and know that it has come to an end once the @'end'@ event has been fired. When that happens, we can do a simple regular expression match to extract our IP address. Try it: +We can get the data from the result object by listening on its `'data'` event; and know that it has come to an end once the `'end'` event has been fired. When that happens, we can do a simple regular expression match to extract our IP address. Try it: {% highlight console %} $ coffee http-client.coffee 123.123.123.123 {% endhighlight %} -h2. Discussion +## Discussion -Note that @http.get@ is a shortcut of @http.request@. The latter allows you to issue HTTP requests with different methods, such as POST or PUT. +Note that `http.get` is a shortcut of `http.request`. The latter allows you to issue HTTP requests with different methods, such as POST or PUT. -For API and overall information on this subject, check node.js's "http":http://nodejs.org/docs/latest/api/http.html and "https":http://nodejs.org/docs/latest/api/https.html documentation pages. Also, the "HTTP spec":http://www.ietf.org/rfc/rfc2616.txt might come in handy. +For API and overall information on this subject, check node.js's [http](http://nodejs.org/docs/latest/api/http.html) and [https](http://nodejs.org/docs/latest/api/https.html) documentation pages. Also, the [HTTP spec](http://www.ietf.org/rfc/rfc2616.txt) might come in handy. -h3. Exercises +### Exercises -* Create a client for the key-value store HTTP server, from the "Basic HTTP Server":http://coffeescriptcookbook.com/chapters/networking/basic-http-server recipe. +* Create a client for the key-value store HTTP server, from the [Basic HTTP Server](basic-http-server) recipe. diff --git a/chapters/networking/basic-http-server.textile b/chapters/networking/basic-http-server.md similarity index 81% rename from chapters/networking/basic-http-server.textile rename to chapters/networking/basic-http-server.md index e59f6f8..cbd11e9 100644 --- a/chapters/networking/basic-http-server.textile +++ b/chapters/networking/basic-http-server.md @@ -4,17 +4,17 @@ title: Basic HTTP Server chapter: Networking --- -h2. Problem +## Problem You want to create a HTTP server over a network. Over the course of this recipe, we'll go step by step from the smallest server possible to a functional key-value store. -h2. Solution +## Solution -We'll use "node.js":http://nodejs.org/ 's HTTP library to our own selfish purposes and create the simplest web server possible in Coffeescript. +We'll use [node.js](http://nodejs.org/)'s HTTP library to our own selfish purposes and create the simplest web server possible in Coffeescript. -h3. Say 'hi\n' +### Say 'hi\n' -We can start by importing the @http@ module. This module has a nice helper function — @createServer@ — which, given a simple request handler, creates a HTTP server. All that's left to do then is have the server listening on a port. +We can start by importing node.js's HTTP module. This contains `createServer` which, given a simple request handler, returns a HTTP server. We can use that server to listen on a TCP port. {% highlight coffeescript %} http = require 'http' @@ -22,7 +22,7 @@ server = http.createServer (req, res) -> res.end 'hi\n' server.listen 8000 {% endhighlight %} -To run this example, simply put in a file and run it. You can kill it with @Ctrl-C@. We can test it using the @curl@ command, available on most *nix platforms: +To run this example, simply put in a file and run it. You can kill it with `Ctrl-C`. We can test it using the `curl` command, available on most \*nix platforms: {% highlight console %} $ curl -D - http://localhost:8000/ @@ -33,7 +33,7 @@ Transfer-Encoding: chunked hi {% endhighlight %} -h3. What's going on? +### What's going on? Let's get a little bit more feedback on what's happening on our server. While we're at it, we could also be friendlier to our clients and provide them some HTTP headers. @@ -51,7 +51,7 @@ server = http.createServer (req, res) -> server.listen 8000 {% endhighlight %} -Try to access it once again, but this time use different URL paths, such as @http://localhost:8000/coffee@. You'll see something like this on the server console: +Try to access it once again, but this time use different URL paths, such as `http://localhost:8000/coffee`. You'll see something like this on the server console: {% highlight console %} $ coffee http-server.coffee @@ -60,9 +60,9 @@ GET /coffee GET /user/1337 {% endhighlight %} -h3. GETting stuff +### GETting stuff -What if our webserver was able to hold some data? We'll try to come up with a simple key-value store in which elements are retrievable via GET requests. Provide a key on the request path and the server will return the corresponding value - or 404 if it doesn't exist. +What if our webserver was able to hold some data? We'll try to come up with a simple key-value store in which elements are retrievable via GET requests. Provide a key on the request path and the server will return the corresponding value — or 404 if it doesn't exist. {% highlight coffeescript %} http = require 'http' @@ -89,6 +89,8 @@ server = http.createServer (req, res) -> server.listen 8000 {% endhighlight %} +We can try several URLs to see how it responds: + {% highlight console %} $ curl -D - http://localhost:8000/coffee HTTP/1.1 200 OK @@ -105,9 +107,9 @@ Transfer-Encoding: chunked {% endhighlight %} -h3. Use your head(ers) +### Use your head(ers) -Let's face it, @text/plain@ is kind of lame. How about if we use something hip like @application/json@ or @text/xml@? Also, our store retrieval process could use a bit of refactoring — how about some exception throwing & handling? Let's see what we can come up with: +Let's face it, `text/plain` is kind of lame. How about if we use something hip like `application/json` or `text/xml`? Also, our store retrieval process could use a bit of refactoring — how about some exception throwing & handling? Let's see what we can come up with: {% highlight coffeescript %} http = require 'http' @@ -149,7 +151,7 @@ server = http.createServer (req, res) -> server.listen 8000 {% endhighlight %} -This server will still return the value which matches a given key, or 404 if non-existent. But it will structure the response either in JSON or XML, according to the @Accept@ header. See for yourself: +This server will still return the value which matches a given key, or 404 if non-existent. But it will structure the response either in JSON or XML, according to the `Accept` header. See for yourself: {% highlight console %} $ curl http://localhost:8000/ @@ -166,7 +168,7 @@ $ curl -H "Accept: image/png" http://localhost:8000/coffee Unknown format {% endhighlight %} -h3. You gotta give to get back +### You gotta give to get back The obvious last step in our adventure is to provide the client the ability to store data. We'll keep our RESTiness by listening to POST requests for this purpose. @@ -232,7 +234,7 @@ server = http.createServer (req, res) -> server.listen 8000 {% endhighlight %} -Notice how the data is received in a POST request. By attaching some handlers on the @'data'@ and @'end'@ events of the request object, we're able to buffer and finally save the data from the client in the @store@. +Notice how the data is received in a POST request. By attaching some handlers on the `'data'` and `'end'` events of the request object, we're able to buffer and finally save the data from the client in the `store`. {% highlight console %} $ curl -D - http://localhost:8000/cookie @@ -247,13 +249,13 @@ HTTP/1.1 200 OK # ... {"key":"cookie","value":"monster"} {% endhighlight %} -h2. Discussion +## Discussion -Give @http.createServer@ a function in the shape of @(request, response) -> ...@ and it will return a server object, which we can use to listen on a port. Interact with the @request@ and @response@ objects to give the server its behaviour. Listen on port 8000 using @server.listen 8000@. +Give `http.createServer` a function in the shape of `(request, response) -> ...` and it will return a server object, which we can use to listen on a port. Interact with the `request` and `response` objects to give the server its behaviour. Listen on port 8000 using `server.listen 8000`. -For API and overall information on this subject, check node.js's "http":http://nodejs.org/docs/latest/api/http.html and "https":http://nodejs.org/docs/latest/api/https.html documentation pages. Also, the "HTTP spec":http://www.ietf.org/rfc/rfc2616.txt might come in handy. +For API and overall information on this subject, check node.js's [http](http://nodejs.org/docs/latest/api/http.html) and [https](http://nodejs.org/docs/latest/api/https.html) documentation pages. Also, the [HTTP spec](http://www.ietf.org/rfc/rfc2616.txt) might come in handy. -h3. Exercises +### Exercises * Create a layer in between the server and the developer which would allow the developer to do something like: From 2d517ef87ec41ccefb85c43d2b9907ff6477b92c Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 22 Jun 2011 22:39:29 -0400 Subject: [PATCH 083/267] Fix the "Design Patterns/Strategy" recipe through a reformatting. --- chapters/design_patterns/builder.md | 2 +- chapters/design_patterns/strategy.md | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/chapters/design_patterns/builder.md b/chapters/design_patterns/builder.md index ead9d3f..97ccc01 100644 --- a/chapters/design_patterns/builder.md +++ b/chapters/design_patterns/builder.md @@ -87,4 +87,4 @@ builder.newTodo "Fill gas tank" ### Exercises * Expand the project- and context-tag generation code to filter out duplicate entries. -* Some Todo.txt users like to insert project and context tags inside the description of their to-do items. * Add code to identify these tags and filter them out of the end tags. +* Some Todo.txt users like to insert project and context tags inside the description of their to-do items. Add code to identify these tags and filter them out of the end tags. diff --git a/chapters/design_patterns/strategy.md b/chapters/design_patterns/strategy.md index a06c6c2..6faffcd 100644 --- a/chapters/design_patterns/strategy.md +++ b/chapters/design_patterns/strategy.md @@ -13,11 +13,16 @@ Encapsulate your algorithms inside of Strategy objects. Given an unsorted list, for example, we can change the sorting algorithm under different circumstances. +###The base class: + {% highlight coffeescript %} StringSorter = (@algorithm) -> - sort: (list) -> - @algorithm list + sort: (list) -> @algorithm list +{% endhighlight %} +###The strategies: + +{% highlight coffeescript %} bubbleSort = (list) -> anySwaps = false swapPass = -> @@ -45,7 +50,11 @@ reverseBubbleSort = (list) -> anySwaps = false swapPass() list +{% endhighlight %} + +###Using the strategies: +{% highlight coffeescript %} sorter = new StringSorter bubbleSort unsortedList = ['e', 'b', 'd', 'c', 'x', 'a'] From 050f2dd6ffd6f63c983e8f4cb358d2ddfbc896ad Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 22 Jun 2011 23:05:56 -0400 Subject: [PATCH 084/267] Updated "Wanted Recipes" and fixed its formatting. --- wanted-recipes.md | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/wanted-recipes.md b/wanted-recipes.md index df42004..050d22b 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -90,33 +90,38 @@ foo 1, 2, 3 ## Networking -* Basic HTTP server -* Basic HTTP client +* Streaming HTTP server +* Streaming HTTP client ## AJAX -* Getting data from a remote server # using raw XHTTPRequest instead of jQuery's $.ajax +* Getting data from a remote server # using raw XHTTPRequest instead of jQuery's `$.ajax` ## Design patterns * Creational Patterns -** Abstract Factory -** Prototype -** Singleton + * Abstract Factory + * Prototype + * Singleton * Structural Patterns -** Adapter -** Composite -** Facade -** Flyweight -** Proxy + * Adapter + * Composite + * Facade + * Flyweight + * Proxy * Behavioral Patterns -** Chain of Responsibility -** Command -** Iterator -** Mediator -** Observer -** State -** Template Method -** Visitor + * Chain of Responsibility + * Command + * Iterator + * Mediator + * Observer + * State + * Template Method + * Visitor + +## Databases + +* MongoDB/Couch access +* MySQL/PostgreSQL access From 347b83b51e839b8d78863edc2d9ba77ddd6fde42 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Wed, 22 Jun 2011 21:10:31 -0700 Subject: [PATCH 085/267] add Replacing substrings recipe to Regular Expressions chapter --- .../replacing-substrings.md | 39 +++++++++++++++++++ wanted-recipes.md | 1 - 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 chapters/regular_expressions/replacing-substrings.md diff --git a/chapters/regular_expressions/replacing-substrings.md b/chapters/regular_expressions/replacing-substrings.md new file mode 100644 index 0000000..cfd0978 --- /dev/null +++ b/chapters/regular_expressions/replacing-substrings.md @@ -0,0 +1,39 @@ +--- +layout: recipe +title: Replacing substrings +chapter: Regular Expressions +--- +## Problem + +You need to replace a portion of a string with another value. + +## Solution + +Use the JavaScript `replace` method. `replace` matches with the given string, and returns the edited string. + +The first version takes 2 arguments: _pattern_ and _string replacement_ + +{% highlight coffeescript %} +"JavaScript is my favorite!".replace /Java/, "Coffee" +# => 'CoffeeScript is my favorite!' + +"foo bar baz".replace /ba./, "foo" +# => 'foo foo baz' + +"foo bar baz".replace /ba./g, "foo" +# => 'foo foo foo' +{% endhighlight %} + +The second version takes 2 arguments: _pattern_ and _callback function_ + +{% highlight coffeescript %} +"CoffeeScript is my favorite!".replace /(\w+)/g, (match) -> + match.toUpperCase() +# => 'COFFEESCRIPT IS MY FAVORITE!' +{% endhighlight %} + +The callback function is invoked for each match, and the match value is passed as the argument to the callback. + +## Discussion + +Regular Expressions are a powerful way to match and replace strings. diff --git a/wanted-recipes.md b/wanted-recipes.md index 050d22b..9052f42 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -86,7 +86,6 @@ foo 1, 2, 3 * Searching for substrings # "foo bar baz".match(/ba./) # => [ 'bar', index: 4, input: 'foo bar baz' ] * Searching for substrings # "foo bar baz".search(/ba./) # => 4 -* Replacing substrings # "foo bar baz".replace( /ba./, 'foo') # => "foo foo baz" ## Networking From 020ca0594c0b2a9fd13e25baa7a99bbb821530a0 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Wed, 22 Jun 2011 22:21:16 -0700 Subject: [PATCH 086/267] add Searching for Substrings recipe to Regular Expressions --- .../searching-for-substrings.md | 63 +++++++++++++++++++ wanted-recipes.md | 3 - 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 chapters/regular_expressions/searching-for-substrings.md diff --git a/chapters/regular_expressions/searching-for-substrings.md b/chapters/regular_expressions/searching-for-substrings.md new file mode 100644 index 0000000..fd63b5f --- /dev/null +++ b/chapters/regular_expressions/searching-for-substrings.md @@ -0,0 +1,63 @@ +--- +layout: recipe +title: Searching for substrings +chapter: Regular Expressions +--- +## Problem + +You need to search for a substring, and return either the starting position of the match or the matching value itself. + +## Solution + +There are several ways to accomplish this using regular expressions. Some methods are called on a `RegExp` pattern or object and some are called on `String` objects. + +### `RegExp` objects + +The first way is to call the `test` method on a `RegExp` pattern or object. The `test` method returns a boolean value: + +{% highlight coffeescript %} +match = /sample/.test("Sample text") +# => false + +match = /sample/i.test("Sample text") +# => true +{% endhighlight %} + +The next way to is to call the `exec` method on a `RegExp` pattern or object. The `exec` method returns an array an array with the match information or `null`: + +{% highlight coffeescript %} +match = /s(amp)le/i.exec "Sample text" +# => [ 'Sample', 'amp', index: 0, input: 'Sample text' ] + +match = /s(amp)le/.exec "Sample text" +# => null +{% endhighlight %} + +### `String` objects + +The `match` method matches a given string with the `RegExp`. With 'g' flag returns an array containing the matches, without 'g' flag returns just the first match or if no match is found returns `null`. + +{% highlight coffeescript %} +"Watch out for the rock!".match(/r?or?/g) +# => [ 'o', 'or', 'ro' ] + +"Watch out for the rock!".match(/r?or?/) +# => [ 'o', index: 6, input: 'Watch out for the rock!' ] + +"Watch out for the rock!".match(/ror/) +# => null +{% endhighlight %} + +The `search` method matches `RegExp` with string and returns the index of the beginning of the match if found, -1 if not. + +{% highlight coffeescript %} +"Watch out for the rock!".search /for/ +# => 10 + +"Watch out for the rock!".search /rof/ +# => -1 +{% endhighlight %} + +## Discussion + +Regular Expressions are a powerful way to test and match substrings. diff --git a/wanted-recipes.md b/wanted-recipes.md index 9052f42..1f07a2a 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -84,9 +84,6 @@ foo 1, 2, 3 ## Regular Expressions -* Searching for substrings # "foo bar baz".match(/ba./) # => [ 'bar', index: 4, input: 'foo bar baz' ] -* Searching for substrings # "foo bar baz".search(/ba./) # => 4 - ## Networking * Streaming HTTP server From 2c9f909f1ca05ac0db4cc17e8234877bd718de0a Mon Sep 17 00:00:00 2001 From: James Holder Date: Thu, 23 Jun 2011 13:53:26 -0400 Subject: [PATCH 087/267] Add "Design Patterns/Command" recipe and remove it from "Wanted Recipes". --- chapters/design_patterns/command.md | 59 +++++++++++++++++++++++++++++ wanted-recipes.md | 1 - 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 chapters/design_patterns/command.md diff --git a/chapters/design_patterns/command.md b/chapters/design_patterns/command.md new file mode 100644 index 0000000..3c1355d --- /dev/null +++ b/chapters/design_patterns/command.md @@ -0,0 +1,59 @@ +--- +layout: recipe +title: Command Pattern +chapter: Design Patterns +--- +## Problem + +You need to let another object handle when your private code is executed. + +## Solution + +Use the [Command pattern](http://en.wikipedia.org/wiki/Command_pattern) to pass along references to your functions. + +{% highlight coffeescript %} +# Using a private variable to simulate external scripts or modules +incrementers = (() -> + privateVar = 0 + + singleIncrementer = () -> + privateVar += 1 + + doubleIncrementer = () -> + privateVar += 2 + + commands = + single: singleIncrementer + double: doubleIncrementer + value: -> privateVar +)() + +class RunsAll + constructor: (@commands...) -> + run: -> command() for command in @commands + +runner = new RunsAll(incrementers.single, incrementers.double, incrementers.single, incrementers.double) +runner.run() +incrementers.value() # => 6 +{% endhighlight %} + +## Discussion + +With functions as first-class objects and with the function-bound variable scope inherited from Javascript, the CoffeeScript language makes the pattern nearly invisible. In fact, any function passed along as callbacks can act as a *Command*. + +The `jqXHR` object returned by jQuery AJAX methods uses this pattern. + +{% highlight coffeescript %} +jqxhr = $.ajax + url: "/" + +logMessages = "" + +jqxhr.success -> logMessages += "Success!\n" +jqxhr.error -> logMessages += "Error!\n" +jqxhr.complete -> logMessages += "Completed!\n" + +# On a valid AJAX request: +# logMessages == "Success!\nCompleted!\n" +{% endhighlight %} + diff --git a/wanted-recipes.md b/wanted-recipes.md index 050d22b..afeea8c 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -113,7 +113,6 @@ foo 1, 2, 3 * Behavioral Patterns * Chain of Responsibility - * Command * Iterator * Mediator * Observer From 94baea8d3f8610504cb328f1aa1ae638e33fb052 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 22 Jun 2011 22:39:29 -0400 Subject: [PATCH 088/267] Fix the "Design Patterns/Strategy" recipe through a reformatting. --- chapters/design_patterns/builder.md | 2 +- chapters/design_patterns/strategy.md | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/chapters/design_patterns/builder.md b/chapters/design_patterns/builder.md index ead9d3f..97ccc01 100644 --- a/chapters/design_patterns/builder.md +++ b/chapters/design_patterns/builder.md @@ -87,4 +87,4 @@ builder.newTodo "Fill gas tank" ### Exercises * Expand the project- and context-tag generation code to filter out duplicate entries. -* Some Todo.txt users like to insert project and context tags inside the description of their to-do items. * Add code to identify these tags and filter them out of the end tags. +* Some Todo.txt users like to insert project and context tags inside the description of their to-do items. Add code to identify these tags and filter them out of the end tags. diff --git a/chapters/design_patterns/strategy.md b/chapters/design_patterns/strategy.md index a06c6c2..6faffcd 100644 --- a/chapters/design_patterns/strategy.md +++ b/chapters/design_patterns/strategy.md @@ -13,11 +13,16 @@ Encapsulate your algorithms inside of Strategy objects. Given an unsorted list, for example, we can change the sorting algorithm under different circumstances. +###The base class: + {% highlight coffeescript %} StringSorter = (@algorithm) -> - sort: (list) -> - @algorithm list + sort: (list) -> @algorithm list +{% endhighlight %} +###The strategies: + +{% highlight coffeescript %} bubbleSort = (list) -> anySwaps = false swapPass = -> @@ -45,7 +50,11 @@ reverseBubbleSort = (list) -> anySwaps = false swapPass() list +{% endhighlight %} + +###Using the strategies: +{% highlight coffeescript %} sorter = new StringSorter bubbleSort unsortedList = ['e', 'b', 'd', 'c', 'x', 'a'] From ffdce653ab952ededea828f2c3533bdd61d72b33 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 22 Jun 2011 23:05:56 -0400 Subject: [PATCH 089/267] Updated "Wanted Recipes" and fixed its formatting. --- wanted-recipes.md | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/wanted-recipes.md b/wanted-recipes.md index df42004..050d22b 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -90,33 +90,38 @@ foo 1, 2, 3 ## Networking -* Basic HTTP server -* Basic HTTP client +* Streaming HTTP server +* Streaming HTTP client ## AJAX -* Getting data from a remote server # using raw XHTTPRequest instead of jQuery's $.ajax +* Getting data from a remote server # using raw XHTTPRequest instead of jQuery's `$.ajax` ## Design patterns * Creational Patterns -** Abstract Factory -** Prototype -** Singleton + * Abstract Factory + * Prototype + * Singleton * Structural Patterns -** Adapter -** Composite -** Facade -** Flyweight -** Proxy + * Adapter + * Composite + * Facade + * Flyweight + * Proxy * Behavioral Patterns -** Chain of Responsibility -** Command -** Iterator -** Mediator -** Observer -** State -** Template Method -** Visitor + * Chain of Responsibility + * Command + * Iterator + * Mediator + * Observer + * State + * Template Method + * Visitor + +## Databases + +* MongoDB/Couch access +* MySQL/PostgreSQL access From 68f58675f10970e7734ab647e08388e3e70bc0e9 Mon Sep 17 00:00:00 2001 From: James Holder Date: Thu, 23 Jun 2011 14:43:51 -0400 Subject: [PATCH 090/267] Add highlight tags around "Wanted Recipes" one-liners that other break Markdown format. --- wanted-recipes.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/wanted-recipes.md b/wanted-recipes.md index 46fedad..6c39cfd 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -17,8 +17,14 @@ In the notes below, "JS" means the recipe is just a simple passthrough to an exi ## Strings * HTML methods # JS .sup(), .sub(), .blink(), .link(url), etc. May not exist in your JS impl! -* substr # str.substr(x,y) === str[x..x+y-1] === str[x...x+y] -* substring # str.substring(x,y) === str.slice(x,y) === str[x..y-1] === str[x...y] +* substr +{% highlight coffeescript %} +str.substr(x,y) === str[x..x+y-1] === str[x...x+y] +{% endhighlight %} +* substring +{% highlight coffeescript %} +str.substring(x,y) === str.slice(x,y) === str[x..y-1] === str[x...y] +{% endhighlight %} * Replacing substrings ## Arrays @@ -30,10 +36,10 @@ even = (x) -> x % 2 == 0 evens.every even # => true {% endhighlight %} -* Filtering arrays # [1..10.filter (x) -> x % 2 == 0 # => [ 2, 4, 6, 8, 10 ] -* Detecting presence of matching items in an array # [1..10].some (x) -> x % 2 == 0 # => true -* Processing an array item by item # [10..1].forEach (x) -> console.log x # => nothing;, but a countdown is displayed on the console -* Replace all duplicates of an array +* Detecting presence of matching items in an array +{% highlight coffeescript %} +[1..10].some (x) -> x % 2 == 0 # => true +{% endhighlight %} ## Dates and Times From ada9eb320a796c4189bb025c9c1e4eda596a033d Mon Sep 17 00:00:00 2001 From: Joe Fiorini Date: Fri, 24 Jun 2011 09:02:56 -0400 Subject: [PATCH 091/267] Array.every recipe --- chapters/arrays/testing-every-element.md | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 chapters/arrays/testing-every-element.md diff --git a/chapters/arrays/testing-every-element.md b/chapters/arrays/testing-every-element.md new file mode 100644 index 0000000..5978500 --- /dev/null +++ b/chapters/arrays/testing-every-element.md @@ -0,0 +1,49 @@ +--- +layout: recipe +title: Testing Every Element +chapter: Arrays +--- +## Problem + +You want to be able to check that every element in an array meets a particular condition. + +## Solution + +Use Array.every (ECMAScript 5): + +{% highlight coffeescript %} +evens = (x for x in [1..10] by 2) + +evens.every (x)-> x % 2 == 0 +# => true +{% endhighlight %} + +Array.every was addded to Mozilla's Javascript 1.6 and made standard with EcmaScript 5. If you to support browsers that do not implement EC5 then check out [`_.all` from underscore.js][underscore]. + +For a real world example, prentend you have a multiple select list that looks like: + +{% highlight html %} + +{% endhighlight %} + +Now you want to verify that the user selected only numbers. Let's use Array.every: + +{% highlight coffeescript %} +validateNumeric = (item)-> + parseFloat(item) == parseInt(item) && !isNaN(item) + +values = $("#my-select-list").val() + +values.every validateNumeric +{% endhighlight %} + +## Discussion + +This is similar to using ruby's Array#all? method. + +[underscore]: http://documentcloud.github.com/underscore/ From 85a18ec5086f021e68ce679e52b80195b9d0f6b2 Mon Sep 17 00:00:00 2001 From: KungD Date: Sat, 25 Jun 2011 20:36:00 +0200 Subject: [PATCH 092/267] removed unnecessary invocation parens --- chapters/metaprogramming/detecting-and-replacing-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/metaprogramming/detecting-and-replacing-functions.md b/chapters/metaprogramming/detecting-and-replacing-functions.md index d448a41..6a99931 100644 --- a/chapters/metaprogramming/detecting-and-replacing-functions.md +++ b/chapters/metaprogramming/detecting-and-replacing-functions.md @@ -14,7 +14,7 @@ Use `::` to detect the function, and assign to it if it does not exist. {% highlight coffeescript %} unless Array::filter Array::filter = (callback) -> - element for element in this when callback(element) + element for element in this when callback element array = [1..10] From 98792e2e8e832fb77289b097737f9955502c6d43 Mon Sep 17 00:00:00 2001 From: KungD Date: Sat, 25 Jun 2011 20:53:10 +0200 Subject: [PATCH 093/267] removed invocation parens from Concatenating Arrays recipe --- chapters/arrays/concatenating-arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/concatenating-arrays.md b/chapters/arrays/concatenating-arrays.md index b6fcd13..af614ac 100644 --- a/chapters/arrays/concatenating-arrays.md +++ b/chapters/arrays/concatenating-arrays.md @@ -14,7 +14,7 @@ Use JavaScript's Array concat() method: {% highlight coffeescript %} ray1 = [1,2,3] ray2 = [4,5,6] -ray3 = ray1.concat(ray2) +ray3 = ray1.concat ray2 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} From e8884f92196dc962c9fbc4bc4d8e05761acf77f4 Mon Sep 17 00:00:00 2001 From: KungD Date: Sat, 25 Jun 2011 21:00:40 +0200 Subject: [PATCH 094/267] removed more invocation parens --- chapters/strings/finding-substrings.md | 8 ++++---- chapters/strings/repeating.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chapters/strings/finding-substrings.md b/chapters/strings/finding-substrings.md index 1bebca9..a33af8c 100644 --- a/chapters/strings/finding-substrings.md +++ b/chapters/strings/finding-substrings.md @@ -10,18 +10,18 @@ You need to find the first or last occurrence of a search string within a messag ## Solution Use Javascript's indexOf() and lastIndexOf() to find the first and last occurrences of a string, respectively. -Syntax: string.indexOf(searchstring, start) +Syntax: string.indexOf searchstring, start {% highlight coffeescript %} message = "This is a test string. This has a repeat or two. This might even have a third." -message.indexOf("This",0) +message.indexOf "This", 0 # => 0 # Modifying the start parameter -message.indexOf("This",5) +message.indexOf "This", 5 # => 23 -message.lastIndexOf("This") +message.lastIndexOf "This" # => 49 {% endhighlight %} diff --git a/chapters/strings/repeating.md b/chapters/strings/repeating.md index 7749444..2df0c3c 100644 --- a/chapters/strings/repeating.md +++ b/chapters/strings/repeating.md @@ -13,7 +13,7 @@ Create an array of n+1 nulls, and then join it with the repetition string as the {% highlight coffeescript %} # create a string of 10 foos -Array(11).join('foo') +Array(11).join 'foo' # => "foofoofoofoofoofoofoofoofoofoo" {% endhighlight %} From 37835c5a028d4b9ef35674214726626116a611f3 Mon Sep 17 00:00:00 2001 From: KungD Date: Sat, 25 Jun 2011 21:02:39 +0200 Subject: [PATCH 095/267] another parens bites the dust --- chapters/arrays/max-array-value.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index ab8cb70..537344c 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -13,7 +13,7 @@ In ECMAScript 5, use `Array#reduce`. In older javascripts, use Math.max over a l {% highlight coffeescript %} # ECMAScript 5 -[12,32,11,67,1,3].reduce (a,b) -> Math.max(a,b) +[12,32,11,67,1,3].reduce (a,b) -> Math.max a, b # => 67 # Pre-EC5 From 17b13d8f7dfb8b7fc52c91806db64a09cf874991 Mon Sep 17 00:00:00 2001 From: KungD Date: Sun, 26 Jun 2011 11:21:49 +0200 Subject: [PATCH 096/267] invokation parens removed from fibonacci recipe --- chapters/math/fast-fibonacci.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chapters/math/fast-fibonacci.md b/chapters/math/fast-fibonacci.md index b8f7027..9c285bb 100644 --- a/chapters/math/fast-fibonacci.md +++ b/chapters/math/fast-fibonacci.md @@ -52,8 +52,8 @@ fib_bits = (n) -> bits = [] while n > 0 - [n, bit] = divmodBasic(n, 2) - bits.push(bit) + [n, bit] = divmodBasic n, 2 + bits.push bit bits.reverse() return bits @@ -67,7 +67,7 @@ fibFast = (n) -> [a, b, c] = [1, 0, 1] - for bit in fib_bits(n) + for bit in fib_bits n if bit [a, b] = [(a+c)*b, b*b + c*c] else @@ -88,12 +88,12 @@ divmodBasic = (x, y) -> Burnikel / Ziegler _if_ possible... ### - return [(q = Math.floor(x/y)), (r = if x < y then x else x % y)] + return [(q = Math.floor x/y), (r = if x < y then x else x % y)] start = (new Date).getTime(); calc_value = fibFast(MAXIMUM_JS_FIB_N) diff = (new Date).getTime() - start; -console.log("[#{calc_value}] took #{diff} ms.") +console.log "[#{calc_value}] took #{diff} ms." {% endhighlight %} ## Discussion From 8278b7f89f677fd3f73f940a4bb99595cad293ae Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Sun, 26 Jun 2011 23:09:11 -0600 Subject: [PATCH 097/267] more solarized and more whitespace --- css/default.css | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/css/default.css b/css/default.css index 0631591..6a5efce 100644 --- a/css/default.css +++ b/css/default.css @@ -23,7 +23,10 @@ article, header, section, footer { } /* Colors */ - +body { + background-color: #073642; + color: #93a1a1; +} a, .header a:visited { color: #268bd2; text-decoration: none; @@ -35,15 +38,22 @@ a:hover { } footer { - border-top: 1px solid #eee; + border-top: 1px solid #073642; padding-top: 22px; margin: 21px 0 22px 0; - color: #888; + color: #586e75; font-size: 14px; } /* Layout (margins, padding) */ - +header { + margin-top: 50px; + margin-left: 100px; +} +section.content { + margin-left: 100px; + margin-right: 100px; +} pre { padding: 22px; line-height: 22px; @@ -65,7 +75,6 @@ ul, ol { body { text-rendering: optimizeLegibility; - color: #002b36; font-family: Palatino, "Palatino Linotype", serif; text-shadow: 0 0 0 transparent; font-size: 16px; From 123c8d081c59ac5ae67865823a5718750c4688a0 Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Sun, 26 Jun 2011 23:11:13 -0600 Subject: [PATCH 098/267] let's add margins to the footer too --- css/default.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/default.css b/css/default.css index 6a5efce..06b1b21 100644 --- a/css/default.css +++ b/css/default.css @@ -50,7 +50,7 @@ header { margin-top: 50px; margin-left: 100px; } -section.content { +section.content, footer { margin-left: 100px; margin-right: 100px; } From aa288f76cc49fae5ade4aa943119a2432f9b3a20 Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Sun, 26 Jun 2011 23:19:10 -0600 Subject: [PATCH 099/267] base03 looks better --- css/default.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/default.css b/css/default.css index 06b1b21..a39431e 100644 --- a/css/default.css +++ b/css/default.css @@ -24,7 +24,7 @@ article, header, section, footer { /* Colors */ body { - background-color: #073642; + background-color: #002b36; color: #93a1a1; } a, .header a:visited { From 203e48afa6342d549a05fa572b912e83c4bea0fc Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Sun, 26 Jun 2011 23:24:15 -0600 Subject: [PATCH 100/267] taking some credit --- authors.md | 3 ++- designers-guide.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/authors.md b/authors.md index faf6555..0ac943e 100644 --- a/authors.md +++ b/authors.md @@ -32,4 +32,5 @@ The following people are totally rad and awesome because they have contributed r The following people are astonishingly rad and awesome because they did great design for the site! -* Oh please SOMEBODY put their name here! Check out the [contributing](/contributing) section and get cracking! +* Tad Thorley +* ...You! Check out the [contributing](/contributing) section and get cracking! diff --git a/designers-guide.md b/designers-guide.md index bc47757..7dafdb8 100644 --- a/designers-guide.md +++ b/designers-guide.md @@ -7,8 +7,8 @@ title: Designer's Guide Start with the [Developer's Guide](/developers-guide) to get a test version of the cookbook up and running on your machine, and get started! -First designer to actually DO some design gets to write this page! - If you have been waiting for your chance to literally _write the designer's guide_ for a website that is also a cookbook about CoffeeScript... ...welcome home. + +The default color scheme is based on "Solarized" by Ethan Schoonover: http://ethanschoonover.com/solarized From adafb62708d7ff1c50e7b37af092644b42d3d2c0 Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Sun, 26 Jun 2011 23:40:32 -0600 Subject: [PATCH 101/267] because I like the headers to stand out more, and for a bit more color --- css/default.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/css/default.css b/css/default.css index a39431e..59fb0f0 100644 --- a/css/default.css +++ b/css/default.css @@ -27,6 +27,10 @@ body { background-color: #002b36; color: #93a1a1; } +h1, h2, h3, h4, h5, +h1 a, h2 a, h3 a, h4 a, h5 a { + color: #b58900; +} a, .header a:visited { color: #268bd2; text-decoration: none; @@ -75,7 +79,7 @@ ul, ol { body { text-rendering: optimizeLegibility; - font-family: Palatino, "Palatino Linotype", serif; + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;; text-shadow: 0 0 0 transparent; font-size: 16px; line-height: 22px; @@ -87,9 +91,12 @@ pre { font-size: 14px; } -h1, h2, h3 { +h1, h2, h3, h4, h5, +h1 a, h2 a, h3 a, h4 a, h5 a { margin: 0; padding: 0; + font-family: sans-serif; + font-weight: lighter; } h1 { font-size: 30px; line-height: 44px; } From e7374b5af6e032a7d12c417a217f25f8c0493482 Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Sun, 26 Jun 2011 23:43:40 -0600 Subject: [PATCH 102/267] better linky --- designers-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designers-guide.md b/designers-guide.md index 7dafdb8..3a510a0 100644 --- a/designers-guide.md +++ b/designers-guide.md @@ -11,4 +11,4 @@ If you have been waiting for your chance to literally _write the designer's guid ...welcome home. -The default color scheme is based on "Solarized" by Ethan Schoonover: http://ethanschoonover.com/solarized +The default color scheme is based on [Solarized](http://ethanschoonover.com/solarized) by Ethan Schoonover. From e5d849e3f7564b4f2cec08ab4703fce055e26f38 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 27 Jun 2011 07:38:50 -0600 Subject: [PATCH 103/267] Tweak new style --- css/default.css | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/css/default.css b/css/default.css index 59fb0f0..65b6663 100644 --- a/css/default.css +++ b/css/default.css @@ -25,14 +25,14 @@ article, header, section, footer { /* Colors */ body { background-color: #002b36; - color: #93a1a1; + color: #839496; } h1, h2, h3, h4, h5, h1 a, h2 a, h3 a, h4 a, h5 a { color: #b58900; } a, .header a:visited { - color: #268bd2; + color: #b58900; text-decoration: none; } @@ -81,14 +81,14 @@ body { text-rendering: optimizeLegibility; font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;; text-shadow: 0 0 0 transparent; - font-size: 16px; + font-size: 18px; line-height: 22px; margin: 22px; } -pre { +code { font-family: Monaco, Monospace; - font-size: 14px; + font-size: 18px; } h1, h2, h3, h4, h5, @@ -99,13 +99,13 @@ h1 a, h2 a, h3 a, h4 a, h5 a { font-weight: lighter; } -h1 { font-size: 30px; line-height: 44px; } -h2 { font-size: 22px; line-height: 44px; } -h3 { font-size: 18px; line-height: 22px; } +h1 { font-size: 28px; line-height: 44px; } +h2 { font-size: 24px; line-height: 36px; } +h3 { font-size: 20px; line-height: 24px; } -.highlight { background: #fdf6e3; color: #657b83; line-height: 1em; font-size: 13px; } -.highlight .lineno { background-color: #eee8d5; color: #93a1a1; padding-left: .5em; padding-right: .5em; } -.highlight .hll { background-color: #eee8d5; } /* Don't know what this is... */ +.highlight { background: #073642; color: #839496; font-size: 18px; line-height: 18px; } +.highlight .lineno { background-color: #002b36; color: #93a1a1; padding: 0 8px; } +.highlight .hll { background-color: #002b36; } /* Don't know what this is... */ .highlight .c { color: #93a1a1; font-style: italic; } /* Comment */ .highlight .err { color: #dc322f; } /* Error */ .highlight .g { color: #839496; } /* Generic */ From 097a6ae60f578a89c1d9145d4eef8dc24bce3888 Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Mon, 27 Jun 2011 10:00:15 -0600 Subject: [PATCH 104/267] push the big reset button on the site style --- css/default.css | 182 +++++++++++++++++++++--------------------------- 1 file changed, 79 insertions(+), 103 deletions(-) diff --git a/css/default.css b/css/default.css index 65b6663..0c2d2f9 100644 --- a/css/default.css +++ b/css/default.css @@ -23,41 +23,27 @@ article, header, section, footer { } /* Colors */ -body { - background-color: #002b36; - color: #839496; -} -h1, h2, h3, h4, h5, -h1 a, h2 a, h3 a, h4 a, h5 a { - color: #b58900; -} + a, .header a:visited { - color: #b58900; + color: #26ADE4; text-decoration: none; } a:hover { text-decoration: underline; - text-shadow: 0 0 0 #2aa198; + text-shadow: 0 0 0 #DCEEF5; } footer { - border-top: 1px solid #073642; + border-top: 1px solid #eee; padding-top: 22px; margin: 21px 0 22px 0; - color: #586e75; + color: #888; font-size: 14px; } /* Layout (margins, padding) */ -header { - margin-top: 50px; - margin-left: 100px; -} -section.content, footer { - margin-left: 100px; - margin-right: 100px; -} + pre { padding: 22px; line-height: 22px; @@ -79,98 +65,88 @@ ul, ol { body { text-rendering: optimizeLegibility; - font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;; + color: #373837; + font-family: Palatino, "Palatino Linotype", serif; text-shadow: 0 0 0 transparent; - font-size: 18px; + font-size: 16px; line-height: 22px; margin: 22px; } -code { +pre { font-family: Monaco, Monospace; - font-size: 18px; + font-size: 14px; } -h1, h2, h3, h4, h5, -h1 a, h2 a, h3 a, h4 a, h5 a { +h1, h2, h3 { margin: 0; padding: 0; - font-family: sans-serif; - font-weight: lighter; } -h1 { font-size: 28px; line-height: 44px; } -h2 { font-size: 24px; line-height: 36px; } -h3 { font-size: 20px; line-height: 24px; } - -.highlight { background: #073642; color: #839496; font-size: 18px; line-height: 18px; } -.highlight .lineno { background-color: #002b36; color: #93a1a1; padding: 0 8px; } -.highlight .hll { background-color: #002b36; } /* Don't know what this is... */ -.highlight .c { color: #93a1a1; font-style: italic; } /* Comment */ -.highlight .err { color: #dc322f; } /* Error */ -.highlight .g { color: #839496; } /* Generic */ -.highlight .k { color: #859900; } /* Keyword */ -.highlight .l { color: #839496; } /* Literal */ -.highlight .n { color: #839496; } /* Name */ -.highlight .o { color: #859900; } /* Operator */ -.highlight .x { color: #839496; } /* Other */ -.highlight .p { color: #839496; } /* Punctuation */ -.highlight .cm { color: #93a1a1; font-style: italic; } /* Comment.Multiline */ -.highlight .cp { color: #93a1a1; font-style: italic; } /* Comment.Preproc */ -.highlight .c1 { color: #93a1a1; font-style: italic; } /* Comment.Single */ -.highlight .cs { color: #93a1a1; font-style: italic; } /* Comment.Special */ -.highlight .gd { color: #dc322f; } /* Generic.Deleted */ -.highlight .ge { color: #839496; font-style: italic; } /* Generic.Emph */ -.highlight .gr { color: #839496; } /* Generic.Error */ -.highlight .gh { color: #839496; font-weight: bold; } /* Generic.Heading */ -.highlight .gi { color: #859900; } /* Generic.Inserted */ -.highlight .go { color: #839496; } /* Generic.Output */ -.highlight .gp { color: #839496; font-weight: bold; } /* Generic.Prompt */ -.highlight .gs { color: #839496; font-weight: bold; } /* Generic.Strong */ -.highlight .gu { color: #839496; font-weight: bold; } /* Generic.Subheading */ -.highlight .gt { color: #839496; } /* Generic.Traceback */ -.highlight .kc { color: #839496; } /* Keyword.Constant */ -.highlight .kd { color: #839496; } /* Keyword.Declaration */ -.highlight .kn { color: #839496; } /* Keyword.Namespace */ -.highlight .kp { color: #2aa198; } /* Keyword.Pseudo */ -.highlight .kr { color: #839496; } /* Keyword.Reserved */ -.highlight .kt { color: #839496; } /* Keyword.Type */ -.highlight .ld { color: #839496; } /* Literal.Date */ -.highlight .m { color: #839496; } /* Literal.Number */ -.highlight .s { color: #2aa198; } /* Literal.String */ -.highlight .na { color: #839496; } /* Name.Attribute */ -.highlight .nb { color: #cb4b16; } /* Name.Builtin */ -.highlight .nc { color: #839496; } /* Name.Class */ -.highlight .no { color: #b58900; } /* Name.Constant */ -.highlight .nd { color: #839496; } /* Name.Decorator */ -.highlight .ni { color: #839496; } /* Name.Entity */ -.highlight .ne { color: #839496; } /* Name.Exception */ -.highlight .nf { color: #268bd2; } /* Name.Function */ -.highlight .nl { color: #839496; } /* Name.Label */ -.highlight .nn { color: #cb4b16; } /* Name.Namespace */ -.highlight .nx { color: #839496; } /* Name.Other */ -.highlight .py { color: #839496; } /* Name.Property */ -.highlight .nt { color: #839496; } /* Name.Tag */ -.highlight .nv { color: #839496; } /* Name.Variable */ -.highlight .ow { color: #839496; } /* Operator.Word */ -.highlight .w { color: #839496; } /* Text.Whitespace */ -.highlight .mf { color: #2aa198; } /* Literal.Number.Float */ -.highlight .mh { color: #2aa198; } /* Literal.Number.Hex */ -.highlight .mi { color: #2aa198; } /* Literal.Number.Integer */ -.highlight .mo { color: #2aa198; } /* Literal.Number.Oct */ -.highlight .sb { color: #2aa198; } /* Literal.String.Backtick */ -.highlight .sc { color: #2aa198; } /* Literal.String.Char */ -.highlight .sd { color: #2aa198; } /* Literal.String.Doc */ -.highlight .s2 { color: #2aa198; } /* Literal.String.Double */ -.highlight .se { color: #dc322f; } /* Literal.String.Escape */ -.highlight .sh { color: #2aa198; } /* Literal.String.Heredoc */ -.highlight .si { color: #2aa198; } /* Literal.String.Interpol */ -.highlight .sx { color: #2aa198; } /* Literal.String.Other */ -.highlight .sr { color: #2aa198; } /* Literal.String.Regex */ -.highlight .s1 { color: #2aa198; } /* Literal.String.Single */ -.highlight .ss { color: #2aa198; } /* Literal.String.Symbol */ -.highlight .bp { color: #839496; } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #839496; } /* Name.Variable.Class */ -.highlight .vg { color: #839496; } /* Name.Variable.Global */ -.highlight .vi { color: #839496; } /* Name.Variable.Instance */ -.highlight .il { color: #839496; } /* Literal.Number.Integer.Long */ +h1 { font-size: 30px; line-height: 44px; } +h2 { font-size: 22px; line-height: 44px; } +h3 { font-size: 18px; line-height: 22px; } + +/* Syntax Highlighting */ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #408080; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ +.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #7D9029 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file From b3c2763f1598d00c3a6498770bc61797c5e4b5fa Mon Sep 17 00:00:00 2001 From: Tad Thorley Date: Mon, 27 Jun 2011 10:43:52 -0600 Subject: [PATCH 105/267] take my name off of the list --- authors.md | 1 - 1 file changed, 1 deletion(-) diff --git a/authors.md b/authors.md index 0ac943e..7f0514c 100644 --- a/authors.md +++ b/authors.md @@ -32,5 +32,4 @@ The following people are totally rad and awesome because they have contributed r The following people are astonishingly rad and awesome because they did great design for the site! -* Tad Thorley * ...You! Check out the [contributing](/contributing) section and get cracking! From c17b3c3cb937e0c8ca966f714d4fce8e7fef99cf Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Mon, 27 Jun 2011 10:59:19 -0600 Subject: [PATCH 106/267] Update Wanted Recipes Add a call for some better introductionary material. --- wanted-recipes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wanted-recipes.md b/wanted-recipes.md index 6c39cfd..7e236b3 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -8,6 +8,10 @@ Here's a list of recipes we think we need. Pick one, implement it, and remove it In the notes below, "JS" means the recipe is just a simple passthrough to an existing JavaScript method. +## Introduction + +We need a better introduction. Right now the first recipe is [Embedding JavaScript](/chapters/syntax/embedding_javascript), which doesn't set the right first impression. How about three or four recipes that hold new users' hands a bit more as the first section? + ## Syntax * Ensuring variables are closed over # with "do" From 512310fbd59e372553702f71b2d2cd50b7cdf2d9 Mon Sep 17 00:00:00 2001 From: James Holder Date: Tue, 28 Jun 2011 14:33:50 -0400 Subject: [PATCH 107/267] Add "SQLite" and "MongoDB" recipes to new "Databases" chapter. --- chapters/databases/index.html | 18 +++++++ chapters/databases/mongodb.md | 67 +++++++++++++++++++++++++ chapters/databases/sqlite.md | 92 +++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 chapters/databases/index.html create mode 100644 chapters/databases/mongodb.md create mode 100644 chapters/databases/sqlite.md diff --git a/chapters/databases/index.html b/chapters/databases/index.html new file mode 100644 index 0000000..6100569 --- /dev/null +++ b/chapters/databases/index.html @@ -0,0 +1,18 @@ +--- +layout: chapter +title: Databases +chapter: Databases +--- + +{% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} +{% capture indexurl %}{{ url }}/index.html{% endcapture %} + +
        +{% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} +
      • {{ page.title }}
      • + {% endunless %} + {% endif %} +{% endfor %} +
      diff --git a/chapters/databases/mongodb.md b/chapters/databases/mongodb.md new file mode 100644 index 0000000..74c4643 --- /dev/null +++ b/chapters/databases/mongodb.md @@ -0,0 +1,67 @@ +--- +layout: recipe +title: MongoDB +chapter: Databases +--- +## Problem + +You need to interface with a MongoDB database. + +## Solution + +### For Node.js + +### Setup +* [Install MongoDB](http://www.mongodb.org/display/DOCS/Quickstart) on your computer if you have not already. + +* [Install the native MongoDB module](https://github.com/christkv/node-mongodb-native). + +#### Saving Records + +{% highlight coffeescript %} +mongo = require 'mongodb' + +server = new mongo.Server "127.0.0.1", 27017, {} + +client = new mongo.Db 'test', server + +# save() updates existing records or inserts new ones as needed +exampleSave = (dbErr, collection) -> + console.log "Unable to access database: #{dbErr}" if dbErr + collection.save { _id: "my_favorite_latte", flavor: "honeysuckle" }, (err, docs) -> + console.log "Unable to save record: #{err}" if err + client.close() + +client.open (err, database) -> + client.collection 'coffeescript_example', exampleSave +{% endhighlight %} + +#### Finding Records + +{% highlight coffeescript %} +mongo = require 'mongodb' + +server = new mongo.Server "127.0.0.1", 27017, {} + +client = new mongo.Db 'test', server + +exampleFind = (dbErr, collection) -> + console.log "Unable to access database: #{dbErr}" if dbErr + collection.find({ _id: "my_favorite_latte" }).nextObject (err, result) -> + if err + console.log "Unable to find record: #{err}" + else + console.log result # => { id: "my_favorite_latte", flavor: "honeysuckle" } + client.close() + +client.open (err, database) -> + client.collection 'coffeescript_example', exampleFind +{% endhighlight %} + +### For Browsers + +A [REST-based interface](https://github.com/tdegrunt/mongodb-rest) is in the works. This will provide AJAX-based access. + +## Discussion + +This recipe breaks the *save* and *find* into separate examples in order to separate the MongoDB-specific concerns from the task of connection and callback management. The [async module](https://github.com/caolan/async) can help with that. diff --git a/chapters/databases/sqlite.md b/chapters/databases/sqlite.md new file mode 100644 index 0000000..3453da0 --- /dev/null +++ b/chapters/databases/sqlite.md @@ -0,0 +1,92 @@ +--- +layout: recipe +title: SQLite +chapter: Databases +--- +## Problem + +You need to interface with a [SQLite](http://www.sqlite.org/) database from inside of Node.js. + +## Solution + +Use the [SQLite module](http://code.google.com/p/node-sqlite/). + +{% highlight coffeescript %} +sqlite = require 'sqlite' + +db = new sqlite.Database + +# The module uses asynchronous methods, +# so we chain the calls the db.execute +exampleCreate = -> + db.execute "CREATE TABLE snacks (name TEXT(25), flavor TEXT(25))", + (exeErr, rows) -> + throw exeErr if exeErr + exampleInsert() + +exampleInsert = -> + db.execute "INSERT INTO snacks (name, flavor) VALUES ($name, $flavor)", + { $name: "Potato Chips", $flavor: "BBQ" }, + (exeErr, rows) -> + throw exeErr if exeErr + exampleSelect() + +exampleSelect = -> + db.execute "SELECT name, flavor FROM snacks", + (exeErr, rows) -> + throw exeErr if exeErr + console.log rows[0] # => { name: 'Potato Chips', flavor: 'BBQ' } + +# :memory: creates a DB in RAM +# You can supply a filepath (like './example.sqlite') to create/open one on disk +db.open ":memory:", (openErr) -> + throw openErr if openErr + exampleCreate() +{% endhighlight %} + +## Discussion + +You can also prepare your SQL queries beforehand: + +{% highlight coffeescript %} +sqlite = require 'sqlite' +async = require 'async' # Not required but added to make the example more concise + +db = new sqlite.Database + +createSQL = "CREATE TABLE drinks (name TEXT(25), price NUM)" + +insertSQL = "INSERT INTO drinks (name, price) VALUES (?, ?)" + +selectSQL = "SELECT name, price FROM drinks WHERE price < ?" + +create = (onFinish) -> + db.execute createSQL, (exeErr) -> + throw exeErr if exeErr + onFinish() + +prepareInsert = (name, price, onFinish) -> + db.prepare insertSQL, (prepErr, statement) -> + statement.bindArray [name, price], (bindErr) -> + statement.fetchAll (fetchErr, rows) -> # Called so that it executes the insert + onFinish() + +prepareSelect = (onFinish) -> + db.prepare selectSQL, (prepErr, statement) -> + statement.bindArray [1.00], (bindErr) -> + statement.fetchAll (fetchErr, rows) -> + console.log rows[0] # => { name: "Mia's Root Beer", price: 0.75 } + onFinish() + +db.open ":memory:", (openErr) -> + async.series([ + (onFinish) -> create onFinish, + (onFinish) -> prepareInsert "LunaSqueeze", 7.95, onFinish, + (onFinish) -> prepareInsert "Viking Sparkling Grog", 4.00, onFinish, + (onFinish) -> prepareInsert "Mia's Root Beer", 0.75, onFinish, + (onFinish) -> prepareSelect onFinish + ]) +{% endhighlight %} + +The [SQLite version of SQL](http://www.sqlite.org/lang.html) and the [node-sqlite module documentation](https://github.com/orlandov/node-sqlite#readme) provide more complete information. + From 3b795a71d1b23005e489196664b1cf80f3839831 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 29 Jun 2011 22:31:49 -0400 Subject: [PATCH 108/267] Add "Databases" chapter listing. --- chapters/index.html | 1 + index.html | 1 + 2 files changed, 2 insertions(+) diff --git a/chapters/index.html b/chapters/index.html index b8c7695..53face9 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -14,6 +14,7 @@ - Regular Expressions - Networking - Design Patterns +- Databases --- {% for chapter in page.chapters %} diff --git a/index.html b/index.html index acfe28d..d25f923 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,7 @@ - Regular Expressions - Networking - Design Patterns +- Databases ---

      Welcome

      From 9d40190ffddbc178bfb4acd60e6c012e80b65db7 Mon Sep 17 00:00:00 2001 From: James Holder Date: Wed, 29 Jun 2011 23:42:20 -0400 Subject: [PATCH 109/267] Remove completed "MongoDB" listing from "Wanted Recipes". --- wanted-recipes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wanted-recipes.md b/wanted-recipes.md index 7e236b3..155fea1 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -128,5 +128,5 @@ foo 1, 2, 3 ## Databases -* MongoDB/Couch access +* Couch access * MySQL/PostgreSQL access From aa730dd0f94527cfb649b77e5afdf2b8bfece67a Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Sat, 23 Jul 2011 13:29:57 -0700 Subject: [PATCH 110/267] add Array::push.apply examples to Concatenating Arrays recipe --- chapters/arrays/concatenating-arrays.md | 40 ++++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/chapters/arrays/concatenating-arrays.md b/chapters/arrays/concatenating-arrays.md index af614ac..2828231 100644 --- a/chapters/arrays/concatenating-arrays.md +++ b/chapters/arrays/concatenating-arrays.md @@ -9,17 +9,43 @@ You want to join two arrays together. ## Solution -Use JavaScript's Array concat() method: +There are two standard options for concatenating arrays in JavaScript. + +The first is to use JavaScript's Array `concat()` method: {% highlight coffeescript %} -ray1 = [1,2,3] -ray2 = [4,5,6] -ray3 = ray1.concat ray2 +array1 = [1, 2, 3] +array2 = [4, 5, 6] +array3 = array1.concat array2 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} -## Discussion +Note that `array1` is _not_ modified by the operation. The concatenated array is returned as a new object. + +If you want to merge two arrays without creating a new object, you can use the following technique: + +{% highlight coffeescript %} +array1 = [1, 2, 3] +array2 = [4, 5, 6] +Array::push.apply array1, array2 +array1 +# => [1, 2, 3, 4, 5, 6] +{% endhighlight %} + +In the example above, the `Array.prototype.push.apply(a, b)` approach modifies `array1` in place without creating a new array object. -CoffeeScript lacks a special syntax for joining arrays, but concat is a standard JavaScript method. +We can simplify the pattern above using CoffeeScript by creating a new `merge()` method for Arrays. + +{% highlight coffeescript %} +Array::merge = (other) -> Array::push.apply @, other + +array1 = [1, 2, 3] +array2 = [4, 5, 6] +array1.merge array2 +array1 +# => [1, 2, 3, 4, 5, 6] +{% endhighlight %} + +## Discussion -Note that ray1 is _not_ modified by the operation. The concatenated array is returned as a new object. +CoffeeScript lacks a special syntax for joining arrays, but `concat()` and `push()` are standard JavaScript methods. From 017b65767176806dd794687b9d1c065e5b653028 Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Sat, 23 Jul 2011 14:10:14 -0700 Subject: [PATCH 111/267] add Shuffling Array Elements recipe --- chapters/arrays/shuffling-array-elements.md | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 chapters/arrays/shuffling-array-elements.md diff --git a/chapters/arrays/shuffling-array-elements.md b/chapters/arrays/shuffling-array-elements.md new file mode 100644 index 0000000..0d2da5f --- /dev/null +++ b/chapters/arrays/shuffling-array-elements.md @@ -0,0 +1,23 @@ +--- +layout: recipe +title: Shuffling Array Elements +chapter: Arrays +--- +## Problem + +You want to shuffle the elements in an array. + +## Solution + +The JavaScript Array `sort()` method accepts a custom sort function. We can write a `shuffle()` method to add some convenience. + +{% highlight coffeescript %} +Array::shuffle = -> @sort -> 0.5 - Math.random() + +[1..9].shuffle() +# => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ] +{% endhighlight %} + +## Discussion + +For more background on how this shuffle logic works, see this [discussion at StackOverflow](http://stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling). From 219b6ab272ff045b9be6789753fa33869906fb9a Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Sat, 23 Jul 2011 14:38:33 -0700 Subject: [PATCH 112/267] add Generating a Unique ID recipe --- chapters/strings/generating-a-unique-id.md | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 chapters/strings/generating-a-unique-id.md diff --git a/chapters/strings/generating-a-unique-id.md b/chapters/strings/generating-a-unique-id.md new file mode 100644 index 0000000..20851c3 --- /dev/null +++ b/chapters/strings/generating-a-unique-id.md @@ -0,0 +1,28 @@ +--- +layout: recipe +title: Generating a Unique ID +chapter: Strings +--- +## Problem + +You want to generate a random unique identifier. + +## Solution + +You can create a Base 36 encoded string from a random number. + +{% highlight coffeescript %} +uniqueId = (length=8) -> + id = "" + id += Math.random().toString(36).substr(2) while id.length < length + id.substr 0, length + +uniqueId() # => n5yjla3b +uniqueId(2) # => 0d +uniqueId(20) # => ox9eo7rt3ej0pb9kqlke +uniqueId(40) # => xu2vo4xjn4g0t3xr74zmndshrqlivn291d584alj +{% endhighlight %} + +## Discussion + +There are other possible techniques, but this is relatively performant and flexible. From 17019c951fa1bd922f8eabe6ab9e785a74db852f Mon Sep 17 00:00:00 2001 From: Jamie Gaskins Date: Sun, 31 Jul 2011 17:56:21 +0800 Subject: [PATCH 113/267] Semanticize the HTML in the chapter index a bit. --- chapters/index.html | 32 ++++++++++++++++++-------------- index.html | 32 ++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/chapters/index.html b/chapters/index.html index 53face9..dc0ad39 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -17,20 +17,24 @@ - Databases --- -{% for chapter in page.chapters %} - {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} - {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
        + {% for chapter in page.chapters %} + {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} + {% capture indexurl %}{{ url }}/index.html{% endcapture %} -

        {{ chapter }}

        +
      1. +

        {{ chapter }}

        -
          - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
        • {{ page.title }}
        • - {% endunless %} - {% endif %} - {% endfor %} -
        +
          + {% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} +
        • {{ page.title }}
        • + {% endunless %} + {% endif %} + {% endfor %} +
        +
      2. -{% endfor %} + {% endfor %} +
    \ No newline at end of file diff --git a/index.html b/index.html index d25f923..247136b 100644 --- a/index.html +++ b/index.html @@ -21,20 +21,24 @@

    Welcome

    Welcome to the CoffeeScript Cookbook! CoffeeScript recipes for the community by the community. Head over to the Contributing page and see what you can do to help out!

    -{% for chapter in page.chapters %} - {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} - {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
      + {% for chapter in page.chapters %} + {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} + {% capture indexurl %}{{ url }}/index.html{% endcapture %} -

      {{ chapter }}

      +
    1. +

      {{ chapter }}

      -
        - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
      • {{ page.title }}
      • - {% endunless %} - {% endif %} - {% endfor %} -
      +
        + {% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} +
      • {{ page.title }}
      • + {% endunless %} + {% endif %} + {% endfor %} +
      +
    2. -{% endfor %} + {% endfor %} + \ No newline at end of file From 2ba85f74c1d873fedb497e1d171ff8777e7685d5 Mon Sep 17 00:00:00 2001 From: Jamie Gaskins Date: Sun, 31 Jul 2011 18:05:07 +0800 Subject: [PATCH 114/267] Make the navigation in the header a bit more semantic --- _layouts/default.html | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/_layouts/default.html b/_layouts/default.html index abbd566..d4e3a13 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -7,10 +7,14 @@

      CoffeeScript Cookbook

      - Chapter Index | - Contributing | - Authors | - License +
      {{ content }} From 11f45119dbb9f0e074398733dde064c61116d3fc Mon Sep 17 00:00:00 2001 From: Jamie Gaskins Date: Sun, 31 Jul 2011 18:07:40 +0800 Subject: [PATCH 115/267] Pretty up the new navigation header. --- css/default.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/css/default.css b/css/default.css index 0c2d2f9..edb4c34 100644 --- a/css/default.css +++ b/css/default.css @@ -61,6 +61,20 @@ ul, ol { margin-bottom: 22px; } +nav ul { + list-style:none; +} + +nav ul li { + border-left:1px solid rgba(0, 0, 0, 0.2); + display:inline-block; + padding:0 10px; +} + +nav ul li:first-child { + border:none; +} + /* Typography */ body { From a18d8149802a8f2bbea931c3ebfc6f1a16906831 Mon Sep 17 00:00:00 2001 From: Jamie Gaskins Date: Sun, 31 Jul 2011 18:36:14 +0800 Subject: [PATCH 116/267] Get the chapter navigation to match the default. --- _layouts/chapter.html | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/_layouts/chapter.html b/_layouts/chapter.html index 39349c4..ae1d592 100644 --- a/_layouts/chapter.html +++ b/_layouts/chapter.html @@ -7,10 +7,14 @@

      CoffeeScript Cookbook

      - Chapter Index | - Contributing | - Authors | - License +

      {{ page.title }}

      From aab856d8d58c9c8ef35d45bbb56794457ebe87b0 Mon Sep 17 00:00:00 2001 From: Jamie Gaskins Date: Sun, 31 Jul 2011 18:36:54 +0800 Subject: [PATCH 117/267] Separate presentation from content a bit in the recipe header. --- _layouts/recipe.html | 10 ++++++---- css/default.css | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/_layouts/recipe.html b/_layouts/recipe.html index 7405cc7..22b23c1 100644 --- a/_layouts/recipe.html +++ b/_layouts/recipe.html @@ -5,12 +5,14 @@ -
      +

      CoffeeScript Cookbook

      diff --git a/css/default.css b/css/default.css index edb4c34..fb906a4 100644 --- a/css/default.css +++ b/css/default.css @@ -75,6 +75,22 @@ nav ul li:first-child { border:none; } +nav ol { + list-style:none; +} + +nav ol li { + display:inline-block; +} + +nav ol.breadcrumbs li:before { + content: '\000bb\000a0'; /* Insert »  between list items. */ +} + +nav ol.breadcrumbs li:first-child:before { + content: ''; +} + /* Typography */ body { From 2e8601c61fff7c2facb5e2da66daf74bfb0ab12c Mon Sep 17 00:00:00 2001 From: jgaskins Date: Sun, 31 Jul 2011 20:56:20 +0800 Subject: [PATCH 118/267] Added myself to the authors page. --- authors.md | 1 + 1 file changed, 1 insertion(+) diff --git a/authors.md b/authors.md index 7f0514c..d008717 100644 --- a/authors.md +++ b/authors.md @@ -26,6 +26,7 @@ The following people are totally rad and awesome because they have contributed r * David Brady *ratgeyser@gmail.com* * Mike Moore *mike@blowmage.com* * Peter Hellberg *peter@c7.se* +* Jamie Gaskins *jgaskins@gmail.com* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Designers From 3d624a3ada5c7e61da7630a07ece183308cf790e Mon Sep 17 00:00:00 2001 From: Mohammad Aidid Bin Mohd Jaafar Date: Tue, 4 Oct 2011 01:23:26 +0800 Subject: [PATCH 119/267] Added a new recipe for define range in an array using CoffeeScript --- chapters/arrays/define-ranges.md | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 chapters/arrays/define-ranges.md diff --git a/chapters/arrays/define-ranges.md b/chapters/arrays/define-ranges.md new file mode 100644 index 0000000..a96d49a --- /dev/null +++ b/chapters/arrays/define-ranges.md @@ -0,0 +1,47 @@ +--- +layout: recipe +title: Define Ranges Array +chapter: Arrays +--- +## Problem + +You want to define range in array. + +## Solution + +There are two method to define range of an array element in CoffeeScript. + +{% highlight coffeescript %} + +myArray = [1..10] +# => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] + +{% endhighlight %} + +{% highlight coffeescript %} + +myArray = [1...10] +# => [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + +{% endhighlight %} + +We can also reverse the range of element by writing it this way. + +{% highlight coffeescript %} + +myLargeArray = [10..1] +# => [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ] + +{% endhighlight %} + +{% highlight coffeescript %} +myLargeArray = [10...1] +# => [ 10, 9, 8, 7, 6, 5, 4, 3, 2 ] + +{% endhighlight %} + +## Discussion + +Inclusive range always define by '..' operator. + +Exclusive range difine by '...', and always omit the last value. From febe194dd0f16171c1a1b5b69bdd2a551c2875a9 Mon Sep 17 00:00:00 2001 From: Mohammad Aidid Bin Mohd Jaafar Date: Tue, 4 Oct 2011 03:07:09 +0800 Subject: [PATCH 120/267] Fixed spelling error 'difine' to 'define' --- chapters/arrays/define-ranges.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/define-ranges.md b/chapters/arrays/define-ranges.md index a96d49a..9adabcc 100644 --- a/chapters/arrays/define-ranges.md +++ b/chapters/arrays/define-ranges.md @@ -44,4 +44,4 @@ myLargeArray = [10...1] Inclusive range always define by '..' operator. -Exclusive range difine by '...', and always omit the last value. +Exclusive range define by '...', and always omit the last value. From 8e3bf94c6ad4af278f92336a438bcd52190dc15f Mon Sep 17 00:00:00 2001 From: Mohammad Aidid Bin Mohd Jaafar Date: Tue, 4 Oct 2011 03:14:17 +0800 Subject: [PATCH 121/267] Fixed spelling mistakes and edit the problem statement sentence and solution sentence to reflect codes intention --- chapters/arrays/define-ranges.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapters/arrays/define-ranges.md b/chapters/arrays/define-ranges.md index 9adabcc..8296e09 100644 --- a/chapters/arrays/define-ranges.md +++ b/chapters/arrays/define-ranges.md @@ -5,11 +5,11 @@ chapter: Arrays --- ## Problem -You want to define range in array. +You want to define a range in an array. ## Solution -There are two method to define range of an array element in CoffeeScript. +There are two ways to define a range of array elements in CoffeeScript. {% highlight coffeescript %} From b6b6ee7345d2d085be8b50302b6c9bda7cefa80d Mon Sep 17 00:00:00 2001 From: Jeff Pickhardt Date: Thu, 20 Oct 2011 21:05:33 -0700 Subject: [PATCH 122/267] Added a zip function, a type function, and a randomInt function. --- authors.md | 1 + chapters/arrays/zip-function.md | 30 ++++++++++++++++ chapters/classes_and_objects/type-function.md | 35 +++++++++++++++++++ chapters/math/random-integer.md | 31 ++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 chapters/arrays/zip-function.md create mode 100644 chapters/classes_and_objects/type-function.md create mode 100644 chapters/math/random-integer.md diff --git a/authors.md b/authors.md index d008717..f48ecbd 100644 --- a/authors.md +++ b/authors.md @@ -17,6 +17,7 @@ The following people are totally rad and awesome because they have contributed r * Jason Giedymin *jasong@apache.org* * Phil Cohen *github@phlippers.net* * João Moreno *coffeecb @joaomoreno .com* +* Jeff Pickhardt *pickhardt (at) gmail (dot) com* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/arrays/zip-function.md b/chapters/arrays/zip-function.md new file mode 100644 index 0000000..80d8006 --- /dev/null +++ b/chapters/arrays/zip-function.md @@ -0,0 +1,30 @@ +--- +layout: recipe +title: Python-like zip function +chapter: Arrays +--- +## Problem + +You want to zip together multiple arrays into an array of arrays, similar to Python's zip function. Python's zip function returns an array of tuples, where each tuple contains the i-th element from each of the argument arrays. + +## Solution + +Use the following CoffeeScript code: + +{% highlight coffeescript %} +# Usage: zip(arr1, arr2, arr3, ...) +zip = () -> + lengthArray = (arr.length for arr in arguments) + length = Math.max.apply(Math, lengthArray) + argumentLength = arguments.length + results = [] + for i in [0...length] + semiResult = [] + for arr in arguments + semiResult.push arr[i] + results.push semiResult + return results + +zip([0, 1, 2, 3], [0, -1, -2, -3]) +# => [[0, 0], [1, -1], [2, -2], [3, -3]] +{% endhighlight %} diff --git a/chapters/classes_and_objects/type-function.md b/chapters/classes_and_objects/type-function.md new file mode 100644 index 0000000..6acf6f5 --- /dev/null +++ b/chapters/classes_and_objects/type-function.md @@ -0,0 +1,35 @@ +--- +layout: recipe +title: A CoffeeScript type function +chapter: Classes and Objects +--- +## Problem + +You'd like to know the type of a function without using typeof. (See http://javascript.crockford.com/remedial.html for more information on why typeof is pretty inferior.) + +## Solution + +Use the following function: + +{% highlight coffeescript %} +type = (obj) -> + if obj == undefined or obj == null + return String obj + classToType = new Object + for name in "Boolean Number String Function Array Date RegExp".split(" ") + classToType["[object " + name + "]"] = name.toLowerCase() + myClass = Object.prototype.toString.call obj + if myClass of classToType + return classToType[myClass] + return "object" +{% endhighlight %} + +## Discussion + +This function was modeled on jQuery's $.type function. (http://api.jquery.com/jQuery.type/) + +Note that, as an alternative to type checking, you can often use duck typing and the existential operator together to eliminating the need to examine an object's type, in certain cases. For example, here is exception-free code that pushes an element to an array, if myArray is in fact an array (or array-like, with a push function), and does nothing otherwise. + +{% highlight coffeescript %} +myArray?.push? myValue +{% endhighlight %} diff --git a/chapters/math/random-integer.md b/chapters/math/random-integer.md new file mode 100644 index 0000000..bcc645c --- /dev/null +++ b/chapters/math/random-integer.md @@ -0,0 +1,31 @@ +--- +layout: recipe +title: A random integer function +chapter: Math +--- +## Problem + +You'd like to get a random integer between two integers, inclusive. + +## Solution + +Use the following function. + +{% highlight coffeescript %} +randomInt = (lower, upper=0) -> + start = Math.random() + if not lower? + [lower, upper] = [0, lower] + if lower > upper + [lower, upper] = [upper, lower] + return Math.floor(start * (upper - lower + 1) + lower) + +(randomInt(1) for i in [0...10]) +# => [0,1,1,0,0,0,1,1,1,0] + +(randomInt(1, 10) for i in [0...10]) +# => [7,3,9,1,8,5,4,10,10,8] + +## Discussion + +Questions? From 063ec7a0bb2bcdd88b7133edbdc20f6af0be8bac Mon Sep 17 00:00:00 2001 From: Matthieu Aussaguel Date: Thu, 27 Oct 2011 13:59:18 +1100 Subject: [PATCH 123/267] Select currentTarget instead of the target --- chapters/jquery/callback-bindings-jquery.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/jquery/callback-bindings-jquery.md b/chapters/jquery/callback-bindings-jquery.md index 7d36bea..a89ffc7 100644 --- a/chapters/jquery/callback-bindings-jquery.md +++ b/chapters/jquery/callback-bindings-jquery.md @@ -16,7 +16,7 @@ $ -> @products = [] $('.product').click (event) => - @add $(event.target).attr 'id' + @add $(event.currentTarget).attr 'id' add: (product) -> @products.push product From 8d23e58b872e5257996af9d79ec97bdbb3d5f5b7 Mon Sep 17 00:00:00 2001 From: basti Date: Fri, 4 Nov 2011 10:25:13 +0100 Subject: [PATCH 124/267] array reduce example: missing names added --- chapters/arrays/reducing-arrays.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/arrays/reducing-arrays.md b/chapters/arrays/reducing-arrays.md index 7e85463..c790967 100644 --- a/chapters/arrays/reducing-arrays.md +++ b/chapters/arrays/reducing-arrays.md @@ -25,9 +25,9 @@ Or it may be something more complex such as aggregating elements from a list int {% highlight coffeescript %} people = - { name: '', age: 10 } - { name: '', age: 16 } - { name: '', age: 17 } + { name: 'alec', age: 10 } + { name: 'bert', age: 16 } + { name: 'chad', age: 17 } people.reduce (x, y) -> x[y.name]= y.age From 7f951c91c717f718de245c978c71f124c5b0d672 Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 08:47:20 +0100 Subject: [PATCH 125/267] More elegant ES3 version to determine the maximum value of an Array --- chapters/arrays/max-array-value.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index 537344c..e703b38 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -9,7 +9,7 @@ You need to find the largest value contained in an array. ## Solution -In ECMAScript 5, use `Array#reduce`. In older javascripts, use Math.max over a list comprehension: +In ECMAScript 5, use `Array#reduce`. In older javascripts, use Math.max: {% highlight coffeescript %} # ECMAScript 5 @@ -17,7 +17,7 @@ In ECMAScript 5, use `Array#reduce`. In older javascripts, use Math.max over a l # => 67 # Pre-EC5 -max = Math.max(max or= num, num) for num in [12,32,11,67,1,3] +max = Math.max.apply(null, [12,32,11,67,1,3]) # => [ 12, 32, 32, 67, 67, 67 ] max # => 67 @@ -25,6 +25,4 @@ max ## Discussion -`Math.max` compares two numbers and returns the larger of the two; the rest of this recipe (both versions) is just iterating over the array to find the largest one. It is interesting to note that when assigning from a list comprehension, the last item evaluated will be returned to the assignment but the expression itself (such as in the node.js coffeescript interpreter) evaluates to the list comprehension's complete mapping. This means that the Pre-EC5 version will duplicate the array. - -For very large arrays in ECMAScript 4, a more memory efficient approach might be to initialize `max` to the first element of the array, and then loop over it with a traditional for loop. Since non-idiomatic CoffeeScript is discouraged in the Cookbook, this approach is left as an exercise to the reader. +`Math.max` compares two numbers and returns the larger of the two; the rest of this recipe (both versions) is just iterating over the array to find the largest one. From db17fc3515633c65d18ff13e860218af7b7f35be Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 09:30:25 +0100 Subject: [PATCH 126/267] Use polyfill for String::trim instead of global function declaration --- chapters/strings/trimming-whitespace-from-a-string.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md index b133e7f..881f7f3 100644 --- a/chapters/strings/trimming-whitespace-from-a-string.md +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -34,13 +34,12 @@ To trim only trailing whitespace, use the following: ## Discussion -Opera, Firefox and Chrome all have a native string prototype `trim` method, and the other browsers could add one as well. For this particular method, I would use the built-in method where possible: +Opera, Firefox and Chrome all have a native string prototype `trim` method, and the other browsers could add one as well. For this particular method, I would use the built-in method where possible, otherwise create a polyfill: {% highlight coffeescript %} -trim = (val) -> - if String::trim? then val.trim() else val.replace /^\s+|\s+$/g, "" +if !String::trim? then String::trim = -> @replace /^\s+|\s+$/g, "" -trim " padded string " +" padded string ".trim # => 'padded string' {% endhighlight %} From 2a6fdee1e324ea5beb811bbad946ebb5b4a4575d Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 09:47:33 +0100 Subject: [PATCH 127/267] Fix wrong return value for Array#toString --- chapters/arrays/creating-a-string-from-an-array.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/creating-a-string-from-an-array.md b/chapters/arrays/creating-a-string-from-an-array.md index d2b83bb..bcd8693 100644 --- a/chapters/arrays/creating-a-string-from-an-array.md +++ b/chapters/arrays/creating-a-string-from-an-array.md @@ -13,7 +13,7 @@ Use JavaScript's Array toString() method: {% highlight coffeescript %} ["one", "two", "three"].toString() -# => 'three,two,one' +# => 'one,two,three' {% endhighlight %} ## Discussion From 671301effd965b625baa8dd4b0b50666ac04e5aa Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 10:06:58 +0100 Subject: [PATCH 128/267] Fix function result --- chapters/functions/splat_arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/functions/splat_arguments.md b/chapters/functions/splat_arguments.md index f0e39cb..70bd1b6 100644 --- a/chapters/functions/splat_arguments.md +++ b/chapters/functions/splat_arguments.md @@ -37,7 +37,7 @@ loadTruck = (firstDibs, secondDibs, tooSlow..., leftAtHome) -> passengerSeat: leftAtHome loadTruck("Amanda", "Joel", "Bob", "Mary", "Phillip", "Austin") -# => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: ["Bob", "Mary", "Phillip"] }, taxi: "Austin" } +# => { truck: { driversSeat: 'Amanda', passengerSeat: 'Joel', trunkBed: [ 'Bob', 'Mary', 'Phillip' ] }, taxi: { passengerSeat: 'Austin' } } loadTruck("Amanda") # => { truck: { driversSeat: "Amanda", passengerSeat: undefined, trunkBed: [] }, taxi: undefined } From 5a0944e25536d1ee2b121c405140857f38432704 Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 10:13:40 +0100 Subject: [PATCH 129/267] Rename file (fixing terminology) --- .../{extending-classes.md => extending-built-in-objects.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename chapters/metaprogramming/{extending-classes.md => extending-built-in-objects.md} (100%) diff --git a/chapters/metaprogramming/extending-classes.md b/chapters/metaprogramming/extending-built-in-objects.md similarity index 100% rename from chapters/metaprogramming/extending-classes.md rename to chapters/metaprogramming/extending-built-in-objects.md From b81f32e66266528bec50071c8cf18a69435d4e7a Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 10:13:58 +0100 Subject: [PATCH 130/267] Change headline (fixing terminology) The recipe doesn't describe the extension of classes but of built-in objects (which can either be native or host objects). Native objects: String, Array, Boolean, Object, Function, etc. Host objects (browser): window, document, etc. --- chapters/metaprogramming/extending-built-in-objects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/metaprogramming/extending-built-in-objects.md b/chapters/metaprogramming/extending-built-in-objects.md index fb401eb..cbd7934 100644 --- a/chapters/metaprogramming/extending-built-in-objects.md +++ b/chapters/metaprogramming/extending-built-in-objects.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Extending Classes +title: Extending Built-in Objects chapter: Metaprogramming --- ## Problem From 1afb13be3b5c99f148ccede555af786fb66117cd Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 10:24:47 +0100 Subject: [PATCH 131/267] Update Pre-ES5 example --- chapters/arrays/max-array-value.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index e703b38..6e662a3 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -9,17 +9,15 @@ You need to find the largest value contained in an array. ## Solution -In ECMAScript 5, use `Array#reduce`. In older javascripts, use Math.max: +In ECMAScript 5, you can use `Array#reduce`. For compatibility with older javascripts, use Math.max.apply: {% highlight coffeescript %} # ECMAScript 5 [12,32,11,67,1,3].reduce (a,b) -> Math.max a, b # => 67 -# Pre-EC5 -max = Math.max.apply(null, [12,32,11,67,1,3]) -# => [ 12, 32, 32, 67, 67, 67 ] -max +# Pre-ES5 +Math.max.apply(null, [12,32,11,67,1,3]) # => 67 {% endhighlight %} From bbd37254fe1af4497de3cc55f16ccb3daa8b828b Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 10:59:25 +0100 Subject: [PATCH 132/267] Use 'unless' instead of 'if !' --- chapters/strings/trimming-whitespace-from-a-string.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md index 881f7f3..bf92de6 100644 --- a/chapters/strings/trimming-whitespace-from-a-string.md +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -37,7 +37,7 @@ To trim only trailing whitespace, use the following: Opera, Firefox and Chrome all have a native string prototype `trim` method, and the other browsers could add one as well. For this particular method, I would use the built-in method where possible, otherwise create a polyfill: {% highlight coffeescript %} -if !String::trim? then String::trim = -> @replace /^\s+|\s+$/g, "" +unless String::trim then String::trim = -> @replace /^\s+|\s+$/g, "" " padded string ".trim # => 'padded string' From bcf6771c2725ef29086447ca80e42675568eb23e Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 11:57:48 +0100 Subject: [PATCH 133/267] Fixed Class Variables recipe The recipe mixed up class variables and instance variables, so it was just the other way round. From http://coffeescript.org/#classes > Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. Because in the context of a class definition, this is the class object itself (the constructor function), you can assign static properties by using > `@property: value`, and call functions defined in parent classes: `@attr 'title', type: 'text'` Also added a case for an instance variable to better distinguish between the two. --- .../classes_and_objects/class-variables.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/chapters/classes_and_objects/class-variables.md b/chapters/classes_and_objects/class-variables.md index d9f3a9a..dd772ef 100644 --- a/chapters/classes_and_objects/class-variables.md +++ b/chapters/classes_and_objects/class-variables.md @@ -9,18 +9,22 @@ You want to create a class variable. ## Solution -Use json notation in the class body; use `::` to access it outside: - {% highlight coffeescript %} class Zoo - MAX_ANIMALS: 50 - -Zoo::MAX_ZOOKEEPERS = 5 + @MAX_ANIMALS: 50 + MAX_ZOOKEEPERS: 3 -Zoo::MAX_ANIMALS +console.log Zoo.MAX_ANIMALS # => 50 + +console.log Zoo.MAX_ZOOKEEPERS +# => undefined (it is an instance variable) + +zoo = new Zoo +console.log zoo.MAX_ZOOKEEPERS +# => 3 {% endhighlight %} ## Discussion -Coffeescript will store these values on the class prototype (e.g. Zoo.prototype.MAX_ANIMALS) rather than on individual object instances, which conserves memory and gives a central location to store class-level values. +Coffeescript will store these values on the object itself rather than on the object prototype (and thus on individual object instances), which conserves memory and gives a central location to store class-level values. From 5042a472a434c3edb935e581d8f46c8e1f80022d Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 11:57:48 +0100 Subject: [PATCH 134/267] Fixed Class Variables recipe The recipe mixed up class variables and instance variables, so it was just the other way round. From http://coffeescript.org/#classes > Finally, class definitions are blocks of executable code, which make for interesting metaprogramming possibilities. Because in the context of a class definition, this is the class object itself (the constructor function), you can assign static properties by using > `@property: value`, and call functions defined in parent classes: `@attr 'title', type: 'text'` Also added a case for an instance variable to better distinguish between the two. --- .../classes_and_objects/class-variables.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/chapters/classes_and_objects/class-variables.md b/chapters/classes_and_objects/class-variables.md index d9f3a9a..9de66e5 100644 --- a/chapters/classes_and_objects/class-variables.md +++ b/chapters/classes_and_objects/class-variables.md @@ -3,24 +3,28 @@ layout: recipe title: Class Variables chapter: Classes and Objects --- -## Creating Class Variables +## Problem You want to create a class variable. ## Solution -Use json notation in the class body; use `::` to access it outside: - {% highlight coffeescript %} class Zoo - MAX_ANIMALS: 50 - -Zoo::MAX_ZOOKEEPERS = 5 + @MAX_ANIMALS: 50 + MAX_ZOOKEEPERS: 3 -Zoo::MAX_ANIMALS +console.log Zoo.MAX_ANIMALS # => 50 + +console.log Zoo.MAX_ZOOKEEPERS +# => undefined (it is an instance variable) + +zoo = new Zoo +console.log zoo.MAX_ZOOKEEPERS +# => 3 {% endhighlight %} ## Discussion -Coffeescript will store these values on the class prototype (e.g. Zoo.prototype.MAX_ANIMALS) rather than on individual object instances, which conserves memory and gives a central location to store class-level values. +Coffeescript will store these values on the object itself rather than on the object prototype (and thus on individual object instances), which conserves memory and gives a central location to store class-level values. From d7e1874c1962ca06c12312efa8d0aeae4b25f76c Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 12:32:21 +0100 Subject: [PATCH 135/267] Remove console.log from example --- chapters/classes_and_objects/class-variables.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/classes_and_objects/class-variables.md b/chapters/classes_and_objects/class-variables.md index 9de66e5..d48ed9c 100644 --- a/chapters/classes_and_objects/class-variables.md +++ b/chapters/classes_and_objects/class-variables.md @@ -14,14 +14,14 @@ class Zoo @MAX_ANIMALS: 50 MAX_ZOOKEEPERS: 3 -console.log Zoo.MAX_ANIMALS +Zoo.MAX_ANIMALS # => 50 -console.log Zoo.MAX_ZOOKEEPERS +Zoo.MAX_ZOOKEEPERS # => undefined (it is an instance variable) zoo = new Zoo -console.log zoo.MAX_ZOOKEEPERS +zoo.MAX_ZOOKEEPERS # => 3 {% endhighlight %} From c6cfa7ea6f806e22272bd5657b6e09e7e0f92e3b Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Thu, 23 Feb 2012 12:35:18 +0100 Subject: [PATCH 136/267] Add recipe "Class Methods and Instance Methods" --- authors.md | 1 + .../class-methods-and-instance-methods.md | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 chapters/classes_and_objects/class-methods-and-instance-methods.md diff --git a/authors.md b/authors.md index f48ecbd..c759d8b 100644 --- a/authors.md +++ b/authors.md @@ -18,6 +18,7 @@ The following people are totally rad and awesome because they have contributed r * Phil Cohen *github@phlippers.net* * João Moreno *coffeecb @joaomoreno .com* * Jeff Pickhardt *pickhardt (at) gmail (dot) com* +* Frederic Hemberger * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/classes_and_objects/class-methods-and-instance-methods.md b/chapters/classes_and_objects/class-methods-and-instance-methods.md new file mode 100644 index 0000000..53977a6 --- /dev/null +++ b/chapters/classes_and_objects/class-methods-and-instance-methods.md @@ -0,0 +1,35 @@ +--- +layout: recipe +title: Class Methods and Instance Methods +chapter: Classes and Objects +--- +## Problem + +You want to create a class methods and instance methods. + +## Solution + +{% highlight coffeescript %} +class Songs + @_titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. + + @get_count: -> + @_titles + + constructor: (@artist, @title) -> + Songs._titles++ + +Songs.get_count() +# => 0 + +song = new Songs("Rick Astley", "Never Gonna Give You Up") +Songs.get_count() +# => 1 + +song.get_count() +# => TypeError: Object # has no method 'get_count' +{% endhighlight %} + +## Discussion + +Coffeescript will store class methods (also called static methods) on the object itself rather than on the object prototype (and thus on individual object instances), which conserves memory and gives a central location to store class-level values. From b43601841e5d1459630882b48e2d1be794d5393f Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Mon, 5 Mar 2012 19:22:18 +0100 Subject: [PATCH 137/267] Max Array Value: Add splats example. See #39. --- chapters/arrays/max-array-value.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index 6e662a3..906eaac 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -9,7 +9,14 @@ You need to find the largest value contained in an array. ## Solution -In ECMAScript 5, you can use `Array#reduce`. For compatibility with older javascripts, use Math.max.apply: +You can use Math.max() JavaScript method along with splats. + +{% highlight coffeescript %} +Math.max [12, 32, 11, 67, 1, 3]... +# => 67 +{% endhighlight %} + +Alternatively, it's possible to use ES5 `reduce` method. For backward compatibility with older JavaScript implementations, use Math.max.apply: {% highlight coffeescript %} # ECMAScript 5 @@ -23,4 +30,4 @@ Math.max.apply(null, [12,32,11,67,1,3]) ## Discussion -`Math.max` compares two numbers and returns the larger of the two; the rest of this recipe (both versions) is just iterating over the array to find the largest one. +`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable ammount of arguments, such as `console.log`. \ No newline at end of file From 5e7fe4760a0595f39c8d2771d3a7e1e9db1bd2d9 Mon Sep 17 00:00:00 2001 From: Frederic Hemberger Date: Mon, 19 Mar 2012 19:26:32 +0100 Subject: [PATCH 138/267] Add note about extending native objects, fixes #1 --- chapters/arrays/filtering-arrays.md | 7 ++++--- chapters/arrays/removing-duplicate-elements-from-arrays.md | 4 ++++ chapters/arrays/shuffling-array-elements.md | 2 ++ chapters/metaprogramming/extending-built-in-objects.md | 2 ++ chapters/strings/lowercasing-a-string.md | 2 ++ chapters/strings/uppercasing-a-string.md | 2 ++ 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/chapters/arrays/filtering-arrays.md b/chapters/arrays/filtering-arrays.md index 34e14cb..1d2c7e3 100644 --- a/chapters/arrays/filtering-arrays.md +++ b/chapters/arrays/filtering-arrays.md @@ -18,12 +18,13 @@ array.filter (x) -> x > 5 # => [6,7,8,9,10] {% endhighlight %} -In pre-EC5 implementations, extend the Array prototype to add a filter function which will take a callback and perform a comprension over itself, collecting all elements for which the callback is true. +In pre-EC5 implementations, extend the Array prototype to add a filter function which will take a callback and perform a comprension over itself, collecting all elements for which the callback is true. Be sure to check if the function is already implemented before overwriting it: {% highlight coffeescript %} # Extending Array's prototype -Array::filter = (callback) -> - element for element in this when callback(element) +unless Array::filter + Array::filter = (callback) -> + element for element in this when callback(element) array = [1..10] diff --git a/chapters/arrays/removing-duplicate-elements-from-arrays.md b/chapters/arrays/removing-duplicate-elements-from-arrays.md index 7ecd731..4e079ad 100644 --- a/chapters/arrays/removing-duplicate-elements-from-arrays.md +++ b/chapters/arrays/removing-duplicate-elements-from-arrays.md @@ -22,3 +22,7 @@ Array::unique = -> ## Discussion There are many implementations of the `unique` method in JavaScript. This one is based on "The fastest method to find unique items in array" found [here](http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/). + +**Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). + + diff --git a/chapters/arrays/shuffling-array-elements.md b/chapters/arrays/shuffling-array-elements.md index 0d2da5f..9f2f8e9 100644 --- a/chapters/arrays/shuffling-array-elements.md +++ b/chapters/arrays/shuffling-array-elements.md @@ -21,3 +21,5 @@ Array::shuffle = -> @sort -> 0.5 - Math.random() ## Discussion For more background on how this shuffle logic works, see this [discussion at StackOverflow](http://stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling). + +**Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). \ No newline at end of file diff --git a/chapters/metaprogramming/extending-built-in-objects.md b/chapters/metaprogramming/extending-built-in-objects.md index cbd7934..93b6b58 100644 --- a/chapters/metaprogramming/extending-built-in-objects.md +++ b/chapters/metaprogramming/extending-built-in-objects.md @@ -22,3 +22,5 @@ String::capitalize = () -> ## Discussion Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the `::` operator. + +**Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). diff --git a/chapters/strings/lowercasing-a-string.md b/chapters/strings/lowercasing-a-string.md index 729dafa..f39dd91 100644 --- a/chapters/strings/lowercasing-a-string.md +++ b/chapters/strings/lowercasing-a-string.md @@ -43,3 +43,5 @@ String.prototype.downcase = function() { }; "ONE TWO THREE".downcase(); {% endhighlight %} + +**Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). \ No newline at end of file diff --git a/chapters/strings/uppercasing-a-string.md b/chapters/strings/uppercasing-a-string.md index fd0e103..7cbc242 100644 --- a/chapters/strings/uppercasing-a-string.md +++ b/chapters/strings/uppercasing-a-string.md @@ -43,3 +43,5 @@ String.prototype.upcase = function() { }; "one two three".upcase(); {% endhighlight %} + +**Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). From bcdad46e8324b03be3bcc6c1a765ce218b25a187 Mon Sep 17 00:00:00 2001 From: Rong Shen Date: Thu, 12 Apr 2012 11:42:53 +0800 Subject: [PATCH 139/267] Recipe-Clone(Deep Copy): Add clone support for Date and RegExp type --- chapters/classes_and_objects/cloning.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/chapters/classes_and_objects/cloning.md b/chapters/classes_and_objects/cloning.md index bdf508c..3b1ceba 100644 --- a/chapters/classes_and_objects/cloning.md +++ b/chapters/classes_and_objects/cloning.md @@ -14,6 +14,17 @@ clone = (obj) -> if not obj? or typeof obj isnt 'object' return obj + if obj instanceof Date + return new Date(obj.getTime()) + + if obj instanceof RegExp + flags = '' + flags += 'g' if obj.global? + flags += 'i' if obj.ignoreCase? + flags += 'm' if obj.multiline? + flags += 'y' if obj.sticky? + return new RegExp(obj.source, flags) + newInstance = new obj.constructor() for key of obj From bbfcdd922d534bbe4688d69d2cf5a3c811e7496f Mon Sep 17 00:00:00 2001 From: Paul Rosenzweig Date: Fri, 13 Apr 2012 22:29:42 -0500 Subject: [PATCH 140/267] made more idiomatic --- chapters/arrays/zip-function.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/chapters/arrays/zip-function.md b/chapters/arrays/zip-function.md index 80d8006..1271acb 100644 --- a/chapters/arrays/zip-function.md +++ b/chapters/arrays/zip-function.md @@ -15,15 +15,9 @@ Use the following CoffeeScript code: # Usage: zip(arr1, arr2, arr3, ...) zip = () -> lengthArray = (arr.length for arr in arguments) - length = Math.max.apply(Math, lengthArray) - argumentLength = arguments.length - results = [] + length = Math.max(lengthArray...) for i in [0...length] - semiResult = [] - for arr in arguments - semiResult.push arr[i] - results.push semiResult - return results + arr[i] for arr in arguments zip([0, 1, 2, 3], [0, -1, -2, -3]) # => [[0, 0], [1, -1], [2, -2], [3, -3]] From 8617cfdd0b319c7c210bd81e4838636d0ffdb023 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 29 Apr 2012 17:05:07 -0300 Subject: [PATCH 141/267] Added recipe for reusing CoffeeScript code in a web application as well as Node.js --- .../syntax/code_reuse_on_client_and_server.md | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 chapters/syntax/code_reuse_on_client_and_server.md diff --git a/chapters/syntax/code_reuse_on_client_and_server.md b/chapters/syntax/code_reuse_on_client_and_server.md new file mode 100644 index 0000000..f7a1aca --- /dev/null +++ b/chapters/syntax/code_reuse_on_client_and_server.md @@ -0,0 +1,90 @@ +--- +layout: recipe +title: Code Reuse on Client and Server +chapter: Syntax +--- +## Problem + +You have created some functionality in CoffeeScript that you wish to use on the client with a web browser and on the server with Node.js. + +## Solution + +Export the functionality in the following manner: + +{% highlight coffeescript %} + +# simpleMath.coffee + +# these methods are private +add = (a, b) -> + a + b + +subtract = (a, b) -> + a - b + +square = (x) -> + x * x + +# create a namespace to export our public methods +SimpleMath = exports? and exports or @SimpleMath = {} + +# items attached to our namespace are available in Node.js as well as client browsers +class SimpleMath.Calculator + add: add + subtract: subtract + square: square + +{% endhighlight %} + +## Discussion + +In the above example, we create a new namespace called SimpleMath. If `export` is available, our class is exported as a Node.js module. If `export` is *not* available, then SimpleMath is added to the global namespace and available to our web page. + +In Node.js, we can include our module using the `require` command. + +{% highlight console %} + +$ node + +> var SimpleMath = require('./simpleMath'); +undefined +> var Calc = new SimpleMath.Calculator(); +undefined +> console.log("5 + 6 = ", Calc.add(5, 6)); +5 + 6 = 11 +undefined +> + +{% endhighlight %} + +In our web page, we can include our module using by including it as a script. + +{% highlight html %} + + + + + + SimpleMath Module Example + + + + + +

      A SimpleMath Example

      +
        + + + +{% endhighlight %} + +Result: + +#A SimpleMath Example +* 5 + 6 = 11 \ No newline at end of file From 86a90af8d73e03faef88f4d641dc1ca668d7956f Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Thu, 3 May 2012 13:02:09 -0300 Subject: [PATCH 142/267] Added place holders for Testing chapter. --- chapters/index.html | 1 + .../testing/behavior_testing_with_jasmine.md | 9 +++++++++ chapters/testing/index.html | 18 ++++++++++++++++++ chapters/testing/unit_testing_with_qunit.md | 9 +++++++++ index.html | 1 + 5 files changed, 38 insertions(+) create mode 100644 chapters/testing/behavior_testing_with_jasmine.md create mode 100644 chapters/testing/index.html create mode 100644 chapters/testing/unit_testing_with_qunit.md diff --git a/chapters/index.html b/chapters/index.html index dc0ad39..778f38e 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -15,6 +15,7 @@ - Networking - Design Patterns - Databases +- Testing ---
          diff --git a/chapters/testing/behavior_testing_with_jasmine.md b/chapters/testing/behavior_testing_with_jasmine.md new file mode 100644 index 0000000..d1252c8 --- /dev/null +++ b/chapters/testing/behavior_testing_with_jasmine.md @@ -0,0 +1,9 @@ +--- +layout: recipe +title: Behavior Testing with Jasmine +chapter: Testing +--- +## Problem + +You have some CoffeeScript and you want to verify that it is working correctly. + diff --git a/chapters/testing/index.html b/chapters/testing/index.html new file mode 100644 index 0000000..a838b6f --- /dev/null +++ b/chapters/testing/index.html @@ -0,0 +1,18 @@ +--- +layout: chapter +title: Testing +chapter: Testing +--- + +{% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} +{% capture indexurl %}{{ url }}/index.html{% endcapture %} + +
            +{% for page in site.pages %} + {% if page.url contains url %} + {% unless page.url == indexurl %} +
          • {{ page.title }}
          • + {% endunless %} + {% endif %} +{% endfor %} +
          diff --git a/chapters/testing/unit_testing_with_qunit.md b/chapters/testing/unit_testing_with_qunit.md new file mode 100644 index 0000000..1486765 --- /dev/null +++ b/chapters/testing/unit_testing_with_qunit.md @@ -0,0 +1,9 @@ +--- +layout: recipe +title: Unit Testing with QUint +chapter: Testing +--- +## Problem + +You have some CoffeeScript and you want to verify that it is working correctly. + diff --git a/index.html b/index.html index 247136b..0b997af 100644 --- a/index.html +++ b/index.html @@ -15,6 +15,7 @@ - Networking - Design Patterns - Databases +- Testing ---

          Welcome

          From b62a29d0e690b30ffe319932601d7ba9fdb11639 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Thu, 3 May 2012 20:47:29 -0300 Subject: [PATCH 143/267] Completed Jasmine recipe --- .../testing/images/jasmine_failing_all.jpg | Bin 0 -> 243478 bytes .../testing/images/jasmine_failing_better.jpg | Bin 0 -> 173204 bytes chapters/testing/images/jasmine_passing.jpg | Bin 0 -> 26427 bytes chapters/testing/testing_with_jasmine.md | 80 ++++++++++++++++++ ..._with_jasmine.md => testing_with_mocha.md} | 6 +- chapters/testing/testing_with_qunit.md | 13 +++ chapters/testing/unit_testing_with_qunit.md | 9 -- 7 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 chapters/testing/images/jasmine_failing_all.jpg create mode 100644 chapters/testing/images/jasmine_failing_better.jpg create mode 100644 chapters/testing/images/jasmine_passing.jpg create mode 100644 chapters/testing/testing_with_jasmine.md rename chapters/testing/{behavior_testing_with_jasmine.md => testing_with_mocha.md} (55%) create mode 100644 chapters/testing/testing_with_qunit.md delete mode 100644 chapters/testing/unit_testing_with_qunit.md diff --git a/chapters/testing/images/jasmine_failing_all.jpg b/chapters/testing/images/jasmine_failing_all.jpg new file mode 100644 index 0000000000000000000000000000000000000000..29a601cce93e2ccb255e330905570d8ea5e03b45 GIT binary patch literal 243478 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%0D_ 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 mX4Q-J|02yQg*liC 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

          rvPDjU>gMc-Gj-l+q&0fs-V^QP#!D(f%*S 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 z zMcgjgIq>UV=)1=>99dXCcmqCX1Er~O{ljzzkWb8NdGv!m^fL?880o8nt#kvz*RCtj zS$gvd(`>pxLRb-G7_IKepZoJb1Pgd|AWzNF%(jK1<`UDtaz2G0TZsf?gx=l2d6(reCAl zx!rjLZ0%0V~IJe5BhQe+}oPpP|SKC;&(Bx49?DQRo1H0q%?%wEdrHjc59H8VygZ z|L8vwfAwDhM=oPiSr6liCn6N;0M`pNabUMS z-Xz`_21##@j|Yax?G4+l z6`=!A2*8SDxs`ZYL)Ei_?m@|mpoKB9dsn#2 zJ%oT$f73MQ^?9JD=@x+R$%6^&ah=^EcD!?$v7e}pPohimYkoKD1XWkRgaXOf9S6uH zfXA;-0Sen$a_*a>0MGDC6kIg+63v<5Wa#axA7@c(;4O!&&mhP|z_ZWHKV<5>VS0Am zCL#PM=qorXVCat@P<6&Jt|NGL$x)S*ef>3tj>&WXVE)Et*POweUY2+$!yF#a@AWdCVD$j`aW!ana?sdNw!O#5Oo zqEpuCLcm-_Vd~LXJD~I(e3>76o#}t~LTepY;ue727f_&=lmR&ySL#-jmx(~)cUDO! z@rY{=dMujJKtZ*ZuKdF!`iHc2An+KxwgBEO3|an90dGH)8@I54a|a)R(5O953Tv1V z)FFz-w)}AUc=1rQO=;*rHjfLp?o$5`)2PY^hk&=msNN?up!ey@jbVJK?VGAnBMNFI zDFA^iB`e(9`|GLyFiDh>`x#xDGQc5!4*dnT42?!r!#i<6=Hspc>k62I&Z?Xgy?Yd< zLNH1?HKT;SaFw4oC}(cJWFRFcH`YB>H<|Z|JScMxaw_)o z+fLoIA~kczKA*og%ZkX_ff%Krx~-!1!Llz1Ka*&~(S_Ld5eG3?`P+Ek+rEQ4>dhZ6 zPOB^4=AXTNT1xOfilmLS2E47J$wxr<)?tX@2(T;{IOj0OT;OOTO`Cw)UaCH(5_*p* zKt^PeMYfE$(UEo@NKlf7CtG42 z^R|fP_`mErsPB%QH0J0CAc%$m+$wgV7f| zlwQUU(1|_Vn~ZSq7(;sKvELjzCj91a4ZRSmZ|%4HK6c*gk8GZpBb#JkRvKZ)v>}VX zp#Q;p)~oGw!adnDh6`nzqhO5<&izu&(23QR%@+L8_BN2Il(!g257OwMI?y;OkqVT| zE)*BtG}R;s2?b%x0m6;tE+zw zcIHaxoDL5uC0#>&v>p<)^B<;IA`2XQ&7q6!?FLe&)V>Q=*8JkY==tR(yes_0B2(@& z@&DD{mA^yXwtd}6MI~e@A=#4cR+b3U%8gKjn3y7InwTPtOs2&)Bq1tmS#LBZ%b2Wl z+e4CVgcxJ8Z!^^RGGltL=lSV9ULD8deSdxX1HRXBeXs5OoagyDKc{f3@NkQe?dIcXHIDXKK2*z@B`@y>h8>*fku zcfY#WFQ3{Ad#E9)#(EsMTBSz9cO|-yqrTltPvHU^^#fTMJY!j!jB@Bg+f7VSMiSF^ zNwR&I&GkbH=UIu#u*ZH**pr!<{BDQEs?+ZI=N65Y3w*?`209}Hn%oy7ZUtW1(o|T^ z+4KeCjCd19&`&`6JXi%9rCp-V>h|@JP=i`!&MltCRjku|VV&|%McHlT*y84()PJ6t zmEr0_Ab@So3e%T)yUFR?@=V?yl}7aHA)GLU849|;jkO=^Z8$lslhg|<%5)%A&uMs& zuZ611n(K_RO5b)zEu*GWyKR=8=a=U>X#pIw;E#?JxgQ$J3n!sW7L5L}!iChHnH%aS zbmK~oLVDX*Z~5}FS2EqLvX>vIqqVT zDjA>&sJT=VuO97~XLwhu6V7e&ZjS|_nX;OP8#p%djw058#F}Y?nNRH-m>lHv+rR5K z4p6m`vtG2;SnTW1O5*K$VeUHg;+l_~G{sX!7-%sWZOX^g&DfLQLw81xacy#h4BXG8 zjcH6gP^5Q8nU&F71nr9@$D2+zJ!b1O8!6E>+NhTM#QP--uLRQ5iZD!7xEjyO%|0+8CXQz$q2K}KTJVnx8Gf8L-^gL zjCEL2supqKK=Yp%61*Q1Gs4SzGc9{Ckuj1RrfH<|20t%^?wHeHh7e_fR9(mzC#U_rzg|;QUx!~9f zh{cI4Tgy(_ovf|A?QL05_-F^-$Q|(YV2!X)=k#cM;Rrf2F&DM7u;j>rXzw)X0wh&B zIx^oQ#I$*@fq`zNrw4Z!UIE>rbQ_I+2m9uvCPxs zdWxZ${Az#5oPXL}i(p!V1hCM^LI`&68yDymp-Odh(#Tgi`wljK+IgP1zsa+_M+MW7 z()FRC=5sjTmcxAFAlmSst&p|2_8$5+)A(f{{-9!XS@D=fobQrnbIg~3%S=i=3h_v;g#4fiKDtxCDOj@;glviU zd$fpK*+Iz>B&kBQa+PDOXHbmhCnftx;X^Bt0z-uK@{J&n1l7LGL#;Y#(Z-CsNQWOV*^K=GcCIeo|K9{E16{n>nVjco*;HBm8%7$ z=3IcdyKK<*9M+Qx?}XQlXtlp?l?JJ~T~KV*?3bM%Up@SMvQ4s0Lt z+k37wRQ6D%%a@n*BbUtE-H|T9#h}3T0T#MjX{_|Mj))9TKSWPqAo2B{8L;`CIRRa2< z@R0eP97y@T+c4uS$0#1 zcojjuxO0|(KNEkb@>)=3@|0k7{f-pY!wn(fQ+W}VFZz!=)7;xQKsi%0^anQ8@|F7( zA+Tm2yQbPWn(VbYQ$F&4Kd9lO6t0+7efsXI`=Hqoq|yu+3N6E-};l z()7Jbt}eB@H}3|AbbU1qog3UZyp8!?Zktz?^cEgIhbxw7Ze-w&Joe`^_E9JC_ea!> z9=E;s|KQ;UU3dRrC@Z*@h}9iQe*zP$LZX%5{Efgm!TG7P3(l&y5;Qcd@)fls>bKnZ zvaqWozI$`z{CdQ@{&3)X8ZM5H=c)wJF3tESQ>uHjP=ZdzCa#zLl-f|vy5Eb7$@BMA z-0Oh2Z0+$`-1P;~sm*lHMy;r_n(fTBo)`Pjk36Xw^Ez<*yB-pptTKHs@6a}NtJ|mE zxxRLL{TViroMcu{iwIaH*23^OaqbrS9Ku=lKn*hupFgM>W!#_qDcQ}&OOeX@xUzIG z>{3tIfh|Pf*8LA}?ia2{q4KorC&wQ#i9B|t21h|F|5{a5x@#_qMknTQf@iFTBikG> zy8wjd@BOS1Yyd<*R;2A04iGp$FcAm)#|qO-&%vaXL^Xi=rBs-bs|@0;5l)4uG%IJ zhr~fDYWlN`k&bZKAIdDRPpN=1BW2rRSXX3T@0?4v4Sy$(=aS47F7ptR6yjPpwx=Ul>&Z^j<@1N82zYP!Q)=;dc6srf=4$rev ztDi2%fA69Oapy*0q3NQqD+?ga%#@pLr>~(Hj$ya1-WaK1A)dVb-bVOCs_W3};*hhs z4|U|lBd#kw|0a4;`WlGb?eydciZLnYnY6iFCfg>=<_*{ba+oe|7p4VGf(uR7#|C;C z!p+!7H^oh{2d+#UaH|R3!0917RHf_W>8lL`sq574E;uNbH2xu4H)bBB`?8zuPgYTK zg*#KJp>tz6jZ!0_1{f1?&cGWX_zeiCEkuC6<=m?UaMU|YyeTNFFBr@zq9;!t6lqjW zW3xN!cW=IPsMD}KwoTwe(~EjxNX7F})!wZQs=+EZYYszi2w2Qg^R>b}Mb6-bx5^gTd)Mxz zQachUsYDLJD*!bY;66bNkps#XLR>BA;Yd5WAEO^jPDct`k+y#MNAkE&?MLw4PQikZ+GixdNFL&$+fx~#BWFoCrP)Z zms+b&EDCOuN)1yLJ@G+c?qh!aByIz<&-Y_lEd?1MsK(tqule|zZ)TffPx`#|P{W1Q zP{+tj|1$io#O4kV1{_H;3}}dX60E_Zg$Ju+-I!((K-k7R{or;!vPg1a^~tor);t^kwGUXnp=36 zmLq?~y`6b~t~e8~FKT+*>^+Em{YTeZ$x$@UA|>JNa*XBR+}%nJ%^#_*X{kJ>?xj3{ zq7zF_0tJ;cS!m=T!y$#M8SjbzN!PRQi$PQrO^dWNS546+`S9u7YcX`@yYvf%{C5H6UVSF4O z73FkIZ(Nm-g&cC0jCfS#r*7lHv>P!Y(*ebB^k0n?m%do(Kk?|kOAt^CbUy8o*$)@R zD_`*cupFS@B`zJd*MPCPXCvkcU>S3whZ>4%>=nQY4A!dLSNE+mF6B;7w*yD_$C#BG zZvw6YX?|c*@Bo3YsWDU<|HhtV9R;4Om3uvvCr@IXG1--FUuL*RzijZ_alSH+#hK!R zZDZT(&@Br5k&9|DNSTc19pX{tO3@KjNCrAmZWq>oC2ZUMrU*nE5iX^X!n=_(*(dpV z*R=Mx?|5`EPWzCEg@Au*uF9O;22OoOs>S#D1j&5zmj$^6RF*Au^@ayRtL4s-C)qK% zewtq5_g!)*6YX8rK5-V*#Oeh)Z8xx@!JApK51EU?p#$NOHG5~|1m6n8(^JuPeKR}P z67UxtNzcD377d-@IZHTowp@W9C=pE;gN>w`$UyB(*eM`3g_g!zP=9m?P0tZ;4C}@_ zL<)-{9kWyLSKdG3$J^VtZsQV?PjR#UzB-TCfM09X+feOd;JQ4lU%e2tm0$j zep&TLCFJg#(+xC6pB?75n@qR8S?Y+QjJz7aHKd0S=;@IZ2@pR`KTqnrg*D5B!Z;2k z`*OpH>b1M8VOn{WZo#XKYA_L}gbgp8 z?|yd&=^tOKeza--$^)H1;YZv4X@o@rnIx-rxzT*gu4&NLT}lTAx`LX%|18;%be*!_ zxryX)TVaV&o%;I9?3^I%)xM_I`okxM$h3{z;1{J0$6ez)$?SW>fDP*z;8#*vGr|m1 zu)6P`SOz@r?6=?rt0*oRa&nrC;fL%2e)|XVWqJY(WVCg}vP1kdAnpl5bfci$tiI0n zBd&_$*B)lwzz5y7{<{CeiM`KKioVqx#?|h}Ze=+ykM3}+F@%U&81$$~K2N>}tDA$m z1E12q+3_xUlCdaMvVd1y91A?Cxp~iVJ9O`QXZsh4g#x$u@rz@)OYBGSGzIaFkEqAo#Cb80|%VJRG!QjQIk$>8UZn=#P4Z#{?gkO4DXG;l1cX*7IHC z?RJgq^B+qhejH2c$&`rysPy{Mwxtx+3uLs=s2rrI|3G(zL{I{<_lMi)xp>{2z8{G2 z#EJ&MY8!kJ-yZA#((2=FqxZ1{MykS4=ZVpplnuQ7)`jw=Yw@7ur=^#+QaR%l-@6NA z6qj-H(ttIGL*n>=Btv0=*$~<{hM7Lw{~B|Y9S&+f2ohd@p@LOA@2&}s_ol&$PNJ_Z zm6}S`2{bU8)?>EB^uGnTPRx^G!nIYUrc~WzX`81UXtf~3dkRJrM0T+!z zhwEulV|PjLR|NXAyr)}V{=N8le`xLM@r^j&r*aPNJk|wL(?PTqat(R1Ef2(N8tX1i zI|LOvv+p_4mJM@CQ7Ab{ClEE#t+HX6vEQv_V$U*cq_O9LvPVb6y~Na=G2DZzb6Mvx zk8NBIZ&O*4$Bf^&CYlwCKM{HUXz84RNiAzLZ##JeCNe1}d;oI!8&g0}D`MvVgEZMo z9-~BXxh`mSQ1o6A+pBAGx~q$uwQ-vV)zG*-EV$iA5ak zF$9XlCEf22Ri`Lsf8teJm6z*d`Z%@|{%fC-2B>{QPYGEaJE2$L0!%R%n$QmcnYl&{ zGdSu%F2o=ly@E_xZ<9*TW8y8U)xTM8Y1pl3kzaV;Z0y#6r#y@ zS!#@cXcA~qh{qyt;OGja#Wk!g4I*yF%AS86Bum&n=WDQ+^p)`3t6V*}k)1gFJ|W01 zh&GGPwMN}V_~>CSC)&JX8Cp_slx!`F22VH}cMS5snjgD^By0zS?<^^{Fc{_DDsZy7(mO7u3A+r z&uF7ShSuKGpp>$C#z&*KJdHO5Nsm#!fLhH5u=lXtZfMeFJvO+7H#9JL_C* zIXjzB(FPL7CP^Y@+n?K7CTr)a_|G%o{$I;3+ICj!bobePgdr*Y9kx&=BPQI{0J6t< zWdXy^ks8yJV!d-uisXhL39Y1UHgaBgmrEYe{459D1s3 zWK$U(`%W_mr~WFuB36H2yz%YKe#JWbs~R4=54X05;Zj7mu{RR0%nLntRxbre!tW6rBY^9lpyxg))j&6` zD$!~e3DCA zv5GATj;$xrJG9M2L--HA9($P*hic^M;cB(+0zwQ)B9j$#7En~v{|VOY)rq*P8WZ}X zz!G)GQ%peQu$8CUAx-_*N@@JDy#}jV8#&-hqXy4=Rs1GosXXri{>f`UR5_~dQ|}j; zeZ$MuYYEFei^OPHkz_as(WCo=tRNd!R73F5J{>jlTZ+|`_ceHuhVN{=Vkw zI(B~ct+nJ<;EepoNQiAhpqfSe&1Dkf`Vf&_krlrvSKMm<)mIStesRdOy33K2UNkv~ zTvp>6K)5Wf3WR$DA^u4$a0d-)>vkTg8!|t*O+MN^+9&Xs{bm)yEo#69_O#Q$F(oD2 z!isVXWOz+WfASUQojoPBYOWzVM$AAg^LJ*HP&N<^$z7%`P5qnD5INBXo4jfR%qcJ# ztycTgHewWRtSxg_aai2Sf!|;Mzh1Sag@HkymE9&d+rq|Za7tKVfQF&z8l_5 zhGI^uHhCN~@|P2_`!ly~dy~x&u{aLRyPOvFzY-)IH2Ak5;UXFjD}p`XsbK*3tHD(n zE#owu^d`FSQxu~;;0!r6P6zKom_@sVP+si6iMT#0;$yw#DkxPfuaU-e{Hvcj_joYi z#g1j~arm0|-Kf_A>14hJfbp(z$m=H`pnOSEm~&9cPrk(GxLrvOthS<50#^ z`!B{Mn{gcG7k?49xf~_FW?(r$Coy*4^<FUUn<#!@n2~bn{!ca%cA!ybHRS{*ecc*e}LgzjBd>##|o%-iZIU ztG`^h-y8A!M*M!7em_mwdw}8kZ6kgg>t7hx-!|g6jo65d=w#~8?qyEUmm7?Q8VyfD zGv}$;qjxlh6Ic6UNNYETKOr@8NP!Lm?A1$_FEzatcAS5vcx!hc@mfYC67Hs?qT=82 Hv;V&Up*HLV literal 0 HcmV?d00001 diff --git a/chapters/testing/images/jasmine_passing.jpg b/chapters/testing/images/jasmine_passing.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c3f2d7c2b65782b24759045224556d473fd80a4 GIT binary patch literal 26427 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 + + calculator = null + + beforeEach -> + calculator = new Calculator() + + it 'can add two positive numbers', -> + result = calculator.add 2, 3 + expect(result).toBe 5 + + it 'can handle negative number addition', -> + result = calculator.add -10, 5 + expect(result).toBe -5 + + it 'can subtract two positive numbers', -> + result = calculator.subtract 10, 6 + expect(result).toBe 4 + + it 'can handle negative number subtraction', -> + result = calculator.subtract 4, -6 + expect(result).toBe 10 + +{% endhighlight %} + +## Discussion + +This test describes our Calculator and tests that it can add and subtract positive and negative numbers. + +To test our specification (spec), we need to set up our test framework. Refer to the Jasmine website to download the framework. It's super easy. In the following example, we have our SpecRunner.html set up referencing the Jasmine JavaScript llibrary and css files. Our tests are also referenced. You can see the result of running out tests below. +All failing tests + +The tests are all failing, complaining that Calculator does not exist. Of course it doesn't, we haven't created it yet. Let's do that now. + +{% highlight coffeescript %} + +# calculator.coffee + +window.Calculator = class Calculator + +{% endhighlight %} + +When we re-run we see the following. + +Still failing, but better + +We now have 4 failures instead of our previous 8. That's a 50% improvment with 1 line of code. Not bad. + +Let's implement our methods and see if we can get these tests to pass. + +{% highlight coffeescript %} + +# calculator.coffee + +window.Calculator = class Calculator + add: (a, b) -> + a + b + + subtract: (a, b) -> + a - b + +{% endhighlight %} + +When we refresh we see they all pass. + +All passing \ No newline at end of file diff --git a/chapters/testing/behavior_testing_with_jasmine.md b/chapters/testing/testing_with_mocha.md similarity index 55% rename from chapters/testing/behavior_testing_with_jasmine.md rename to chapters/testing/testing_with_mocha.md index d1252c8..6973154 100644 --- a/chapters/testing/behavior_testing_with_jasmine.md +++ b/chapters/testing/testing_with_mocha.md @@ -1,9 +1,9 @@ --- layout: recipe -title: Behavior Testing with Jasmine +title: With Mocha chapter: Testing --- ## Problem -You have some CoffeeScript and you want to verify that it is working correctly. - +You have some CoffeeScript and you want to verify that it is working correctly. You decide to the use +Mocha test framework for your tests. \ No newline at end of file diff --git a/chapters/testing/testing_with_qunit.md b/chapters/testing/testing_with_qunit.md new file mode 100644 index 0000000..35fcca9 --- /dev/null +++ b/chapters/testing/testing_with_qunit.md @@ -0,0 +1,13 @@ +--- +layout: recipe +title: With QUint +chapter: Testing +--- +## Problem + +You have some CoffeeScript and you want to verify that it is working correctly. You decide to the use +QUnit test framework for your tests. + +## Solution +In this example, we are creating a simple calculator class that can add and subtract two numbers. We will begin by writing our tests in CoffeeScript. + diff --git a/chapters/testing/unit_testing_with_qunit.md b/chapters/testing/unit_testing_with_qunit.md deleted file mode 100644 index 1486765..0000000 --- a/chapters/testing/unit_testing_with_qunit.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: recipe -title: Unit Testing with QUint -chapter: Testing ---- -## Problem - -You have some CoffeeScript and you want to verify that it is working correctly. - From 341ff7b1bdd87acf2e5e09d17df29e97ccee4245 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 6 May 2012 15:08:43 -0300 Subject: [PATCH 144/267] Revisions to Jasmine testing recipe. --- chapters/testing/testing_with_jasmine.md | 142 ++++++++++++++++++++--- chapters/testing/testing_with_mocha.md | 2 +- chapters/testing/testing_with_qunit.md | 2 +- 3 files changed, 131 insertions(+), 15 deletions(-) diff --git a/chapters/testing/testing_with_jasmine.md b/chapters/testing/testing_with_jasmine.md index 5dae9aa..ae267f7 100644 --- a/chapters/testing/testing_with_jasmine.md +++ b/chapters/testing/testing_with_jasmine.md @@ -1,12 +1,17 @@ --- layout: recipe -title: With Jasmine +title: Testing with Jasmine chapter: Testing --- ## Problem -You have some CoffeeScript and you want to verify that it is working correctly. You decide to the use -Jasmine test framework for your tests. +You are writing a new calculator library using CoffeeScript code and you want to verify it functions as expected. You decide to use the Jasmine test framework. + +## Discussion + +When using the Jasmine test framework, you write tests in a spec that describe the expected functionality of the code to be tested. + +For example, we expect our calculator will be able to add and subtract both positive and negative numbers correctly. Our spec is listed below. {% highlight coffeescript %} @@ -14,37 +19,111 @@ Jasmine test framework for your tests. describe 'Calculator', -> - calculator = null - - beforeEach -> - calculator = new Calculator() - it 'can add two positive numbers', -> + calculator = new Calculator() result = calculator.add 2, 3 expect(result).toBe 5 it 'can handle negative number addition', -> + calculator = new Calculator() result = calculator.add -10, 5 expect(result).toBe -5 it 'can subtract two positive numbers', -> + calculator = new Calculator() result = calculator.subtract 10, 6 expect(result).toBe 4 it 'can handle negative number subtraction', -> + calculator = new Calculator() result = calculator.subtract 4, -6 expect(result).toBe 10 {% endhighlight %} -## Discussion -This test describes our Calculator and tests that it can add and subtract positive and negative numbers. +### Configuring Jasmine + +Before you can run your tests, you must download and configure Jasmine. This involves: +1. Download the latest Jasmine zip file +2. Create a spec and a spec/jasmine folder in your project +3. Extract the Jasmine files into the spec/jasmine folder +4. Create a test runner + +### Create a Test Runner + +Jasmine can run your tests within a web browser by using a spec runner HTML file. The spec runner is a simple HTML page that links the necessary JavaScript and CSS files for both Jasmine and your code. A sample is below. + +{% highlight html linenos %} + + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + + + + +{% endhighlight %} + +This spec runner can be downloaded from this GitHub gist. + +To use the SpecRunner.html, simply reference your compiled JavaScript files and compiled tests after jasmine.js and its dependencies. + +In the example, you can see we include our yet-to-be-developed calculator.js file (line 14) and the our compiled calculatorSpec.js file (line 17). + +## Running the Tests + +To run our tests, simply open SpecRunner.html in a web browser. In this example we see 4 failing specs with a total of 8 failures (below). -To test our specification (spec), we need to set up our test framework. Refer to the Jasmine website to download the framework. It's super easy. In the following example, we have our SpecRunner.html set up referencing the Jasmine JavaScript llibrary and css files. Our tests are also referenced. You can see the result of running out tests below. All failing tests -The tests are all failing, complaining that Calculator does not exist. Of course it doesn't, we haven't created it yet. Let's do that now. +It appears our tests are failing because Jasmine can not find the variable Calculator. That's because it has not been created yet. Let's do that now in a new file named js/calculator.coffee. + {% highlight coffeescript %} @@ -60,6 +139,8 @@ When we re-run we see the following. We now have 4 failures instead of our previous 8. That's a 50% improvment with 1 line of code. Not bad. +## Getting the Tests to Pass + Let's implement our methods and see if we can get these tests to pass. {% highlight coffeescript %} @@ -77,4 +158,39 @@ window.Calculator = class Calculator When we refresh we see they all pass. -All passing \ No newline at end of file +All passing + + +## Refactoring the Tests + +Now that our tests pass, we should look to see if our code or our test(s) can be refactored. In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initializaton code into a routine that runs before each test. + +{% highlight coffeescript %} + +describe 'Calculator', -> + calculator = null + + beforeEach -> + calculator = new Calculator() + + it 'can add two positive numbers', -> + result = calculator.add 2, 3 + expect(result).toBe 5 + + it 'can handle negative number addition', -> + result = calculator.add -10, 5 + expect(result).toBe -5 + + it 'can subtract two positive numbers', -> + result = calculator.subtract 10, 6 + expect(result).toBe 4 + + it 'can handle negative number subtraction', -> + result = calculator.subtract 4, -6 + expect(result).toBe 10 + +{% endhighlight %} + +When we recompile our spec and refresh the browser we see the tests still all pass. + +All passing diff --git a/chapters/testing/testing_with_mocha.md b/chapters/testing/testing_with_mocha.md index 6973154..97f92d6 100644 --- a/chapters/testing/testing_with_mocha.md +++ b/chapters/testing/testing_with_mocha.md @@ -1,6 +1,6 @@ --- layout: recipe -title: With Mocha +title: Testing with Mocha chapter: Testing --- ## Problem diff --git a/chapters/testing/testing_with_qunit.md b/chapters/testing/testing_with_qunit.md index 35fcca9..d96078f 100644 --- a/chapters/testing/testing_with_qunit.md +++ b/chapters/testing/testing_with_qunit.md @@ -1,6 +1,6 @@ --- layout: recipe -title: With QUint +title: Testing with QUint chapter: Testing --- ## Problem From 60008653fbcab8d3f24847441e9de74f5d9cdacb Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 6 May 2012 18:58:37 -0300 Subject: [PATCH 145/267] Minor wording changes to Jasmine test recipe. --- chapters/testing/testing_with_jasmine.md | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/chapters/testing/testing_with_jasmine.md b/chapters/testing/testing_with_jasmine.md index ae267f7..f0613f4 100644 --- a/chapters/testing/testing_with_jasmine.md +++ b/chapters/testing/testing_with_jasmine.md @@ -5,13 +5,13 @@ chapter: Testing --- ## Problem -You are writing a new calculator library using CoffeeScript code and you want to verify it functions as expected. You decide to use the Jasmine test framework. +You are writing a simple calculator using CoffeeScript and you want to verify it functions as expected. You decide to use the Jasmine test framework. ## Discussion -When using the Jasmine test framework, you write tests in a spec that describe the expected functionality of the code to be tested. +When using the Jasmine test framework, you write tests in a specification (spec) file that describes the expected functionality of the code to be tested. -For example, we expect our calculator will be able to add and subtract both positive and negative numbers correctly. Our spec is listed below. +For example, we expect our calculator will be able to add and subtract and will function correctly with both positive and negative numbers. Our spec is listed below. {% highlight coffeescript %} @@ -45,10 +45,10 @@ describe 'Calculator', -> ### Configuring Jasmine Before you can run your tests, you must download and configure Jasmine. This involves: -1. Download the latest Jasmine zip file -2. Create a spec and a spec/jasmine folder in your project -3. Extract the Jasmine files into the spec/jasmine folder -4. Create a test runner +1. downloading the latest Jasmine zip file; +2. creating a spec and a spec/jasmine folder in your project; +3. extracting the downloaded Jasmine files into the spec/jasmine folder; and +4. creating a test runner. ### Create a Test Runner @@ -114,15 +114,15 @@ This spec runner can be downloaded from this GitHub Running the Tests -To run our tests, simply open SpecRunner.html in a web browser. In this example we see 4 failing specs with a total of 8 failures (below). +To run our tests, simply open SpecRunner.html in a web browser. In our example we see 4 failing specs with a total of 8 failures (below). All failing tests -It appears our tests are failing because Jasmine can not find the variable Calculator. That's because it has not been created yet. Let's do that now in a new file named js/calculator.coffee. +It appears our tests are failing because Jasmine can not find the variable Calculator. That's because it has not been created yet. Let's do that now by creating a new file named js/calculator.coffee. {% highlight coffeescript %} @@ -133,11 +133,11 @@ window.Calculator = class Calculator {% endhighlight %} -When we re-run we see the following. +Compile calculator.coffee and refresh the browser to re-run the test suite. Still failing, but better -We now have 4 failures instead of our previous 8. That's a 50% improvment with 1 line of code. Not bad. +We now have 4 failures instead of our previous 8. That's a 50% improvment with only one line of code. ## Getting the Tests to Pass @@ -163,7 +163,7 @@ When we refresh we see they all pass. ## Refactoring the Tests -Now that our tests pass, we should look to see if our code or our test(s) can be refactored. In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initializaton code into a routine that runs before each test. +Now that our tests pass, we should look to see if our code or our test(s) can be refactored. In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initializaton code into a routine that runs before each test. Luckily Jasmine has a beforeEach function just for this purpose. {% highlight coffeescript %} From ddff1c78e3374848158923d5a0290b9b2cfe9e08 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 6 May 2012 19:00:05 -0300 Subject: [PATCH 146/267] Removed empty mocha and qunit recipes. --- chapters/testing/testing_with_mocha.md | 9 --------- chapters/testing/testing_with_qunit.md | 13 ------------- 2 files changed, 22 deletions(-) delete mode 100644 chapters/testing/testing_with_mocha.md delete mode 100644 chapters/testing/testing_with_qunit.md diff --git a/chapters/testing/testing_with_mocha.md b/chapters/testing/testing_with_mocha.md deleted file mode 100644 index 97f92d6..0000000 --- a/chapters/testing/testing_with_mocha.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: recipe -title: Testing with Mocha -chapter: Testing ---- -## Problem - -You have some CoffeeScript and you want to verify that it is working correctly. You decide to the use -Mocha test framework for your tests. \ No newline at end of file diff --git a/chapters/testing/testing_with_qunit.md b/chapters/testing/testing_with_qunit.md deleted file mode 100644 index d96078f..0000000 --- a/chapters/testing/testing_with_qunit.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -layout: recipe -title: Testing with QUint -chapter: Testing ---- -## Problem - -You have some CoffeeScript and you want to verify that it is working correctly. You decide to the use -QUnit test framework for your tests. - -## Solution -In this example, we are creating a simple calculator class that can add and subtract two numbers. We will begin by writing our tests in CoffeeScript. - From 78aa401e9fbdc1c1f2a75d6baaff80ca1f6ac858 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 6 May 2012 19:01:24 -0300 Subject: [PATCH 147/267] Another minor change to Jasmine recipe. --- chapters/testing/testing_with_jasmine.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/chapters/testing/testing_with_jasmine.md b/chapters/testing/testing_with_jasmine.md index f0613f4..79722ae 100644 --- a/chapters/testing/testing_with_jasmine.md +++ b/chapters/testing/testing_with_jasmine.md @@ -163,7 +163,11 @@ When we refresh we see they all pass. ## Refactoring the Tests -Now that our tests pass, we should look to see if our code or our test(s) can be refactored. In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initializaton code into a routine that runs before each test. Luckily Jasmine has a beforeEach function just for this purpose. +Now that our tests pass, we should look to see if our code or our test(s) can be refactored. + +In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initializaton code into a routine that runs before each test. + +Luckily Jasmine has a beforeEach function just for this purpose. {% highlight coffeescript %} From 10cd9826bd1c2978d8d9a5f132a36b3a52107211 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 6 May 2012 19:07:46 -0300 Subject: [PATCH 148/267] Corrected spelling mistakes. --- chapters/testing/testing_with_jasmine.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/testing/testing_with_jasmine.md b/chapters/testing/testing_with_jasmine.md index 79722ae..4dfffb9 100644 --- a/chapters/testing/testing_with_jasmine.md +++ b/chapters/testing/testing_with_jasmine.md @@ -122,7 +122,7 @@ To run our tests, simply open SpecRunner.html in a web browser. In our example All failing tests -It appears our tests are failing because Jasmine can not find the variable Calculator. That's because it has not been created yet. Let's do that now by creating a new file named js/calculator.coffee. +It appears our tests are failing because Jasmine cannot find the variable Calculator. That's because it has not been created yet. Let's do that now by creating a new file named js/calculator.coffee. {% highlight coffeescript %} @@ -137,7 +137,7 @@ Compile calculator.coffee and refresh the browser to re-run the test suite. Still failing, but better -We now have 4 failures instead of our previous 8. That's a 50% improvment with only one line of code. +We now have 4 failures instead of our previous 8. That's a 50% improvement with only one line of code. ## Getting the Tests to Pass @@ -165,7 +165,7 @@ When we refresh we see they all pass. Now that our tests pass, we should look to see if our code or our test(s) can be refactored. -In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initializaton code into a routine that runs before each test. +In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initialization code into a routine that runs before each test. Luckily Jasmine has a beforeEach function just for this purpose. From 438cb4d7c66af16594f65080790a9099d35befe7 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Sun, 6 May 2012 21:15:44 -0300 Subject: [PATCH 149/267] Added new moon phase calculator recipe. --- .../dates_and_times/moon-phase-for-date.md | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 chapters/dates_and_times/moon-phase-for-date.md diff --git a/chapters/dates_and_times/moon-phase-for-date.md b/chapters/dates_and_times/moon-phase-for-date.md new file mode 100644 index 0000000..2c63e61 --- /dev/null +++ b/chapters/dates_and_times/moon-phase-for-date.md @@ -0,0 +1,136 @@ +--- +layout: recipe +title: Calculate Phase of the Moon for a Date +chapter: Dates and Times +--- +## Problem + +You want to find the current phase of the moon. + +## Solution + +The following code provides a method to calcualate the phase of the moon for a given date. + +{% highlight coffeescript %} +# moonPhase.coffee + +# Moon-phase calculator +# Roger W. Sinnott, Sky & Telescope, June 16, 2006 +# http://www.skyandtelescope.com/observing/objects/javascript/moon_phases +# +# Translated to CoffeeScript by Mike Hatfield @WebCoding4Fun + +proper_ang = (big) -> + tmp = 0 + if big > 0 + tmp = big / 360.0 + tmp = (tmp - (~~tmp)) * 360.0 + else + tmp = Math.ceil(Math.abs(big / 360.0)) + tmp = big + tmp * 360.0 + + tmp + +jdn = (date) -> + month = date.getMonth() + day = date.getDate() + year = date.getFullYear() + zone = date.getTimezoneOffset() / 1440 + + mm = month + dd = day + yy = year + + yyy = yy + mmm = mm + if mm < 3 + yyy = yyy - 1 + mmm = mm + 12 + + day = dd + zone + 0.5 + a = ~~( yyy / 100 ) + b = 2 - a + ~~( a / 4 ) + jd = ~~( 365.25 * yyy ) + ~~( 30.6001 * ( mmm+ 1 ) ) + day + 1720994.5 + jd + b if jd > 2299160.4999999 + +moonElong = (jd) -> + dr = Math.PI / 180 + rd = 1 / dr + meeDT = Math.pow((jd - 2382148), 2) / (41048480 * 86400) + meeT = (jd + meeDT - 2451545.0) / 36525 + meeT2 = Math.pow(meeT, 2) + meeT3 = Math.pow(meeT, 3) + meeD = 297.85 + (445267.1115 * meeT) - (0.0016300 * meeT2) + (meeT3 / 545868) + meeD = (proper_ang meeD) * dr + meeM1 = 134.96 + (477198.8676 * meeT) + (0.0089970 * meeT2) + (meeT3 / 69699) + meeM1 = (proper_ang meeM1) * dr + meeM = 357.53 + (35999.0503 * meeT) + meeM = (proper_ang meeM) * dr + + elong = meeD * rd + 6.29 * Math.sin( meeM1 ) + elong = elong - 2.10 * Math.sin( meeM ) + elong = elong + 1.27 * Math.sin( 2*meeD - meeM1 ) + elong = elong + 0.66 * Math.sin( 2*meeD ) + elong = proper_ang elong + elong = Math.round elong + + moonNum = ( ( elong + 6.43 ) / 360 ) * 28 + moonNum = ~~( moonNum ) + + if moonNum is 28 then 0 else moonNum + +getMoonPhase = (age) -> + moonPhase = "new Moon" + moonPhase = "first quarter" if age > 3 and age < 11 + moonPhase = "full Moon" if age > 10 and age < 18 + moonPhase = "last quarter" if age > 17 and age < 25 + + if ((age is 1) or (age is 8) or (age is 15) or (age is 22)) + moonPhase = "1 day past " + moonPhase + + if ((age is 2) or (age is 9) or (age is 16) or (age is 23)) + moonPhase = "2 days past " + moonPhase + + if ((age is 3) or (age is 1) or (age is 17) or (age is 24)) + moonPhase = "3 days past " + moonPhase + + if ((age is 4) or (age is 11) or (age is 18) or (age is 25)) + moonPhase = "3 days before " + moonPhase + + if ((age is 5) or (age is 12) or (age is 19) or (age is 26)) + moonPhase = "2 days before " + moonPhase + + if ((age is 6) or (age is 13) or (age is 20) or (age is 27)) + moonPhase = "1 day before " + moonPhase + + moonPhase + +MoonPhase = exports? and exports or @MoonPhase = {} + +class MoonPhase.Calculator + getMoonDays: (date) -> + jd = jdn date + moonElong jd + + getMoonPhase: (date) -> + jd = jdn date + getMoonPhase( moonElong jd ) +{% endhighlight %} + +## Discussion + +This code exposes a MoonPhase Calculator object with two methods. Calculator -> getMoonPhase will return a text representation of the lunar phase for the date provided. + +This can be used in both the browser and Node.js. + +{% highlight console %} +$ node +> var MoonPhase = require('./moonPhase.js'); + undefined +> var calc = new MoonPhase.Calculator(); + undefined +> calc.getMoonPhase(new Date()); + 'full moon' +> calc.getMoonPhase(new Date(1972, 6, 30)); + '3 days before last quarter' +{% endhighlight %} \ No newline at end of file From 02b3e00125483db66adc56e49e24d0f0443eeabb Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 8 May 2012 18:50:10 -0300 Subject: [PATCH 150/267] Title-cased all recipes as per issue 45. --- chapters/arrays/removing-duplicate-elements-from-arrays.md | 2 +- chapters/arrays/zip-function.md | 2 +- chapters/classes_and_objects/cloning.md | 2 +- chapters/classes_and_objects/object-literal.md | 2 +- chapters/classes_and_objects/type-function.md | 2 +- chapters/dates_and_times/date-of-easter.md | 2 +- chapters/dates_and_times/date-of-thanksgiving.md | 2 +- chapters/dates_and_times/days-between-two-dates.md | 2 +- chapters/functions/parentheses.md | 2 +- chapters/jquery/callback-bindings-jquery.md | 2 +- chapters/jquery/plugin.md | 2 +- chapters/math/fast-fibonacci.md | 2 +- chapters/math/random-integer.md | 2 +- .../replacing-html-tags-with-html-named-entities.md | 2 +- chapters/regular_expressions/replacing-substrings.md | 2 +- chapters/regular_expressions/searching-for-substrings.md | 2 +- chapters/strings/trimming-whitespace-from-a-string.md | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/chapters/arrays/removing-duplicate-elements-from-arrays.md b/chapters/arrays/removing-duplicate-elements-from-arrays.md index 4e079ad..545ae17 100644 --- a/chapters/arrays/removing-duplicate-elements-from-arrays.md +++ b/chapters/arrays/removing-duplicate-elements-from-arrays.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Removing duplicate elements from Arrays +title: Removing Duplicate Elements from Arrays chapter: Arrays --- ## Problem diff --git a/chapters/arrays/zip-function.md b/chapters/arrays/zip-function.md index 1271acb..f2a1091 100644 --- a/chapters/arrays/zip-function.md +++ b/chapters/arrays/zip-function.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Python-like zip function +title: Python-like Zip Function chapter: Arrays --- ## Problem diff --git a/chapters/classes_and_objects/cloning.md b/chapters/classes_and_objects/cloning.md index 3b1ceba..7325e03 100644 --- a/chapters/classes_and_objects/cloning.md +++ b/chapters/classes_and_objects/cloning.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Cloning an object (deep copy) +title: Cloning an Object (Deep Copy) chapter: Classes and Objects --- ## Problem diff --git a/chapters/classes_and_objects/object-literal.md b/chapters/classes_and_objects/object-literal.md index 616f7c4..7d9d2f0 100644 --- a/chapters/classes_and_objects/object-literal.md +++ b/chapters/classes_and_objects/object-literal.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Create an object literal if it does not already exist +title: Create an Object Literal if It Does Not Already Exist chapter: Classes and Objects --- ## Problem diff --git a/chapters/classes_and_objects/type-function.md b/chapters/classes_and_objects/type-function.md index 6acf6f5..6a7f9b8 100644 --- a/chapters/classes_and_objects/type-function.md +++ b/chapters/classes_and_objects/type-function.md @@ -1,6 +1,6 @@ --- layout: recipe -title: A CoffeeScript type function +title: A CoffeeScript Type Function chapter: Classes and Objects --- ## Problem diff --git a/chapters/dates_and_times/date-of-easter.md b/chapters/dates_and_times/date-of-easter.md index 1ce890e..09ad7bb 100644 --- a/chapters/dates_and_times/date-of-easter.md +++ b/chapters/dates_and_times/date-of-easter.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Calculate the date of Easter Sunday +title: Calculate the Date of Easter Sunday chapter: Dates and Times --- ## Problem diff --git a/chapters/dates_and_times/date-of-thanksgiving.md b/chapters/dates_and_times/date-of-thanksgiving.md index 7df1584..5438244 100644 --- a/chapters/dates_and_times/date-of-thanksgiving.md +++ b/chapters/dates_and_times/date-of-thanksgiving.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Calculate the date of Thanksgiving (USA and Canada) +title: Calculate the Date of Thanksgiving (USA and Canada) chapter: Dates and Times --- ## Problem diff --git a/chapters/dates_and_times/days-between-two-dates.md b/chapters/dates_and_times/days-between-two-dates.md index f89b7f7..0f5c789 100644 --- a/chapters/dates_and_times/days-between-two-dates.md +++ b/chapters/dates_and_times/days-between-two-dates.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Get Days Between two Dates +title: Get Days Between Two Dates chapter: Dates and Times --- ## Problem diff --git a/chapters/functions/parentheses.md b/chapters/functions/parentheses.md index 8256056..3988247 100644 --- a/chapters/functions/parentheses.md +++ b/chapters/functions/parentheses.md @@ -1,6 +1,6 @@ --- layout: recipe -title: When Function Parentheses are not Optional +title: When Function Parentheses Are Not Optional chapter: Functions --- ## Problem diff --git a/chapters/jquery/callback-bindings-jquery.md b/chapters/jquery/callback-bindings-jquery.md index a89ffc7..242927d 100644 --- a/chapters/jquery/callback-bindings-jquery.md +++ b/chapters/jquery/callback-bindings-jquery.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Callback bindings # using => instead of -> +title: Callback Bindings # using => instead of -> chapter: jQuery --- ## Problem diff --git a/chapters/jquery/plugin.md b/chapters/jquery/plugin.md index bf08b90..481d00e 100644 --- a/chapters/jquery/plugin.md +++ b/chapters/jquery/plugin.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Create a jQuery plugin +title: Create a jQuery Plugin chapter: jQuery --- ## Problem diff --git a/chapters/math/fast-fibonacci.md b/chapters/math/fast-fibonacci.md index 9c285bb..024773f 100644 --- a/chapters/math/fast-fibonacci.md +++ b/chapters/math/fast-fibonacci.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Faster Fibonacci algorithm +title: Faster Fibonacci Algorithm chapter: Math --- ## Problem diff --git a/chapters/math/random-integer.md b/chapters/math/random-integer.md index bcc645c..d8726ff 100644 --- a/chapters/math/random-integer.md +++ b/chapters/math/random-integer.md @@ -1,6 +1,6 @@ --- layout: recipe -title: A random integer function +title: A Random Integer Function chapter: Math --- ## Problem diff --git a/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md b/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md index cbd23a8..4b975d0 100644 --- a/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md +++ b/chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Replacing HTML tags with HTML named entities +title: Replacing HTML Tags with HTML Named Entities chapter: Regular Expressions --- ## Problem diff --git a/chapters/regular_expressions/replacing-substrings.md b/chapters/regular_expressions/replacing-substrings.md index cfd0978..25468ab 100644 --- a/chapters/regular_expressions/replacing-substrings.md +++ b/chapters/regular_expressions/replacing-substrings.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Replacing substrings +title: Replacing Substrings chapter: Regular Expressions --- ## Problem diff --git a/chapters/regular_expressions/searching-for-substrings.md b/chapters/regular_expressions/searching-for-substrings.md index fd63b5f..1928591 100644 --- a/chapters/regular_expressions/searching-for-substrings.md +++ b/chapters/regular_expressions/searching-for-substrings.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Searching for substrings +title: Searching for Substrings chapter: Regular Expressions --- ## Problem diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md index bf92de6..bb0ddc6 100644 --- a/chapters/strings/trimming-whitespace-from-a-string.md +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -1,6 +1,6 @@ --- layout: recipe -title: Trimming whitespace from a String +title: Trimming Whitespace from a String chapter: Strings --- ## Problem From 9c9df33b66f74ab9bd2393ac53bb7f5e0185f8b5 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 15 May 2012 20:57:04 -0300 Subject: [PATCH 151/267] Added Ajax Without jQuery recipe. --- chapters/ajax/ajax_request_without_jquery.md | 96 ++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 chapters/ajax/ajax_request_without_jquery.md diff --git a/chapters/ajax/ajax_request_without_jquery.md b/chapters/ajax/ajax_request_without_jquery.md new file mode 100644 index 0000000..8d04d2b --- /dev/null +++ b/chapters/ajax/ajax_request_without_jquery.md @@ -0,0 +1,96 @@ +--- +layout: recipe +title: AJAX Request Without jQuery +chapter: AJAX +--- +## Problem + +You want to load data from your server via AJAX without using the jQuery library. + +## Solution + +You will use the native XMLHttpRequest object. + +Begin by making sure the XMLHttpRequest object exsits. + +{% highlight coffeescript %} +# XMLHttpRequest.coffee + +if (typeof @XMLHttpRequest == "undefined") + console.log 'XMLHttpRequest is undefined' + + @XMLHttpRequest = -> + try + return new ActiveXObject("Msxml2.XMLHTTP.6.0") + catch error + try + return new ActiveXObject("Msxml2.XMLHTTP.3.0") + catch error + try + return new ActiveXObject("Microsoft.XMLHTTP") + catch error + throw new Error("This browser does not support XMLHttpRequest.") +{% endhighlight %} + +We can also set up some status codes. + +{% highlight coffeescript %} +READYSTATE_UNINITIALIZED = 0 +READYSTATE_LOADING = 1 +READYSTATE_LOADED = 2 +READYSTATE_INTERACTIVE = 3 +READYSTATE_COMPLETE = 4 +{% endhighlight %} + +Let's set up a simple test HTML page with a button. + +{% highlight html %} + + + + + XMLHttpRequest Tester + + +

          XMLHttpRequest Tester

          + + + + + +{% endhighlight %} + +Let's finish our XMLHttpRequest.coffee by adding a 'click' event listener then create our XMLHttpRequest object. + +{% highlight coffeescript linenos %} +loadDataFromServer = -> + req = new XMLHttpRequest() + + req.addEventListener 'readystatechange', -> + if req.readyState is READYSTATE_COMPLETE + if req.status is 200 or req.status is 304 + data = eval '(' + req.responseText + ')' + console.log 'data message: ', data.message + else + console.log 'Error loading data...' + + req.open 'GET', 'data.json', false + req.send() + +loadDataButton = document.getElementById 'loadDataButton' +loadDataButton.addEventListener 'click', loadDataFromServer, false +{% endhighlight %} + +## Discussion + +In the above code, we create a new XMLHttpRequest instance on line 2. Then, we add an event listener for the readystatechange event. This fires whenever the XMLHttpRequest readyState changes. + +In the event handler we check to see if the readyState = READYSTATE_COMPLETE (value of 4). Then, we check to see if the status is either 200 or 304, both values are success indicators. + +If the request was indeed successful, we eval the JSON returned from the server and assign it to a data variable. At this point, we can use the returned data in any way we need to. + +The last thing we need to do is actually make our request. + +Line 12 opens a 'GET' request to retreive the data.json file. + +Line 13 sends our request to the server. From 1d4c372e16454624ffe2c717c105301b4883b2d3 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 15 May 2012 20:57:53 -0300 Subject: [PATCH 152/267] Closed highlighting section with missing {% endhighlight %}. --- chapters/math/random-integer.md | 1 + 1 file changed, 1 insertion(+) diff --git a/chapters/math/random-integer.md b/chapters/math/random-integer.md index bcc645c..dfcb9f2 100644 --- a/chapters/math/random-integer.md +++ b/chapters/math/random-integer.md @@ -25,6 +25,7 @@ randomInt = (lower, upper=0) -> (randomInt(1, 10) for i in [0...10]) # => [7,3,9,1,8,5,4,10,10,8] +{% endhighlight %} ## Discussion From c9c11fb10ae72c388063b2adcdbf588a6f7c1c9f Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 15 May 2012 20:58:17 -0300 Subject: [PATCH 153/267] Added Ajax chapter. --- chapters/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapters/index.html b/chapters/index.html index 778f38e..59ec176 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -11,6 +11,7 @@ - Functions - Metaprogramming - jQuery +- Ajax - Regular Expressions - Networking - Design Patterns @@ -38,4 +39,4 @@

          {{ chapter }}

          {% endfor %} - \ No newline at end of file +
        \ No newline at end of file From f783b8075ddbde69430e107ed8b7169ed05dbc86 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 15 May 2012 20:58:41 -0300 Subject: [PATCH 154/267] Updated authors and wanted-recipes removing completed recipes. --- authors.md | 1 + wanted-recipes.md | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/authors.md b/authors.md index c759d8b..b1eef97 100644 --- a/authors.md +++ b/authors.md @@ -19,6 +19,7 @@ The following people are totally rad and awesome because they have contributed r * João Moreno *coffeecb @joaomoreno .com* * Jeff Pickhardt *pickhardt (at) gmail (dot) com* * Frederic Hemberger +* Mike Hatfield *oakraven13@gmail.com* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/wanted-recipes.md b/wanted-recipes.md index 155fea1..64eec51 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -47,8 +47,7 @@ evens.every even ## Dates and Times -* Calculating the phase of the moon -* Number of days between two dates +* Empty ## Math @@ -107,8 +106,7 @@ foo 1, 2, 3 * Creational Patterns * Abstract Factory - * Prototype - * Singleton + * Prototype * Structural Patterns * Adapter From 457b3a44327db857b191efed8e1dcad1023f80f4 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 15 May 2012 22:50:19 -0300 Subject: [PATCH 155/267] Added Ajax chapter. --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 0b997af..3fb3c07 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@ - Functions - Metaprogramming - jQuery +- Ajax - Regular Expressions - Networking - Design Patterns @@ -42,4 +43,4 @@

        {{ chapter }}

        {% endfor %} - \ No newline at end of file +
      \ No newline at end of file From c943705839c93ccd0f933a32edebce7cd57345da Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 15 May 2012 22:51:07 -0300 Subject: [PATCH 156/267] Simplified code examples and reworked recipe text. --- chapters/ajax/ajax_request_without_jquery.md | 86 +++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/chapters/ajax/ajax_request_without_jquery.md b/chapters/ajax/ajax_request_without_jquery.md index 8d04d2b..5956548 100644 --- a/chapters/ajax/ajax_request_without_jquery.md +++ b/chapters/ajax/ajax_request_without_jquery.md @@ -1,7 +1,7 @@ --- layout: recipe -title: AJAX Request Without jQuery -chapter: AJAX +title: Ajax Request Without jQuery +chapter: Ajax --- ## Problem @@ -11,37 +11,6 @@ You want to load data from your server via AJAX without using the jQuery library You will use the native XMLHttpRequest object. -Begin by making sure the XMLHttpRequest object exsits. - -{% highlight coffeescript %} -# XMLHttpRequest.coffee - -if (typeof @XMLHttpRequest == "undefined") - console.log 'XMLHttpRequest is undefined' - - @XMLHttpRequest = -> - try - return new ActiveXObject("Msxml2.XMLHTTP.6.0") - catch error - try - return new ActiveXObject("Msxml2.XMLHTTP.3.0") - catch error - try - return new ActiveXObject("Microsoft.XMLHTTP") - catch error - throw new Error("This browser does not support XMLHttpRequest.") -{% endhighlight %} - -We can also set up some status codes. - -{% highlight coffeescript %} -READYSTATE_UNINITIALIZED = 0 -READYSTATE_LOADING = 1 -READYSTATE_LOADED = 2 -READYSTATE_INTERACTIVE = 3 -READYSTATE_COMPLETE = 4 -{% endhighlight %} - Let's set up a simple test HTML page with a button. {% highlight html %} @@ -60,15 +29,25 @@ Let's set up a simple test HTML page with a button. {% endhighlight %} -Let's finish our XMLHttpRequest.coffee by adding a 'click' event listener then create our XMLHttpRequest object. +When the button is clicked, we want to send an Ajax request to the server to retrieve some data. For this sample, we have a small JSON file. + +{% highlight javascript %} +// data.json +{ + message: "Hello World" +} +{% endhighlight %} + +Next, create the CoffeeScript file to hold the page logic. The code in this file creates a function to be called when the Load Data button is clicked. {% highlight coffeescript linenos %} +# XMLHttpRequest.coffee loadDataFromServer = -> req = new XMLHttpRequest() req.addEventListener 'readystatechange', -> - if req.readyState is READYSTATE_COMPLETE - if req.status is 200 or req.status is 304 + if req.readyState is 4 # ReadyState Compelte + if req.status is 200 or req.status is 304 # Success result codes data = eval '(' + req.responseText + ')' console.log 'data message: ', data.message else @@ -83,14 +62,41 @@ loadDataButton.addEventListener 'click', loadDataFromServer, false ## Discussion -In the above code, we create a new XMLHttpRequest instance on line 2. Then, we add an event listener for the readystatechange event. This fires whenever the XMLHttpRequest readyState changes. +In the above code we essentially grab a handle to the button in our HTML (line 16) and add a *click* event listener (line 17). In our event listener, we define our callback function as loadDataFromServer. + +We define our loadDataFromServer callback beginning on line 2. -In the event handler we check to see if the readyState = READYSTATE_COMPLETE (value of 4). Then, we check to see if the status is either 200 or 304, both values are success indicators. +We create a XMLHttpRequest request object (line 3) and add a *readystatechange* event handler. This fires whenever the request's readyState changes. + +In the event handler we check to see if the readyState = 4, indicating the request has completed. Then, we check the request status value. Both 200 or 304 represent a succsessful request. Anything else represents an error condition. If the request was indeed successful, we eval the JSON returned from the server and assign it to a data variable. At this point, we can use the returned data in any way we need to. The last thing we need to do is actually make our request. -Line 12 opens a 'GET' request to retreive the data.json file. +Line 13 opens a 'GET' request to retreive the data.json file. + +Line 14 sends our request to the server. + +## Older Browser Support + +If your application needs to target older versions of Internet Explorer, you will need to ensure the XMLHttpRequest object exists. You can do this by including this code before creating the XMLHttpRequest instance. + +{% highlight coffeescript %} +if (typeof @XMLHttpRequest == "undefined") + console.log 'XMLHttpRequest is undefined' + @XMLHttpRequest = -> + try + return new ActiveXObject("Msxml2.XMLHTTP.6.0") + catch error + try + return new ActiveXObject("Msxml2.XMLHTTP.3.0") + catch error + try + return new ActiveXObject("Microsoft.XMLHTTP") + catch error + throw new Error("This browser does not support XMLHttpRequest.") +{% endhighlight %} + +This code ensures the XMLHttpRequest object is available in the global namespace. -Line 13 sends our request to the server. From 92a105c835ddb3b7528ae58cc28dee0f740daa4f Mon Sep 17 00:00:00 2001 From: Peter Hellberg Date: Wed, 6 Jun 2012 23:02:52 +0200 Subject: [PATCH 157/267] Corrected a minor bug as pointed out by @MaskRay --- chapters/design_patterns/strategy.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/design_patterns/strategy.md b/chapters/design_patterns/strategy.md index 6faffcd..415f72d 100644 --- a/chapters/design_patterns/strategy.md +++ b/chapters/design_patterns/strategy.md @@ -16,8 +16,8 @@ Given an unsorted list, for example, we can change the sorting algorithm under d ###The base class: {% highlight coffeescript %} -StringSorter = (@algorithm) -> - sort: (list) -> @algorithm list +StringSorter = (algorithm) -> + sort: (list) -> algorithm list {% endhighlight %} ###The strategies: @@ -26,7 +26,7 @@ StringSorter = (@algorithm) -> bubbleSort = (list) -> anySwaps = false swapPass = -> - for r in [0..list.length-1] + for r in [0..list.length-2] if list[r] > list[r+1] anySwaps = true [list[r], list[r+1]] = [list[r+1], list[r]] From 24691c4d9f4ea575a6b1595e0680dc7f4222d2a1 Mon Sep 17 00:00:00 2001 From: Aleksandr Motsjonov Date: Mon, 18 Jun 2012 00:33:35 +0300 Subject: [PATCH 158/267] We need to call trim() function --- chapters/strings/trimming-whitespace-from-a-string.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/strings/trimming-whitespace-from-a-string.md b/chapters/strings/trimming-whitespace-from-a-string.md index bb0ddc6..e7e9ecd 100644 --- a/chapters/strings/trimming-whitespace-from-a-string.md +++ b/chapters/strings/trimming-whitespace-from-a-string.md @@ -39,7 +39,7 @@ Opera, Firefox and Chrome all have a native string prototype `trim` method, and {% highlight coffeescript %} unless String::trim then String::trim = -> @replace /^\s+|\s+$/g, "" -" padded string ".trim +" padded string ".trim() # => 'padded string' {% endhighlight %} From 17d6e4919e881bbf9dc0735d93a28c4d92ef2b75 Mon Sep 17 00:00:00 2001 From: Anton Rissanen Date: Sat, 23 Jun 2012 16:40:46 +0300 Subject: [PATCH 159/267] New title: Creating a dictionary Object from an Array --- authors.md | 1 + ...ating-a-dictionary-object-from-an-array.md | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 chapters/arrays/creating-a-dictionary-object-from-an-array.md diff --git a/authors.md b/authors.md index b1eef97..6b21f9b 100644 --- a/authors.md +++ b/authors.md @@ -20,6 +20,7 @@ The following people are totally rad and awesome because they have contributed r * Jeff Pickhardt *pickhardt (at) gmail (dot) com* * Frederic Hemberger * Mike Hatfield *oakraven13@gmail.com* +* [Anton Rissanen](http://github.com/antris) *hello@anton.fi* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/arrays/creating-a-dictionary-object-from-an-array.md b/chapters/arrays/creating-a-dictionary-object-from-an-array.md new file mode 100644 index 0000000..cf8dd53 --- /dev/null +++ b/chapters/arrays/creating-a-dictionary-object-from-an-array.md @@ -0,0 +1,63 @@ +--- +layout: recipe +title: Creating a dictionary Object from an Array +chapter: Arrays +--- +## Problem + +You have an Array of Objects, such as: + +{% highlight coffeescript %} +cats = [ + { + name: "Bubbles" + age: 1 + }, + { + name: "Sparkle" + favoriteFood: "tuna" + } +] +{% endhighlight %} + +But you want to access it as a dictionary by key, like `cats["Bubbles"]`. + +## Solution + +You need to convert your array into an Object. Use reduce for this. + +{% highlight coffeescript %} +# key = The key by which to index the dictionary +Array::toDict = (key) -> + @reduce ((dict, obj) -> dict[ obj[key] ] = obj if obj[key]?; return dict), {} +{% endhighlight %} + +To use this: + +{% highlight coffeescript %} + catsDict = cats.toDict('name') + catsDict["Bubbles"] + # => { age: 1, name: "Bubbles" } +{% endhighlight %} + +## Discussion + +Alternatively, you can use an Array comprehension: + +{% highlight coffeescript %} +Array::toDict = (key) -> + dict = {} + dict[obj[key]] = obj for obj in this when obj[key]? + dict +{% endhighlight %} + +If you use Underscore.js, you can create a mixin: + +{% highlight coffeescript %} +_.mixin toDict: (arr, key) -> + throw new Error('_.toDict takes an Array') unless _.isArray arr + _.reduce arr, ((dict, obj) -> dict[ obj[key] ] = obj if obj[key]?; return dict), {} +catsDict = _.toDict(cats, 'name') +catsDict["Sparkle"] +# => { favoriteFood: "tuna", name: "Sparkle" } +{% endhighlight %} \ No newline at end of file From bf78e5ddfdf931ec668ff79d450601c4c6cf9d62 Mon Sep 17 00:00:00 2001 From: Amsul Date: Tue, 28 Aug 2012 15:11:50 -0400 Subject: [PATCH 160/267] design updated --- _layouts/chapter.html | 107 +++++- _layouts/default.html | 103 +++++- _layouts/recipe.html | 111 ++++-- authors.md | 1 + chapters/index.html | 39 ++- css/style.css | 785 ++++++++++++++++++++++++++++++++++++++++++ fonts/entypo.svg | 198 +++++++++++ fonts/entypo.ttf | Bin 0 -> 29264 bytes images/favicon.ico | Bin 0 -> 1232 bytes images/title.png | Bin 0 -> 4453 bytes index.html | 39 +-- 11 files changed, 1267 insertions(+), 116 deletions(-) create mode 100644 css/style.css create mode 100644 fonts/entypo.svg create mode 100644 fonts/entypo.ttf create mode 100644 images/favicon.ico create mode 100644 images/title.png diff --git a/_layouts/chapter.html b/_layouts/chapter.html index ae1d592..7298575 100644 --- a/_layouts/chapter.html +++ b/_layouts/chapter.html @@ -1,29 +1,98 @@ +--- +chapters: +- Syntax +- Classes and Objects +- Strings +- Arrays +- Dates and Times +- Math +- Functions +- Metaprogramming +- jQuery +- Ajax +- Regular Expressions +- Networking +- Design Patterns +- Databases +- Testing +--- + + + CoffeeScript Cookbook » {{ page.title }} - + + -
      -

      CoffeeScript Cookbook

      - -
      -
      -

      {{ page.title }}

      - {{ content }} + +
      + +
      +

      óCoffeeScript Cookbook

      +
      + +
      + +
      +

      {{ page.title }}

      + {{ content }} +
      + + + +
      +
      - + + + diff --git a/_layouts/default.html b/_layouts/default.html index d4e3a13..369e873 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -1,28 +1,95 @@ +--- +chapters: +- Syntax +- Classes and Objects +- Strings +- Arrays +- Dates and Times +- Math +- Functions +- Metaprogramming +- jQuery +- Ajax +- Regular Expressions +- Networking +- Design Patterns +- Databases +- Testing +--- + + + CoffeeScript Cookbook » {{ page.title }} - + + -
      -

      CoffeeScript Cookbook

      - -
      -
      - {{ content }} + +
      + +
      +

      óCoffeeScript Cookbook

      +
      + +
      + +
      {{ content }}
      + +
      +

      Don't see a recipe you want? See an entire missing chapter? Add it yourself by reading the Contributor's Guide, or request it by adding it to Wanted Recipes.

      + +
      + +
      +
      -
      -

      Don't see a recipe you want? See an entire missing chapter? Add it yourself by reading the Contributor's Guide, or request it by adding it to Wanted Recipes.

      - -
      + + + diff --git a/_layouts/recipe.html b/_layouts/recipe.html index 22b23c1..39ada1a 100644 --- a/_layouts/recipe.html +++ b/_layouts/recipe.html @@ -1,34 +1,97 @@ +--- +chapters: +- Syntax +- Classes and Objects +- Strings +- Arrays +- Dates and Times +- Math +- Functions +- Metaprogramming +- jQuery +- Ajax +- Regular Expressions +- Networking +- Design Patterns +- Databases +- Testing +--- + + + CoffeeScript Cookbook » {{ page.title }} - + + -
      -

      CoffeeScript Cookbook

      - -
      -
      -

      {{ page.title }}

      - {{ content }} + +
      + +
      +

      óCoffeeScript Cookbook

      +
      + +
      + +
      +

      {{ page.title }}

      + {{ content }} +
      + +
      +

      Is this recipe wrong, incomplete, or non idiomatic? Help fix it by reading the Contributor's Guide!

      + +
      +
      +
      - + + + diff --git a/authors.md b/authors.md index 6b21f9b..be4922c 100644 --- a/authors.md +++ b/authors.md @@ -37,4 +37,5 @@ The following people are totally rad and awesome because they have contributed r The following people are astonishingly rad and awesome because they did great design for the site! +* [Amsul](http://github.com/amsul) reach@amsul.ca * ...You! Check out the [contributing](/contributing) section and get cracking! diff --git a/chapters/index.html b/chapters/index.html index 59ec176..9e2f0e9 100644 --- a/chapters/index.html +++ b/chapters/index.html @@ -19,24 +19,27 @@ - Testing --- -
        - {% for chapter in page.chapters %} - {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} - {% capture indexurl %}{{ url }}/index.html{% endcapture %} +
        -
      1. -

        {{ chapter }}

        + -
          - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
        • {{ page.title }}
        • - {% endunless %} - {% endif %} - {% endfor %} -
        -
      2. + - {% endfor %} -
      \ No newline at end of file + \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..217e53e --- /dev/null +++ b/css/style.css @@ -0,0 +1,785 @@ +/* + Author: Amsul - http://github.com/amsul + Version: 0.8 beta + Last Updated: 28 August, 2012 +*/ + + + +/* + Font-face +======================================================================== */ + +@font-face { + font-family: 'Entypo'; + src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'), + url(http://github.com/fonts/entypo.ttf) format('truetype'), + url(http://github.com/fonts/entypo.svg#EntypoRegular) format('svg'); +} + + + + + + +/* + HTML5 element display +======================================================================== */ + +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } +audio, canvas, video { display: inline-block; } +audio:not([controls]) { display: none; } + + + +/* + Base +======================================================================== */ + +html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } + +body { margin: 0; font-size: 16px; text-align: left; line-height: 1.5; } +body, button, input, select, textarea { font-family: "Lucida Grande", "Lucida Sans Unicode", sans-serif; font-weight: 200; color: #424242; } + + +::-moz-selection { background: #b3d4fc; text-shadow: none; } +::selection { background: #b3d4fc; text-shadow: none; } + + + + +/* + Links +======================================================================== */ + +a { + color: #27ace3; + text-decoration: underline; +} + +a:visited { +} + +a:hover, +a:active { + cursor: pointer; +} + +a:focus { outline: thin dotted; } + + +/* + Typography +======================================================================== */ + +p, div { word-wrap: break-word; } + +abbr[title] { border-bottom: 1px dotted; } + +b, strong { font-weight: bold; } + +blockquote { margin: 1em 0 40px; } + +dfn, em { font-style: italic; } + +hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } + +ins { background: #ff9; color: #000; text-decoration: none; } + +mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } + +pre, code, kbd, samp { font-family: 'Courier', 'Monaco', monospace, serif; font-size: 1em; } + +pre { display: table; width: 100%; margin: 0; padding: 24px; } + +pre > code { display: block; } + +q { quotes: none; } +q:before, q:after { content: ""; content: none; } + +small { font-size: 85%; } + +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } +sup { top: -.5em; } +sub { bottom: -.25em; } + + + + + +/* + Lists +======================================================================== */ + +ul, ol { margin: 1em 0; padding: 0 0 0 20px; } + +ul { list-style: disc; } + +dd { margin: 0 0 0 40px; } + + + + +/* + Embedded content +======================================================================== */ + +object, embed, img { max-width: 100%; } + +img { border: 0; height: auto; vertical-align: middle; -ms-interpolation-mode: bicubic; } + +svg:not(:root) { overflow: hidden; } + + + +/* + Figures +======================================================================== */ + +figure { + margin: 0; +} + +figcaption { + line-height: 1.5; + padding: 6px 8px; + font-size: 12px; +} + + + + + +/* + Forms +======================================================================== */ + +form { + margin: 0; +} + +fieldset { + border: 0; + margin: 0 0 14px; + padding: 0; +} + +legend { border: 0; padding: 0; white-space: normal; } + + +label { +} + +label:hover { + cursor: default; + color: #27ace3; +} + + +button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; } + + +button, input { line-height: normal; } + +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +button, input[type=button], input[type=reset], input[type=submit], [role=button] { cursor: pointer; -webkit-appearance: button; } + +button[disabled], input[disabled] { cursor: default; } + + +input[type=checkbox], input[type=radio] { box-sizing: border-box; cursor: pointer; padding: 0; margin-right: 5px; } + +input[type=file] { cursor: pointer; font-size: 11px; color: #555; } + +input[type=search] { + -webkit-appearance: textfield; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + + +textarea { overflow: auto; vertical-align: top; resize: vertical; } + + +input:valid, textarea:valid { } + +input:invalid, textarea:invalid { background-color: #f0dddd; } + + + + +/* + Tables +======================================================================== */ + +table { + border-spacing: 0; + border-collapse: separate; + background: #fff; + border: 1px solid #ccc; + line-height: 1.5; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +th, +td { + vertical-align: top; + padding: 6px 12px; + border-top: 1px solid #e2e2e2; + border-right: 1px solid #e2e2e2; +} +th:last-child, +td:last-child { + border-right: 0; +} + +thead th { + background: #f5f5f5; + border-top: 0; +} +thead th:first-child { + -webkit-border-radius: 5px 0 0 0; + -moz-border-radius: 5px 0 0 0; + border-radius: 5px 0 0 0; +} +thead th:last-child { + -webkit-border-radius: 0 5px 0 0; + -moz-border-radius: 0 5px 0 0; + border-radius: 0 5px 0 0; +} + +tbody th, +tbody td { + font-size: 12px; +} + +tbody tr:nth-child(even) th, +tbody tr:nth-child(even) td { + background-color: #f9f9f9; +} + +tbody tr:last-child th:first-child, +tbody tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 5px; + -moz-border-radius: 0 0 0 5px; + border-radius: 0 0 0 5px; +} + +tbody tr:last-child th:last-child, +tbody tr:last-child td:last-child { + -webkit-border-radius: 0 0 5px 0; + -moz-border-radius: 0 0 5px 0; + border-radius: 0 0 5px 0; +} + + + + + + + + + + + + + + +/* + ======================================================================== + ======================================================================== + ======================================================================== +*/ + + + + + + + + + + + + +/* + + Primary styling + +======================================================================== */ + + +/** Headings **/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: bold; + line-height: 1; +} + +h1:first-child, +h2:first-child, +h3:first-child, +h4:first-child, +h5:first-child, +h6:first-child, +p:first-child { + margin-top: 0; +} + +h1 { + font-size: 32px; + margin-bottom: .66em; +} + +h2 { + font-size: 24px; + margin: 1.33em 0 .66em; +} + +h3 { + font-size: 20px; + margin-bottom: .25em; +} + +h4 { + font-size: 18px; + margin-bottom: .75em; +} + +h5 { + font-size: 16px; + margin: 1.5em 0 .75em; +} + +h6 { + font-size: 14px; + margin: 1.5em 0 .25em; +} + + + +/* + Lists +======================================================================== */ + +ul:not(.plain-list) li { + margin-bottom: 5px; +} + +ul.plain-list, +.pseudo-list { + list-style: none; + margin: 0; + padding: 0; +} + +.pseudo-list li { + position: relative; + padding-left: 14px; +} + +.pseudo-list li:before { + content: "\2013"; + position: absolute; + margin-left: -12px; +} + +.inline-list li { + display: inline-block; + margin-right: 20px; +} + + + + + + + + +/* + Site structure +======================================================================== */ + +.container { + margin-left: 444px; + min-width: 640px; + padding: 40px; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.header a, +.navigation a { + text-decoration: none; +} + +.navigation:not(.pages-nav) a:hover { + text-decoration: underline; +} + + +/** Header **/ + +.header { + margin-bottom: 32px; +} + +.header .entypo { + font-size: 82px; + line-height: 0.2; + margin-right: 10px; + color: #27ace3; + position: relative; + top: -2px; +} + +.header .title { + background: url(http://github.com/images/title.png) no-repeat 0 0 transparent; + width: 421px; + height: 33px; + display: inline-block; + vertical-align: top; +} + + +/** Footer **/ + +.footer { + border-top: 1px solid #eee; + padding-top: 42px; + font-size: 12px; + color: #888; +} + + + +/** Content blocks and sections **/ + +.content { + margin-right: 250px; +} + +.content-block { + margin-bottom: 100px; +} + +.content-full { + width: auto; +} + + + + + + + + +/* + Sidebar +======================================================================== */ + +#sidebar { + background: #f0f5f6; + border-right: 1px solid #e0e5e6; + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 444px; + padding: 40px 20px 40px 40px; + font-size: 12px; + overflow-y: scroll; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.sidebar-block { + margin-bottom: 54px; +} + +.sidebar-block .navigation a { + color: #424242; +} + +.block-short { + margin-bottom: 24px; +} + + +.sidebar-subtitle { + font-size: 14px; + margin-bottom: 6px; +} + + +/** Pages nav **/ + +.pages-nav .nav-link { + text-transform: uppercase; +} +.pages-nav .nav-link:hover { + text-decoration: none; + color: #27ace3; +} + +.pages-nav .entypo { + font-size: 36px; + vertical-align: baseline; + position: relative; + top: 4px; + margin-right: 8px; + line-height: .5; +} + +.home-nav .nav-link { + text-transform: none; +} +.home-nav .entypo { + margin-right: 4px; +} + + + +/** Chapters index **/ + +.chapters-list { + margin: 0; +} + +.chapter-title { + color: #424242 +} + + +.recipes-list { + line-height: 1.25; + margin-bottom: 24px; +} + +.recipes-list .list-item { + color: #888; +} + + + + + + + +/* + Recipes page +======================================================================== */ + +.recipe-content h1 { + font-size: 28px; +} + +.recipe-content h2 { + font-size: 20px; + margin-bottom: .5em; +} + +.recipe-content p { + margin-top: 0; +} + + + + + + + + + +/* + ======================================================================== + ======================================================================== + ======================================================================== +*/ + + + + + + + + + + + + + +/* + Non-semantic helper classes +======================================================================== */ + +.dimmed, a.dimmed { color: #888; } + +.warning { color: red; } + + +.hidden { display: none; visibility: hidden; } + +.invisible { visibility: hidden; } + +.thin { font-weight: 100; } + +.entypo { + font-family: 'Entypo'; + line-height: 1; + text-transform: initial; +} + + +.alignleft { float: left; } +.alignright { float: right; } + + +.clear:before, .clear:after { content: ""; display: table; } +.clear:after { clear: both; } +.clear { zoom: 1; } + + +.ir { border: 0; font: 0/0 a; text-shadow: none; color: transparent; background-color: transparent; } + + + + + + + + + +/* + Code highlighting +======================================================================== */ + +/* +base03: #002b36; +base02: #073642; +base01: #586e75; +base00: #657b83; +base0: #839496; +base1: #93a1a1; +base2: #eee8d5; +base3: #fdf6e3; +yellow: #b58900; +orange: #cb4b16; +red: #dc322f; +magenta: #d33682; +violet: #6c71c4; +blue: #268bd2; +cyan: #2aa198; +green: #859900; +*/ + +.highlight { + background: #f8f8f8; + font-size: 14px; + overflow-x: scroll; + margin-right: -250px; + margin-bottom: 36px; +} +.highlight .hll { background-color: #ffffcc } +.highlight .c { color: #408080; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ +.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #808080 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #7D9029 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #A0A000 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ + + + + + + + + + +/* + Print media queries +======================================================================== */ + + +@media print { + * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; } /* Black prints faster: h5bp.com/s */ + a, a:visited { text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } /* h5bp.com/t */ + tr, img { page-break-inside: avoid; } + img { max-width: 100% !important; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3 { page-break-after: avoid; } +} diff --git a/fonts/entypo.svg b/fonts/entypo.svg new file mode 100644 index 0000000..c24d92a --- /dev/null +++ b/fonts/entypo.svg @@ -0,0 +1,198 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Creative Commons CC BYSA 2012 +Designer : Daniel Bruce +Foundry : Daniel Bruce +Foundry URL : wwwdanielbrucese + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/entypo.ttf b/fonts/entypo.ttf new file mode 100644 index 0000000000000000000000000000000000000000..89b7a194cc767bb1fd97782ff8b38635af64a7b9 GIT binary patch literal 29264 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 literal 0 HcmV?d00001 diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9f242ebb5cd1972fdd2bfcb97035eee41812ddd0 GIT binary patch literal 1232 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 L9$Rhdb@qP%c>UDz literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 3fb3c07..98947f9 100644 --- a/index.html +++ b/index.html @@ -1,46 +1,11 @@ --- layout: default title: Home -chapters: -- Syntax -- Classes and Objects -- Strings -- Arrays -- Dates and Times -- Math -- Functions -- Metaprogramming -- jQuery -- Ajax -- Regular Expressions -- Networking -- Design Patterns -- Databases -- Testing --- -

      Welcome

      -

      Welcome to the CoffeeScript Cookbook! CoffeeScript recipes for the community by the community. Head over to the Contributing page and see what you can do to help out!

      +

      Welcome to the CoffeeScript Cookbook!

      +

      CoffeeScript recipes for the community by the community. Head over to the Contribute page and see what you can do to help out!

      -
        - {% for chapter in page.chapters %} - {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} - {% capture indexurl %}{{ url }}/index.html{% endcapture %} -
      1. -

        {{ chapter }}

        -
          - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
        • {{ page.title }}
        • - {% endunless %} - {% endif %} - {% endfor %} -
        -
      2. - - {% endfor %} -
      \ No newline at end of file From 9d10d31bfbfab720e1a0d7d99074d898b3f7a1b5 Mon Sep 17 00:00:00 2001 From: Bryce Culhane Date: Wed, 29 Aug 2012 15:47:55 -0700 Subject: [PATCH 161/267] Add a splat technique to concatenating arrays The article lacked any mention of the CoffeeScript's splat, which is more concise than prototype.apply. --- chapters/arrays/concatenating-arrays.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/chapters/arrays/concatenating-arrays.md b/chapters/arrays/concatenating-arrays.md index 2828231..df0be98 100644 --- a/chapters/arrays/concatenating-arrays.md +++ b/chapters/arrays/concatenating-arrays.md @@ -46,6 +46,16 @@ array1 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} +Alternatively, we can pass a CoffeeScript splat (`array2...`) directly into `push()`, avoiding the Array prototype. + +{% highlight coffeescript %} +array1 = [1, 2, 3] +array2 = [4, 5, 6] +array1.push array2... +array1 +# => [1, 2, 3, 4, 5, 6] +{% endhighlight %} + ## Discussion CoffeeScript lacks a special syntax for joining arrays, but `concat()` and `push()` are standard JavaScript methods. From 88ebc13bd4a20152fdbcbb36b7bc778d03b3e14d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 4 Sep 2012 08:51:46 -0400 Subject: [PATCH 162/267] Debounce. --- chapters/functions/debounce.md | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 chapters/functions/debounce.md diff --git a/chapters/functions/debounce.md b/chapters/functions/debounce.md new file mode 100644 index 0000000..2a3b714 --- /dev/null +++ b/chapters/functions/debounce.md @@ -0,0 +1,44 @@ +--- +layout: recipe +title: Debounce Functions +chapter: Functions +--- +## Problem + +You want to execute a function only once, coalescing multiple sequential calls into a single execution at the beginning or end. + +## Solution + +With a named function: + +{% highlight coffeescript %} +debounce: (func, threshold, execAsap) -> + timeout = null + (args...) -> + obj = this + delayed = -> + func.apply(obj, args) unless execAsap + timeout = null + if timeout + clearTimeout(timeout) + else if (execAsap) + func.apply(obj, args) + timeout = setTimeout delayed, threshold || 100 +{% endhighlight %} + +{% highlight coffeescript %} +mouseMoveHandler: (e) -> + @debounce((e) -> + # Do something here, but only once 300 milliseconds after the mouse cursor stops. + 300) + +someOtherHandler: (e) -> + @debounce((e) -> + # Do something here, but only once 250 milliseconds after initial execuction. + 250, true) +{% endhighlight %} + +## Discussion + +Learn about [debouncing JavaScript methods](http://unscriptable.com/2009/03/20/debouncing-javascript-methods/) at John Hann's excellent blog article. + From 95d4e89c7ed037a04658b15fbcb7dd2253d8c9f8 Mon Sep 17 00:00:00 2001 From: Amsul Date: Tue, 11 Sep 2012 23:57:36 -0400 Subject: [PATCH 163/267] sidebar scrolls to active page + updated to boilerplate v4 --- _layouts/chapter.html | 14 +- _layouts/default.html | 14 +- _layouts/recipe.html | 12 +- css/style.css | 483 ++++++++++++++++++++++++++++-------------- developers-guide.md | 2 +- js/scripts.js | 33 +++ js/tumbleweed.js | 1 - 7 files changed, 382 insertions(+), 177 deletions(-) create mode 100644 js/scripts.js delete mode 100644 js/tumbleweed.js diff --git a/_layouts/chapter.html b/_layouts/chapter.html index 7298575..b075d2c 100644 --- a/_layouts/chapter.html +++ b/_layouts/chapter.html @@ -76,12 +76,12 @@ {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
    3. - +
        - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
      • {{ page.title }}
      • + {% for site_page in site.pages %} + {% if site_page.url contains url %} + {% unless site_page.url == indexurl %} +
      • {{ site_page.title }}
      • {% endunless %} {% endif %} {% endfor %} @@ -93,6 +93,8 @@ - + + + diff --git a/_layouts/default.html b/_layouts/default.html index 369e873..1f00656 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -73,12 +73,12 @@ {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
      • - +
          - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
        • {{ page.title }}
        • + {% for site_page in site.pages %} + {% if site_page.url contains url %} + {% unless site_page.url == indexurl %} +
        • {{ site_page.title }}
        • {% endunless %} {% endif %} {% endfor %} @@ -90,6 +90,8 @@ - + + + diff --git a/_layouts/recipe.html b/_layouts/recipe.html index 39ada1a..d71dfba 100644 --- a/_layouts/recipe.html +++ b/_layouts/recipe.html @@ -75,12 +75,12 @@ {% capture url %}/chapters/{{ chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
        • - +
            - {% for page in site.pages %} - {% if page.url contains url %} - {% unless page.url == indexurl %} -
          • {{ page.title }}
          • + {% for site_page in site.pages %} + {% if site_page.url contains url %} + {% unless site_page.url == indexurl %} +
          • {{ site_page.title }}
          • {% endunless %} {% endif %} {% endfor %} @@ -92,6 +92,8 @@ + + diff --git a/css/style.css b/css/style.css index 217e53e..138d955 100644 --- a/css/style.css +++ b/css/style.css @@ -1,7 +1,8 @@ /* Author: Amsul - http://github.com/amsul - Version: 0.8 beta - Last Updated: 28 August, 2012 + Description: CoffeeScript Cookbook stylings + Version: 1.0 + Last Updated: 11 September, 2012 */ @@ -22,13 +23,22 @@ +/* ======================================================================== + + Cross-browser normalize (from HTML5 Boilerplate v4.0) + +======================================================================== */ + + + /* - HTML5 element display + HTML5 element display (old browsers) ======================================================================== */ -article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } -audio, canvas, video { display: inline-block; } -audio:not([controls]) { display: none; } +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary { display: block; } +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } +audio:not([controls]) { display: none; height: 0; } +[hidden] { display: none; } @@ -38,8 +48,9 @@ audio:not([controls]) { display: none; } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } -body { margin: 0; font-size: 16px; text-align: left; line-height: 1.5; } -body, button, input, select, textarea { font-family: "Lucida Grande", "Lucida Sans Unicode", sans-serif; font-weight: 200; color: #424242; } +html, button, input, select, textarea { font-family: "Lucida Grande", "Lucida Sans Unicode", sans-serif; font-weight: 200; color: #424242; } + +body { margin: 0; } ::-moz-selection { background: #b3d4fc; text-shadow: none; } @@ -52,52 +63,77 @@ body, button, input, select, textarea { font-family: "Lucida Grande", "Lucida Sa Links ======================================================================== */ -a { - color: #27ace3; - text-decoration: underline; -} - -a:visited { -} - a:hover, a:active { - cursor: pointer; + outline: 0; } a:focus { outline: thin dotted; } + + /* Typography ======================================================================== */ -p, div { word-wrap: break-word; } +h1 { + font-size: 2em; + margin: .67em 0; +} + +h2 { + font-size: 1.5em; + margin: .83em 0; +} + +h3 { + font-size: 1.17em; + margin: 1em 0; +} + +h4 { + font-size: 1em; + margin: 1.33em 0; +} + +h5 { + font-size: .83em; + margin: 1.67em 0; +} + +h6 { + font-size: .75em; + margin: 2.33em 0; +} + + +p, div { + -ms-word-break: break-all; + word-break: break-all; + word-break: break-word; /* old webkit */ +} abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } -blockquote { margin: 1em 0 40px; } - -dfn, em { font-style: italic; } +blockquote { margin: 1em 40px; } -hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } +dfn { font-style: italic; } -ins { background: #ff9; color: #000; text-decoration: none; } +mark { background: #ff0; color: #000; } -mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } +p, pre { margin: 1em 0; } pre, code, kbd, samp { font-family: 'Courier', 'Monaco', monospace, serif; font-size: 1em; } -pre { display: table; width: 100%; margin: 0; padding: 24px; } - -pre > code { display: block; } +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } q { quotes: none; } q:before, q:after { content: ""; content: none; } -small { font-size: 85%; } +small { font-size: 80%; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -.5em; } @@ -106,17 +142,22 @@ sub { bottom: -.25em; } - /* Lists ======================================================================== */ -ul, ol { margin: 1em 0; padding: 0 0 0 20px; } - -ul { list-style: disc; } +dl, menu, ol, ul { margin: 1em 0; } dd { margin: 0 0 0 40px; } +menu, ol, ul { padding: 0 0 0 40px; } + +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + @@ -126,7 +167,7 @@ dd { margin: 0 0 0 40px; } object, embed, img { max-width: 100%; } -img { border: 0; height: auto; vertical-align: middle; -ms-interpolation-mode: bicubic; } +img { border: 0; -ms-interpolation-mode: bicubic; } svg:not(:root) { overflow: hidden; } @@ -136,15 +177,7 @@ svg:not(:root) { overflow: hidden; } Figures ======================================================================== */ -figure { - margin: 0; -} - -figcaption { - line-height: 1.5; - padding: 6px 8px; - font-size: 12px; -} +figure { margin: 0; } @@ -154,60 +187,48 @@ figcaption { Forms ======================================================================== */ -form { - margin: 0; -} +form { margin: 0; } fieldset { - border: 0; - margin: 0 0 14px; - padding: 0; + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: .35em .625em .75em; } legend { border: 0; padding: 0; white-space: normal; } - -label { -} - -label:hover { - cursor: default; - color: #27ace3; -} +label:hover { cursor: default; } button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; } - -button, input { line-height: normal; } - -button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } - -button, input[type=button], input[type=reset], input[type=submit], [role=button] { cursor: pointer; -webkit-appearance: button; } +button, +html input[type=button], +input[type=reset], +input[type=submit] { + -webkit-appearance: button; + cursor: pointer; +} button[disabled], input[disabled] { cursor: default; } +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } -input[type=checkbox], input[type=radio] { box-sizing: border-box; cursor: pointer; padding: 0; margin-right: 5px; } - -input[type=file] { cursor: pointer; font-size: 11px; color: #555; } +input[type=checkbox], input[type=radio] { + cursor: pointer; + padding: 0; +} input[type=search] { -webkit-appearance: textfield; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; } - textarea { overflow: auto; vertical-align: top; resize: vertical; } -input:valid, textarea:valid { } - -input:invalid, textarea:invalid { background-color: #f0dddd; } - - /* @@ -216,71 +237,9 @@ input:invalid, textarea:invalid { background-color: #f0dddd; } table { border-spacing: 0; - border-collapse: separate; - background: #fff; - border: 1px solid #ccc; - line-height: 1.5; - - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} - -th, -td { - vertical-align: top; - padding: 6px 12px; - border-top: 1px solid #e2e2e2; - border-right: 1px solid #e2e2e2; -} -th:last-child, -td:last-child { - border-right: 0; -} - -thead th { - background: #f5f5f5; - border-top: 0; -} -thead th:first-child { - -webkit-border-radius: 5px 0 0 0; - -moz-border-radius: 5px 0 0 0; - border-radius: 5px 0 0 0; -} -thead th:last-child { - -webkit-border-radius: 0 5px 0 0; - -moz-border-radius: 0 5px 0 0; - border-radius: 0 5px 0 0; -} - -tbody th, -tbody td { - font-size: 12px; -} - -tbody tr:nth-child(even) th, -tbody tr:nth-child(even) td { - background-color: #f9f9f9; -} - -tbody tr:last-child th:first-child, -tbody tr:last-child td:first-child { - -webkit-border-radius: 0 0 0 5px; - -moz-border-radius: 0 0 0 5px; - border-radius: 0 0 0 5px; + border-collapse: collapse; } -tbody tr:last-child th:last-child, -tbody tr:last-child td:last-child { - -webkit-border-radius: 0 0 5px 0; - -moz-border-radius: 0 0 5px 0; - border-radius: 0 0 5px 0; -} - - - - - @@ -307,14 +266,45 @@ tbody tr:last-child td:last-child { -/* +/* ======================================================================== Primary styling ======================================================================== */ -/** Headings **/ + +/* + Base +======================================================================== */ + +body { + font-size: 16px; + line-height: 1.5; +} + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} + +img { + vertical-align: middle; + height: auto; + max-width: 100%; +} + +a { + cursor: pointer; + text-decoration: underline; + color: #27ace3; +} + + h1, h2, @@ -323,7 +313,7 @@ h4, h5, h6 { font-weight: bold; - line-height: 1; + line-height: 1.2; } h1:first-child, @@ -402,6 +392,110 @@ ul.plain-list, +/* + Tables +======================================================================== */ + +table { + background: #fff; + border: 1px solid #ccc; + line-height: 1.5; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +th, +td { + vertical-align: top; + padding: 6px 12px; + border-top: 1px solid #e2e2e2; + border-right: 1px solid #e2e2e2; +} +th:last-child, +td:last-child { + border-right: 0; +} + +thead th { + background: #f5f5f5; + border-top: 0; +} +thead th:first-child { + -webkit-border-radius: 5px 0 0 0; + -moz-border-radius: 5px 0 0 0; + border-radius: 5px 0 0 0; +} +thead th:last-child { + -webkit-border-radius: 0 5px 0 0; + -moz-border-radius: 0 5px 0 0; + border-radius: 0 5px 0 0; +} + +tbody th, +tbody td { + font-size: 12px; +} + +tbody tr:nth-child(even) th, +tbody tr:nth-child(even) td { + background-color: #f9f9f9; +} + +tbody tr:last-child th:first-child, +tbody tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 5px; + -moz-border-radius: 0 0 0 5px; + border-radius: 0 0 0 5px; +} + +tbody tr:last-child th:last-child, +tbody tr:last-child td:last-child { + -webkit-border-radius: 0 0 5px 0; + -moz-border-radius: 0 0 5px 0; + border-radius: 0 0 5px 0; +} + + + + +/* + Form elements +======================================================================== */ + +fieldset { + border: 0; + margin: 0 0 14px; + padding: 0; +} + + +input[type=checkbox], +input[type=radio] { + margin-right: 5px; +} + +label:hover { + color: #27ace3; +} + + + + +/* + Code blocks +======================================================================== */ + +pre { display: table; width: 100%; margin: 0; padding: 24px; } + +pre > code { display: block; } + + + + + + @@ -412,7 +506,8 @@ ul.plain-list, .container { margin-left: 444px; - min-width: 640px; + min-width: 666px; + max-width: 1000px; padding: 40px; -webkit-box-sizing: border-box; @@ -601,6 +696,39 @@ ul.plain-list, +/* + Active page +======================================================================== */ + +.page-active { + position: relative; + background: #fff; + padding-right: 24px; + + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} + +.page-active:after { + content: 'M'; + position: absolute; + top: 4px; + right: 0; + font-family: 'Entypo'; + font-size: 36px; + line-height: 6px; + color: #666; +} + +.recipes-list .page-active:after { + top: 1px; +} + + + + + @@ -633,9 +761,9 @@ ul.plain-list, .hidden { display: none; visibility: hidden; } - .invisible { visibility: hidden; } + .thin { font-weight: 100; } .entypo { @@ -649,12 +777,23 @@ ul.plain-list, .alignright { float: right; } -.clear:before, .clear:after { content: ""; display: table; } +.clear:before, .clear:after { content: " "; display: table; } .clear:after { clear: both; } -.clear { zoom: 1; } +.clear { *zoom: 1; } -.ir { border: 0; font: 0/0 a; text-shadow: none; color: transparent; background-color: transparent; } +.ir { + background-color: transparent; + border: 0; + overflow: hidden; + *text-indent: -9999px; +} +.ir:before { + content: ""; + display: block; + width: 0; + height: 100%; +} @@ -759,6 +898,28 @@ green: #859900; +/* ========================================================================== + + Media Queries + +========================================================================== */ + +@media (max-width: 1110px) { + + body:after { + content: 'narrow'; + display: none; + } + + #sidebar { + position: absolute; + bottom: auto; + border-bottom: 1px solid #e0e5e6; + } + +} + + @@ -768,18 +929,24 @@ green: #859900; Print media queries ======================================================================== */ - @media print { - * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; } /* Black prints faster: h5bp.com/s */ - a, a:visited { text-decoration: underline; } - a[href]:after { content: " (" attr(href) ")"; } - abbr[title]:after { content: " (" attr(title) ")"; } - .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ - pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } - thead { display: table-header-group; } /* h5bp.com/t */ - tr, img { page-break-inside: avoid; } - img { max-width: 100% !important; } - @page { margin: 0.5cm; } - p, h2, h3 { orphans: 3; widows: 3; } - h2, h3 { page-break-after: avoid; } + * { + background: transparent !important; + color: #000 !important; + box-shadow:none !important; + text-shadow: none !important; + } + a, a:visited { text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } + tr, img { page-break-inside: avoid; } + img { max-width: 100% !important; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3 { page-break-after: avoid; } } + + diff --git a/developers-guide.md b/developers-guide.md index 484a7e0..8d8c210 100644 --- a/developers-guide.md +++ b/developers-guide.md @@ -67,6 +67,6 @@ serve This will start a webserver in the `_site` folder. Open a browser and visit `http://localhost:4000/` and you should see the site. -## Minituiae and Other Trivialities +## Minutiae and Other Trivialities jekyll can take a second or two to catch up when you save a file. If you edit a file and don't see the changes in your browser, give it a second or two and try again. You may also see Maruku warnings, but as long as it prints `Successfully generated site` you should be alright. diff --git a/js/scripts.js b/js/scripts.js new file mode 100644 index 0000000..42e30b8 --- /dev/null +++ b/js/scripts.js @@ -0,0 +1,33 @@ + +/*jshint browser: true, devel: true, debug: true */ + + +(function( window, document, undefined ) { + + var activePage, position, sidebar, + narrowScreen = ( window.getComputedStyle( document.body, ':after' ).getPropertyValue( 'content' ) === 'narrow' ) ? true : false + + + // if it's not the index page and not a narrow screen + if ( window.location.pathname.length && !narrowScreen ) { + + // get the sidebar + sidebar = document.getElementById( 'sidebar' ) + + // query the dom for the active page + activePage = document.getElementsByClassName( 'page-active' ) + + // if an active page was found + if ( activePage.length ) { + + // get the offset position and give a padding of 80px + position = activePage[0].offsetTop - 80 + + // set the scroll position of the sidebar to the new position + sidebar.scrollTop = position + } + } + + +})( window, document ) + diff --git a/js/tumbleweed.js b/js/tumbleweed.js deleted file mode 100644 index 2b933b8..0000000 --- a/js/tumbleweed.js +++ /dev/null @@ -1 +0,0 @@ -// tumbleweed.js - nothing to see here, podner. From 892921318b6aaec945a5ed6f24e74b1fde40c146 Mon Sep 17 00:00:00 2001 From: nicoder Date: Wed, 12 Sep 2012 22:59:02 +0300 Subject: [PATCH 164/267] Fixed typo --- chapters/syntax/embedding_javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/syntax/embedding_javascript.md b/chapters/syntax/embedding_javascript.md index 4b5d446..69d386c 100644 --- a/chapters/syntax/embedding_javascript.md +++ b/chapters/syntax/embedding_javascript.md @@ -23,7 +23,7 @@ greet "Coffee" ## Discussion -This is a simple way to integrate small snippets of JavaScript code into your CoffeeScript without converting it over to use CoffeeScript syntax. As shown in the [CoffeeScript Language Reference](http://jashkenas.github.com/coffee-script/#embedded) you can mix to the two languages to a certain extent: +This is a simple way to integrate small snippets of JavaScript code into your CoffeeScript without converting it over to use CoffeeScript syntax. As shown in the [CoffeeScript Language Reference](http://jashkenas.github.com/coffee-script/#embedded) you can mix the two languages to a certain extent: {% highlight coffeescript %} hello = `function (name) { From 3f2895a6e5bb2d32599fc4aeb34b066a7bb08939 Mon Sep 17 00:00:00 2001 From: Amsul Date: Wed, 12 Sep 2012 23:39:17 -0400 Subject: [PATCH 165/267] added recipe for checking type of value is array --- chapters/arrays/check-type-is-array.md | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 chapters/arrays/check-type-is-array.md diff --git a/chapters/arrays/check-type-is-array.md b/chapters/arrays/check-type-is-array.md new file mode 100644 index 0000000..1206ef1 --- /dev/null +++ b/chapters/arrays/check-type-is-array.md @@ -0,0 +1,44 @@ +--- +layout: recipe +title: Check if type of value is an Array +chapter: Arrays +--- +## Problem + +You want to check if a value is an `Array`. + +{% highlight coffeescript %} +myArray = [] +console.log typeof myArray // outputs 'object' +{% endhighlight %} + +The `typeof` operator gives a faulty output for arrays. + +## Solution + +Use the following code: + +{% highlight coffeescript %} +typeIsArray = Array.isArray || ( value ) -> return {}.toString.call( value ) is '[object Array]' +{% endhighlight %} + +To use this, just call `typeIsArray` as such: + +{% highlight coffeescript %} +myArray = [] +typeIsArray myArray // outputs true +{% endhighlight %} + +## Discussion + +The method above has been adopted from "the Miller Device". An alternative is to use Douglas Crockford's snippet: + +{% highlight coffeescript %} +typeIsArray = ( value ) -> + value and + typeof value is 'object' and + value instanceof Array and + typeof value.length is 'number' and + typeof value.splice is 'function' and + not ( value.propertyIsEnumerable 'length' ) +{% endhighlight %} From b2bcb002bd880d6cea078c693cd9f59cdad2fb2c Mon Sep 17 00:00:00 2001 From: Scott Carleton Date: Thu, 13 Sep 2012 19:29:49 -0400 Subject: [PATCH 166/267] instance method example for clarification --- .../class-methods-and-instance-methods.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/chapters/classes_and_objects/class-methods-and-instance-methods.md b/chapters/classes_and_objects/class-methods-and-instance-methods.md index 53977a6..492f0b9 100644 --- a/chapters/classes_and_objects/class-methods-and-instance-methods.md +++ b/chapters/classes_and_objects/class-methods-and-instance-methods.md @@ -9,6 +9,7 @@ You want to create a class methods and instance methods. ## Solution +### Class Method {% highlight coffeescript %} class Songs @_titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. @@ -30,6 +31,25 @@ song.get_count() # => TypeError: Object # has no method 'get_count' {% endhighlight %} +### Instance Method +class Songs + _titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. + + get_count: -> + @_titles + + constructor: (@artist, @title) -> + @_titles++ + +song = new Songs("Rick Astley", "Never Gonna Give You Up") +song.get_count() +# => 1 + +Songs.get_count() +# => TypeError: Object function Songs(artist, title) ... has no method 'get_count' +{% endhighlight %} + + ## Discussion Coffeescript will store class methods (also called static methods) on the object itself rather than on the object prototype (and thus on individual object instances), which conserves memory and gives a central location to store class-level values. From d6c64d8a98de68294aa29434f1a6fcbcffffbf2a Mon Sep 17 00:00:00 2001 From: Scott Carleton Date: Fri, 14 Sep 2012 10:33:18 -0400 Subject: [PATCH 167/267] fixed coffeescript highlighting --- Gemfile.lock | 2 +- .../class-methods-and-instance-methods.md | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8b54f2a..0b46357 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: http://rubygems.org/ specs: - RedCloth (4.2.7) + RedCloth (4.2.9) activesupport (3.0.7) classifier (1.3.3) fast-stemmer (>= 1.0.0) diff --git a/chapters/classes_and_objects/class-methods-and-instance-methods.md b/chapters/classes_and_objects/class-methods-and-instance-methods.md index 492f0b9..36960b3 100644 --- a/chapters/classes_and_objects/class-methods-and-instance-methods.md +++ b/chapters/classes_and_objects/class-methods-and-instance-methods.md @@ -10,7 +10,9 @@ You want to create a class methods and instance methods. ## Solution ### Class Method + {% highlight coffeescript %} + class Songs @_titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. @@ -28,10 +30,13 @@ Songs.get_count() # => 1 song.get_count() -# => TypeError: Object # has no method 'get_count' +# => TypeError: Object has no method 'get_count' + {% endhighlight %} ### Instance Method +{% highlight coffeescript %} + class Songs _titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. @@ -47,6 +52,7 @@ song.get_count() Songs.get_count() # => TypeError: Object function Songs(artist, title) ... has no method 'get_count' + {% endhighlight %} From 80f34cf476397acb48eef854c89221242a8dd9e6 Mon Sep 17 00:00:00 2001 From: Vinny Diehl Date: Sat, 13 Oct 2012 18:17:46 -0400 Subject: [PATCH 168/267] Wrap whole words Wrapping words at characters is seriously nasty. --- css/style.css | 7 ------- 1 file changed, 7 deletions(-) diff --git a/css/style.css b/css/style.css index 138d955..9caf5c6 100644 --- a/css/style.css +++ b/css/style.css @@ -107,13 +107,6 @@ h6 { margin: 2.33em 0; } - -p, div { - -ms-word-break: break-all; - word-break: break-all; - word-break: break-word; /* old webkit */ -} - abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } From e19a815b27ac09924d039f86e315c143b7701fee Mon Sep 17 00:00:00 2001 From: Paul Masurel Date: Tue, 6 Nov 2012 15:06:21 +0100 Subject: [PATCH 169/267] Update chapters/arrays/zip-function.md Using the max length of the list arguments does not match python specification of zip. I guess that's ok if we make the behavior in this boundary case clear. --- chapters/arrays/zip-function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/zip-function.md b/chapters/arrays/zip-function.md index f2a1091..bf1dfca 100644 --- a/chapters/arrays/zip-function.md +++ b/chapters/arrays/zip-function.md @@ -15,7 +15,7 @@ Use the following CoffeeScript code: # Usage: zip(arr1, arr2, arr3, ...) zip = () -> lengthArray = (arr.length for arr in arguments) - length = Math.max(lengthArray...) + length = Math.min(lengthArray...) for i in [0...length] arr[i] for arr in arguments From c9d17ba1ff71ec72e9546afc20759a0573566e47 Mon Sep 17 00:00:00 2001 From: onlyjsmith Date: Fri, 23 Nov 2012 10:18:14 +0000 Subject: [PATCH 170/267] Solution was giving wrong answer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have tested this in Chrome 23, Firefox 17 and Opera 12. This SO page also relates http://stackoverflow.com/questions/222309/calculate-last-day-of-month-in-javascript --- chapters/dates_and_times/finding-last-day-of-the-month.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapters/dates_and_times/finding-last-day-of-the-month.md b/chapters/dates_and_times/finding-last-day-of-the-month.md index c054afd..98ccec6 100644 --- a/chapters/dates_and_times/finding-last-day-of-the-month.md +++ b/chapters/dates_and_times/finding-last-day-of-the-month.md @@ -13,9 +13,9 @@ Use JavaScript's Date underflow to find the -1th day of the following month: {% highlight coffeescript %} now = new Date -lastDayOfTheMonth = new Date(1900+now.getYear(), now.getMonth()+1, -1) +lastDayOfTheMonth = new Date(1900+now.getYear(), now.getMonth()+1, 0) {% endhighlight %} ## Discussion -JavaScript's Date constructor cheerfully handles overflow and underflow conditions, which makes date math very easy. Given this ease of manipulation, it doesn't make sense to worry about how many days are in a given month; just nudge the math around. In December, the solution above will actually ask for the -1th day of the 13th month of the current year, which works out to the -1th day of January of NEXT year, which works out to the 31st day of December of the current year. +JavaScript's Date constructor cheerfully handles overflow and underflow conditions, which makes date math very easy. Given this ease of manipulation, it doesn't make sense to worry about how many days are in a given month; just nudge the math around. In December, the solution above will actually ask for the 0th day of the 13th month of the current year, which works out to the day before the 1st day of January of NEXT year, which works out to the 31st day of December of the current year. From 12d5623d7ebfa5b9875577a272ba372f1b906434 Mon Sep 17 00:00:00 2001 From: Devin Weaver Date: Wed, 26 Dec 2012 17:18:37 -0500 Subject: [PATCH 171/267] Refactors the code in the singlton design pattern The original code was a little misleading and it also would compile with a runtime error. This offers a new version that illistrates the use of closures inside a class definition. It also provides an example of how to handle modules (including private classes) The two example together illistrate some of the flexability and elegance CoffeeScript can offer. It also explains how the use of the wrapper around a CS file. --- chapters/design_patterns/singleton.md | 96 ++++++++++++++++----------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/chapters/design_patterns/singleton.md b/chapters/design_patterns/singleton.md index 4f40859..18bbf27 100644 --- a/chapters/design_patterns/singleton.md +++ b/chapters/design_patterns/singleton.md @@ -7,59 +7,75 @@ chapter: Design Patterns Many times you only want one, and only one, instance of a class. For example, you may only need one class that creates server resources and you want to ensure that the one object can control those resources. Beware, however, because the singleton pattern can be easily abused to mimic unwanted global variables. + ## Solution The publicly available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned. -The actual definition of the singleton class follows. +This is works because CoffeeScript allows you to define executable statements inside a class definition. However, because most CoffeeScript compiles into a [IIFE][] wrapper you do not have to place the private class inside the class definition if this style suits you. The later might be useful when developing modular code such as found in [CommonJS][] (Node.js) or [Require.js][] (See the discussion for an example). -Note that I am using the idiomatic module export feature to emphasize the publicly accessible portion of the module. Remember coffeescript wraps all files in a function block to protect the global namespace. +[IIFE]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/ +[CommonJS]: http://www.commonjs.org/ +[Require.js]: http://requirejs.org/ {% highlight coffeescript %} -root = exports ? this # http://stackoverflow.com/questions/4214731/coffeescript-global-variables - -# The publicly accessible Singleton fetcher -class root.Singleton - _instance = undefined # Must be declared here to force the closure on the class - @get: (args) -> # Must be a static method - _instance ?= new _Singleton args - -# The actual Singleton class -class _Singleton - constructor: (@args) -> - - echo: -> - @args - -a = root.Singleton.get 'Hello A' -a.echo() -# => 'Hello A' - -b = root.Singleton.get 'Hello B' -a.echo() -# => 'Hello A' +class Singleton + # You can add statements inside the class definition + # which helps establish private scope (due to closures) + # instance is defined as null to force correct scope + instance = null + # Create a private class that we can initialize however + # defined inside this scope to force the use of the + # singleton class. + class PrivateClass + constructor: (@message) -> + echo: -> @message + # This is a static method used to either retrieve the + # instance or create a new one. + @get: (message) -> + instance ?= new PrivateClass(message) + +a = Singleton.get "Hello A" +a.echo() # => "Hello A" + +b = Singleton.get "Hello B" +b.echo() # => "Hello A" + +Singleton.instance # => undefined +a.instance # => undefined +Singleton.PrivateClass # => undefined +{% endhighlight %} -b.echo() -# => 'Hello A' -root.Singleton._instance -# => undefined +## Discussion -root.Singleton._instance = 'foo' +See in the above example how all instances are outputting from the same instance of the Singleton class. You can also see that the PrivateClass and instance variable are not accessible outside the Singleton class. In essance the Singleton class provides a static method get which returns only one instance of PrivateClass and only one. It also hides the PrivateClass from the world so that you can not create your own. -root.Singleton._instance -# => 'foo' +The idea of hiding or making private the inner workings is preference. Especially since by default CoffeeScript wraps the compiled code inside it's own IIFE (closure) allowing you to define classes without worry that it might be accessible from outside the file. In this example, note that I am using the idiomatic module export feature to emphasize the publicly accessible portion of the module. (See this discussion for further explanation on [exporting to the global namespace][1]). -c = root.Singleton.get 'Hello C' -c.foo() -# => 'Hello A' +[1]: http://stackoverflow.com/questions/4214731/coffeescript-global-variables -a.foo() -# => 'Hello A' +{% highlight coffeescript %} +root = exports ? this + +# Create a private class that we can initialize however +# defined inside the wrapper scope. +class ProtectedClass + constructor: (@message) -> + echo: -> @message + +class Singleton + # You can add statements inside the class definition + # which helps establish private scope (due to closures) + # instance is defined as null to force correct scope + instance = null + # This is a static method used to either retrieve the + # instance or create a new one. + @get: (message) -> + instance ?= new ProtectedClass(message) + +# Export Singleton as a module +root.Singleton = Singleton {% endhighlight %} -## Discussion - -See in the above example how all instances are outputting from the same instance of the Singleton class. - Note how incredibly simple coffeescript makes this design pattern. For reference and discussion on nice javascript implementations, check out [Essential JavaScript Design Patterns For Beginners](http://addyosmani.com/resources/essentialjsdesignpatterns/book/). From aa07fbd7132b6aa0893908c64d2ad36d54fe6e06 Mon Sep 17 00:00:00 2001 From: Kolja Date: Fri, 18 Jan 2013 12:28:59 +0100 Subject: [PATCH 172/267] Update chapters/jquery/plugin.md --- chapters/jquery/plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/jquery/plugin.md b/chapters/jquery/plugin.md index 481d00e..df95cf1 100644 --- a/chapters/jquery/plugin.md +++ b/chapters/jquery/plugin.md @@ -46,7 +46,7 @@ Here are a couple of examples of how to use your new plugin. {% highlight javascript %} $("body").pluginName({ debug: true -}; +}); {% endhighlight %} From 19d514dfd3cd7096bd42b7c106c70ed2009f1c74 Mon Sep 17 00:00:00 2001 From: wangyang Date: Wed, 30 Jan 2013 10:42:11 +0800 Subject: [PATCH 173/267] fix bug 1."Math.min l1, l2 == 0" is equivalent to "Math.min(l1, (l2 == 0))".I think this is not the author would like to express. 2."[0...l1 + 1]" is equivalent to "[0..l1]". --- chapters/strings/matching-strings.md | 87 ++++++++++++++-------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/chapters/strings/matching-strings.md b/chapters/strings/matching-strings.md index 100aefa..ecc18a0 100644 --- a/chapters/strings/matching-strings.md +++ b/chapters/strings/matching-strings.md @@ -1,45 +1,42 @@ ---- -layout: recipe -title: Matching Strings -chapter: Strings ---- -## Problem - -You want to match two or more strings. - -## Solution - -Calculate the edit distance, or number of operations required to transform one string into the other. - -{% highlight coffeescript %} - -Levenshtein = - (str1, str2) -> - - l1 = str1.length - l2 = str2.length - - Math.max l1, l2 if Math.min l1, l2 == 0 - - i = 0; j = 0; distance = [] - - for i in [0...l1 + 1] - distance[i] = [] - distance[i][0] = i - - distance[0][j] = j for j in [0...l2 + 1] - - for i in [1...l1 + 1] - for j in [1...l2 + 1] - distance[i][j] = Math.min distance[i - 1][j] + 1, - distance[i][j - 1] + 1, - distance[i - 1][j - 1] + - if (str1.charAt i - 1) == (str2.charAt j - 1) then 0 else 1 - - distance[l1][l2] - -{% endhighlight %} - -## Discussion - -You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. +--- +layout: recipe +title: Matching Strings +chapter: Strings +--- +## Problem + +You want to match two or more strings. + +## Solution + +Calculate the edit distance, or number of operations required to transform one string into the other. + +{% highlight coffeescript %} + +Levenshtein = + (str1, str2) -> + + l1 = str1.length + l2 = str2.length + + return Math.max l1, l2 unless l1 and l2 + + i = 0; j = 0; distance = [] + + distance[i] = [i] for i in [0..l1] + distance[0][j] = j for j in [0..l2] + + for i in [1..l1] + for j in [1..l2] + distance[i][j] = Math.min distance[i - 1][j] + 1, + distance[i][j - 1] + 1, + distance[i - 1][j - 1] + + if (str1.charAt i - 1) is (str2.charAt j - 1) then 0 else 1 + + distance[l1][l2] + +{% endhighlight %} + +## Discussion + +You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. From c94b78a51873d6dcf66370182c69c434fe51486f Mon Sep 17 00:00:00 2001 From: Paul Masurel Date: Thu, 31 Jan 2013 02:08:01 +0100 Subject: [PATCH 174/267] Levenshtein, programming style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version before this commit was I believe correct. I would therefore totally understand if this pull request was rejected. However I think levenshtein provides a very nice example to demonstrate some of the functionality and pitfalls of coffeescript. First I am not too fond of the "return unless" statement. It feels a bit uncanny to find a non indented return in the middle of a function. Most important I am not fond of what this statement is trying to hide. In most languages, checking for the empty string is not useful.  see Discussion section. --- chapters/strings/matching-strings.md | 43 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/chapters/strings/matching-strings.md b/chapters/strings/matching-strings.md index ecc18a0..1f16f05 100644 --- a/chapters/strings/matching-strings.md +++ b/chapters/strings/matching-strings.md @@ -13,30 +13,37 @@ Calculate the edit distance, or number of operations required to transform one s {% highlight coffeescript %} -Levenshtein = - (str1, str2) -> - + levenshtein = (str1, str2) -> + l1 = str1.length l2 = str2.length + prevDist = [0..l2] + nextDist = [0..l2] + + for i in [1..l1] by 1 + nextDist[0] = i + for j in [1..l2] by 1 + if (str1.charAt i-1) == (str2.charAt j-1) + nextDist[j] = prevDist[j-1] + else + nextDist[j] = 1 + Math.min prevDist[j], nextDist[j-1], prevDist[j-1] + [prevDist,nextDist]=[nextDist, prevDist] + + prevDist[l2] - return Math.max l1, l2 unless l1 and l2 +{% endhighlight %} - i = 0; j = 0; distance = [] +## Discussion - distance[i] = [i] for i in [0..l1] - distance[0][j] = j for j in [0..l2] +You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. - for i in [1..l1] - for j in [1..l2] - distance[i][j] = Math.min distance[i - 1][j] + 1, - distance[i][j - 1] + 1, - distance[i - 1][j - 1] + - if (str1.charAt i - 1) is (str2.charAt j - 1) then 0 else 1 +This version of Levenshtein algorithm is linear in memory, quadratic in time. - distance[l1][l2] - -{% endhighlight %} +str.charAt i is preferred here to str[i] because the latter syntax is not supported by some browsers (e.g. IE7). -## Discussion +At first glance the use of "by 1" in the two loops might look useless. It is actually here to avoid a common danger +of the coffeescript [i..j] syntax. If str1 or str2 is an empty string, then [1..l1] or [1..l2] will return [1,0]. +The loops with the "by 1" statement also compiles to cleaner / slightly more performant javascript. -You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. +Finally the optimization of recycling of arrays at the end of the loops is mainly here to +demonstrate the syntax of coffeescript for swapping two variables. From 3565f28456775cfec79e770f1e728601a9b93f21 Mon Sep 17 00:00:00 2001 From: ZhiCun Date: Thu, 31 Jan 2013 13:49:34 +0800 Subject: [PATCH 175/267] spelling mistake the `function` in "You'd like to know the type of a function without using typeof. " must be `object`. --- chapters/classes_and_objects/type-function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/classes_and_objects/type-function.md b/chapters/classes_and_objects/type-function.md index 6a7f9b8..42952ac 100644 --- a/chapters/classes_and_objects/type-function.md +++ b/chapters/classes_and_objects/type-function.md @@ -5,7 +5,7 @@ chapter: Classes and Objects --- ## Problem -You'd like to know the type of a function without using typeof. (See http://javascript.crockford.com/remedial.html for more information on why typeof is pretty inferior.) +You'd like to know the type of a object without using typeof. (See http://javascript.crockford.com/remedial.html for more information on why typeof is pretty inferior.) ## Solution From f1d710e78ca0dde34a3bffb265bc93125e9689f4 Mon Sep 17 00:00:00 2001 From: "derek.lee" Date: Mon, 25 Feb 2013 21:06:20 -0700 Subject: [PATCH 176/267] Added a line break to the ajax call so "error:" would be on it's own line. --- chapters/jquery/ajax.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapters/jquery/ajax.md b/chapters/jquery/ajax.md index e5563fa..97d0769 100644 --- a/chapters/jquery/ajax.md +++ b/chapters/jquery/ajax.md @@ -25,7 +25,8 @@ $(document).ready -> # Advanced Settings $.ajax '/', type: 'GET' - dataType: 'html' error: (jqXHR, textStatus, errorThrown) -> + dataType: 'html' + error: (jqXHR, textStatus, errorThrown) -> $('body').append "AJAX Error: #{textStatus}" success: (data, textStatus, jqXHR) -> $('body').append "Successful AJAX call: #{data}" From fa13f3bd8ee0eaffe05a721c00b0707b9d41484b Mon Sep 17 00:00:00 2001 From: d8uv Date: Thu, 28 Feb 2013 11:03:08 -0900 Subject: [PATCH 177/267] Change the shuffle to the Fisher-Yates shuffle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current "Shuffling Array Elements" page recommends a naive algorithm, which produces  a slow and biased shuffle. Included is a highly idiomatic refactorization of the Fisher- Yates shuffle (aka the Knuth Shuffle), a less-idiomatic-but-better refactorization, and a version of the algorithm that adds to Array.prototype, for those that program in that  style. --- chapters/arrays/shuffling-array-elements.md | 87 +++++++++++++++++++-- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/chapters/arrays/shuffling-array-elements.md b/chapters/arrays/shuffling-array-elements.md index 9f2f8e9..7963ac4 100644 --- a/chapters/arrays/shuffling-array-elements.md +++ b/chapters/arrays/shuffling-array-elements.md @@ -9,17 +9,94 @@ You want to shuffle the elements in an array. ## Solution -The JavaScript Array `sort()` method accepts a custom sort function. We can write a `shuffle()` method to add some convenience. +The [Fisher-Yates shuffle] is a highly efficient and completely unbiased way to randomize +the elements in an array. It's a fairly simple method: Start at the end of the list, and +swap the last element with a random element from earlier in the list. Go down one and +repeat, until you're at the beginning of the list, with all of the shuffled elements +at the end of the list. This [Fisher-Yates shuffle Visualization] may help you understand +the algorithm. {% highlight coffeescript %} -Array::shuffle = -> @sort -> 0.5 - Math.random() +shuffle = (a) -> + # From the end of the list to the beginning, pick element `i`. + for i in [a.length-1..1] + # Choose random element `j` to the front of `i` to swap with. + j = Math.floor Math.random() * (i + 1) + # Swap `j` with `i`, using destructured assignment + [a[i], a[j]] = [a[j], a[i]] + # Return the shuffled array. + a -[1..9].shuffle() +shuffle([1..9]) # => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ] {% endhighlight %} +[Fisher-Yates shuffle]: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle +[Fisher-Yates Shuffle Visualization]: http://bost.ocks.org/mike/shuffle/ + ## Discussion -For more background on how this shuffle logic works, see this [discussion at StackOverflow](http://stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling). +### The Wrong Way to do it + +There is a common--but terribly wrong way--to shuffle an array, by sorting by a random +number. + +{% highlight coffeescript %} +shuffle = (a) -> a.sort -> 0.5 - Math.random() +{% endhighlight %} + +If you do a sort randomly, it should give you a random order, right? Even [Microsoft used +this random-sort algorithm][msftshuffle]. Turns out, [this random-sort algorithm produces +biased results][naive], because it only has the illusion of shuffling. Randomly sorting +will not result in a neat, tidy shuffle; it will result in a wild mass of inconsistent +sorting. + +[msftshuffle]: http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html +[naive]: http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html + +### Optimizing for speed and space + +The solution above isn't as fast, or as lean, as it can be. The list comprehension, when +transformed into Javascript, is far more complex than it needs to be, and the +destructured assignment is far slower than dealing with bare variables. The following +code is less idiomatic, and takes up more source-code space... but will compile down +smaller and run a bit faster: + +{% highlight coffeescript %} +shuffle = (a) -> + i = a.length + while --i > 0 + j = ~~(Math.random() * (i + 1)) # ~~ is a common optimization for Math.floor + t = a[j] + a[j] = a[i] + a[i] = t + a +{% endhighlight %} + +### Extending Javascript to include this shuffle. + +The following code adds the shuffle function to the Array prototype, which means that +you are able to run it on any array you wish, in a much more direct manner. + +{% highlight coffeescript %} +Array::shuffle = -> + for i in [@length-1..1] + j = Math.floor Math.random() * (i + 1) + [@[i], @[j]] = [@[j], @[i]] + @ + +[1..9].shuffle() +# => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ] +{% endhighlight %} + +**Note:** Although it's quite common in languages like Ruby, extending native objects is +often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify +objects you don’t own][dontown]; [Extending built-in native objects. Evil or not?] +[extendevil]). + +Also, if you think you'll be using a lot of these utility functions, consider using a +utility library, like [Lo-dash](http://lodash.com/). They include a lot of nifty +features, like maps and forEach, in a cross-browser, lean, high-performance way. -**Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). \ No newline at end of file +[dontown]: http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/ +[extendevil]: http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/ From 50ab300204df022fedf51ba5117627d01199c31c Mon Sep 17 00:00:00 2001 From: David Brady Date: Thu, 28 Feb 2013 15:56:02 -0700 Subject: [PATCH 178/267] Fixed decorator sample (thanks tixz) * TextDecorator was written in JavaScript-style function assignment, making the @processors variable drop out of scope * Rewrote it as class TextDecorator and included a constructor to set the var --- chapters/design_patterns/decorator.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chapters/design_patterns/decorator.md b/chapters/design_patterns/decorator.md index 0be3d7a..a40ed5c 100644 --- a/chapters/design_patterns/decorator.md +++ b/chapters/design_patterns/decorator.md @@ -26,7 +26,9 @@ miniMarkdown = (line) -> stripComments = (line) -> line.replace /\s*\/\/.*$/, '' # Removes one-line, double-slash C-style comments -TextProcessor = (@processors) -> +class TextProcessor + constructor: (@processors) -> + reducer: (existing, processor) -> if processor processor(existing or '') From 0648bcf79c475a0313eed4756cce16575169162c Mon Sep 17 00:00:00 2001 From: d8uv Date: Fri, 1 Mar 2013 10:35:26 -0900 Subject: [PATCH 179/267] Make Array::shuffle safer As mentioned by @dbrady in #72 we shouldn't overwrite a native `Array::shuffle` --- chapters/arrays/shuffling-array-elements.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chapters/arrays/shuffling-array-elements.md b/chapters/arrays/shuffling-array-elements.md index 7963ac4..6e115da 100644 --- a/chapters/arrays/shuffling-array-elements.md +++ b/chapters/arrays/shuffling-array-elements.md @@ -79,7 +79,7 @@ The following code adds the shuffle function to the Array prototype, which means you are able to run it on any array you wish, in a much more direct manner. {% highlight coffeescript %} -Array::shuffle = -> +do -> Array::shuffle ?= -> for i in [@length-1..1] j = Math.floor Math.random() * (i + 1) [@[i], @[j]] = [@[j], @[i]] @@ -92,7 +92,9 @@ Array::shuffle = -> **Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own][dontown]; [Extending built-in native objects. Evil or not?] -[extendevil]). +[extendevil]). That being said, the code above is really quite safe to add. It only adds +`Array::shuffle` if it doesn't exist already, thanks to the existential assignment +operator (`?=`). That way, we don't overwrite someone else's, or a native browser method. Also, if you think you'll be using a lot of these utility functions, consider using a utility library, like [Lo-dash](http://lodash.com/). They include a lot of nifty From 7eb59ba8fb4b470a3e8f1ac53c33ec1f96842dc4 Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Sat, 2 Mar 2013 21:16:40 -0500 Subject: [PATCH 180/267] Adds initial content for testing with nodeunit This commit history was rewriten by Devin Weaver --- chapters/testing/testing_with_nodeunit.md | 172 ++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 chapters/testing/testing_with_nodeunit.md diff --git a/chapters/testing/testing_with_nodeunit.md b/chapters/testing/testing_with_nodeunit.md new file mode 100644 index 0000000..ecdd5e9 --- /dev/null +++ b/chapters/testing/testing_with_nodeunit.md @@ -0,0 +1,172 @@ +--- +layout: recipe +title: Testing with Nodeunit +chapter: Testing +--- +## Problem + +You are writing a simple calculator using CoffeeScript and you want to verify it functions as expected. You decide to use the Nodeunit test framework. + +## Discussion + +Nodeunit is a JavaScript implementation of the xUnit family of Unit Testing libraries, similar libraries are available for Java, Python, Ruby, Smalltalk etc. + +When using xUnit family test frameworks, you write tests in a file that describes the expected functionality of the code to be tested. + +For example, we expect our calculator will be able to add and subtract and will function correctly with both positive and negative numbers. Our test is listed below. + +{% highlight coffeescript %} + +# tests/calculator.test.coffee + +Calculator = require '../calculator' + +exports.CalculatorTest = + + 'test can add two positive numbers': (test) -> + calculator = new Calculator + result = calculator.add 2, 3 + test.equal(result, 5) + test.done() + + 'test can handle negative number addition': (test) -> + calculator = new Calculator + result = calculator.add -10, 5 + test.equal(result, -5) + test.done() + + 'test can subtract two positive numbers': (test) -> + calculator = new Calculator + result = calculator.subtract 10, 6 + test.equal(result, 4) + test.done() + + 'test can handle negative number subtraction': (test) -> + calculator = new Calculator + result = calculator.subtract 4, -6 + test.equal(result, 10) + test.done() + +{% endhighlight %} + +### Installing Nodeunit + +Before you can run your tests, you must install Nodeunit: + +First of allcreate a package.json file + +{% highlight javascript %} +{ + "name": "calculator", + "version": "0.0.1", + "scripts": { + "test": "./node_modules/.bin/nodeunit test" + }, + "dependencies": { + "coffee-script": "~1.4.0", + "nodeunit": "~0.7.4" + } +} +{% endhighlight %} + +Next from a terminal run. + +{% highlight bash %} +$ npm install +{% endhighlight %} + +## Running the Tests + +It's easy to run the tests from the command-line: + +{% highlight bash %} +$ npm test +{% endhighlight %} + +The test runner should fail, because we have no calculator.coffee + +All failing tests + +Let's create a simple file + + +{% highlight coffeescript %} + +# calculator.coffee + +class Calculator + +module.exports = Calculator +{% endhighlight %} + +And re-run the test suite. + +Still failing, but better + +## Getting the Tests to Pass + +Let's implement our methods and see if we can get these tests to pass. + +{% highlight coffeescript %} + +# calculator.coffee + +class Calculator + + add: (a, b) -> + a + b + + subtract: (a, b) -> + a - b + +module.exports = Calculator +{% endhighlight %} + +When we rerun the tests we see they're all passing: + +All passing + + +## Refactoring the Tests + +Now that our tests pass, we should look to see if our code or our test(s) can be refactored. + +In our test file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initialization code into a routine that runs before each test. + +In common with other xUnit libraries, Nodeunit provides a setUp (and tearDown) function which will be called before each test. + +{% highlight coffeescript %} + +Calculator = require '../calculator' + +exports.CalculatorTest = + + setUp: (callback) -> + @calculator = new Calculator + callback() + + 'test can add two positive numbers': (test) -> + result = @calculator.add 2, 3 + test.equal(result, 5) + test.done() + + 'test can handle negative number addition': (test) -> + result = @calculator.add -10, 5 + test.equal(result, -5) + test.done() + + 'test can subtract two positive numbers': (test) -> + result = @calculator.subtract 10, 6 + test.equal(result, 4) + test.done() + + 'test can handle negative number subtraction': (test) -> + result = @calculator.subtract 4, -6 + test.equal(result, 10) + test.done() + +{% endhighlight %} + +We can rerun the tests and everything should continue to pass. + +All passing From 5d615dab716fd4879b2a8494a857b2267671b2ec Mon Sep 17 00:00:00 2001 From: Devin Weaver Date: Sat, 2 Mar 2013 21:33:43 -0500 Subject: [PATCH 181/267] Converts screen shots to inline code blocks --- chapters/testing/testing_with_nodeunit.md | 68 ++++++++++++++++++++--- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/chapters/testing/testing_with_nodeunit.md b/chapters/testing/testing_with_nodeunit.md index ecdd5e9..30af5b8 100644 --- a/chapters/testing/testing_with_nodeunit.md +++ b/chapters/testing/testing_with_nodeunit.md @@ -53,7 +53,7 @@ exports.CalculatorTest = Before you can run your tests, you must install Nodeunit: -First of allcreate a package.json file +First of all create a `package.json` file {% highlight javascript %} { @@ -85,10 +85,21 @@ $ npm test The test runner should fail, because we have no calculator.coffee -All failing tests + suki@Yuzuki:nodeunit_testing (master)$ npm test + npm WARN package.json calculator@0.0.1 No README.md file found! -Let's create a simple file + > calculator@0.0.1 test /Users/suki/tmp/nodeunit_testing + > ./node_modules/.bin/nodeunit test + + + /Users/suki/tmp/nodeunit_testing/node_modules/nodeunit/lib/nodeunit.js:72 + if (err) throw err; + ^ + Error: ENOENT, stat '/Users/suki/tmp/nodeunit_testing/test' + npm ERR! Test failed. See above for more details. + npm ERR! not ok code 0 +Let's create a simple file {% highlight coffeescript %} @@ -101,7 +112,39 @@ module.exports = Calculator And re-run the test suite. -Still failing, but better + suki@Yuzuki:nodeunit_testing (master)$ npm test + npm WARN package.json calculator@0.0.1 No README.md file found! + + > calculator@0.0.1 test /Users/suki/tmp/nodeunit_testing + > ./node_modules/.bin/nodeunit test + + + calculator.test + ✖ CalculatorTest - test can add two positive numbers + + TypeError: Object # has no method 'add' + ... + + ✖ CalculatorTest - test can handle negative number addition + + TypeError: Object # has no method 'add' + ... + + ✖ CalculatorTest - test can subtract two positive numbers + + TypeError: Object # has no method 'subtract' + ... + + ✖ CalculatorTest - test can handle negative number subtraction + + TypeError: Object # has no method 'subtract' + ... + + + FAILURES: 4/4 assertions failed (31ms) + npm ERR! Test failed. See above for more details. + npm ERR! not ok code 0 + ## Getting the Tests to Pass @@ -124,7 +167,20 @@ module.exports = Calculator When we rerun the tests we see they're all passing: -All passing + suki@Yuzuki:nodeunit_testing (master)$ npm test + npm WARN package.json calculator@0.0.1 No README.md file found! + + > calculator@0.0.1 test /Users/suki/tmp/nodeunit_testing + > ./node_modules/.bin/nodeunit test + + + calculator.test + ✔ CalculatorTest - test can add two positive numbers + ✔ CalculatorTest - test can handle negative number addition + ✔ CalculatorTest - test can subtract two positive numbers + ✔ CalculatorTest - test can handle negative number subtraction + + OK: 4 assertions (27ms) ## Refactoring the Tests @@ -168,5 +224,3 @@ exports.CalculatorTest = {% endhighlight %} We can rerun the tests and everything should continue to pass. - -All passing From d082571bd6776c2d65fd03aa2ad15c89d4c0b727 Mon Sep 17 00:00:00 2001 From: d8uv Date: Mon, 11 Mar 2013 13:51:26 -0700 Subject: [PATCH 182/267] Detecting and Creating Missing Functions rewrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New solution, and greatly expanded discussion section to explain it. From:   unless Class::member     Class::member = contents To:   do -> Class::member ?= contents --- .../detecting-and-replacing-functions.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/chapters/metaprogramming/detecting-and-replacing-functions.md b/chapters/metaprogramming/detecting-and-replacing-functions.md index 6a99931..f8d81b5 100644 --- a/chapters/metaprogramming/detecting-and-replacing-functions.md +++ b/chapters/metaprogramming/detecting-and-replacing-functions.md @@ -9,12 +9,11 @@ You want to detect if a function exists and create it if it does not (such as an ## Solution -Use `::` to detect the function, and assign to it if it does not exist. +Use the existential assignment operator (`?=`) to assign a function to the classes' prototype (using the `::` shorthand), and wrap it all in a IIFE (`do ->`) to contain the variables. {% highlight coffeescript %} -unless Array::filter - Array::filter = (callback) -> - element for element in this when callback element +do -> Array::filter ?= (callback) -> + element for element in this when callback element array = [1..10] @@ -24,4 +23,10 @@ array.filter (x) -> x > 5 ## Discussion -Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the `::` operator. +Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. +In Coffeescript, you access this prototype using the `::` shortcut. So, if you want to add a filter function to the array class, you do `Array::filter = ...`. This will add the filter function to all arrays. + +However, we don't ever want to overwrite a prototype that we haven't created in the first place. For example, if `Array::filter` already exists in a fast native form in the browser, or a library maker has their own specific version of `Array::filter`, then you'll either replace the quick native version with a slow Javascript version, or you will break the library that depends on their own Array::shuffle. +What you need to do is only add the function if it doesn't already exist. That's where the existential assignment operator (`?=`) comes in. If we do `Array::filter ?= ...` instead, it will see if `Array::filter` already exists. If it does, then it will use the current version. If it doesn't, it will add yours. + +Finally, because the existential assignment operator--when compiled--creates a few variables, we clean up the code by wrapping it in an [Immediately-Invoked Function Expression (IIFE)](http://benalman.com/news/2010/11/immediately-invoked-function-expression/). This hides those internal-use-only variables from leaking outside. So, if the function we're writing already exists, it runs, does basically nothing, and exits, affecting absolutely none of your code. But, if the function we're writing *doesn't* exist, we send out only the function we're writing as a closure, so only the function you've made affects the code. The internal workings of `?=` are hidden either way. From e9076fec3027a267eb585c6ff94e414d445ff29e Mon Sep 17 00:00:00 2001 From: d8uv Date: Mon, 11 Mar 2013 18:11:19 -0700 Subject: [PATCH 183/267] Add annotated javascript of Polyfill pattern rewrite --- .../detecting-and-replacing-functions.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/chapters/metaprogramming/detecting-and-replacing-functions.md b/chapters/metaprogramming/detecting-and-replacing-functions.md index f8d81b5..4ba7b64 100644 --- a/chapters/metaprogramming/detecting-and-replacing-functions.md +++ b/chapters/metaprogramming/detecting-and-replacing-functions.md @@ -30,3 +30,33 @@ However, we don't ever want to overwrite a prototype that we haven't created in What you need to do is only add the function if it doesn't already exist. That's where the existential assignment operator (`?=`) comes in. If we do `Array::filter ?= ...` instead, it will see if `Array::filter` already exists. If it does, then it will use the current version. If it doesn't, it will add yours. Finally, because the existential assignment operator--when compiled--creates a few variables, we clean up the code by wrapping it in an [Immediately-Invoked Function Expression (IIFE)](http://benalman.com/news/2010/11/immediately-invoked-function-expression/). This hides those internal-use-only variables from leaking outside. So, if the function we're writing already exists, it runs, does basically nothing, and exits, affecting absolutely none of your code. But, if the function we're writing *doesn't* exist, we send out only the function we're writing as a closure, so only the function you've made affects the code. The internal workings of `?=` are hidden either way. + +### Example + +Below, we've compiled and annotated the coffeescript written in the solution above + +{% highlight javascript %} +// (function(){ ... })() is an IIFE, compiled in thanks to `do ->` +(function() { + + // This is from the `?=` operator, used to check if Array.prototype.filter (`Array::filter`) exists. + // If it does, we set it to itself, and return. If it doesn't, then we set it to the function, and return the function. + // The IIFE is only used to hide _base and _ref from the outside world. + var _base, _ref; + return (_ref = (_base = Array.prototype).filter) != null ? _ref : _base.filter = function(callback) { + + // `element for element in this when callback element` + var element, _i, _len, _results; + _results = []; + for (_i = 0, _len = this.length; _i < _len; _i++) { + element = this[_i]; + if (callback(element)) { + _results.push(element); + } + } + return _results; + + }; +// The end of the IIFE from `do ->` +})(); +{% endhighlight %} From 0836946e99dc65051fc9a91bf568dcc612dc5dd5 Mon Sep 17 00:00:00 2001 From: Kris Budde Date: Mon, 18 Mar 2013 22:30:52 +0100 Subject: [PATCH 184/267] Chaining recipe changed. Property array was shared between instances --- chapters/classes_and_objects/chaining.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/chapters/classes_and_objects/chaining.md b/chapters/classes_and_objects/chaining.md index bec3a46..65f450f 100644 --- a/chapters/classes_and_objects/chaining.md +++ b/chapters/classes_and_objects/chaining.md @@ -13,10 +13,11 @@ Return the `this` (i.e. `@`) object after every chained method. {% highlight coffeescript %} class CoffeeCup - properties: - strength: 'medium' - cream: false - sugar: false + constructor: -> + @properties= + strength: 'medium' + cream: false + sugar: false strength: (newStrength) -> @properties.strength = newStrength @ @@ -57,13 +58,13 @@ addChainedAttributeAccessor = (obj, propertyAttr, attr) -> obj class TeaCup - properties: - size: 'medium' - type: 'black' - sugar: false - cream: false - -addChainedAttributeAccessor(TeaCup.prototype, 'properties', attr) for attr of TeaCup.prototype.properties + constructor: -> + @properties= + size: 'medium' + type: 'black' + sugar: false + cream: false + addChainedAttributeAccessor(this, 'properties', attr) for attr of @properties earlgrey = new TeaCup().size('small').type('Earl Grey').sugar('false') From 57c445a9d4f23b1b744c3dd06d3e7f1a85311441 Mon Sep 17 00:00:00 2001 From: Iain Beeston Date: Tue, 9 Apr 2013 15:20:30 +1000 Subject: [PATCH 185/267] Showed how to access from an instance method --- chapters/classes_and_objects/class-variables.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chapters/classes_and_objects/class-variables.md b/chapters/classes_and_objects/class-variables.md index d48ed9c..b7025bd 100644 --- a/chapters/classes_and_objects/class-variables.md +++ b/chapters/classes_and_objects/class-variables.md @@ -13,6 +13,9 @@ You want to create a class variable. class Zoo @MAX_ANIMALS: 50 MAX_ZOOKEEPERS: 3 + + helpfulInfo: => + "Zoos may contain a maximum of #{@constructor.MAX_ANIMALS} animals" Zoo.MAX_ANIMALS # => 50 @@ -23,6 +26,8 @@ Zoo.MAX_ZOOKEEPERS zoo = new Zoo zoo.MAX_ZOOKEEPERS # => 3 +zoo.helpfulInfo() +# => "Zoos may contain a maximum of 50 animals" {% endhighlight %} ## Discussion From fab61fb07fd55c37f62da1973f38818242ccc002 Mon Sep 17 00:00:00 2001 From: Jameson Quinn Date: Thu, 25 Apr 2013 14:53:15 -0600 Subject: [PATCH 186/267] Mixins recipe --- chapters/classes_and_objects/mixins.md | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 chapters/classes_and_objects/mixins.md diff --git a/chapters/classes_and_objects/mixins.md b/chapters/classes_and_objects/mixins.md new file mode 100644 index 0000000..8f99d84 --- /dev/null +++ b/chapters/classes_and_objects/mixins.md @@ -0,0 +1,49 @@ +--- +layout: recipe +title: Mixins for classes +chapter: Classes and Objects +--- +## Problem + +You have a few utility methods that you want to include in a number of different classes. + +## Solution + +Use a mixOf factory function that generates a mixed superclass for you. + +{% highlight coffeescript %} +mixOf = (base, mixins...) -> + class Mixed extends base + for mixin in mixins by -1 #earlier mixins override later ones + for name, method of mixin:: + Mixed::[name] = method + Mixed + +... + +class DeepThought + answer: -> + 42 + +class PhilosopherMixin + pontificate: -> + console.log "hmm..." + @wise = yes + +class DeeperThought extends mixOf DeepThought, PhilosopherMixin + answer: -> + @pontificate() + super() + +earth = new DeeperThought +earth.answer() +# hmm... +# => 42 +{% endhighlight %} + +## Discussion + +This is intended for lightweight mixins. Thus you inherit methods of the +base and its ancestors, and those of the mixins, but not those of the ancestors of +the mixins. Also, after declaring a mixed class, further changes in the mixins are not +reflected. From b16a9071dd6833cf2689e6380b7fc417c99a4f0b Mon Sep 17 00:00:00 2001 From: guillaumebiton Date: Thu, 30 May 2013 12:13:47 +0200 Subject: [PATCH 187/267] new template for arrays of objects (collections?) --- .../arrays/where-for-arrays-of-objects.md | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 chapters/arrays/where-for-arrays-of-objects.md diff --git a/chapters/arrays/where-for-arrays-of-objects.md b/chapters/arrays/where-for-arrays-of-objects.md new file mode 100644 index 0000000..1c6d4d7 --- /dev/null +++ b/chapters/arrays/where-for-arrays-of-objects.md @@ -0,0 +1,76 @@ +--- +layout: recipe +title: where for arrays of objects +chapter: Arrays +--- +## Problem + +You want to get an array of objects that match your request for some properties + +You have an Array of Objects, such as: + +{% highlight coffeescript %} +cats = [ + { + name: "Bubbles" + favoriteFood: "mice" + age: 1 + }, + { + name: "Sparkle" + favoriteFood: "tuna" + }, + { + name: "flyingCat" + favoriteFood: "mice" + age: 1 + } +] +{% endhighlight %} + +You want to filter with some properties, like cats.where({ age: 1}) or cats.where({ age: 1, favoriteFood: "mice"}) + +## Solution + +You can extend Array like this : + +{% highlight coffeescript %} +Array::where = (query) -> + return [] if typeof query isnt "object" + hit = Object.keys(query).length + @filter (item) -> + match = 0 + for key, val of query + match += 1 if item[key] is val + if match is hit then true else false + +cats.where age:1 +# => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 },{ name: 'flyingCat', favoriteFood: 'mice', age: 1 } ] + +cats.where age:1, name: "Bubbles" +# => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 } ] + +cats.where age:1, favoriteFood:"tuna" +# => [] +{% endhighlight %} + +## Discussion + +This is an exact match. we could make it more flexible with a matcher function : + +{% highlight coffeescript %} +Array::where = (query, matcher = (a,b) -> a is b) -> + return [] if typeof query isnt "object" + hit = Object.keys(query).length + @filter (item) -> + match = 0 + for key, val of query + match += 1 if matcher(item[key], val) + if match is hit then true else false + +cats.where name:"bubbles", (a, b) -> "#{ a }".toLowerCase() is "#{ b }".toLowerCase() +# => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 } ] +# now it's case insensitive +{% endhighlight %} + +it's more a method to deal with collection and it could be rename as "find" but popular libraires like underscore or lodash name it "where". From 0b3e4cdbdbfa0a10cdc04516aa405a6a1730b9b2 Mon Sep 17 00:00:00 2001 From: guillaumebiton Date: Thu, 30 May 2013 12:20:05 +0200 Subject: [PATCH 188/267] update discussion example --- chapters/arrays/where-for-arrays-of-objects.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chapters/arrays/where-for-arrays-of-objects.md b/chapters/arrays/where-for-arrays-of-objects.md index 1c6d4d7..51eba6d 100644 --- a/chapters/arrays/where-for-arrays-of-objects.md +++ b/chapters/arrays/where-for-arrays-of-objects.md @@ -68,6 +68,10 @@ Array::where = (query, matcher = (a,b) -> a is b) -> match += 1 if matcher(item[key], val) if match is hit then true else false +cats.where name:"bubbles" +# => [] +# it's case sensitive + cats.where name:"bubbles", (a, b) -> "#{ a }".toLowerCase() is "#{ b }".toLowerCase() # => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 } ] # now it's case insensitive From 2be90d11d14e1f86e8ce769b3f1efa11c0b414a3 Mon Sep 17 00:00:00 2001 From: Michael Glass Date: Tue, 18 Jun 2013 12:05:55 -0700 Subject: [PATCH 189/267] lesson on class variables is incorrect. putting attributes on the prototype does not use more memory than putting it on the constructor function. --- chapters/classes_and_objects/class-variables.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/chapters/classes_and_objects/class-variables.md b/chapters/classes_and_objects/class-variables.md index b7025bd..dd8713f 100644 --- a/chapters/classes_and_objects/class-variables.md +++ b/chapters/classes_and_objects/class-variables.md @@ -15,21 +15,29 @@ class Zoo MAX_ZOOKEEPERS: 3 helpfulInfo: => - "Zoos may contain a maximum of #{@constructor.MAX_ANIMALS} animals" + "Zoos may contain a maximum of #{@constructor.MAX_ANIMALS} animals and #{@MAX_ZOOKEEPERS} zoo keepers." Zoo.MAX_ANIMALS # => 50 Zoo.MAX_ZOOKEEPERS -# => undefined (it is an instance variable) +# => undefined (it is a prototype member) + +Zoo::MAX_ZOOKEEPERS +# => 3 zoo = new Zoo zoo.MAX_ZOOKEEPERS # => 3 zoo.helpfulInfo() -# => "Zoos may contain a maximum of 50 animals" +# => "Zoos may contain a maximum of 50 animals and 3 zoo keepers." + +zoo.MAX_ZOOKEEPERS = "smelly" +zoo.MAX_ANIMALS = "seventeen" +zoo.helpfulInfo() +# => "Zoos may contain a maximum of 50 animals and smelly zoo keepers." {% endhighlight %} ## Discussion -Coffeescript will store these values on the object itself rather than on the object prototype (and thus on individual object instances), which conserves memory and gives a central location to store class-level values. +Coffeescript will store these values on the class itself rather than on the prototype it defines. These are useful for defining variables on classes which can't be overrided by instance attribute variables. From cb75e69b835b1a4bd6ce1295cf0003f42f9d0e2d Mon Sep 17 00:00:00 2001 From: tashemi Date: Sat, 27 Jul 2013 01:37:49 +0300 Subject: [PATCH 190/267] Create observer.md --- chapters/design_patterns/observer.md | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 chapters/design_patterns/observer.md diff --git a/chapters/design_patterns/observer.md b/chapters/design_patterns/observer.md new file mode 100644 index 0000000..1fe5ab2 --- /dev/null +++ b/chapters/design_patterns/observer.md @@ -0,0 +1,44 @@ +--- +layout: recipe Observer Pattern +title: Observer Pattern +chapter: Design patterns +--- +## Problem + +You have to notify some objects about an event happen + +## Solution + +Use an [Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern) + +{% highlight coffeescript %} +class PostOffice + constructor: () -> + @subscribers = [] + sendNewItemReleased: (item) -> + subscriber.callback(item) for subscriber in @subscribers when subscriber.item is item + return + subscribe: (to, onNewItemReleased) -> + @subscribers.push({'item':to, 'callback':onNewItemReleased}) +class MagazineSubscriber + onNewMagazine: (item) -> + alert "I've got new "+item +class NewspaperSubscriber + onNewNewspaper: (item) -> + alert "I've got new "+item + +postOffice = new PostOffice() +sub1 = new MagazineSubscriber() +sub2 = new NewspaperSubscriber() +postOffice.subscribe "Mens Health", sub1.onNewMagazine +postOffice.subscribe "Times", sub2.onNewNewspaper +postOffice.sendNewItemReleased "Times" +postOffice.sendNewItemReleased "Mens Health" +{% endhighlight %} + +## Discussion + +Here you have an observer object (PostOffice) and observable objects (MagazineSubscriber, NewspaperSubscriber). +To be notified about an event of publishing new periodical observable object should make subscribtion on PostOffice. +Every of subscribed objects is stored internaly in the PostOffice array of subscribers. +Every subscriber is notified on new concrete periodical is published. From 28e5392493057dc64b5087bf6eb0671cc30b4fb4 Mon Sep 17 00:00:00 2001 From: tashemi Date: Sat, 27 Jul 2013 01:51:26 +0300 Subject: [PATCH 191/267] Update code --- chapters/design_patterns/observer.md | 31 +++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/chapters/design_patterns/observer.md b/chapters/design_patterns/observer.md index 1fe5ab2..4976e63 100644 --- a/chapters/design_patterns/observer.md +++ b/chapters/design_patterns/observer.md @@ -12,33 +12,36 @@ You have to notify some objects about an event happen Use an [Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern) {% highlight coffeescript %} + class PostOffice - constructor: () -> - @subscribers = [] - sendNewItemReleased: (item) -> - subscriber.callback(item) for subscriber in @subscribers when subscriber.item is item - return - subscribe: (to, onNewItemReleased) -> - @subscribers.push({'item':to, 'callback':onNewItemReleased}) + constructor: () -> + @subscribers = [] + notifyNewItemReleased: (item) -> + subscriber.callback(item) for subscriber in @subscribers when subscriber.item is item + subscribe: (to, onNewItemReleased) -> + @subscribers.push {'item':to, 'callback':onNewItemReleased} + class MagazineSubscriber - onNewMagazine: (item) -> - alert "I've got new "+item + onNewMagazine: (item) -> + alert "I've got new "+item + class NewspaperSubscriber - onNewNewspaper: (item) -> - alert "I've got new "+item + onNewNewspaper: (item) -> + alert "I've got new "+item postOffice = new PostOffice() sub1 = new MagazineSubscriber() sub2 = new NewspaperSubscriber() postOffice.subscribe "Mens Health", sub1.onNewMagazine postOffice.subscribe "Times", sub2.onNewNewspaper -postOffice.sendNewItemReleased "Times" -postOffice.sendNewItemReleased "Mens Health" +postOffice.notifyNewItemReleased "Times" +postOffice.notifyNewItemReleased "Mens Health" + {% endhighlight %} ## Discussion Here you have an observer object (PostOffice) and observable objects (MagazineSubscriber, NewspaperSubscriber). To be notified about an event of publishing new periodical observable object should make subscribtion on PostOffice. -Every of subscribed objects is stored internaly in the PostOffice array of subscribers. +Every of subscribed objects is stored internaly in the PostOffice array of subscribtions. Every subscriber is notified on new concrete periodical is published. From 46a0b9e3c2c159b18125aea2cde15a67a52f3ac7 Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Sat, 27 Jul 2013 22:12:15 -0300 Subject: [PATCH 192/267] fix observer page layout and remove :rubygems that is deprecated from Gemfile --- Gemfile | 2 +- chapters/design_patterns/observer.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index b744ba1..ebe0f96 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source :rubygems +source 'http://rubygems.org/' group :development do gem "RedCloth", "~> 4.2" diff --git a/chapters/design_patterns/observer.md b/chapters/design_patterns/observer.md index 4976e63..f796b78 100644 --- a/chapters/design_patterns/observer.md +++ b/chapters/design_patterns/observer.md @@ -1,5 +1,5 @@ --- -layout: recipe Observer Pattern +layout: recipe title: Observer Pattern chapter: Design patterns --- @@ -20,7 +20,7 @@ class PostOffice subscriber.callback(item) for subscriber in @subscribers when subscriber.item is item subscribe: (to, onNewItemReleased) -> @subscribers.push {'item':to, 'callback':onNewItemReleased} - + class MagazineSubscriber onNewMagazine: (item) -> alert "I've got new "+item @@ -41,7 +41,7 @@ postOffice.notifyNewItemReleased "Mens Health" ## Discussion -Here you have an observer object (PostOffice) and observable objects (MagazineSubscriber, NewspaperSubscriber). -To be notified about an event of publishing new periodical observable object should make subscribtion on PostOffice. -Every of subscribed objects is stored internaly in the PostOffice array of subscribtions. +Here you have an observer object (PostOffice) and observable objects (MagazineSubscriber, NewspaperSubscriber). +To be notified about an event of publishing new periodical observable object should make subscribtion on PostOffice. +Every of subscribed objects is stored internaly in the PostOffice array of subscribtions. Every subscriber is notified on new concrete periodical is published. From 57747556034440204359f767646b458ca4bb0ed9 Mon Sep 17 00:00:00 2001 From: Tatiana Shemiakina Date: Tue, 30 Jul 2013 22:23:19 +0300 Subject: [PATCH 193/267] Create adapter --- chapters/design_patterns/adapter | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 chapters/design_patterns/adapter diff --git a/chapters/design_patterns/adapter b/chapters/design_patterns/adapter new file mode 100644 index 0000000..95db31a --- /dev/null +++ b/chapters/design_patterns/adapter @@ -0,0 +1,60 @@ +--- +layout: default +title: Adapter pattern +--- + +## Sample recipe template + +Create a new `my-recipe.md` file and use this text as a start. + +{% highlight text %} +--- +layout: recipe +title: Adapter patter +chapter: Design patterns +--- +## Problem + +Suppose we have 3-rd party grid component. We want to apply there our own custom sorting but a small problem. Our custom sorter does not implement required interface by grid component. +To understand the problem completely best example would be an socket from our usual life. Everybody knows this device. In some countries it has 3 pins and in other contries it has only 2 pins. +This is exactly right situation to use adapter. + +## Solution + +#grid component +class AwesomeGrid + constructor: (@datasource)-> + @sort_order = 'ASC' + @sorter = new NullSorter # in this place we use NullObject pattern (another usefull pattern) + setCustomSorter: (@customSorter) -> + @sorter = customSorter + sort: () -> + @datasource = @sorter.sort @datasource, @sort_order + # don't forget to change sort order + + +class NullSorter + sort: (data, order) -> # do nothing; it is just a stub + +class RandomSorter + sort: (data)-> + for i in [data.length-1..1] #let's shuffle the data a bit + j = Math.floor Math.random() * (i + 1) + [data[i], data[j]] = [data[j], data[i]] + return data + +class RandomSorterAdapter + constructor: (@sorter) -> + sort: (data, order) -> + @sorter.sort data + +agrid = new AwesomeGrid ['a','b','c','d','e','f'] +agrid.setCustomSorter new RandomSorterAdapter(new RandomSorter) +agrid.sort() # sort data with custom sorter through adapter + +## Discussion + +Adapter is usefull when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3-rd party libraries or you work with legacy code. +In any case be carefull with adapter: it can be helpfull but it can instigate design errors. + +{% endhighlight %} From 8a510977fd926dcf3e0ada13545b6990b514a1a5 Mon Sep 17 00:00:00 2001 From: Tatiana Shemiakina Date: Tue, 30 Jul 2013 22:26:02 +0300 Subject: [PATCH 194/267] Update and rename adapter to adapter.md --- chapters/design_patterns/{adapter => adapter.md} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename chapters/design_patterns/{adapter => adapter.md} (95%) diff --git a/chapters/design_patterns/adapter b/chapters/design_patterns/adapter.md similarity index 95% rename from chapters/design_patterns/adapter rename to chapters/design_patterns/adapter.md index 95db31a..e6a3d67 100644 --- a/chapters/design_patterns/adapter +++ b/chapters/design_patterns/adapter.md @@ -20,8 +20,8 @@ To understand the problem completely best example would be an socket from our us This is exactly right situation to use adapter. ## Solution - -#grid component +{% highlight coffeescript %} +# a fragment of 3-rd party grid component class AwesomeGrid constructor: (@datasource)-> @sort_order = 'ASC' @@ -52,6 +52,8 @@ agrid = new AwesomeGrid ['a','b','c','d','e','f'] agrid.setCustomSorter new RandomSorterAdapter(new RandomSorter) agrid.sort() # sort data with custom sorter through adapter +{% endhighlight %} + ## Discussion Adapter is usefull when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3-rd party libraries or you work with legacy code. From 5fc588889f546f4f958f782c6c4b268712dacd6f Mon Sep 17 00:00:00 2001 From: Tatiana Shemiakina Date: Tue, 30 Jul 2013 22:33:03 +0300 Subject: [PATCH 195/267] Update markup; add link to wiki adapter page --- chapters/design_patterns/adapter.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/chapters/design_patterns/adapter.md b/chapters/design_patterns/adapter.md index e6a3d67..8be4ece 100644 --- a/chapters/design_patterns/adapter.md +++ b/chapters/design_patterns/adapter.md @@ -1,14 +1,4 @@ --- -layout: default -title: Adapter pattern ---- - -## Sample recipe template - -Create a new `my-recipe.md` file and use this text as a start. - -{% highlight text %} ---- layout: recipe title: Adapter patter chapter: Design patterns @@ -17,9 +7,10 @@ chapter: Design patterns Suppose we have 3-rd party grid component. We want to apply there our own custom sorting but a small problem. Our custom sorter does not implement required interface by grid component. To understand the problem completely best example would be an socket from our usual life. Everybody knows this device. In some countries it has 3 pins and in other contries it has only 2 pins. -This is exactly right situation to use adapter. +This is exactly right situation to use [adapter pattern](https://en.wikipedia.org/wiki/Adapter_pattern). ## Solution + {% highlight coffeescript %} # a fragment of 3-rd party grid component class AwesomeGrid @@ -58,5 +49,3 @@ agrid.sort() # sort data with custom sorter through adapter Adapter is usefull when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3-rd party libraries or you work with legacy code. In any case be carefull with adapter: it can be helpfull but it can instigate design errors. - -{% endhighlight %} From a97b9d157f8bb95c14bb45fca2ed151b27334916 Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 6 Aug 2013 11:51:51 -0500 Subject: [PATCH 196/267] Simplified and sped up the "type-function" example. --- chapters/classes_and_objects/type-function.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/chapters/classes_and_objects/type-function.md b/chapters/classes_and_objects/type-function.md index 42952ac..96e8cea 100644 --- a/chapters/classes_and_objects/type-function.md +++ b/chapters/classes_and_objects/type-function.md @@ -12,16 +12,20 @@ You'd like to know the type of a object without using typeof. (See http://javasc Use the following function: {% highlight coffeescript %} -type = (obj) -> - if obj == undefined or obj == null - return String obj - classToType = new Object - for name in "Boolean Number String Function Array Date RegExp".split(" ") - classToType["[object " + name + "]"] = name.toLowerCase() - myClass = Object.prototype.toString.call obj - if myClass of classToType - return classToType[myClass] - return "object" + type = (obj) -> + if obj == undefined or obj == null + return String obj + classToType = { + '[object Boolean]': 'boolean', + '[object Number]': 'number', + '[object String]': 'string', + '[object Function]': 'function', + '[object Array]': 'array', + '[object Date]': 'date', + '[object RegExp]': 'regexp', + '[object Object]': 'object' + } + return classToType[Object.prototype.toString.call(obj)] {% endhighlight %} ## Discussion From 8bd340fb0c863c116e4cf248c7c0d733d6e662fb Mon Sep 17 00:00:00 2001 From: Frankie Bagnardi Date: Sat, 10 Aug 2013 03:01:08 -0700 Subject: [PATCH 197/267] Math.max.apply note is confusing --- chapters/arrays/max-array-value.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index 906eaac..ce3adc1 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -16,18 +16,14 @@ Math.max [12, 32, 11, 67, 1, 3]... # => 67 {% endhighlight %} -Alternatively, it's possible to use ES5 `reduce` method. For backward compatibility with older JavaScript implementations, use Math.max.apply: +Alternatively, it's possible to use ES5 `reduce` method. For backward compatibility with older JavaScript implementations, use the above. {% highlight coffeescript %} # ECMAScript 5 [12,32,11,67,1,3].reduce (a,b) -> Math.max a, b # => 67 - -# Pre-ES5 -Math.max.apply(null, [12,32,11,67,1,3]) -# => 67 {% endhighlight %} ## Discussion -`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable ammount of arguments, such as `console.log`. \ No newline at end of file +`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable ammount of arguments, such as `console.log`. From 3d3acde05f6d749adcdf87e85617dd689e96d29c Mon Sep 17 00:00:00 2001 From: Frankie Bagnardi Date: Sat, 10 Aug 2013 03:19:18 -0700 Subject: [PATCH 198/267] r.randi is undefined, should be r.rand --- chapters/math/generating-predictable-random-numbers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/math/generating-predictable-random-numbers.md b/chapters/math/generating-predictable-random-numbers.md index a7cc561..128bc61 100644 --- a/chapters/math/generating-predictable-random-numbers.md +++ b/chapters/math/generating-predictable-random-numbers.md @@ -62,4 +62,4 @@ Avoid the temptation to modulus the output of this generator. If you need an int r.randn() % 2 {% endhighlight %} -because you will most definitely not get random digits. Use `r.randi(2)` instead. +because you will most definitely not get random digits. Use `r.rand(2)` instead. From c2ff4ae0fa8123dfdefe3f2653fea3c92cc82e5a Mon Sep 17 00:00:00 2001 From: Peter Hellberg Date: Sun, 18 Aug 2013 14:19:13 +0200 Subject: [PATCH 199/267] Updated development dependencies --- Gemfile | 12 +++---- Gemfile.lock | 98 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 40 deletions(-) diff --git a/Gemfile b/Gemfile index ebe0f96..b4d66ba 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,9 @@ -source 'http://rubygems.org/' +source 'https://rubygems.org/' group :development do - gem "RedCloth", "~> 4.2" - gem "foreman", "~> 0.13" - gem "serve", "~> 1.0" - gem "jekyll", "~> 0.10" - gem "thin", "~> 1.2" + gem "github-pages" + gem "tzinfo-data" + + gem "foreman", "~> 0.63" + gem "serve", "~> 1.5" end diff --git a/Gemfile.lock b/Gemfile.lock index 0b46357..d8185b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,47 +1,77 @@ GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: RedCloth (4.2.9) - activesupport (3.0.7) + activesupport (3.2.14) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) classifier (1.3.3) fast-stemmer (>= 1.0.0) - daemons (1.1.3) - directory_watcher (1.4.0) - eventmachine (0.12.10) - fast-stemmer (1.0.0) - foreman (0.13.0) - term-ansicolor (~> 1.0.5) + colorator (0.1) + commander (4.1.5) + highline (~> 1.6.11) + directory_watcher (1.4.1) + dotenv (0.8.0) + fast-stemmer (1.0.2) + foreman (0.63.0) + dotenv (>= 0.7) thor (>= 0.13.6) - i18n (0.4.2) - jekyll (0.10.0) - classifier (>= 1.3.1) - directory_watcher (>= 1.1.1) - liquid (>= 1.9.0) - maruku (>= 0.5.9) - liquid (2.2.2) - maruku (0.6.0) + github-pages (1) + RedCloth (= 4.2.9) + jekyll (= 1.1.2) + kramdown (= 1.0.2) + liquid (= 2.5.1) + maruku (= 0.6.1) + rdiscount (= 1.6.8) + redcarpet (= 2.2.2) + highline (1.6.19) + i18n (0.6.5) + jekyll (1.1.2) + classifier (~> 1.3) + colorator (~> 0.1) + commander (~> 4.1.3) + directory_watcher (~> 1.4.1) + kramdown (~> 1.0.2) + liquid (~> 2.5.1) + maruku (~> 0.5) + pygments.rb (~> 0.5.0) + redcarpet (~> 2.2.2) + safe_yaml (~> 0.7.0) + kramdown (1.0.2) + liquid (2.5.1) + maruku (0.6.1) syntax (>= 1.0.0) - rack (1.2.2) - serve (1.0.0) - activesupport (~> 3.0.1) - i18n (~> 0.4.1) - rack (~> 1.2.1) - tzinfo (~> 0.3.23) + multi_json (1.7.9) + posix-spawn (0.3.6) + pygments.rb (0.5.2) + posix-spawn (~> 0.3.6) + yajl-ruby (~> 1.1.0) + rack (1.5.2) + rack-test (0.6.2) + rack (>= 1.0) + rdiscount (1.6.8) + redcarpet (2.2.2) + safe_yaml (0.7.1) + serve (1.5.2) + activesupport (~> 3.2.12) + i18n + rack (~> 1.5.2) + rack-test (~> 0.6.2) + tilt (~> 1.3.3) + tzinfo syntax (1.0.0) - term-ansicolor (1.0.5) - thin (1.2.11) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) - thor (0.14.6) - tzinfo (0.3.27) + thor (0.18.1) + tilt (1.3.7) + tzinfo (1.0.1) + tzinfo-data (1.2013.4) + tzinfo (>= 1.0.0) + yajl-ruby (1.1.0) PLATFORMS ruby DEPENDENCIES - RedCloth (~> 4.2) - foreman (~> 0.13) - jekyll (~> 0.10) - serve (~> 1.0) - thin (~> 1.2) + foreman (~> 0.63) + github-pages + serve (~> 1.5) + tzinfo-data From fffd98eed9629cb74817e6c9bc2d1a7d906b6b20 Mon Sep 17 00:00:00 2001 From: Peter Hellberg Date: Sun, 18 Aug 2013 14:21:24 +0200 Subject: [PATCH 200/267] Now using the github-pages gem in order to better mirror the production environment. --- .ruby-gemset | 1 + .ruby-version | 1 + Procfile | 2 +- _config.yml | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .ruby-gemset create mode 100644 .ruby-version diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 0000000..119b7fd --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +coffeescript-cookbook diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..77fee73 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +1.9.3 diff --git a/Procfile b/Procfile index c41ebb6..26a670a 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ -jekyll: bundle exec jekyll --auto +jekyll: bundle exec jekyll build --watch serve: bundle exec serve 4000 development _site diff --git a/_config.yml b/_config.yml index f61488e..fef8911 100644 --- a/_config.yml +++ b/_config.yml @@ -1,5 +1,7 @@ +safe: true pygments: true lsi: false +markdown: redcarpet exclude: - README.md - CNAME From 7ebd0275b2c5569b7a5f30723551028d8d8ea6b5 Mon Sep 17 00:00:00 2001 From: Peter Hellberg Date: Sun, 18 Aug 2013 15:13:54 +0200 Subject: [PATCH 201/267] Updated the Developer's Guide to better reflect the current workflow. --- developers-guide.md | 54 ++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/developers-guide.md b/developers-guide.md index 8d8c210..909847e 100644 --- a/developers-guide.md +++ b/developers-guide.md @@ -9,7 +9,8 @@ _Please help out by updating this page_ ### Operating System -It works on Mac OSX. Probably works without any changes or issues on linux. A masochist could probably get it working on Windows. +It works on Mac OSX. Probably works without any changes or issues on Linux. +A masochist could probably get it working on Windows. ## Installation @@ -19,54 +20,47 @@ It works on Mac OSX. Probably works without any changes or issues on linux. A ma git clone git://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com.git {% endhighlight %} -### Create a Ruby Gemset +### Ruby environment -Optional, but highly recommended. +You probably want to have [RVM](http://rvm.io/) installed. -{% highlight bash %} -$ rvm gemset create jekyll -$ echo 'rvm gemset use jekyll' >> .rvmrc -{% endhighlight %} - -### Install Required Gems +The project includes a `.ruby-version` file locked to +*1.9.3* since that is what Github Pages are currently using. -{% highlight bash %} -gem install jekyll # needed for testing building the site -gem install RedCloth # needed for .md rendering -gem install serve # needed for resolving .html files w/o extension -gem install thin # optional; more efficient webserver than Webrick but not strictly necessary -{% endhighlight %} +There is also a `.ruby-gemset` that is set to *coffeescript-cookbook* -### Install pygments +### Required dependencies -You'll need python installed for this. Macs and most linuces come with it preinstalled. +We are using [Bundler](http://bundler.io/) to install the required Ruby dependencies. {% highlight bash %} -easy_install pygments # for syntax highlighting +bundle install {% endhighlight %} -## Building and Viewing the Website - -### Run jekyll +#### Install pygments -Open a terminal window, cd into the project folder and run jekyll from the project root. +You'll need python installed for this. +Macs and most Linuces come with it preinstalled. {% highlight bash %} -jekyll --auto +easy_install pygments # for syntax highlighting {% endhighlight %} -Leave this window running while you work. Any time you change a file, jekyll will rerender it into the `_site` folder. - -### Run serve +## Building and Viewing the Website -Open another terminal window, cd into the project folder, then cd into the `_site` subfolder, and run +Open a terminal window, cd into the project folder and run `foreman start` from the project root. {% highlight bash %} -serve +foreman start {% endhighlight %} -This will start a webserver in the `_site` folder. Open a browser and visit `http://localhost:4000/` and you should see the site. +Leave this window running while you work. +Any time you change a file, jekyll will rerender it into the `_site` folder. + +Open a browser and visit and you should see the site. ## Minutiae and Other Trivialities -jekyll can take a second or two to catch up when you save a file. If you edit a file and don't see the changes in your browser, give it a second or two and try again. You may also see Maruku warnings, but as long as it prints `Successfully generated site` you should be alright. +Jekyll can take a second or two to catch up when you save a file. +If you edit a file and don't see the changes in your browser, give it a second or two and try again. +As long as it prints `Successfully generated site` you should be alright. From 31a0d63425aa933afd11fa3a731fba5d1b06ef77 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 5 Sep 2013 18:24:15 +0800 Subject: [PATCH 202/267] Fix 5 typos on the Adapter design pattern page. --- chapters/design_patterns/adapter.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chapters/design_patterns/adapter.md b/chapters/design_patterns/adapter.md index 8be4ece..2d6fc56 100644 --- a/chapters/design_patterns/adapter.md +++ b/chapters/design_patterns/adapter.md @@ -1,12 +1,12 @@ --- layout: recipe -title: Adapter patter +title: Adapter pattern chapter: Design patterns --- ## Problem Suppose we have 3-rd party grid component. We want to apply there our own custom sorting but a small problem. Our custom sorter does not implement required interface by grid component. -To understand the problem completely best example would be an socket from our usual life. Everybody knows this device. In some countries it has 3 pins and in other contries it has only 2 pins. +To understand the problem completely best example would be an socket from our usual life. Everybody knows this device. In some countries it has 3 pins and in other countries it has only 2 pins. This is exactly right situation to use [adapter pattern](https://en.wikipedia.org/wiki/Adapter_pattern). ## Solution @@ -47,5 +47,5 @@ agrid.sort() # sort data with custom sorter through adapter ## Discussion -Adapter is usefull when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3-rd party libraries or you work with legacy code. -In any case be carefull with adapter: it can be helpfull but it can instigate design errors. +Adapter is useful when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3-rd party libraries or you work with legacy code. +In any case be careful with adapter: it can be helpful but it can instigate design errors. From 68aaa998b96077340a707b5ad286e07b405fb061 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 5 Sep 2013 18:26:21 +0800 Subject: [PATCH 203/267] Anglicize the problem statement. --- chapters/design_patterns/adapter.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/chapters/design_patterns/adapter.md b/chapters/design_patterns/adapter.md index 2d6fc56..19aaca9 100644 --- a/chapters/design_patterns/adapter.md +++ b/chapters/design_patterns/adapter.md @@ -5,9 +5,12 @@ chapter: Design patterns --- ## Problem -Suppose we have 3-rd party grid component. We want to apply there our own custom sorting but a small problem. Our custom sorter does not implement required interface by grid component. -To understand the problem completely best example would be an socket from our usual life. Everybody knows this device. In some countries it has 3 pins and in other countries it has only 2 pins. -This is exactly right situation to use [adapter pattern](https://en.wikipedia.org/wiki/Adapter_pattern). +Imagine you are traveling to a foreign country and once at your hotel room you realize your power cord is not compatible with the wall electric socket. +Luckily you remember you brought with you a socket adapter. +It will connect to the wall socket on one side and to your power cord on the other side, allowing for communication between them. + +The same situation may arise in code, when 2 (or more) instances (of classes, modules, etc.) want to talk to each other, but whose communication protocol (e.i. the language they use to communicate) is different from each other. +In such a situation, the [Adapter Pattern](//en.wikipedia.org/wiki/Adapter_pattern) comes in handy. It will do the translation, from one side to the other. ## Solution @@ -47,5 +50,5 @@ agrid.sort() # sort data with custom sorter through adapter ## Discussion -Adapter is useful when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3-rd party libraries or you work with legacy code. +Adapter is useful when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3rd party libraries or you work with legacy code. In any case be careful with adapter: it can be helpful but it can instigate design errors. From 2324b0344fb569798d30db10de69287554826d0a Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 5 Sep 2013 19:06:11 +0800 Subject: [PATCH 204/267] Bring CoffeeScript idiom to conditional statement. An advantage of CoffeeScript compared to JavaScript is its ability to check for array membership with the `in` keyword. A feature it borrowed from Python. --- chapters/ajax/ajax_request_without_jquery.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/ajax/ajax_request_without_jquery.md b/chapters/ajax/ajax_request_without_jquery.md index 5956548..7f69c93 100644 --- a/chapters/ajax/ajax_request_without_jquery.md +++ b/chapters/ajax/ajax_request_without_jquery.md @@ -47,7 +47,7 @@ loadDataFromServer = -> req.addEventListener 'readystatechange', -> if req.readyState is 4 # ReadyState Compelte - if req.status is 200 or req.status is 304 # Success result codes + if req.status in [200, 304] # Success result codes data = eval '(' + req.responseText + ')' console.log 'data message: ', data.message else From cf2867beba00fe2cc0dbe9bf315086b4b7cd615f Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 5 Sep 2013 19:29:49 +0800 Subject: [PATCH 205/267] Amount takes only 1 'm'. --- chapters/arrays/max-array-value.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index ce3adc1..25f1472 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -26,4 +26,4 @@ Alternatively, it's possible to use ES5 `reduce` method. For backward compatibil ## Discussion -`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable ammount of arguments, such as `console.log`. +`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable amount of arguments, such as `console.log`. From 227978f0c15f08ff7b4970cd531f8354e44496c3 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 5 Sep 2013 21:29:53 +0800 Subject: [PATCH 206/267] Improve readability. --- chapters/ajax/ajax_request_without_jquery.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapters/ajax/ajax_request_without_jquery.md b/chapters/ajax/ajax_request_without_jquery.md index 7f69c93..d95713c 100644 --- a/chapters/ajax/ajax_request_without_jquery.md +++ b/chapters/ajax/ajax_request_without_jquery.md @@ -47,7 +47,8 @@ loadDataFromServer = -> req.addEventListener 'readystatechange', -> if req.readyState is 4 # ReadyState Compelte - if req.status in [200, 304] # Success result codes + successResultCodes = [200, 304] + if req.status in successResultCodes data = eval '(' + req.responseText + ')' console.log 'data message: ', data.message else From 026fd00545b36330624e6de3189ee0864ef60539 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 7 Sep 2013 13:21:39 +0800 Subject: [PATCH 207/267] Better rephrase the problem statement. --- chapters/design_patterns/adapter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/design_patterns/adapter.md b/chapters/design_patterns/adapter.md index 19aaca9..7e2af73 100644 --- a/chapters/design_patterns/adapter.md +++ b/chapters/design_patterns/adapter.md @@ -5,9 +5,9 @@ chapter: Design patterns --- ## Problem -Imagine you are traveling to a foreign country and once at your hotel room you realize your power cord is not compatible with the wall electric socket. -Luckily you remember you brought with you a socket adapter. -It will connect to the wall socket on one side and to your power cord on the other side, allowing for communication between them. +Imagine you are traveling to a foreign country and once at your hotel room you realize your power cord socket is not compatible with the wall socket. +Luckily, you remembered you've brought your power adapter with you. +It will connect your power cord socket on one side and wall socket on the other side, allowing for communication between them. The same situation may arise in code, when 2 (or more) instances (of classes, modules, etc.) want to talk to each other, but whose communication protocol (e.i. the language they use to communicate) is different from each other. In such a situation, the [Adapter Pattern](//en.wikipedia.org/wiki/Adapter_pattern) comes in handy. It will do the translation, from one side to the other. From 5f35db8957f5938c27bc9bdf3721a25af075e898 Mon Sep 17 00:00:00 2001 From: Jakob Krigovsky Date: Fri, 13 Sep 2013 19:40:39 +0200 Subject: [PATCH 208/267] =?UTF-8?q?Automatically=20update=20=E2=80=9Clast?= =?UTF-8?q?=20updated=E2=80=9D=20date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _layouts/chapter.html | 2 +- _layouts/default.html | 2 +- _layouts/recipe.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/_layouts/chapter.html b/_layouts/chapter.html index b075d2c..2bfe664 100644 --- a/_layouts/chapter.html +++ b/_layouts/chapter.html @@ -55,7 +55,7 @@

            {{ page.title }}

            diff --git a/_layouts/default.html b/_layouts/default.html index 1f00656..b45615d 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -52,7 +52,7 @@

            ó

    4. diff --git a/_layouts/recipe.html b/_layouts/recipe.html index d71dfba..fd6e969 100644 --- a/_layouts/recipe.html +++ b/_layouts/recipe.html @@ -54,7 +54,7 @@

      {{ page.title }}

      From 0776427f49975b494a218e3144be078a7ce714bf Mon Sep 17 00:00:00 2001 From: Calum Robertson Date: Thu, 19 Sep 2013 19:08:22 +0100 Subject: [PATCH 209/267] Added the Template Method Pattern. --- authors.md | 1 + chapters/design_patterns/template_method.md | 45 +++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 chapters/design_patterns/template_method.md diff --git a/authors.md b/authors.md index be4922c..7876aa2 100644 --- a/authors.md +++ b/authors.md @@ -21,6 +21,7 @@ The following people are totally rad and awesome because they have contributed r * Frederic Hemberger * Mike Hatfield *oakraven13@gmail.com* * [Anton Rissanen](http://github.com/antris) *hello@anton.fi* +* Calum Robertson *http://github.com/randusr836* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/design_patterns/template_method.md b/chapters/design_patterns/template_method.md new file mode 100644 index 0000000..607be35 --- /dev/null +++ b/chapters/design_patterns/template_method.md @@ -0,0 +1,45 @@ +--- +layout: recipe +title: Template Method Pattern +chapter: Design Patterns +--- +## Problem + +You need to execute a series of steps according to some algorithm or recipe and wish to provide the implementation for any of the steps. + +## Solution + +Use the Template Method to describe each step in a superclass, delegating the implementation of each step to a subclass. + +For example, imagine you wish to model various types of document and each one may contain a header and a body. + +{% highlight coffeescript %} +class Document + produceDocument: -> + @produceHeader() + @produceBody() + + produceHeader: -> + produceBody: -> + +class DocWithHeader extends Document + produceHeader: -> + console.log "Producing header for DocWithHeader" + + produceBody: -> + console.log "Producing body for DocWithHeader" + +class DocWithoutHeader extends Document + produceBody: -> + console.log "Producing body for DocWithoutHeader" + +doc1 = new DocWithHeader +doc1.produceDocument() + +doc2 = new DocWithoutHeader +doc2.produceDocument() +{% endhighlight %} + +## Discussion + +In this example, there are two steps, one for producing a document header and the second for producing the document body; these two steps are given empty implementations in the superclass. The DocWithHeader implements both the body and header steps, whereas the DocWithoutHeader only impements the body step. From 198eed36fca85a819aac55ee534432d4dfcb0b9c Mon Sep 17 00:00:00 2001 From: Calum Robertson Date: Fri, 20 Sep 2013 21:39:13 +0100 Subject: [PATCH 210/267] Changes after feedback. Wrote a little about polymorphism being used in the pattern. Described in greater detail the concepts of the algorithm structure and the specification of algorithm step behaviour. --- chapters/design_patterns/template_method.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/chapters/design_patterns/template_method.md b/chapters/design_patterns/template_method.md index 607be35..31fe475 100644 --- a/chapters/design_patterns/template_method.md +++ b/chapters/design_patterns/template_method.md @@ -5,13 +5,13 @@ chapter: Design Patterns --- ## Problem -You need to execute a series of steps according to some algorithm or recipe and wish to provide the implementation for any of the steps. +Define the structure of an algorithm as a series of high-level steps, making it possible to specify the behaviour of each step, giving rise to a family of algorithms that have the same structure but different behaviours. ## Solution -Use the Template Method to describe each step in a superclass, delegating the implementation of each step to a subclass. +Use the Template Method to describe the algorithm structure in a superclass, delegating the implementation of some steps to one or more concrete subclasses. -For example, imagine you wish to model various types of document and each one may contain a header and a body. +For example, imagine you wish to model the production of various types of document and each one may contain a header and a body. {% highlight coffeescript %} class Document @@ -33,13 +33,12 @@ class DocWithoutHeader extends Document produceBody: -> console.log "Producing body for DocWithoutHeader" -doc1 = new DocWithHeader -doc1.produceDocument() - -doc2 = new DocWithoutHeader -doc2.produceDocument() +docs = [new DocWithHeader, new DocWithoutHeader] +doc.produceDocument() for doc in docs {% endhighlight %} ## Discussion -In this example, there are two steps, one for producing a document header and the second for producing the document body; these two steps are given empty implementations in the superclass. The DocWithHeader implements both the body and header steps, whereas the DocWithoutHeader only impements the body step. +In this example, the algorithm consists of two steps describing document production: one for producing a document header and the second for producing the document body. An empty method implementation for each step is present in the superclass and polymorphism is exploited such that each concrete subclass can provide a different implementation for a step by overriding a step method. In this example,the DocWithHeader implements both the body and header steps, whereas the DocWithoutHeader only implements the body step. + +The production of different types of document is then straightforward when document objects are stored in an array, and it is then a simple of matter of iterating over each document object and calling its produceDocument method. \ No newline at end of file From 2228b639d6e1b06cdb9d15b4a18aa1e971e9b0cf Mon Sep 17 00:00:00 2001 From: Devin Weaver Date: Tue, 8 Oct 2013 00:48:21 -0400 Subject: [PATCH 211/267] Changed to best practice of the `this` keyword I've seen several people on IRC talk about readability. It seems there is some opinions that when chaining like this the `this` keyword is more readable then a dangling `@` symbol. I think it is a good idea to offer code readability in this project. Feel free to revert this commit if this is disputed. (Unable to open a PR from in the Github UI). --- chapters/classes_and_objects/chaining.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/classes_and_objects/chaining.md b/chapters/classes_and_objects/chaining.md index 65f450f..be19212 100644 --- a/chapters/classes_and_objects/chaining.md +++ b/chapters/classes_and_objects/chaining.md @@ -20,13 +20,13 @@ class CoffeeCup sugar: false strength: (newStrength) -> @properties.strength = newStrength - @ + this cream: (newCream) -> @properties.cream = newCream - @ + this sugar: (newSugar) -> @properties.sugar = newSugar - @ + this morningCup = new CoffeeCup() From 5358690140a8d0a8efffe43cf3bac8ccb2857e06 Mon Sep 17 00:00:00 2001 From: Stephen Daves Date: Tue, 22 Oct 2013 23:20:23 -0600 Subject: [PATCH 212/267] Remove unneeded word --- chapters/design_patterns/singleton.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/design_patterns/singleton.md b/chapters/design_patterns/singleton.md index 18bbf27..6aff70c 100644 --- a/chapters/design_patterns/singleton.md +++ b/chapters/design_patterns/singleton.md @@ -12,7 +12,7 @@ Many times you only want one, and only one, instance of a class. For example, yo The publicly available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned. -This is works because CoffeeScript allows you to define executable statements inside a class definition. However, because most CoffeeScript compiles into a [IIFE][] wrapper you do not have to place the private class inside the class definition if this style suits you. The later might be useful when developing modular code such as found in [CommonJS][] (Node.js) or [Require.js][] (See the discussion for an example). +This works because CoffeeScript allows you to define executable statements inside a class definition. However, because most CoffeeScript compiles into a [IIFE][] wrapper you do not have to place the private class inside the class definition if this style suits you. The later might be useful when developing modular code such as found in [CommonJS][] (Node.js) or [Require.js][] (See the discussion for an example). [IIFE]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/ [CommonJS]: http://www.commonjs.org/ From 2cc50bce27bbd9f70341678c6acf3c565ed255b9 Mon Sep 17 00:00:00 2001 From: Orban Botond Date: Sun, 3 Nov 2013 15:48:40 +0200 Subject: [PATCH 213/267] FIX #95 --- chapters/arrays/testing-every-element.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/testing-every-element.md b/chapters/arrays/testing-every-element.md index 5978500..4f82c2a 100644 --- a/chapters/arrays/testing-every-element.md +++ b/chapters/arrays/testing-every-element.md @@ -12,7 +12,7 @@ You want to be able to check that every element in an array meets a particular c Use Array.every (ECMAScript 5): {% highlight coffeescript %} -evens = (x for x in [1..10] by 2) +evens = (x for x in [0..10] by 2) evens.every (x)-> x % 2 == 0 # => true From 2d16a576acd38b3666737d4861aca87652ee3502 Mon Sep 17 00:00:00 2001 From: jlburkhead Date: Sun, 15 Dec 2013 04:49:15 -0500 Subject: [PATCH 214/267] add an example of generating random numbers in an arbitrary range --- chapters/math/generating-random-numbers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chapters/math/generating-random-numbers.md b/chapters/math/generating-random-numbers.md index f181940..afb1c5c 100644 --- a/chapters/math/generating-random-numbers.md +++ b/chapters/math/generating-random-numbers.md @@ -24,6 +24,12 @@ percentile = Math.floor(Math.random() * 100) dice = Math.floor(Math.random() * 6) + 1 1 <= dice <= 6 # => true + +max = 42 +min = -13 +range = Math.random() * (max - min) + min +-13 <= range < 42 +# => true {% endhighlight %} ## Discussion From dd4f0b9ea9a60ba4b3d31abe34f92f643074a129 Mon Sep 17 00:00:00 2001 From: jlburkhead Date: Sun, 15 Dec 2013 05:06:33 -0500 Subject: [PATCH 215/267] add recipe on exponents and logarithms --- .../working-with-exponents-and-logarithms.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 chapters/math/working-with-exponents-and-logarithms.md diff --git a/chapters/math/working-with-exponents-and-logarithms.md b/chapters/math/working-with-exponents-and-logarithms.md new file mode 100644 index 0000000..6891530 --- /dev/null +++ b/chapters/math/working-with-exponents-and-logarithms.md @@ -0,0 +1,37 @@ +--- +layout: recipe +title: Working with Exponents and Logarithms +chapter: Math +--- +## Problem + +You need to do some calculations that involve exponents and logarithms. + +## Solution + +Use Javascript's Math object to provide common mathematical functions. + +{% highlight coffeescript %} +# Math.pow(x, y) returns x^y +Math.pow(2, 4) +# => 16 + +# Math.exp(x) returns E^x and is shorthand for Math.pow(Math.E, x) +Math.exp(2) +# => 7.38905609893065 + +# Math.log returns the natural (base E) log +Math.log(5) +# => 1.6094379124341003 +Math.log(Math.exp(42)) +# => 42 + +# To get a log with some other base n, divide by Math.log(n) +Math.log(100) / Math.log(10) +# => 2 + +{% endhighlight %} + +## Discussion + +For more information on the Math object see the documentation on the [Mozilla Developer Netword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). Also refer to [Converting Radians and Degrees](/chapters/math/radians-degrees) for discussion of the various constants in the Math object. From 658abba3357ff67286019b5a376a0642be341ca3 Mon Sep 17 00:00:00 2001 From: jlburkhead Date: Sun, 15 Dec 2013 15:10:48 -0500 Subject: [PATCH 216/267] fix another typo and link to correct reference --- authors.md | 1 + chapters/math/working-with-exponents-and-logarithms.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/authors.md b/authors.md index 7876aa2..70bea43 100644 --- a/authors.md +++ b/authors.md @@ -22,6 +22,7 @@ The following people are totally rad and awesome because they have contributed r * Mike Hatfield *oakraven13@gmail.com* * [Anton Rissanen](http://github.com/antris) *hello@anton.fi* * Calum Robertson *http://github.com/randusr836* +* Jake Burkhead *https://github.com/jlburkhead* * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/math/working-with-exponents-and-logarithms.md b/chapters/math/working-with-exponents-and-logarithms.md index 6891530..c8e7c49 100644 --- a/chapters/math/working-with-exponents-and-logarithms.md +++ b/chapters/math/working-with-exponents-and-logarithms.md @@ -34,4 +34,4 @@ Math.log(100) / Math.log(10) ## Discussion -For more information on the Math object see the documentation on the [Mozilla Developer Netword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). Also refer to [Converting Radians and Degrees](/chapters/math/radians-degrees) for discussion of the various constants in the Math object. +For more information on the Math object see the documentation on the [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). Also refer to [Math Constants](/chapters/math/constants) for discussion of the various constants in the Math object. From 3ff4351575f1dd89e9689a8b78ab7710a0e44961 Mon Sep 17 00:00:00 2001 From: Ayush Gupta Date: Tue, 18 Mar 2014 17:20:15 +0530 Subject: [PATCH 217/267] updated string interpolation to mention that it works only with double quoted strings --- chapters/strings/interpolation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapters/strings/interpolation.md b/chapters/strings/interpolation.md index b1e1e9e..a437be4 100644 --- a/chapters/strings/interpolation.md +++ b/chapters/strings/interpolation.md @@ -11,7 +11,8 @@ CoffeeScript Variable. ## Solution Use CoffeeScript's ruby-like string interpolation instead of -JavaScript's string addition. +JavaScript's string addition. You must use Double-quoted strings to +allow for interpolation. Single-quoted strings are treated as literals. Interpolation: From dc70747b1a4e8679f975b424311ed9ea08943412 Mon Sep 17 00:00:00 2001 From: Marc Bodmer Date: Sat, 29 Mar 2014 16:43:43 -0400 Subject: [PATCH 218/267] Remove completed recipes for wanted recipes documentation --- wanted-recipes.md | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/wanted-recipes.md b/wanted-recipes.md index 64eec51..5adc15d 100644 --- a/wanted-recipes.md +++ b/wanted-recipes.md @@ -8,16 +8,10 @@ Here's a list of recipes we think we need. Pick one, implement it, and remove it In the notes below, "JS" means the recipe is just a simple passthrough to an existing JavaScript method. -## Introduction - -We need a better introduction. Right now the first recipe is [Embedding JavaScript](/chapters/syntax/embedding_javascript), which doesn't set the right first impression. How about three or four recipes that hold new users' hands a bit more as the first section? - ## Syntax * Ensuring variables are closed over # with "do" -## Objects - ## Strings * HTML methods # JS .sup(), .sub(), .blink(), .link(url), etc. May not exist in your JS impl! @@ -45,10 +39,6 @@ evens.every even [1..10].some (x) -> x % 2 == 0 # => true {% endhighlight %} -## Dates and Times - -* Empty - ## Math * square root # JS Math.sqrt @@ -89,24 +79,11 @@ foo 1, 2, 3 # => 6 {% endhighlight %} -## jQuery - -## Regular Expressions - -## Networking - -* Streaming HTTP server -* Streaming HTTP client - -## AJAX - -* Getting data from a remote server # using raw XHTTPRequest instead of jQuery's `$.ajax` - ## Design patterns * Creational Patterns * Abstract Factory - * Prototype + * Prototype * Structural Patterns * Adapter From 6fe6cce0b1776795f516415fced927fab29e2c8a Mon Sep 17 00:00:00 2001 From: Simon Taranto Date: Wed, 2 Apr 2014 10:42:38 -0700 Subject: [PATCH 219/267] Remove duplicate words --- chapters/regular_expressions/searching-for-substrings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/regular_expressions/searching-for-substrings.md b/chapters/regular_expressions/searching-for-substrings.md index 1928591..888c774 100644 --- a/chapters/regular_expressions/searching-for-substrings.md +++ b/chapters/regular_expressions/searching-for-substrings.md @@ -23,7 +23,7 @@ match = /sample/i.test("Sample text") # => true {% endhighlight %} -The next way to is to call the `exec` method on a `RegExp` pattern or object. The `exec` method returns an array an array with the match information or `null`: +The next way to is to call the `exec` method on a `RegExp` pattern or object. The `exec` method returns an array with the match information or `null`: {% highlight coffeescript %} match = /s(amp)le/i.exec "Sample text" From 552577922c2209b10ae250926ceb71eb5a4eeb4d Mon Sep 17 00:00:00 2001 From: rdubigny Date: Sat, 5 Apr 2014 17:17:15 +0200 Subject: [PATCH 220/267] Change folder name "tests" -> "test" I just lost one hour because of that. --- chapters/testing/testing_with_nodeunit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/testing/testing_with_nodeunit.md b/chapters/testing/testing_with_nodeunit.md index 30af5b8..ec610d2 100644 --- a/chapters/testing/testing_with_nodeunit.md +++ b/chapters/testing/testing_with_nodeunit.md @@ -17,7 +17,7 @@ For example, we expect our calculator will be able to add and subtract and will {% highlight coffeescript %} -# tests/calculator.test.coffee +# test/calculator.test.coffee Calculator = require '../calculator' From 91e7d3eb8a49a124896fc3101cd4bd0195928e61 Mon Sep 17 00:00:00 2001 From: Joseph Chiocchi Date: Mon, 7 Apr 2014 17:08:07 -0500 Subject: [PATCH 221/267] fix typo on testing-every-element.md --- chapters/arrays/testing-every-element.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/arrays/testing-every-element.md b/chapters/arrays/testing-every-element.md index 4f82c2a..12b78a1 100644 --- a/chapters/arrays/testing-every-element.md +++ b/chapters/arrays/testing-every-element.md @@ -20,7 +20,7 @@ evens.every (x)-> x % 2 == 0 Array.every was addded to Mozilla's Javascript 1.6 and made standard with EcmaScript 5. If you to support browsers that do not implement EC5 then check out [`_.all` from underscore.js][underscore]. -For a real world example, prentend you have a multiple select list that looks like: +For a real world example, pretend you have a multiple select list that looks like: {% highlight html %}