p:first-child{
+ margin-top:0;
+}
+#facebox .content > p:last-child{
+ margin-bottom:0;
+}
+
+#facebox .close{
+ position:absolute;
+ top:5px;
+ right:5px;
+ padding:2px;
+ background:#fff;
+}
+#facebox .close img{
+ opacity:0.3;
+}
+#facebox .close:hover img{
+ opacity:1.0;
+}
+
+#facebox .loading {
+ text-align: center;
+}
+
+#facebox .image {
+ text-align: center;
+}
+
+#facebox img {
+ border: 0;
+ margin: 0;
+}
+
+#facebox_overlay {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ height:100%;
+ width:100%;
+}
+
+.facebox_hide {
+ z-index:-100;
+}
+
+.facebox_overlayBG {
+ background-color: #000;
+ z-index: 99;
+}
+.facebox-footnote{
+ margin-top:40px;
+}
\ No newline at end of file
diff --git a/TryRuby/public/stylesheets/reset.css b/TryRuby/public/stylesheets/reset.css
new file mode 100644
index 0000000..d709810
--- /dev/null
+++ b/TryRuby/public/stylesheets/reset.css
@@ -0,0 +1,46 @@
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+body {
+ line-height: 1;
+ color: black;
+ background: white;
+}
+ol, ul {
+ list-style: none;
+}
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: separate;
+ border-spacing: 0;
+}
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: "";
+}
+blockquote, q {
+ quotes: "" "";
+}
diff --git a/TryRuby/public/stylesheets/scaffold.css b/TryRuby/public/stylesheets/scaffold.css
new file mode 100644
index 0000000..1ae7000
--- /dev/null
+++ b/TryRuby/public/stylesheets/scaffold.css
@@ -0,0 +1,56 @@
+body { background-color: #fff; color: #333; }
+
+body, p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+}
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px;
+}
+
+a { color: #000; }
+a:visited { color: #666; }
+a:hover { color: #fff; background-color:#000; }
+
+div.field, div.actions {
+ margin-bottom: 10px;
+}
+
+#notice {
+ color: green;
+}
+
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table;
+}
+
+#error_explanation {
+ width: 450px;
+ border: 2px solid red;
+ padding: 7px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
+ background-color: #f0f0f0;
+}
+
+#error_explanation h2 {
+ text-align: left;
+ font-weight: bold;
+ padding: 5px 5px 5px 15px;
+ font-size: 12px;
+ margin: -7px;
+ margin-bottom: 0px;
+ background-color: #c00;
+ color: #fff;
+}
+
+#error_explanation ul li {
+ font-size: 12px;
+ list-style: square;
+}
diff --git a/TryRuby/public/tutorials/es_intro.html b/TryRuby/public/tutorials/es_intro.html
new file mode 100644
index 0000000..256de2f
--- /dev/null
+++ b/TryRuby/public/tutorials/es_intro.html
@@ -0,0 +1,666 @@
+
+
+
Usando El Prompt
+
La ventana azul de arriba es el prompt de Ruby. ¡Escribe una línea de código Ruby, aprieta Enter
+ y velo correr!
+
Por ejemplo, trata de escribir algo matemático. Como: 2 + 6
+
\d+
+
+
+
Números & Matemática
+
¡Bien! Hiciste algo de matemática. ¿Ves como la respuesta salta a la vista?
+
Ruby reconoce números y símbolos matemáticos. Puedes probar otras cosas como:
+
+
Seguro, las computadoras son habilidosas y rápidas con las matemáticas. Sigamos... ¿Quieres ver tu nombre al revés?
+ Escribe tu primer nombre entre comillas así: "Jimmy"
+
"(\w+)"
+
+
+
Decir Tu Nombre Al Revés
+
Perfecto, has formado un string de las letras de tu nombre. Un string
+ es un juego de caracteres que la computadora puede procesar.
+
Imagina que las letras están en una cuerda donde
+ se cuelga la ropa y las comillas son los broches que sostienen los bordes. Las comillas marcan el comienzo y el final.
+
Para invertir tu nombre, escribe: "Jimmy".reverse
(¡No olvides el punto!)
+
"(\w+)"
+
+
+
Contando Las Letras
+
¡Has usado el método reverse
sobre tu nombre! Poniendo tu nombre entre comillas,
+ hiciste un string. Luego llamaste al método reverse
, que funciona sobre un string cambiando
+ todas las letras de atrás para adelante.
+
Ahora, vamos a ver cuantas letras tiene tu nombre: "Jimmy".length
+
\d+
+
+
+
Repitiendo
+
Ahora, estoy seguro que te estarás preguntando para que sirve todo esto. Bueno, estoy seguro que habrás estado en alguna
+ pagina web donde te gritaron, ¡Hey, tu password es muy corto! Ves, algunos programas
+ usan este código tan simple.
+
Mira esto. Vamos a multiplicar tu nombre por 5. "Jimmy" * 5
+
"(\w+)"
+
+
+
Hey, Sumario #1 Listo
+
Vamos a ver que es lo que has aprendido en el primer minuto.
+
+ - El prompt. Escribiendo código en el prompt verde obtienes
+ una respuesta del prompt rojo. Todo código te da una respuesta.
+ - Números y strings son objetos matemáticos y de texto de Ruby.
+ - Métodos Has usado métodos en Ingles como
reverse
+ y métodos simbólicos como *
(el método de multiplicación.) ¡Los métodos son acciones!
+
+
Esta es la esencia del aprendizaje. Tomar cosas simples, jugar con ellas
+ y trasformarlas en cosas nuevas. ¿Te sientes cómodo con todo? Te aseguro que lo estas.
+
Bien, vamos a hacer algo incomodo. Trata de invertir un número: 40.reverse
+
NoMethodError: undefined method `reverse' for (\d+):Fixnum
+
+
+
¡Basta, Te Volviste Loco!
+
No puedes invertir el numero cuarenta. Supongo que puedes poner tu monitor en
+ frente de un espejo, pero invertir un numero no tiene sentido. Ruby lanza un
+ mensaje de error. Ruby te dice que no hay un método reverse
para los números.
+
Tal vez si lo conviertes en un string: 40.to_s.reverse
.
+
\"(\d+)\"
+
+
+
Los Chicos Son Diferentes De Las Chicas
+
Y los números son diferentes de los strings. Aunque puedes usar métodos en cualquier objeto
+ en Ruby, algunos métodos solo funcionan en cierto tipo de cosas. Pero siempre puedes
+ convertir entre diferentes tipos usando el método "to" de Ruby.
+
- to_s convierte cosas a strings.
+ - to_i convierte cosas a integers (números.)
+ - to_a convierte cosas a arrays.
+
+
¿Que son los arrays?! Son listas. Escribe entre un par de corchetes: []
.
+
\[\]
+
+
+
Mantenerse en Cola
+
Genial, eso es una lista vacía. Las listas guardan cosas en orden.
+ Como esperando en la cola para palomitas de maíz. Estas atrás de alguien y jamás
+ pensarías en empujarlo a un costado, ¿no es así? Y con respecto al tipo detrás de ti,
+ mantienes un ojo sobre el, ¿correcto?
+
Acá hay una lista para ti. Números de la lotería: [12, 47, 35]
.
+
\[(\d+(, )?){2,}\]
+
+
+
Uno Levanta La Mano
+
Una lista de números de la lotería. ¿Cual es el mayor?
+
Prueba: [12, 47, 35].max
.
+
(\d+)
+
+
+
Manteniendo la Lista
+
Bien, bien. Pero es un fastidio el tener que reescribir esa lista, ¿no es así?
+
Guardemos nuestros números en un ticket
de esta manera: ticket = [12, 47, 35]
+
\[(\d+(, )?){2,}\]
+
+
+
Ahora Escribe Ticket
+
Ahora, escribe: ticket
+
\[(\d+(, )?){2,}\]
+
+
+
Guardado, Escondido
+
¡Fantástico! Te has aferrado a tus números de la lotería, escondiéndolos dentro de una
+ variable llamada ticket
.
+
Vamos a poner tus números en orden, que te parece? Usa: ticket.sort!
+
\[(\d+(, )?){2,}\]
+
+
+
Sumario #2 Está Sobre Nosotros
+
Tenías una lista. Ordenaste la lista. La variable ticket
ahora esta cambiada.
+
¿Te diste cuenta que el método sort!
tiene un claro y llamativo signo de exclamación al final?
+ Muchas veces los métodos de Ruby gritan así si es que alteran la variable para bien. No es nada
+ especial, solo una marca.
+
Ahora, mira como te fue en tu segundo minuto:
+
+ - Errors. Si tratas de invertir un número o hacer algo sospechoso,
+ Ruby salteara el prompt para avisarte.
+ - Arrays son listas para ordenar cosas en orden.
+ - Variables guardan cosas y le dan un nombre. Usaste el
+ signo igual para hacerlo.
Like: ticket = [14, 37, 18]
.
+
+
En total hay ocho lecciones. Estás a dos octavos de camino!
+ Esto es cosa simple, no te parece? Las cosas buenas están mas adelante.
+
Cambiemos de dirección por un momento. Rellené con un poco de poesía cierta
+ variable para ti. Hecha un vistazo. Escribe print poem
+
poem = "My toast has flown from my hand\nAnd my toast has gone to the
+moon.\nBut when I saw it on television,\nPlanting our flag on Halley's
+comet,\nMore still did I want to eat it.\n"
+
My toast (.+)
+
+
+
Desgraciadamente, Tú Odias la poesía de Tostadas
+
Mira, esta bien. No tiene que gustarte. Hackéalo, yo invito.
+
En vez de tostada, ve por un melón o algo. Prueba esto: poem['toast'] = 'honeydew'
+
Y luego escribe print poem
para ver el nuevo poema.
+
My honey(.+)
+
+
+
Listo, Apuntado
+
Los corchetes que acabas de usar son muy comunes en Ruby. Recuerda, escribiste: poem['toast'] = 'honeydew'
. Esa casilla con la palabra toast tiene corchetes a ambos lados, ¿ves?
+
Los
+corchetes son como una mira para alinear un objetivo. Exacto. Estos
+corchetes significan, "Estoy buscando ____." Listo, apuntado. Aquí estas buscando
+una costada e intercambiándola por una fruta.
+
Aquí hay una pregunta: ¿Que pasa si volteamos el poema entero? poem.reverse
+
"\\n.ti tae ot (.+)"
+
+
+
Demasiado Invertido
+
Está bien, seguro. Entonces todo el poema fue puesto al revés. Letra por letra. Sin embargo, yo realmente solo quería
+ invertir las líneas. Mover la última línea a la primera y la primera hacia abajo a la ultima. Al revés, pero no
+ ése revés.
+
Aquí esta como lograrlo: poem.lines.to_a.reverse
+
\["More still did I(.+)"\]
+
+
+
Rizos de Metodos Concatenados
+
Dime, que es lo que ves? Que paso acá? Escribiste poem.lines.to_a.reverse
y ¿que pasó?
+
Dos cosas pasaron. Convertiste poem
en una lista usando
+lines.to_a
. lines
decide la forma en que
+el string se divide, luego to_a
lo
+convierte en un Array. (To array.) Diferentes métodos, como
+bytes
y chars
pueden ser usadas en lugar de
+lines
. Usando lines, Ruby retornará cada línea de poem.
+
Luego, tu revertiste, reverse
d, esa lista. Tenías cada línea. Las revertiste. Eso es todo.
+
Vamos a hilar un método más al final de todo esto: print poem.lines.to_a.reverse.join
+
More still did I(.+)
+
+
+
De Todos los Sumarios, el #3 esta aquí
+
Buen espectáculo, mi amigo! El método join
toma la lista de líneas revertidas y las pone juntas en un string.
+ (Seguro, también podrías haber usado to_s
.)
+
Tiempo de Revisión.
+
+ - Exclamaciones. Métodos pueden tener signos de exclamación (y también de interrogación) en
+ sus nombres. No es la gran cosa. Prueba:
poem.include? "my hand"
+ - Corchetes. Establece objetivos y busca cosas. Busca y reemplaza.
+ - Concatenar métodos te permite hacer mas cosas juntas. Partir poem, revertirlo,
+ reensamblarlo:
poem.lines.to_a.reverse.join
+
+
En este punto, querras manosear un poco más el poema. Una lista completa de métodos
+ String
estan
+
+ acá.
+ No temas y prueba algunos (como poem.downcase
o poem.delete
.)
+
Cuando estés listo para seguir adelante, escribe: books = {}
+
\{\}
+
+
+
Un Pequeñín Libro en Blanco
+
Has hecho un hash vacío. (también conocido como: un diccionario vacío.)
+
Vamos a rellenar con un libro de críticas en miniatura. Acá esta nuestro sistema de calificación:
+
+ :splendid
→ una obra maestra.
+ :quite_good
→ disfrutable, por supuesto que sí.
+ :mediocre
→ partes iguales de bueno y malo.
+ :quite_not_good
→ notablemente malo.
+ :abyssmal
→ una perdida de tiempo.
+
+
Para calificar un libro, pon el título entre corchetes y la calificación luego del signo igual.
+
Por ejemplo: books["Gravity's Rainbow"] = :splendid
+
:\w+
+
+
+
Mas Críticas Tamaño Bocadillo
+
Tú sigue, agrega mas críticas. Y, si quieres ver toda la lista,
+ implemente escribe: books
+
De nuevo, las calificaciones son: :splendid
, :quite_good
, :mediocre
,
+ :quite_not_good
, and :abyssmal
.
+
Estas calificaciones no son strings. Cuando colocas dos puntos frente a una palabra simple, obtienes un
+ symbol. Los símbolos son más baratos que los strings (en términos de memoria de la computadora.) Si usas
+ una palabra una y otra vez en un programa, usa un símbolo. En vez de tener miles de
+ copias de una palabra en memoria, la computadora guardara el símbolo solamente una vez.
+
Una vez que tengas tres o cuatro libros allí
+ dentro, escribe: books.length
.
+
[3-9]
+
+
+
Espera, ¿Me gustó Gravity's Rainbow?
+
Ves, el método length
funciona sobre strings, list y hashes. Una gran cosa acerca de
+ Ruby es que los nombres usualmente se reutilizan, lo que significa menos nombres para recordar.
+
Si quisieras ver una de tus críticas hechas, vuelve a poner el título entre corchetes. Pero deja de lado el
+ signo igual.
+
Al igual que aquí: books["Gravity's Rainbow"]
+
:\w+
+
+
+
Hashes como Pares
+
Ten en mente que los hashes no mantienen las cosas en orden. Ese no es su trabajo. Solo emparejará dos
+ cosas: una key (llave) y un valor. En tus críticas, la key es el
+ titulo del libro y el valor es la calificación.
+
Si simplemente quieres ver los títulos de los libros que calificaste: books.keys
+
\[".*"\]
+
+
+
¿Eres Duro?
+
¿Estás dando duras injustas críticas? Sigamos puntuando con rigurosidad:
ratings = Hash.new {0}
+
Entonces, bien, ahora vamos a contar tus críticas. Trata de seguirme. Escribe:
+ books.values.each { |rate| ratings[rate] += 1 }
+
(La línea vertical es el signo de tubería, probablemente lo logres con AltGr+1 con tu teclado.)
+
\[:.+\]
+
+
+
Un Recuento
+
Genial, wow! Has hecho un recuento de tus calificaciones. Escribe ratings
para ver las cuenta. Este nuevo
+ hash muestra las calificaciones y luego el numero de veces que has dado esa calificación.
+
Una de las asombrosas cosas nuevas que acabamos de usar es un bloque, block. Vamos a explorar
+ explore these more in the next summary. más esto en el próximo sumario. Pero, básicamente, un bloque es un pedazo de código Ruby
+ rodeado por llaves.
+
Probemos otro bloque: 5.times { print "Odelay!" }
+
Odelay!Od.*
+
+
+
Ahora Arribamos al Sumario #4
+
Los bloques están siempre apegados a métodos. Como el método times
, que toma el bloque y lo corre
+ repetidas veces. (En este caso: cinco veces.)
+
Esta última lección fue algo mas larga. Probablemente usaste unos tres minutos aprendiendo sobre:
+
+ - Hashes. El pequeño diccionario con páginas arrugadas:
{}
.
+ - Symbols. Pequeñas, eficientes palabras con dos puntos:
:splendid
.
+ - Blocks. Pedazos de código que pueden ser clavados a muchos métodos de Ruby. Aquí
+ esta el código que usaste para crear el recuento:
books.values.each { |rate| ratings[rate] += 1 }
.
+
+
En tu computadora, probablemente tienes muchos archivos diferentes. Archivos con fotos en ellos,
+ archivos con programas dentro. Y los archivos usualmente se organizan en carpetas, también llamadas:
+ directorios.
+
He preparado algunos directorios para ti. Echa un vistazo:
+ Dir.entries "/"
+
\["\.", .+\]
+
+
+
La Privada Colección de Dr. Dir
+
Acabas de listar todo lo existente en el directorio superior. El directorio raíz, indicado
+ por la barra en diagonal. Conteniendo algunos programas y otros tutoriales y semejantes.
+
Entonces, ¿que es el método Dir.entries
? Bueno, es solo un método ¿si?,
+ entries
es el método llamado sobre la variable Dir
.
+ Y Dir
tiene una colección de métodos para chequear los archivos de los directorios.
+
Otra pequeña cosa de la que no hemos hablado abiertamente. Argumentos de los métodos, resaltados en verde.
+
+ Dir.entries "/"
: Cualquier cosa listada luego de un método
+ es considerado acoplamiento.
+ print poem
: Ves, print
es un método ordinario.
+ Y el poema esta acoplado. Para ser impreso.
+ print "pre", "event", "ual", "ism"
posee varios argumentos,
+ con comas entre ellos.
+
+
Para listar solamente archivos de texto en el directorio: Dir["/*.txt"]
+
\["\/comics\.txt"\]
+
+
+
Ven, Lee Historietas Conmigo
+
El método Dir[]
hace como entries
pero tu buscas por archivos
+ archivos con carácteres de comodín. ¡Aquí, vemos esos corchetes otra vez! Te das
+ cuenta como todavía significan, "Estoy buscando _____.".
+
Mas específicamente: "Estoy buscando archivos que terminen con .txt
."
+
Abramos este archivo con historietas de una vez. Aquí esta la manera:
+ print File.read("/comics.txt")
+
Achewood.+
+
+
+
Mi Comicas, Tu Comicas
+
¡De acuerdo! Podemos comenzar a usar archivos para guardar cosas. Esto es excelente
+ porque normalmente cuando salimos de Ruby, todas nuestras variables desaparecerán.
+ Ruby, por si mismo, olvida estas cosas. Pero si salvamos cosas en archivos,
+ podemos leer esos archivos en futuras escapadas a Ruby.
+
Hey, y ¿adivina que? ¡El directorio /Home
es tuyo! ¡Te lo entrego a ti! ¡Soy generoso! Hagamos una copia del archivo de la historieta.
+
Querrás hacer lo siguiente: FileUtils.copy('/comics.txt', '/Home/comics.txt')
+
Si ya has creado el archivo, usa File.delete('/Home/comics.txt') para arrojarlo a la basura.
+
nil
+
+
+
Tu Propio Territorio
+
Ok, tienes una copia. Chequéala: Dir["/Home/*.txt"]
+
Para agregar tu propia historieta a la lista, abramos el archivo en modo append.
+
Empieza asi: File.open("/Home/comics.txt", "a") do |f|
.
+
..
+
+
+
Y Ahora, para la Sorprendente Conclusión
+
Asi que tu prompt ha cambiado. ¿Lo notas? Tu prompt es doble punto ahora.
+
En este tutorial, este prompt significa que Ruby espera que escribas más. A
+ medida que vayas completando con líneas de código, los doble puntos se mantendrán
+ hasta que hallas finalizado.
+
Hot tip: si quieres parar de trabajar en el código y salirte de los doble puntos, usa el comando reset
.
+ Si quieres volver a la pagina previa del tutorial, usa el comando back
.
+
Aquí esta tu código. Ya has escrito la primera línea, asi que simplemente ingresa la segunda. (El \n
+ es el carácter de Enter.
+
File.open("/Home/comics.txt", "a") do |f|
+ f << "Cat and Girl: http://catandgirl.com/\n"
+ end
+
+
Y, como te has vuelto avanzado y capaz aquí, otro tip: puedes usar las flechas hacia
+ arriba y hacia abajo para editar tus viejos comandos o correrlos otra vez.
+
..
+
+
+
Ruby se Sienta y Espera
+
Esa ultima línea agrega la historieta Cat and Girl a la lista, pero Ruby seguirá esperando
+ hasta que hallas terminado por completo para tomar acción.
+
Ahora, para finalizar el código que has empezado. Empezaste un nuevo bloque cuando escribiste do
.
+ Hasta ahora los bloques que hemos visto usaban llaves. Esta vez usaremos do
y end
en lugar
+ de las llaves. Muchos Rubyistas usan do...end
cuando el bloque ocupa varias líneas.
+
Terminemos ese bloque ahora mismo, con: end
+
File.open("/Home/comics.txt", "a") do |f|
+ f << "Cat and Girl: http://catandgirl.com/\n"
+ end
+
+
#.File:/Home/comics\.txt \(closed\).
+
+
+
El Reloj Clavado en el Archivo
+
¡Bien, bien! Has añadido esa historieta al archivo. Puedes verlo por ti mismo: print File.read("/Home/comics.txt")
+
¿Qué hora era cuando cambiaste el archivo? Veamos. Escribe: File.mtime("/Home/comics.txt")
+
\w+ \w+ \d+ \d{2}:\d{2}:\d{2} [+-]\d{4} \d{4}
+
+
+
Sólo la Manecilla de la Hora
+
Estupendo, allí esta la hora. La hora exacta en la que agregaste la historieta al archivo. El mtime
te devuelve un objeto Time de Ruby.
+
Si sólo quieres ver la hora que era, aprieta la flecha para arriba y cambia la línea a: File.mtime("/Home/comics.txt").hour
+
\d+
+
+
+
Hola, ¿Quién Anda Ahí? Y el Sumario #5 Agita su Sombrero!
+
Bien hecho, bien hecho, bien hecho, ¡bien hecho! Realmente, realmente, realmente, realmente, ¡reaaaaaaaaalllmente!
+
Aquí esta tu último minuto de tu vida en retrospectiva:
+
+ - Archivos. ¿Que más se puede decir? Muchos métodos para editar archivos y revisar directorios.
+ - Argumentos. Los argumentos son listas de cosas mandadas en un método. Separadas con comas.
+ - También hablamos sobre do y end que es otra manera de hacer un bloque.
+
+
Ahora ya sabes como usar Ruby por completo. Me refiero a que tienes lo esencial. Solo necesitas seguir
+ aprendiendo métodos y probar bloques más complejos.
+
Pero existe un lado de Ruby que no hemos hablado. Hacer tus propios métodos y clases.
+
Ahem! Acabemos con ello de una vez.
+
Empieza con: def load_comics( path )
+
..
+
+
+
En Ruby, Def Leppard Significa ¡Define Leppard (un Método)!
+
Hey, bueno, lo hiciste. Estas haciendo tu propio método. Comenzaste con def
, seguido por el nombre del método.
+ Y una lista de argumentos que va a necesitar el método. ¡Esto no da tanto miedo ni es peligroso!
+
Todo lo que debemos hacer es rellenar con Ruby y terminarlo con end
.
+
Aquí esta el código:
+
def load_comics( path )
+ comics = {}
+ File.foreach(path) do |line|
+ url, name = line.split(': ')
+ comics[url] = name.strip
+ end
+ comics
+ end
+
+
No necesitas indentar, si no quieres. Lo hice solo para que sea más legible.
+
nil
+
+
+
La Madura Fruta de tu Propia Creación
+
Un nuevo método ha nacido. Vamos a usarlo: comics = load_comics('/comics.txt')
+
Si tienes un problema, puedes haberlo escrito mal. Usa el comando back
y prueba otra vez.
+
\{.*"Achewood"=."http://achewood.com/".*\}
+
+
+
Hey, Cool, Una Cosa de Historietas
+
En tu ventana de Ruby arriba, mira el código que has escrito para el método load_comics
. ¿Qué esta pasando? Tu estas
+ pasando en la variable path
y estas recibiendo la variable comics
. Ruby permite filtrar el hash comics
+ que es devuelto al final del método.
+
Una cantidad de métodos se usaron para realizar el trabajo. Fíjate si puedes hallarlos.
+
- File.foreach es el método que abre un archivo y manda cada línea al bloque. La variable
line
+ dentro del bloque do...end
va turnando con cada línea del archivo.
+ - split es un método para strings, que rompe los string en colocándolo en un array. Un hacha es arrojada sobre las comas
+ y las líneas se cortan en dos, dándonos la
url
y el nombre, name
, de las historietas.
+ - strip remueve los espacios extra alrededor de name. Por si acaso.
+
+
Justo allí. Bravo. Tienes las historietas en un hash de Ruby. ¿Pero ahora qué? ¿Qué tan bueno es en verdad?
+
Hagamos una página de links. ¿Qué te parece? Vamos a necesitar una pequeña librería que hice para ti.
+
Escribe: require 'popup'
+
true
+
+
+
El Navegador de Títere
+
Excelente, has cargado la librería popup. Está guardada en un archivo en el directorio Libraries. Mira: Dir["/Libraries/*"]
+
La librería popup contiene un puñado de métodos que he escrito y te dejaran controlar ventanas emergentes aquí en Try Ruby.
+
Mira, prueba esto: Popup.goto "http://google.com/"
+
\033\[1;JSm.*popup_goto\(.*\)\033\[m.*
+
+
+
Haciendo Links e Hilando Redes
+
Nuestro propio adorable, pequeño popup para manipular. también puedes rellenarlo con tus cositas. Empecemos por algo pequeño:
+
Popup.make {
+ h1 "My Links"
+ link "Go to Google", "http://google.com/"
+ }
+
+
El termino h1
(h-uno) significa encabezado de nivel uno. En HTML, es el encabezado más grande.
+
\033\[1;JSm.*popup_make\(.*h1.*a href.*\)\033\[m.*
+
+
+
Los Popups son tan fáciles, es una Locura
+
Se ve bien, lo hiciste perfecto, tal como se te pidió. Hagamos una lista entonces.
+
Aquí esta como haces una lista con la librería de popup:
+
Popup.make do
+ h1 "Things To Do"
+ list do
+ p "Try out Ruby"
+ p "Ride a tiger"
+ p "(down River Euphrates)"
+ end
+ end
+
+
El método p
es la manera corta para "párrafo".
+
\033\[1;JSm.*popup_make\(.*h1.*ul.*li.*li.*\)\033\[m.*
+
+
+
Expandiendo las Historietas en la Tabla
+
Bien, esto esta yendo maravilloso. Esto es algo simple, pero mantén en mente que no sabias nada sobre Ruby hace quince minutos atrás!<
+
Ultimo paso. Vamos a juntar todo, ¿sabes? ¡Juntémoslo como esos juegos de
+ campanillas hermosas que tintinean en los pórticos bajo la hermosa luz del sol
+ en la playa!
+
Asegúrate de que las historietas están cargadas: comics = load_comics( '/comics.txt' )
+
Ahora, hagamos una lista de links para cada historieta:
+
Popup.make do
+ h1 "Comics on the Web"
+ list do
+ comics.each do |name, url|
+ link name, url
+ end
+ end
+ end
+
+
Puedes clickear en los links y leer las historietas ¡inclusive en la ventana principal! ¡Bárbaro!
+
\033\[1;JSm.*popup_make\(.*h1.*ul.*li.*a href.*li.*a href.*\)\033\[m.*
+
+
+
Sumario #6 lo que Significa que has Llegado Muy Lejos
+
Eres un clérigo nivel 6 de Ruby. Quiero decir que buen trabajo has hecho. Vamos a revisar:
+
+ - Agregaste tu propio método con def y usaste ese método
load_comics
varias veces.
+ - Librerias. Tú usaste el método
require
para cargar la librería popup.
Escribiendo: require 'popup'
+ - Y como si no fuera suficiente, hiciste tu propia página web para listar los archivos de historietas. ¡Hiciste un programa real!
+
+
Entonces ¿Qué podrá venir luego? ¿Qué deberías aprender posiblemente ahora?
+ Ja, esta es la mejor parte. Has recorrido un largo camino y ahora descubrirás
+ las clases. En dos lecciones mas y ya estarás hecho.
+
Tempranamente, creamos un hash como este: Hash.new
pruébalo.
+
\{\}
+
+
+
No una Clase de Escuela, Una Clase Trabajadora
+
Ves, las llaves vacías {}
son abreviaciones para Hash.new
. El método new
+ es usado para hacer objetos de cierta clase. (Piensa "clase" como en "clase
+ trabajadora" — un grupo especifico de objetos similares, tienen el
+ mismo trabajo, la misma camisa.)
+
Pregúntate esto: ¿Cómo haría mi blog en Ruby? ¿Dónde deberías
+ comenzar? Bien, deberías guardar tus entradas del blog en un archivo, ¿cierto?
+ Pero ¿cómo seguirías los títulos de las entradas y el momento en que fue creado?
+ Y cuando cargas el archivo, ¿cómo se vería en Ruby? ¿Sería un Hash? ¿O un Array? ¿O
+ un Array de Arrys? ¿O alguna otra cosa?
Yo realmente creo que querrás usar una clase. Ya estas familiarizado con varias clases: Hash
, Array
, String
.
+
Hagamos una clase nueva: class BlogEntry
.
+
..
+
+
+
El Relleno del Blog esta Hecho de
+
Has abierto una nueva clase BlogEntry
. ¿De que están hechas las entradas de tu blog? Un titulo, seguro. también,
+ la fecha en la que fue creada. El texto entero de la entrada.
+
Vamos a poner el estado de ánimo, también, tal como LiveJournal.
La internet ha traído de vuelta las personas de palitos y emoticones
+ fuera de la bancarrota.¡Que emoción!
+
Bueno, ya tienes la primera línea de la clase, aquí esta el resto:/p>
+
class BlogEntry
+ attr_accessor :title, :time, :fulltext, :mood
+ end
+
+
nil
+
+
+
Accessors Son las Extremidades Colgantes
+
Hey, buena clase, colega. Tienes una nueva clase BlogEntry
. Para comenzar una entrada:
entry = BlogEntry.new
.
+
En la definición de la clase, usaste un método llamado attr_accessor
. Existen varios métodos attribute
+ atributo, como este que agregan pequeñas configuraciones a la clase. Estos atributos son simplemente variables adosadas a la clase.
+
Piénsalo de este modo. Una clase es como una persona. Esa forma de estrella
+ del humano. Y los atributos son las extremidades, las diferentes partes que
+ hacen un cuerpo.
+
Para crear el titulo de tu entrada: entry.title = "Today Mt. Hood Was Stolen!"
+
".+"
+
+
+
Un Objeto, Ese Estupendo Paquete Pequeño
+
Sigue adelante y pon la hora: entry.time = Time.now
+
Y el estado de animo: entry.mood = :sick
+
Y el anuncio en si: entry.fulltext = "I can't believe Mt. Hood was stolen! I am speechless! It was stolen by a giraffe who drove away
+ in his Cadillac Seville very nonchalant!!"
+
Para ver toda la configuración, simplemente escribe en el prompt: entry
.
+
#.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
+
+
+
Agilizando
+
Genial, tu blog es impresionante. Hey, hagamos las cosas algo mas fácil. No querrás
+ poner la hora asi todas las veces que postees. Solo quieres escribir el titulo,
+ el contenido y el emoticon rápido, ¿verdad?
+
Vamos a agregar un método initialize
.
+
class BlogEntry
+ def initialize( title, mood, fulltext )
+ @time = Time.now
+ @title, @mood, @fulltext = title, mood, fulltext
+ end
+ end
+
+
Una vez que lo hallas escrito, prueba hacer una nueva entrada: BlogEntry.new
+
ArgumentError: wrong number of arguments \(0 for 3\).*
+
+
+
Tu le Has Enseñado al Blog a Rechazar lo Malo
+
¿Viste como usamos dentro de la clase el símbolo arroba? De este modo: @time = Time.now
+
Fuera de la clase, usamos accesos (accessors): entry.time = Time.now
Pero dentro variables de instancia: @time = Time.now
+ Son exactamente lo mismo, pero expresado en dos partes diferentes de tu programa.
+
tu blog ahora necesita un titulo, estado de ánimo y el post para funcionar. Cuando un nuevo BlogEntry
es creado, el método initialize
+ es usado para chequear cualquier argumento para new
. ¡Uh, necesitamos tres argumentos!
+
Prueba de nuevo con los tres.
+
entry2
+= BlogEntry.new( "I Left my Hoodie on the Mountain!", :confused, "I am
+never going back to that mountain and I hope a giraffe steals it." )
+
#.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
+
+
+
Una Jirafa No Ha Robado el Sumario #7
+
Aha, estas aquí. Y todo en una pieza. Todavía vamos a hacer tu blog realidad, pero hasta entonces, vamos a revisar, ¿bien?
+
+ - Clases. Todo en Ruby es algún tipo de objeto. Las clases explican los objetos. Como ciertos objetos
+ trabajan. Por ejemplo, haces algunas entradas de blog y estos objetos están explicados en la clase
BlogEntry
.
+ En otras palabras: los llamas objetos del tipo BlogEntry.
+ - Accessors son variables adosadas a un objeto que pueden ser usadas fuera del objeto. (
entry.time = Time.now
)
+ - Variables de instancia son las mismas variables para accesos dentro del objeto.
+ Como en la definición de un método. (
@time = Time.now
)
+
+
Bueno, vamos a envolver las cosas, niño. Aquí esta el ultimo capitulo de la FASCINANTE épica
+ historia de Try Ruby! Ahora que ya has probado como todo funciona, ¿cómo vas a
+ usar eso alrededor de la casa y en tu tienda de comestibles? Eres una
+ gran persona (una de mis favoritas), pero necesitas dirección.
+
Vamos a terminar tu blog. Tienes entradas de blog, pero no un blog.
+
Pon las entradas en un array: blog = [entry, entry2]
+
\[#.BlogEntry:0x[0-9a-f]+.*, #.BlogEntry:0x[0-9a-f]+.*\]
+
+
+
Todo Se Trata de Combinar Cosas
+
Cosas hermosas pueden hacerse de partes simples de Ruby, especialmente
+ cuando las combinas entre ellas para formar algo nuevo. Aquí tenemos un blog
+ hecho de un array de clases. Y, en realidad, Ruby realmente hace buenas cosas
+ con este tipo de criaturas.
+
Aquí hay un puñado de cosas que puedes hacer con tu blog array:
+
- Querrás ordenar tus entradas de mas reciente a viejas. Puedes hacerlo con:
+ blog.sort_by { |entry| entry.time }.reverse
Ve sort_by para más explicación.
+ - Si quieres buscar en el blog por cualquier cosa relacionada con "cadillac":
+ blog.find_all { |entry| entry.fulltext.match(/cadillac/i) }
+ Lee mas en find_all
+ y match
+ para descubrir como funciona. También: /giraffe/i
es un objeto Regexp, usado para concordar palabras.
+ - Y agregar nuevas entradas con
blog << new_entry
+ Y aquí la documentación del método <<.
+
+
Puedes buscar entre la lista de los métodos con los que viene Ruby en ruby-doc.org's core.
+ Otra buena lista hay en online pickaxe.
+
Un método realmente útil (probablemente yo uso esto mas que otra cosa) es map
. Escribe: blog.map { |entry| entry.mood }
+
\[(:\w+, )+:\w+\]
+
+
+
Mira Su Cara — La Transformación Ha Comenzado
+
El método map
recorre un array y reemplaza cada ítem con algo nuevo. ¿Dices que quieres reemplazar cada entrada de tu blog
+ con el nombre de Bruce Willis?. Hazlo entonces: blog.map { "Bruce Willis" }
+
Como el bloque siempre devuelve el string "Bruce Willis", eso es lo que obtienes. En el código que acabas de usar, la entrada entry
wfue reemplazada
+ por solo el entry.mood
.
+
Ahora, quiero que hagas un popup con las entradas de tu blog. Yo no te voy a
+ dar todo el código. Solo te voy a dar una parte.
+
blog.each do |entry|
+ h2 entry.title
+ p entry.fulltext
+ end
+
+
Ahora, yo espero que pongas el código del popup alrededor y agregues un titulo con el nombre de tu blog usando h1
. Como extra, tienes la hora de cada entrada para mostrar.
+
\033\[1;JSm.*popup_make\(.*h1.*h2.*li.*h2.*li.*\)\033\[m.*
+
+
+
Eres Una Especie de Gurú Web, Tengo Estrellas en Mis Ojos
+
Bien, ¡eso es! Este es exactamente el código que puedes usar para escribir tu
+ propio blog real en Ruby. Si te sientes aventurero, yo chequearía el video de
+ Rails videos donde muestran a un joven compañero creando un blog en 15 minutos. Solo sientete cómodo y mira.
+
Debo mencionar a Rails. Tú has estado aprendiendo el lenguaje Ruby, como
+ hablarlo. Pero Rails es grupo de librerías (algo asi como la librería de popup
+ que hemos estado usando.) Es un poderoso conjunto de herramientas para crear
+ sitios web. Si estas interesado en aprender sobre Rails, yo miraría head
+ por aquí directamente. ¡Empieza a usar tus habilidades en Ruby apropiadamente!
+
Algo que tiene Rails son métodos para manejar fechas fácilmente. Como, prueba: Time.now - 2.weeks
+
class Integer; def weeks; self * 7*24*60*60; end; end
+
\w+ \w+ \d+ \d{2}:\d{2}:\d{2} .*
+
+
+
Si Quieres Empezar Poco a Poco
+
Si quieres comenzar escribiendo pequeños programas en Ruby para practicar, tengo un proyecto llamado MouseHole
+ que es una pequeña caja de herramientas en la web para escribir programas cortos en Ruby. Puedes ver aquí algunos
+ scripts para ver que quiero decir.
+
MouseHole no es para escribir sitios web en realidad. Es para escribir
+ pequeños programas y correrlos dentro del navegador. Como hay un programa block
+ de notas para MouseHole y un programa que agrega una imagen de un ratón a los
+ links de la web que linkean a programas de MouseHole.
+
Tengo un script de MouseHole dentro de un archivo aquí mismo:
+ print File.read("/MouseHole/flickrpedia.user.rb")
+
.*Inserts Wikipedia links for Flickr tags.*
+
+
+
Sumario #8, El Sumario Hey-Relájate-Lo-Hiciste-Bien
+
Esta ultima sección se tomó un momento para relajarse, para darte algunos consejos de como
+ puedes usar Ruby. Si lo has disfrutado, descarga Ruby e instálalo.
+
+
Una vez que tengas Ruby instalado, puedes usar Ruby Interactivo ejecutando el comando irb
en el prompt de tu sistema. Para mas sobre Irb, esta
+ The Tiger's Vest para ayudarte.
+
Tú realmente mereces una torta doble-capa con doble-doble azúcar glaseado y
+ un tipo tocando una de esas guitarras que son doble-guitarra. Quiero decir
+ terminaste, ¡lo hiciste! No hay dudas de eso, ¡eres un gran ser certificado!
+
+
+
\ No newline at end of file
diff --git a/TryRuby/public/tutorials/intro.html b/TryRuby/public/tutorials/intro.html
new file mode 100644
index 0000000..56c1d1c
--- /dev/null
+++ b/TryRuby/public/tutorials/intro.html
@@ -0,0 +1,680 @@
+
+
+
Using the Prompt
+
The blue window above is a Ruby prompt. Type a line of Ruby code, hit Enter
+ and watch it run!
+
For example, try typing some math. Like: 2 + 6
+
\d+
+
+
+
Numbers & Math
+
Good! You did a bit of math. See how the answer popped out?
+
Ruby recognizes numbers and mathematic symbols. You could try some other math like:
+
+
Sure, computers are handy and fast for math. Let's move on. Want to see your name reversed?
+ Type your first name in quotes like this: "Jimmy"
+
"(\w+)"
+
+
+
Say Your Name Backwards
+
Perfect, you've formed a string from the letters of your name. A string
+ is a set of characters the computer can process.
+
Imagine the letters are on a string of
+ laundry line and the quotes are clothespins holding the ends. The quotes mark the beginning and end.
+
To reverse your name, type: "Jimmy".reverse
(Don't forget the dot!)
+
"(\w+)"
+
+
+
Counting the Letters
+
You have used the reverse
method on your name! By enclosing your name in
+ quotes, you made a string. Then you called the reverse
method, which works on strings to flip
+ all the letters backwards.
+
Now, let's see how many letters are in your name: "Jimmy".length
+
\d+
+
+
+
On Repeat
+
Now, I'm sure by now you're wondering what any of this is good for. Well, I'm sure you've been to
+ a website that screamed, Hey, your password is too short! See, some programs
+ use this simple code.
+
Watch this. Let's multiply your name by 5. "Jimmy" * 5
+
"(\w+)"
+
+
+
Hey, Summary #1 Already
+
Let's look at what you've learned in the first minute.
+
+ - The prompt. Typing code into the green prompt gives you
+ an answer from a red prompt. All code gives an answer.
+ - Numbers and strings are Ruby's math and text objects.
+ - Methods. You've used English-language methods like
reverse
+ and symbolic methods like *
(the multiplication method.) Methods are action!
+
+
This is the essence of your learning. Taking simple things, toying with
+ them and turning them into new things. Feeling comfortable yet? I promise you are.
+
Okay, let's do something uncomfortable. Try reversing a number: 40.reverse
+
NoMethodError: undefined method `reverse' for (\d+):Fixnum
+
+
+
Stop, You're Barking Mad!
+
You can't reverse the number forty. I guess you can hold your monitor up to the
+ mirror, but reversing a number just doesn't make sense. Ruby has tossed an error
+ message. Ruby is telling you there is no method reverse
for numbers.
+
Maybe if you turn it into a string: 40.to_s.reverse
.
+
\"(\d+)\"
+
+
+
Boys are Different From Girls
+
And numbers are different from strings. While you can use methods on any object
+ in Ruby, some methods only work on certain types of things. But you can always
+ convert between different types using Ruby's "to" methods.
+
- to_s converts things to strings.
+ - to_i converts things to integers (numbers.)
+ - to_a converts things to arrays.
+
+
What are arrays?! They are lists. Type in a pair of brackets: []
.
+
\[\]
+
+
+
Standing in Line
+
Great, that's an empty list. Lists store things in order.
+ Like standing in line for popcorn. You are behind someone and you wouldn't
+ dream of pushing them aside, right? And the guy behind you, you've got a
+ close eye on him, right?
+
Here's a list for you. Lottery numbers: [12, 47, 35]
.
+
\[(\d+(, )?){2,}\]
+
+
+
One Raises Its Hand
+
A list of lottery numbers. Which one is the highest?
+
Try: [12, 47, 35].max
.
+
(\d+)
+
+
+
Tucking a List Away
+
Good, good. But it's annoying to have to retype that list, isn't it?
+
Let's save our numbers inside a ticket
like so: ticket = [12, 47, 35]
+
\[(\d+(, )?){2,}\]
+
+
+
Now Type Ticket
+
Now, type: ticket
+
\[(\d+(, )?){2,}\]
+
+
+
Saved, Tucked Away
+
Fantastic! You've hung on to your lotto numbers, tucking them away inside a
+ variable called ticket
.
+
Let's put your lotto numbers in order, how about? Use: ticket.sort!
+
\[(\d+(, )?){2,}\]
+
+
+
Summary #2 is Upon Us
+
You had a list. You sorted the list. The ticket
variable is now changed.
+
Did you notice that the sort!
method has a big, bright exclamation at the end?
+ A lot of times Ruby methods shout like that if they alter the variable for good. It's nothin
+ special, just a mark.
+
Now, look how your second minute went:
+
+ - Errors. If you try to reverse a number or do anything fishy,
+ Ruby will skip the prompt and tell you so.
+ - Arrays are lists for storing things in order.
+ - Variables save a thing and give it a name. You used the
+ equals sign to do this.
Like: ticket = [14, 37, 18]
.
+
+
In all there are eight lessons. You are two-eighths of the way there!
+ This is simple stuff, don't you think? Good stuff up ahead.
+
Let's change directions for a moment. I've stuffed a bit of poetry for you in
+ a certain variable. Take a look. Type print poem
+
poem = "My toast has flown from my hand\nAnd my toast has gone to the
+moon.\nBut when I saw it on television,\nPlanting our flag on Halley's
+comet,\nMore still did I want to eat it.\n"
+
My toast (.+)
+
+
+
Sadly, You Hate Toast Poetry
+
Look, it's okay. You don't have to like it. Hack it up, be my guest.
+
Instead of toast, go for a melon or something. Try this: poem['toast'] = 'honeydew'
+
And then type print poem
by itself to see the new poem.
+
My honey(.+)
+
+
+
Ready, Aim
+
The square brackets you just used are very common in Ruby. Remember, you typed: poem['toast'] = 'honeydew'
. That box with the word toast has a square bracket on each side, see?
+
The
+two brackets are like sights used to line up a target. Exactly. These
+brackets mean, "I am looking for ____." Ready, aim. Here you're looking
+for toast and swapping it out with fruit.
+
Here's a question: what happens when we reverse this whole poem? poem.reverse
+
"\\n.ti tae ot (.+)"
+
+
+
Too Much Reversal
+
Okay, sure. So the whole poem's been turned backwards, letter-by-letter. I really want to just
+ reverse the lines, though. Move the last line up to first and the first line down to last. Backwards, but not
+ that backwards.
+
Here's how: poem.lines.to_a.reverse
+
\["More still did I(.+)"\]
+
+
+
Ringlets of Chained Methods
+
So what do you see? What happened there? You typed poem.lines.to_a.reverse
and what happened?
+
Two things happened. You turned the poem
into a
+list using lines.to_a
. lines
decides the way
+the string is split up, then to_a
converted it into an
+Array. (To array.) Different methods, such
+as bytes
and chars
can be used in place
+of lines
. By using lines, ruby will return each line of the poem.
+
Then, you reverse
d that list. You had each line. You reversed them. That's it.
+
Let's tack one more method on the end there: print poem.lines.to_a.reverse.join
+
More still did I(.+)
+
+
+
Of All the Summaries, #3 is Here Now
+
Good show, my friend! The join
method took that list of reversed lines and put them
+ together into a string. (Sure, you could have also just used to_s
.)
+
Review time.
+
+ - Exclamations. Methods may have exclamations (and also question marks)
+ in their name. No big deal. Try:
poem.include? "my hand"
+ - Square brackets. Target and find things. Search and replace.
+ - Chaining methods lets you get a lot more done. Break up a poem,
+ reverse it, reassemble it:
poem.lines.to_a.reverse.join
+
+
At this point, you may want to tinker with the poem a bit more. A complete list of all
+ the String
methods is
+
+ here.
+ Go ahead and try a few (such as poem.downcase
or poem.delete
.)
+
When you're ready to move on, type: books = {}
+
\{\}
+
+
+
A Wee Blank Book
+
You've made an empty hash. (Also known as: an empty dictionary.)
+
We're going to stuff some miniature book reviews in this hash. Here's our rating system:
+
+ :splendid
→ a masterpiece.
+ :quite_good
→ enjoyed, sure, yes.
+ :mediocre
→ equal parts great and terrible.
+ :quite_not_good
→ notably bad.
+ :abyssmal
→ steaming wreck.
+
+
To rate a book, put the title in square brackets and put the rating after the equals.
+
For example: books["Gravity's Rainbow"] = :splendid
+
:\w+
+
+
+
More Bite-Size Reviews
+
Keep going, fill it up with reviews. And, if you want to see the whole list,
+ just type: books
+
Again, the ratings are: :splendid
, :quite_good
, :mediocre
,
+ :quite_not_good
, and :abyssmal
.
+
These ratings are not strings. When you place a colon in front of a simple word, you get a
+ symbol. Symbols are cheaper than strings (in terms of computer memory.) If
+ you use a word over and over in your program, use a symbol. Rather than having thousands of
+ copies of that word in memory, the computer will store the symbol only once.
+
Once you've got three or four books in
+ there, type: books.length
.
+
[3-9]
+
+
+
Wait, Did I Like Gravity's Rainbow?
+
See, the length
method works on strings, list and hashes. One great thing about
+ Ruby is that names are often reused, which means fewer names you need to remember.
+
If you'd like to look up one of your old reviews, again put the title in the square. But leave off
+ the equals.
+
Just like this: books["Gravity's Rainbow"]
+
:\w+
+
+
+
Hashes as Pairs
+
Keep in mind that hashes won't keep things in order. That's not their job. It'll just pair up two
+ things: a key and a value. In your reviews, the key is the book's
+ title and the value is the rating.
+
If you want to just see the titles of the books you've reviewed: books.keys
+
\[".*"\]
+
+
+
Are You Harsh?
+
So are you giving out harsh, unfair reviews? Let's keep score with this hash:
ratings = Hash.new {0}
+
Then, okay, now let's count up your reviews. Just stay with me. Type:
+ books.values.each { |rate| ratings[rate] += 1 }
+
(The straight line in the code is the pipe character, probably located right above the
+ Enter key on your keyboard.)
+
\[:.+\]
+
+
+
A Tally
+
Great, wow! You've made a scorecard of your ratings. Type ratings
to see the count.
+ This new hash shows a rating and then the number of times you've given that rating.
+
One of the amazing new things we've just used is a block. We're going to
+ explore these more in the next summary. But, basically, a block is a bit of Ruby code surrounded
+ by curly braces.
+
Let's try another block: 5.times { print "Odelay!" }
+
Odelay!Od.*
+
+
+
Now Arriving at Summary #4
+
Blocks are always attached to methods. Like the times
method, which takes the
+ block and runs the code over and over. (In this case: five times.)
+
This last lesson was a bit longer. You've probably used up three minutes learning about:
+
+ - Hashes. The little dictionary with the curly pages:
{}
.
+ - Symbols. Tiny, efficient code words with a colon:
:splendid
.
+ - Blocks. Chunks of code which can be tacked on to many of Ruby's methods. Here's the
+ code you used to build a scorecard:
books.values.each { |rate| ratings[rate] += 1 }
.
+
+
On your computer, you probably have a lot of different files. Files with pictures in them,
+ files with programs in them. And files are often organized into folders, also called:
+ directories.
+
I've prepared a few directories for you. Take a look:
+ Dir.entries "/"
+
\["\.", .+\]
+
+
+
The Private Collection of Dr. Dir
+
You've just listed out everything in the top directory. The root directory, indicated
+ by a single slash. Containing some programs and other tutorials and such.
+
So, what is the Dir.entries
method? Well, it's just a method, right?
+ entries
is a method called on the Dir
variable.
+ And Dir
has a collection of methods for checking out file directories.
+
One other little thing we haven't really talked about openly. Method arguments, highlighted in green.
+
+ Dir.entries "/"
: Anything listed after a method
+ is considered an attachment.
+ print poem
: See, print
is an ordinary method. And the
+ poem is attached. To be printed.
+ print "pre", "event", "ual", "ism"
has several arguments, with commas
+ between them.
+
+
To list just the text files in that directory: Dir["/*.txt"]
+
\["\/comics\.txt"\]
+
+
+
Come, Read Comics With Me
+
The Dir[]
method is like entries
but you search for files
+ with wildcard characters. Here, we see those square brackets again! Notice how
+ they still mean, "I am looking for _____?"
+
More specifically: "I am looking for files which end with .txt
."
+
Let's crack open this comics file, then. Here's the way:
+ print File.read("/comics.txt")
+
Achewood.+
+
+
+
Mi Comicas, Tu Comicas
+
All right! We can start to use files to store things. This is great because normally when
+ we exit Ruby, all our variables will be gone. Ruby, by itself, forgets these things.
+ But if we save things in files, we can read those files in future Ruby escapades.
+
Hey, and guess what? The /Home
directory is yours! I gave it to you! I am generous! Let's make a copy of the comics file.
+
You'll want to: FileUtils.copy('/comics.txt', '/Home/comics.txt')
+
If you've already created the file, use File.delete('/Home/comics.txt') to trash it.
+
nil
+
+
+
Your Own Turf
+
Okay, you've got a copy. Check it: Dir["/Home/*.txt"]
+
To add your own comic to the list, let's open the file in append mode.
+
Start like this: File.open("/Home/comics.txt", "a") do |f|
.
+
..
+
+
+
And Now For the Startling Conclusion
+
So your prompt has changed. See that? Your prompt is a double dot now.
+
In this tutorial, this prompt means that Ruby is expecting you to type more.
+ As you type in the lines of Ruby code, the double dots will continue until you
+ are completely finished.
+
Hot tip: If you want to stop working on the code and break out of the double dots, use the reset
+ command. If you want to go the previous page of the tutorial, use the back
command.
+
Here's your code. You've already typed the first line, so just enter the second line. (The \n
+ is an Enter character.
+
File.open("/Home/comics.txt", "a") do |f|
+ f << "Cat and Girl: http://catandgirl.com/\n"
+ end
+
+
And, since you're getting so advanced and capable here, one other tip: you can use the up and down arrow keys to
+ edit your old commands or run them again.
+
..
+
+
+
Ruby Sits Still
+
That last line adds the Cat and Girl comic to the list, but Ruby's going to wait until you're totally finished to
+ take action.
+
Now, to finish the code you've started. You opened a new block when you typed do
.
+ So far the blocks we've seen have used curly braces. This time we'll be using do
and end
instead
+ of curly braces. A lot of Rubyists will use do...end
when the block goes on for many lines.
+
Let's get that block finished now, with: end
+
File.open("/Home/comics.txt", "a") do |f|
+ f << "Cat and Girl: http://catandgirl.com/\n"
+ end
+
+
#.File:/Home/comics\.txt \(closed\).
+
+
+
The Clock Nailed To the File
+
Good, good! You've added that new comic to the file. You can see for yourself: print File.read("/Home/comics.txt")
+
What time was it when you changed the file? Let's check. Type: File.mtime("/Home/comics.txt")
+
\d{4}-\d+-\d+ \d{2}:\d{2}:\d{2} [+-]\d{4}
+
+
+
Just the Hour Hand
+
Great, there's the time. The precise time exactly when you added to the file. The mtime
gives you a Ruby Time object.
+
If you want to check just what hour it was, hit the up arrow key and change the line to: File.mtime("/Home/comics.txt").hour
+
\d+
+
+
+
Hallo, Who's There? And Summary #5 Waves Its Hat!
+
Well done, well done, well done, well done! Truly, truly, truly, truly, truuuuuuuuly!
+
Here's the last few minutes of your life in review:
+
+ - Files. What more can be said? Lots of methods for editing files and lookin around in directories.
+ - Arguments. Arguments are a list of things sent into a method. With commas between.
+ - We also spoke about do and end which are another way to make a block.
+
+
You totally know how to use Ruby now. I mean you've got down the essentials. You just need to keep learning more methods and
+ try out more complex blocks.
+
But there's one side of Ruby we haven't settled. Making your own methods and classes.
+
Ahem! Let's get it over with then.
+
Start with: def load_comics( path )
+
..
+
+
+
In Ruby, Def Leppard Means Define Leppard (a Method)!
+
Hey, okay, you done it. You're making your own method. You started with def
, followed by the name of the method.
+ And a list of arguments which the method will need. This isn't too scary and dangerous!
+
All we have to do is fill it up with Ruby and finish it up with end
.
+
Here's the code:
+
def load_comics( path )
+ comics = {}
+ File.foreach(path) do |line|
+ name, url = line.split(': ')
+ comics[name] = url.strip
+ end
+ comics
+ end
+
+
No need to indent, if you don't want. I just do that to make it read easier.
+
nil
+
+
+
The Ripened Fruit of Your Own Creation
+
A new method is born. Let us use it: comics = load_comics('/comics.txt')
+
If you have a problem, you might have mistyped. Use the back
command and try again.
+
\{.*"Achewood"=."http://achewood.com/".*\}
+
+
+
Hey, Cool, a Comics Thing
+
In your Ruby window above, look at the code you've typed for the load_comics
method. What is happening? You're
+ passing in the path
variable and you're getting back the comics
variable. Ruby lets the comics
+ hash trickle out the end of the method.
+
A number of methods were used to get the job done. See if you can spot them.
+
- File.foreach is a method which opens a file and hands each line to the block. The
line
+ variable inside the do...end
block took turns with each line in the file.
+ - split is a method for strings, which breaks the string up into an array. An axe is laid on the colon
+ and the line is chopped in half, giving us the
url
and name
for each comic.
+ - strip removes extra spaces around the name. Just in case.
+
+
Right on. Bravo. You've got the comics in a Ruby hash. But what now? What good is this really?
+
Let's make a page of links. How about that? I went ahead and loaded a little library I've made for you.
+
Type: next
. This is temporary as I updates new lessons.
+
true
+
+
+
Browser Puppetry
+
Excellent, you've loaded the popup library. It's saved in a file in the Libraries folder. See: Dir["/Libraries/*"]
+
The popup library contains a bunch of methods I've written which let you control a popup here on the Try Ruby site.
+
Here, try this: Popup.goto "http://google.com/"
+
\033\[1;JSm.*popup_goto\(.*\)\033\[m.*
+
+
+
Making Links and Spinning Webs
+
Our own lovely, little popup to manipulate. You can also fill it with your own goodies. We'll start small:
+
Popup.make {
+ h1 "My Links"
+ link "Go to Google", "http://google.com/"
+ }
+
+
The term h1
(h-one) means a level-one header. In HTML, this is the largest size of header.
+
\033\[1;JSm.*popup_make\(.*h1.*a href.*\)\033\[m.*
+
+
+
Popups Are So Easy, It's Crazy
+
Looks good, you did it perfectly, just as you were asked. Let's make a list then.
+
Here's how you make a list with the popup library:
+
Popup.make do
+ h1 "Things To Do"
+ list do
+ p "Try out Ruby"
+ p "Ride a tiger"
+ p "(down River Euphrates)"
+ end
+ end
+
+
The p
method is short for "paragraph".
+
\033\[1;JSm.*popup_make\(.*h1.*ul.*li.*li.*\)\033\[m.*
+
+
+
Spread the Comics on the Table
+
Okay, this is coming along wonderfully. This is simple stuff, but keep in mind that you didn't know any Ruby whatsoever just fifteen minutes ago!
+
Last
+step. Let's tie it all together, you know? Let's make it chime together
+like a very nice set of glistening chimes on the beach in the
+maginificent sunlight!
+
Make sure the comics are loaded: comics = load_comics( '/comics.txt' )
+
Now, let's make a list of the links to each comic:
+
Popup.make do
+ h1 "Comics on the Web"
+ list do
+ comics.each do |name, url|
+ link name, url
+ end
+ end
+ end
+
+
You can click on the links and read the comics in the little window even! Smashing!
+
\033\[1;JSm.*popup_make\(.*h1.*ul.*li.*a href.*li.*a href.*\)\033\[m.*
+
+
+
Summary #6 Which Means You've Come So Far
+
You're a level six Ruby cleric. I mean what a great job you've done. Let's review:
+
+ - You added your own method with def and you used that
load_comics
method several times.
+ - Libraries. You used the
require
method to load the popup library.
By typing: require 'popup'
+ - And if that wasn't enough, you made your own web page from a list of comics in a file. You made a real program!
+
+
So
+what could possibly be next? What could you possibly have to learn now?
+Ha, this is the best part. You've come such a long way that we're going
+to uncover classes. For two more short lessons and you're done.
+
Earlier, we created a hash like this: Hash.new
Try it.
+
\{\}
+
+
+
Not a School Class, a Working Class
+
You see, the empty curly braces {}
is a shortcut for Hash.new
. The new
+method is used to make objects of a certain class. (Think "class" as in
+"working class" — a specific group of objects which are similar, have
+the same jobs, the same shirts.)
+
Ask yourself this: How would I make a blog in Ruby?
+Where would you start? Well, you might store your blog entries in a
+file, right? But how would you keep track of the title of the entry and
+the time it was posted? And when you loaded the file, how would it look
+in Ruby? Would it be a Hash? Or an Array? Or an Array of Arrays? Or
+something else?
I really think you'll want to use a class. You are already familiar with many classes: Hash
, Array
, String
.
+
Let's make a new class: class BlogEntry
.
+
..
+
+
+
The Stuff Blogs are Made of
+
You've opened up a new BlogEntry
class. What is your blog entry made of? A title, sure. Also, a time when the entry was posted. The
+ full text of the entry.
+
We'll do a mood setting, too, just like LiveJournal.
The Internet has really brought back stick people and smileys
+ out of bankruptcy. Emote!
+
Okay, so you've got the first line of the class, here's the rest:
+
class BlogEntry
+ attr_accessor :title, :time, :fulltext, :mood
+ end
+
+
nil
+
+
+
Accessors Are the Dangling Limbs
+
Hey, good class, man. You've got a new BlogEntry
class. To start an entry:
entry = BlogEntry.new
.
+
In the class definition, you used a method called attr_accessor
. There are many attribute methods like
+ this which add little settings to classes. These attributes are just variables attached to a class.
+
Think
+of it this way. A class is like a person. That star-shaped human thing
+out there. And the attributes are the dangling limbs, the different
+parts that make up a body.
+
To set the title of your entry: entry.title = "Today Mt. Hood Was Stolen!"
+
".+"
+
+
+
An Object, That Neat Little Package
+
Go ahead and set the post time: entry.time = Time.now
+
And the mood: entry.mood = :sick
+
And the post itself: entry.fulltext = "I can't believe Mt. Hood was stolen! I am speechless! It was stolen by a giraffe who drove away
+ in his Cadillac Seville very nonchalant!!"
+
To see all your settings, just type at the prompt: entry
.
+
#.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
+
+
+
Quickening it Up
+
Cool,
+you're blog is awesome. Hey, let's make things a bit easier on you.
+You're not going to want to set the time like that every time you post.
+You just want to type in the title and the entry and the mood quickly,
+right?
+
Let's add an initialize
method.
+
class BlogEntry
+ def initialize( title, mood, fulltext )
+ @time = Time.now
+ @title, @mood, @fulltext = title, mood, fulltext
+ end
+ end
+
+
Once you've got that typed in, try making a new entry: BlogEntry.new
+
ArgumentError: wrong number of arguments \(0 for 3\).*
+
+
+
You've Taught Your Blog to Reject Worthless Things
+
Did you see how inside the class we used the at-symbols? Like this: @time = Time.now
+
Outside the class, we use accessors: entry.time = Time.now
But inside we use instance variables: @time = Time.now
+ They're the exact same thing, but expressed in two different places of your program.
+
Your blog now needs a title, a mood and a post in order to work. When a new BlogEntry
is created, the initialize
method
+ is used to check for any arguments to new
. Uh, we need three arguments!
+
Try it again with all three.
+
entry2
+= BlogEntry.new( "I Left my Hoodie on the Mountain!", :confused, "I am
+never going back to that mountain and I hope a giraffe steals it." )
+
#.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
+
+
+
A Giraffe Has Not Stolen Summary #7
+
Aha, you're here. And all in one piece. We're still going to make your blog real, but until then, let's review, okay?
+
+ - Classes. Everything in Ruby is some kind of object. Classes explain objects. How a certain object works.
+ For example, you made a few blog entry objects and these objects are explained in the
BlogEntry
class.
+ In other words: you call them BlogEntry objects.
+ - Accessors are variables attached to an object which can be used outside the object. (
entry.time = Time.now
)
+ - Instance variables are the same variables you're using for accessors when inside the object.
+ Like in a method definition. (
@time = Time.now
)
+
+
Okay,
+let's wrap things up, kid. Here's the last chapter of the GRIPPING epic
+story of Try Ruby! Now that you've got a taste of how it all works, how
+are you going to use it around the house and in your grocer's freezer?
+You're a great person (one of my favorites), but you need guidance.
+
Let's finish your blog. You have blog entries, but no actual blog.
+
Put the entries into an array: blog = [entry, entry2]
+
\[#.BlogEntry:0x[0-9a-f]+.*, #.BlogEntry:0x[0-9a-f]+.*\]
+
+
+
It's All About Combining Things
+
Some
+beautiful things can be done with the simple parts of Ruby, especially
+when you combine them together into new things. Here we've got a blog
+made of an array of classes. And, actually, Ruby really does good with
+this kind of creature.
+
Here's a few things you can do with your array blog:
+
- You'll want to sort your entries from newest to oldest. You can do this with:
+ blog.sort_by { |entry| entry.time }.reverse
See the sort_by explanation for more.
+ - If you want to search your blog for anything related to "cadillac":
+ blog.find_all { |entry| entry.fulltext.match(/cadillac/i) }
+ Read all about find_all
+ and match
+ to figure out how that works. Also: the slashy /giraffe/i
is a Regexp object, used for matching words.
+ - Add new entries with
blog << new_entry
+ And check out the << method documentation.
+
+
You can browse a list of all Ruby's built-in methods at ruby-doc.org's core list.
+ Another good list is at the online pickaxe.
+
One really useful method (I probably use this more than anything else) is map
. Type: blog.map { |entry| entry.mood }
+
\[(:\w+, )+:\w+\]
+
+
+
Look at His Face — The Transformation Has Begun
+
The map
method cycles through an array and replaces each item with something new. Say you wanted to replace each of your blog entries
+ with the name Bruce Willis. Do it so: blog.map { "Bruce Willis" }
+
Since the block always returns the string "Bruce Willis", that's what you get. In the code you just used, the entry
was swapped out
+ for only the entry.mood
.
+
Now,
+I want you to make a popup with your blog entries. I'm not going to
+give you all of the code. I'm just going to give you part of it.
+
blog.each do |entry|
+ h2 entry.title
+ p entry.fulltext
+ end
+
+
Now, I expect you to put the popup code around it and add an h1
title with the name of your blog. For extra haroompf, have the time of each entry display.
+
\033\[1;JSm.*popup_make\(.*h1.*h2.*li.*h2.*li.*\)\033\[m.*
+
+
+
You are Some Kind of Web Guru, I Have Stars in My Eyes
+
Good,
+that's it! This is exactly the code you can use to write your own real
+Ruby blog. If you're feeling adventurous, I'd check out the Rails videos which show a swift young fellow creating a blog in 15 minutes. You just sit back and watch.
+
I
+should mention Rails. You have been learning the Ruby language, how to
+speak it. But Rails is a bunch of libraries (sort of like the popup
+library we've been using.) It's a very powerful toolkit for building
+websites. If you're interested in learning about Rails, I would head
+ over there right away. Start using your Ruby skills proper!
+
One thing Rails has is easy methods for dates. Like, try: Time.now - 2.weeks
+
class Integer; def weeks; self * 7*24*60*60; end; end
+
\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]\d{4}
+
+
+
If You Want to Start Small
+
If you'd like to start writing little Ruby programs just to practice, I have a project called MouseHole
+ which is a little web toolkit for writing short Ruby programs. You can look over a few
+ scripts to see what I mean.
+
MouseHole
+isn't for writing web sites really. It's just for writing little
+programs you run inside your browser. Like there's a notepad program
+for MouseHole and a program which adds a mouse picture next to links on
+the web which link to MouseHole programs.
+
I've got a MouseHole script inside a file here:
+ print File.read("/MouseHole/flickrpedia.user.rb")
+
.*Inserts Wikipedia links for Flickr tags.*
+
+
+
Summary #8, The Hey-Relax-You-Did-Good Summary
+
This last section took a moment to wind down, to give you some pointers as to how you can use Ruby. If you enjoyed yourself,
+ download Ruby and install it.
+
+
Once you have Ruby installed, you can use Interactive Ruby by running irb
on your system's prompt. For more on Irb,
+ there's The Tiger's Vest to help you.
+
You
+really deserve a double-layer cake with double-double frosting and a
+guy playing one of those guitars that's a double guitar. I mean you
+finished, you really did! No doubt about it, you're a certified
+red-blooded smartiac!
+
+
+
+
diff --git a/TryRuby/public/tutorials/intro.html.broken b/TryRuby/public/tutorials/intro.html.broken
new file mode 100755
index 0000000..5a6c27c
--- /dev/null
+++ b/TryRuby/public/tutorials/intro.html.broken
@@ -0,0 +1,638 @@
+
+
+
Using the Prompt
+
The blue window above is a Ruby prompt. Type a line of Ruby code, hit Enter
+ and watch it run!
+
For example, try typing some math. Like: 2 + 6
+
\d+
+
+
+
Numbers & Math
+
Good! You did a bit of math. See how the answer popped out?
+
Ruby recognizes numbers and mathematic symbols. You could try some other math like:
+
+
Sure, computers are handy and fast for math. Let's move on. Want to see your name reversed?
+ Type your first name in quotes like this: "Jimmy"
+
"(\w+)"
+
+
+
Say Your Name Backwards
+
Perfect, you've formed a string from the letters of your name. A string
+ is a set of characters the computer can process.
+
Imagine the letters are on a string of
+ laundry line and the quotes are clothespins holding the ends. The quotes mark the beginning and end.
+
To reverse your name, type: "Jimmy".reverse
(Don't forget the dot!)
+
"(\w+)"
+
+
+
Counting the Letters
+
You have used the reverse
method on your name! By enclosing your name in
+ quotes, you made a string. Then you called the reverse
method, which works on strings to flip
+ all the letters backwards.
+
Now, let's see how many letters are in your name: "Jimmy".length
+
\d+
+
+
+
On Repeat
+
Now, I'm sure by now you're wondering what any of this is good for. Well, I'm sure you've been to
+ a website that screamed, Hey, your password is too short! See, some programs
+ use this simple code.
+
Watch this. Let's multiply your name by 5. "Jimmy" * 5
+
"(\w+)"
+
+
+
Hey, Summary #1 Already
+
Let's look at what you've learned in the first minute.
+
+ - The prompt. Typing code into the green prompt gives you
+ an answer from a red prompt. All code gives an answer.
+ - Numbers and strings are Ruby's math and text objects.
+ - Methods. You've used English-language methods like
reverse
+ and symbolic methods like *
(the multiplication method.) Methods are action!
+
+
This is the essence of your learning. Taking simple things, toying with
+ them and turning them into new things. Feeling comfortable yet? I promise you are.
+
Okay, let's do something uncomfortable. Try reversing a number: 40.reverse
+
NoMethodError: undefined method `reverse' for (\d+):Fixnum
+
+
+
Stop, You're Barking Mad!
+
You can't reverse the number forty. I guess you can hold your monitor up to the
+ mirror, but reversing a number just doesn't make sense. Ruby has tossed an error
+ message. Ruby is telling you there is no method reverse
for numbers.
+
Maybe if you turn it into a string: 40.to_s.reverse
.
+
\"(\d+)\"
+
+
+
Boys are Different From Girls
+
And numbers are different from strings. While you can use methods on any object
+ in Ruby, some methods only work on certain types of things. But you can always
+ convert between different types using Ruby's "to" methods.
+
- to_s converts things to strings.
+ - to_i converts things to integers (numbers.)
+ - to_a converts things to arrays.
+
+
What are arrays?! They are lists. Type in a pair of brackets: []
.
+
\[\]
+
+
+
Standing in Line
+
Great, that's an empty list. Lists store things in order.
+ Like standing in line for popcorn. You are behind someone and you wouldn't
+ dream of pushing them aside, right? And the guy behind you, you've got a
+ close eye on him, right?
+
Here's a list for you. Lottery numbers: [12, 47, 35]
.
+
\[(\d+(, )?){2,}\]
+
+
+
One Raises Its Hand
+
A list of lottery numbers. Which one is the highest?
+
Try: [12, 47, 35].max
.
+
(\d+)
+
+
+
Tucking a List Away
+
Good, good. But it's annoying to have to retype that list, isn't it?
+
Let's save our numbers inside a ticket
like so: ticket = [12, 47, 35]
+
\[(\d+(, )?){2,}\]
+
+
+
Now Type Ticket
+
Now, type: ticket
+
\[(\d+(, )?){2,}\]
+
+
+
Saved, Tucked Away
+
Fantastic! You've hung on to your lotto numbers, tucking them away inside a
+ variable called ticket
.
+
Let's put your lotto numbers in order, how about? Use: ticket.sort!
+
\[(\d+(, )?){2,}\]
+
+
+
Summary #2 is Upon Us
+
You had a list. You sorted the list. The ticket
variable is now changed.
+
Did you notice that the sort!
method has a big, bright exclamation at the end?
+ A lot of times Ruby methods shout like that if they alter the variable for good. It's nothin
+ special, just a mark.
+
Now, look how your second minute went:
+
+ - Errors. If you try to reverse a number or do anything fishy,
+ Ruby will skip the prompt and tell you so.
+ - Arrays are lists for storing things in order.
+ - Variables save a thing and give it a name. You used the
+ equals sign to do this.
Like: ticket = [14, 37, 18]
.
+
+
In all there are eight lessons. You are two-eighths of the way there!
+ This is simple stuff, don't you think? Good stuff up ahead.
+
Let's change directions for a moment. I've stuffed a bit of poetry for you in
+ a certain variable. Take a look. Type print poem
+
poem = "My toast has flown from my hand\nAnd my toast has gone to the
+moon.\nBut when I saw it on television,\nPlanting our flag on Halley's
+comet,\nMore still did I want to eat it.\n"
+
My toast (.+)
+
+
+
Sadly, You Hate Toast Poetry
+
Look, it's okay. You don't have to like it. Hack it up, be my guest.
+
Instead of toast, go for a melon or something. Try this: poem['toast'] = 'honeydew'
+
And then type print poem
by itself to see the new poem.
+
My honey(.+)
+
+
+
Ready, Aim
+
The square brackets you just used are very common in Ruby. Remember, you typed: poem['toast'] = 'honeydew'
. That box with the word toast has a square bracket on each side, see?
+
The
+two brackets are like sights used to line up a target. Exactly. These
+brackets mean, "I am looking for ____." Ready, aim. Here you're looking
+for toast and swapping it out with fruit.
+
Here's a question: what happens when we reverse this whole poem? poem.reverse
+
"\\n.ti tae ot (.+)"
+
+
+
Too Much Reversal
+
Okay, sure. So the whole poem's been turned backwards, letter-by-letter. I really want to just
+ reverse the lines, though. Move the last line up to first and the first line down to last. Backwards, but not
+ that backwards.
+
Here's how: poem.lines.to_a.reverse
+
\["More still did I(.+)"\]
+
+
+
Ringlets of Chained Methods
+
So what do you see? What happened there? You typed poem.lines.to_a.reverse
and what happened?
+
Two things happened. You turned the poem
into a
+list using lines.to_a
. lines
decides the way
+the string is split up, then to_a
converted it into an
+Array. (To array.) Different methods, such
+as bytes
and chars
can be used in place
+of lines
. By using lines, ruby will return each line of the poem.
+
Then, you reverse
d that list. You had each line. You reversed them. That's it.
+
Let's tack one more method on the end there: print poem.lines.to_a.reverse.join
+
More still did I(.+)
+
+
+
Of All the Summaries, #3 is Here Now
+
Good show, my friend! The join
method took that list of reversed lines and put them
+ together into a string. (Sure, you could have also just used to_s
.)
+
Review time.
+
+ - Exclamations. Methods may have exclamations (and also question marks)
+ in their name. No big deal. Try:
poem.include? "my hand"
+ - Square brackets. Target and find things. Search and replace.
+ - Chaining methods lets you get a lot more done. Break up a poem,
+ reverse it, reassemble it:
poem.lines.to_a.reverse.join
+
+
At this point, you may want to tinker with the poem a bit more. A complete list of all
+ the String
methods is
+
+ here.
+ Go ahead and try a few (such as poem.downcase
or poem.delete
.)
+
When you're ready to move on, type: books = {}
+
\{\}
+
+
+
A Wee Blank Book
+
You've made an empty hash. (Also known as: an empty dictionary.)
+
We're going to stuff some miniature book reviews in this hash. Here's our rating system:
+
+ :splendid
→ a masterpiece.
+ :quite_good
→ enjoyed, sure, yes.
+ :mediocre
→ equal parts great and terrible.
+ :quite_not_good
→ notably bad.
+ :abyssmal
→ steaming wreck.
+
+
To rate a book, put the title in square brackets and put the rating after the equals.
+
For example: books["Gravity's Rainbow"] = :splendid
+
:\w+
+
+
+
More Bite-Size Reviews
+
Keep going, fill it up with reviews. And, if you want to see the whole list,
+ just type: books
+
Again, the ratings are: :splendid
, :quite_good
, :mediocre
,
+ :quite_not_good
, and :abyssmal
.
+
These ratings are not strings. When you place a colon in front of a simple word, you get a
+ symbol. Symbols are cheaper than strings (in terms of computer memory.) If
+ you use a word over and over in your program, use a symbol. Rather than having thousands of
+ copies of that word in memory, the computer will store the symbol only once.
+
Once you've got three or four books in
+ there, type: books.length
.
+
[3-9]
+
+
+
Wait, Did I Like Gravity's Rainbow?
+
See, the length
method works on strings, list and hashes. One great thing about
+ Ruby is that names are often reused, which means fewer names you need to remember.
+
If you'd like to look up one of your old reviews, again put the title in the square. But leave off
+ the equals.
+
Just like this: books["Gravity's Rainbow"]
+
:\w+
+
+
+
Hashes as Pairs
+
Keep in mind that hashes won't keep things in order. That's not their job. It'll just pair up two
+ things: a key and a value. In your reviews, the key is the book's
+ title and the value is the rating.
+
If you want to just see the titles of the books you've reviewed: books.keys
+
\[".*"\]
+
+
+
Are You Harsh?
+
So are you giving out harsh, unfair reviews? Let's keep score with this hash:
ratings = Hash.new {0}
+
Then, okay, now let's count up your reviews. Just stay with me. Type:
+ books.values.each { |rate| ratings[rate] += 1 }
+
(The straight line in the code is the pipe character, probably located right above the
+ Enter key on your keyboard.)
+
\[:.+\]
+
+
+
A Tally
+
Great, wow! You've made a scorecard of your ratings. Type ratings
to see the count.
+ This new hash shows a rating and then the number of times you've given that rating.
+
One of the amazing new things we've just used is a block. We're going to
+ explore these more in the next summary. But, basically, a block is a bit of Ruby code surrounded
+ by curly braces.
+
Let's try another block: 5.times { print "Odelay!" }
+
Odelay!Od.*
+
+
+
Now Arriving at Summary #4
+
Blocks are always attached to methods. Like the times
method, which takes the
+ block and runs the code over and over. (In this case: five times.)
+
This last lesson was a bit longer. You've probably used up three minutes learning about:
+
+ - Hashes. The little dictionary with the curly pages:
{}
.
+ - Symbols. Tiny, efficient code words with a colon:
:splendid
.
+ - Blocks. Chunks of code which can be tacked on to many of Ruby's methods. Here's the
+ code you used to build a scorecard:
books.values.each { |rate| ratings[rate] += 1 }
.
+
+<<<<<<< HEAD
+
+
+=======
+
On your computer, you probably have a lot of different files. Files with pictures in them,
+ files with programs in them. And files are often organized into folders, also called:
+ directories.
+
I've prepared a few directories for you. Take a look:
+ Dir.entries "/"
+
\["\.", .+\]
+
+
+
The Private Collection of Dr. Dir
+
You've just listed out everything in the top directory. The root directory, indicated
+ by a single slash. Containing some programs and other tutorials and such.
+
So, what is the Dir.entries
method? Well, it's just a method, right?
+ entries
is a method called on the Dir
variable.
+ And Dir
has a collection of methods for checking out file directories.
+
One other little thing we haven't really talked about openly. Method arguments, highlighted in green.
+
+ Dir.entries "/"
: Anything listed after a method
+ is considered an attachment.
+ print poem
: See, print
is an ordinary method. And the
+ poem is attached. To be printed.
+ print "pre", "event", "ual", "ism"
has several arguments, with commas
+ between them.
+
+
To list just the text files in that directory: Dir["/*.txt"]
+
\["\/comics\.txt"\]
+
+
+
Come, Read Comics With Me
+
The Dir[]
method is like entries
but you search for files
+ with wildcard characters. Here, we see those square brackets again! Notice how
+ they still mean, "I am looking for _____?"
+
More specifically: "I am looking for files which end with .txt
."
+
Let's crack open this comics file, then. Here's the way:
+ print File.read("/comics.txt")
+
Achewood.+
+
+
+
Mi Comicas, Tu Comicas
+
All right! We can start to use files to store things. This is great because normally when
+ we exit Ruby, all our variables will be gone. Ruby, by itself, forgets these things.
+ But if we save things in files, we can read those files in future Ruby escapades.
+
Hey, and guess what? The /Home
directory is yours! I gave it to you! I am generous! Let's make a copy of the comics file.
+
You'll want to: FileUtils.copy('/comics.txt', '/Home/comics.txt')
+
If you've already created the file, use File.delete('/Home/comics.txt') to trash it.
+
nil
+
+
+
Your Own Turf
+
Okay, you've got a copy. Check it: Dir["/Home/*.txt"]
+
To add your own comic to the list, let's open the file in append mode.
+
Start like this: File.open("/Home/comics.txt", "a") do |f|
.
+
..
+
+
+
And Now For the Startling Conclusion
+
So your prompt has changed. See that? Your prompt is a double dot now.
+
In this tutorial, this prompt means that Ruby is expecting you to type more.
+ As you type in the lines of Ruby code, the double dots will continue until you
+ are completely finished.
+
Hot tip: If you want to stop working on the code and break out of the double dots, use the reset
+ command. If you want to go the previous page of the tutorial, use the back
command.
+
Here's your code. You've already typed the first line, so just enter the second line. (The \n
+ is an Enter character.
+
File.open("/Home/comics.txt", "a") do |f|
+ f << "Cat and Girl: http://catandgirl.com/\n"
+ end
+
+
And, since you're getting so advanced and capable here, one other tip: you can use the up and down arrow keys to
+ edit your old commands or run them again.
+
..
+
+
+
Ruby Sits Still
+
That last line adds the Cat and Girl comic to the list, but Ruby's going to wait until you're totally finished to
+ take action.
+
Now, to finish the code you've started. You opened a new block when you typed do
.
+ So far the blocks we've seen have used curly braces. This time we'll be using do
and end
instead
+ of curly braces. A lot of Rubyists will use do...end
when the block goes on for many lines.
+
Let's get that block finished now, with: end
+
File.open("/Home/comics.txt", "a") do |f|
+ f << "Cat and Girl: http://catandgirl.com/\n"
+ end
+
+
#.File:/Home/comics\.txt \(closed\).
+
+
+
The Clock Nailed To the File
+
Good, good! You've added that new comic to the file. You can see for yourself: print File.read("/Home/comics.txt")
+
What time was it when you changed the file? Let's check. Type: File.mtime("/Home/comics.txt")
+
\d{4}-\d+-\d+ \d{2}:\d{2}:\d{2} [+-]\d{4}
+
+
+
Just the Hour Hand
+
Great, there's the time. The precise time exactly when you added to the file. The mtime
gives you a Ruby Time object.
+
If you want to check just what hour it was, hit the up arrow key and change the line to: File.mtime("/Home/comics.txt").hour
+
\d+
+>>>>>>> nanothief/master
+
+
+
+
Hallo, Who's There? And Summary #5 Waves Its Hat!
+
Well done, well done, well done, well done! Truly, truly, truly, truly, truuuuuuuuly!
+
Here's the last few minutes of your life in review:
+
+ - Arguments. Arguments are a list of things sent into a method. With commas between.
+ - We also spoke about do and end which are another way to make a block.
+
+
You totally know how to use Ruby now. I mean you've got down the essentials. You just need to keep learning more methods and
+ try out more complex blocks.
+
But there's one side of Ruby we haven't settled. Making your own methods and classes.
+
Ahem! Let's get it over with then.
+
+
+
In Ruby, Def Leppard Means Define Leppard (a Method)!
+
Hey, okay, you done it. You're making your own method. You started with def
, followed by the name of the method.
+ And a list of arguments which the method will need. This isn't too scary and dangerous!
+
All we have to do is fill it up with Ruby and finish it up with end
.
+
+
+
Hey, Cool, a Popup
+
Let's make a page of links. How about that? We'll need to load a little library I've made for you.
+
Type: require 'popup'
+
true
+
+
+
Browser Puppetry
+
Excellent, you've loaded the popup library.
+
The popup library contains a bunch of methods I've written which let you control a popup here on the Try Ruby site.
+
Here, try this: Popup.goto "http://google.com/"
+
\033\[1;JSm.*popup_goto\(.*\)\033\[m.*
+
+
+
Making Links and Spinning Webs
+
Our own lovely, little popup to manipulate. You can also fill it with your own goodies. We'll start small:
+
Popup.make {
+ h1 "My Links"
+ link "Go to Google", "http://google.com/"
+ }
+
+
The term h1
(h-one) means a level-one header. In HTML, this is the largest size of header.
+
\033\[1;JSm.*popup_make\(.*h1.*a href.*\)\033\[m.*
+
+
+
Popups Are So Easy, It's Crazy
+
Looks good, you did it perfectly, just as you were asked. Let's make a list then.
+
Here's how you make a list with the popup library:
+
Popup.make do
+ h1 "Things To Do"
+ list do
+ p "Try out Ruby"
+ p "Ride a tiger"
+ p "(down River Euphrates)"
+ end
+ end
+
+
The p
method is short for "paragraph".
+
\033\[1;JSm.*popup_make\(.*h1.*ul.*li.*li.*\)\033\[m.*
+
+
+
Spread the Comics on the Table
+
Okay, this is coming along wonderfully. This is simple stuff, but keep in mind that you didn't know any Ruby whatsoever just fifteen minutes ago!
+
Last
+step. Let's tie it all together, you know? Let's make it chime together
+like a very nice set of glistening chimes on the beach in the
+maginificent sunlight!
+
Now, let's make a list of the links to each comic:
+
Popup.make do
+ h1 "Comics on the Web"
+ list do
+ comics.each do |name, url|
+ link name, url
+ end
+ end
+ end
+
+
You can click on the links and read the comics in the little window even! Smashing!
+
\033\[1;JSm.*popup_make\(.*h1.*ul.*li.*a href.*li.*a href.*\)\033\[m.*
+
+
+
Summary #6 Which Means You've Come So Far
+
You're a level six Ruby cleric. I mean what a great job you've done. Let's review:
+
+ - You added your own method with def and you used that [coming soon]
+ - You used the
require
method to load the popup library.
By typing: require 'popup'
+ - And if that wasn't enough, you made your own web page from a list of
comics in a file no you havent. You made a real program!
+
+
So
+what could possibly be next? What could you possibly have to learn now?
+Ha, this is the best part. You've come such a long way that we're going
+to uncover classes. For two more short lessons and you're done.
+
Earlier, we created a hash like this: Hash.new
Try it.
+
\{\}
+
+
+
Not a School Class, a Working Class
+
You see, the empty curly braces {}
is a shortcut for Hash.new
. The new
+method is used to make objects of a certain class. (Think "class" as in
+"working class" — a specific group of objects which are similar, have
+the same jobs, the same shirts.)
+
Ask yourself this: How would I make a blog in Ruby?
+Where would you start? Well, you might store your blog entries in a
+file, right? But how would you keep track of the title of the entry and
+the time it was posted? And when you loaded the file, how would it look
+in Ruby? Would it be a Hash? Or an Array? Or an Array of Arrays? Or
+something else?
I really think you'll want to use a class. You are already familiar with many classes: Hash
, Array
, String
.
+
Let's make a new class: class BlogEntry
.
+
..
+
+
+
The Stuff Blogs are Made of
+
You've opened up a new BlogEntry
class. What is your blog entry made of? A title, sure. Also, a time when the entry was posted. The
+ full text of the entry.
+
We'll do a mood setting, too, just like LiveJournal.
The Internet has really brought back stick people and smileys
+ out of bankruptcy. Emote!
+
Okay, so you've got the first line of the class, here's the rest:
+
class BlogEntry
+ attr_accessor :title, :time, :fulltext, :mood
+ end
+
+
nil
+
+
+
Accessors Are the Dangling Limbs
+
Hey, good class, man. You've got a new BlogEntry
class. To start an entry:
entry = BlogEntry.new
.
+
In the class definition, you used a method called attr_accessor
. There are many attribute methods like
+ this which add little settings to classes. These attributes are just variables attached to a class.
+
Think
+of it this way. A class is like a person. That star-shaped human thing
+out there. And the attributes are the dangling limbs, the different
+parts that make up a body.
+
To set the title of your entry: entry.title = "Today Mt. Hood Was Stolen!"
+
".+"
+
+
+
An Object, That Neat Little Package
+
Go ahead and set the post time: entry.time = Time.now
+
And the mood: entry.mood = :sick
+
And the post itself: entry.fulltext = "I can't believe Mt. Hood was stolen! I am speechless! It was stolen by a giraffe who drove away
+ in his Cadillac Seville very nonchalant!!"
+
To see all your settings, just type at the prompt: entry
.
+
#.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
+
+
+
Quickening it Up
+
Cool,
+you're blog is awesome. Hey, let's make things a bit easier on you.
+You're not going to want to set the time like that every time you post.
+You just want to type in the title and the entry and the mood quickly,
+right?
+
Let's add an initialize
method.
+
class BlogEntry
+ def initialize( title, mood, fulltext )
+ @time = Time.now
+ @title, @mood, @fulltext = title, mood, fulltext
+ end
+ end
+
+
Once you've got that typed in, try making a new entry: BlogEntry.new
+
ArgumentError: wrong number of arguments \(0 for 3\).*
+
+
+
You've Taught Your Blog to Reject Worthless Things
+
Did you see how inside the class we used the at-symbols? Like this: @time = Time.now
+
Outside the class, we use accessors: entry.time = Time.now
But inside we use instance variables: @time = Time.now
+ They're the exact same thing, but expressed in two different places of your program.
+
Your blog now needs a title, a mood and a post in order to work. When a new BlogEntry
is created, the initialize
method
+ is used to check for any arguments to new
. Uh, we need three arguments!
+
Try it again with all three.
+
entry2
+= BlogEntry.new( "I Left my Hoodie on the Mountain!", :confused, "I am
+never going back to that mountain and I hope a giraffe steals it." )
+
#.BlogEntry:0x[0-9a-f]+ ((@title|@mood|@time|@fulltext)=.*?, ){3}.*
+
+
+
A Giraffe Has Not Stolen Summary #7
+
Aha, you're here. And all in one piece. We're still going to make your blog real, but until then, let's review, okay?
+
+ - Classes. Everything in Ruby is some kind of object. Classes explain objects. How a certain object works.
+ For example, you made a few blog entry objects and these objects are explained in the
BlogEntry
class.
+ In other words: you call them BlogEntry objects.
+ - Accessors are variables attached to an object which can be used outside the object. (
entry.time = Time.now
)
+ - Instance variables are the same variables you're using for accessors when inside the object.
+ Like in a method definition. (
@time = Time.now
)
+
+
Okay,
+let's wrap things up, kid. Here's the last chapter of the GRIPPING epic
+story of Try Ruby! Now that you've got a taste of how it all works, how
+are you going to use it around the house and in your grocer's freezer?
+You're a great person (one of my favorites), but you need guidance.
+
Let's finish your blog. You have blog entries, but no actual blog.
+
Put the entries into an array: blog = [entry, entry2]
+
\[#.BlogEntry:0x[0-9a-f]+.*, #.BlogEntry:0x[0-9a-f]+.*\]
+
+
+
It's All About Combining Things
+
Some
+beautiful things can be done with the simple parts of Ruby, especially
+when you combine them together into new things. Here we've got a blog
+made of an array of classes. And, actually, Ruby really does good with
+this kind of creature.
+
Here's a few things you can do with your array blog:
+
- You'll want to sort your entries from newest to oldest. You can do this with:
+ blog.sort_by { |entry| entry.time }.reverse
See the sort_by explanation for more.
+ - If you want to search your blog for anything related to "cadillac":
+ blog.find_all { |entry| entry.fulltext.match(/cadillac/i) }
+ Read all about find_all
+ and match
+ to figure out how that works. Also: the slashy /giraffe/i
is a Regexp object, used for matching words.
+ - Add new entries with
blog << new_entry
+ And check out the << method documentation.
+
+
You can browse a list of all Ruby's built-in methods at ruby-doc.org's core list.
+ Another good list is at the online pickaxe.
+
One really useful method (I probably use this more than anything else) is map
. Type: blog.map { |entry| entry.mood }
+
\[(:\w+, )+:\w+\]
+
+
+
Look at His Face — The Transformation Has Begun
+
The map
method cycles through an array and replaces each item with something new. Say you wanted to replace each of your blog entries
+ with the name Bruce Willis. Do it so: blog.map { "Bruce Willis" }
+
Since the block always returns the string "Bruce Willis", that's what you get. In the code you just used, the entry
was swapped out
+ for only the entry.mood
.
+
Now,
+I want you to make a popup with your blog entries. I'm not going to
+give you all of the code. I'm just going to give you part of it.
+
blog.each do |entry|
+ h2 entry.title
+ p entry.fulltext
+ end
+
+
Now, I expect you to put the popup code around it and add an h1
title with the name of your blog. For extra haroompf, have the time of each entry display.
+
\033\[1;JSm.*popup_make\(.*h1.*h2.*li.*h2.*li.*\)\033\[m.*
+
+
+
You are Some Kind of Web Guru, I Have Stars in My Eyes
+
Good,
+that's it! This is exactly the code you can use to write your own real
+Ruby blog. If you're feeling adventurous, I'd check out the Rails videos which show a swift young fellow creating a blog in 15 minutes. You just sit back and watch.
+
I
+should mention Rails. You have been learning the Ruby language, how to
+speak it. But Rails is a bunch of libraries (sort of like the popup
+library we've been using.) It's a very powerful toolkit for building
+websites. If you're interested in learning about Rails, I would head
+ over there right away. Start using your Ruby skills proper!
+
One thing Rails has is easy methods for dates. Like, try: Time.now - 2.weeks
+
class Integer; def weeks; self * 7*24*60*60; end; end
+
\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]\d{4}
+
+
+
Summary #8, The Hey-Relax-You-Did-Good Summary
+
This last section took a moment to wind down, to give you some pointers as to how you can use Ruby. If you enjoyed yourself,
+ download Ruby and install it.
+
+
Once you have Ruby installed, you can use Interactive Ruby by running irb
on your system's prompt. For more on Irb,
+ there's The Tiger's Vest to help you.
+
You
+really deserve a double-layer cake with double-double frosting and a
+guy playing one of those guitars that's a double guitar. I mean you
+finished, you really did! No doubt about it, you're a certified
+red-blooded smartiac!
+
+
+
diff --git a/TryRuby/public/tutorials/intro_files/sick.gif b/TryRuby/public/tutorials/intro_files/sick.gif
new file mode 100755
index 0000000000000000000000000000000000000000..217a67cbae60740fdc90b92e06b977cf7fc56699
GIT binary patch
literal 94
zcmZ?wbhEHb|6B "37"
+ assigns(:irb).should be(mock_irb)
+ end
+ end
+
+ describe "GET new" do
+ it "assigns a new irb as @irb" do
+ Irb.stub(:new) { mock_irb }
+ get :new
+ assigns(:irb).should be(mock_irb)
+ end
+ end
+
+ describe "GET edit" do
+ it "assigns the requested irb as @irb" do
+ Irb.stub(:find).with("37") { mock_irb }
+ get :edit, :id => "37"
+ assigns(:irb).should be(mock_irb)
+ end
+ end
+
+ describe "POST create" do
+ describe "with valid params" do
+ it "assigns a newly created irb as @irb" do
+ Irb.stub(:new).with({'these' => 'params'}) { mock_irb(:save => true) }
+ post :create, :irb => {'these' => 'params'}
+ assigns(:irb).should be(mock_irb)
+ end
+
+ it "redirects to the created irb" do
+ Irb.stub(:new) { mock_irb(:save => true) }
+ post :create, :irb => {}
+ response.should redirect_to(irb_url(mock_irb))
+ end
+ end
+
+ describe "with invalid params" do
+ it "assigns a newly created but unsaved irb as @irb" do
+ Irb.stub(:new).with({'these' => 'params'}) { mock_irb(:save => false) }
+ post :create, :irb => {'these' => 'params'}
+ assigns(:irb).should be(mock_irb)
+ end
+
+ it "re-renders the 'new' template" do
+ Irb.stub(:new) { mock_irb(:save => false) }
+ post :create, :irb => {}
+ response.should render_template("new")
+ end
+ end
+ end
+
+ describe "PUT update" do
+ describe "with valid params" do
+ it "updates the requested irb" do
+ Irb.stub(:find).with("37") { mock_irb }
+ mock_irb.should_receive(:update_attributes).with({'these' => 'params'})
+ put :update, :id => "37", :irb => {'these' => 'params'}
+ end
+
+ it "assigns the requested irb as @irb" do
+ Irb.stub(:find) { mock_irb(:update_attributes => true) }
+ put :update, :id => "1"
+ assigns(:irb).should be(mock_irb)
+ end
+
+ it "redirects to the irb" do
+ Irb.stub(:find) { mock_irb(:update_attributes => true) }
+ put :update, :id => "1"
+ response.should redirect_to(irb_url(mock_irb))
+ end
+ end
+
+ describe "with invalid params" do
+ it "assigns the irb as @irb" do
+ Irb.stub(:find) { mock_irb(:update_attributes => false) }
+ put :update, :id => "1"
+ assigns(:irb).should be(mock_irb)
+ end
+
+ it "re-renders the 'edit' template" do
+ Irb.stub(:find) { mock_irb(:update_attributes => false) }
+ put :update, :id => "1"
+ response.should render_template("edit")
+ end
+ end
+ end
+
+ describe "DELETE destroy" do
+ it "destroys the requested irb" do
+ Irb.stub(:find).with("37") { mock_irb }
+ mock_irb.should_receive(:destroy)
+ delete :destroy, :id => "37"
+ end
+
+ it "redirects to the irb list" do
+ Irb.stub(:find) { mock_irb }
+ delete :destroy, :id => "1"
+ response.should redirect_to(irb_url)
+ end
+ end
+
+end
diff --git a/TryRuby/spec/controllers/public_controller_spec.rb b/TryRuby/spec/controllers/public_controller_spec.rb
new file mode 100644
index 0000000..61a96a3
--- /dev/null
+++ b/TryRuby/spec/controllers/public_controller_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe PublicController do
+
+end
diff --git a/TryRuby/spec/helpers/classic_helper_spec.rb b/TryRuby/spec/helpers/classic_helper_spec.rb
new file mode 100644
index 0000000..2614b0f
--- /dev/null
+++ b/TryRuby/spec/helpers/classic_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the ClassicHelper. For example:
+#
+# describe ClassicHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# helper.concat_strings("this","that").should == "this that"
+# end
+# end
+# end
+describe ClassicHelper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/TryRuby/spec/helpers/irb_helper_spec.rb b/TryRuby/spec/helpers/irb_helper_spec.rb
new file mode 100644
index 0000000..325a0b0
--- /dev/null
+++ b/TryRuby/spec/helpers/irb_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the IrbHelper. For example:
+#
+# describe IrbHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# helper.concat_strings("this","that").should == "this that"
+# end
+# end
+# end
+describe IrbHelper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/TryRuby/spec/helpers/public_helper_spec.rb b/TryRuby/spec/helpers/public_helper_spec.rb
new file mode 100644
index 0000000..434276a
--- /dev/null
+++ b/TryRuby/spec/helpers/public_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the PublicHelper. For example:
+#
+# describe PublicHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# helper.concat_strings("this","that").should == "this that"
+# end
+# end
+# end
+describe PublicHelper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/TryRuby/spec/models/irb_spec.rb b/TryRuby/spec/models/irb_spec.rb
new file mode 100644
index 0000000..26e0fa0
--- /dev/null
+++ b/TryRuby/spec/models/irb_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe Irb do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/TryRuby/spec/requests/irb_spec.rb b/TryRuby/spec/requests/irb_spec.rb
new file mode 100644
index 0000000..d0c7946
--- /dev/null
+++ b/TryRuby/spec/requests/irb_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe "Irb" do
+ describe "GET /irb" do
+ it "works! (now write some real specs)" do
+ # Run the generator again with the --webrat flag if you want to use webrat methods/matchers
+ get irb_path
+ response.status.should be(200)
+ end
+ end
+end
diff --git a/TryRuby/spec/routing/irb_routing_spec.rb b/TryRuby/spec/routing/irb_routing_spec.rb
new file mode 100644
index 0000000..25ea295
--- /dev/null
+++ b/TryRuby/spec/routing/irb_routing_spec.rb
@@ -0,0 +1,35 @@
+require "spec_helper"
+
+describe IrbController do
+ describe "routing" do
+
+ it "recognizes and generates #index" do
+ { :get => "/irb" }.should route_to(:controller => "irb", :action => "index")
+ end
+
+ it "recognizes and generates #new" do
+ { :get => "/irb/new" }.should route_to(:controller => "irb", :action => "new")
+ end
+
+ it "recognizes and generates #show" do
+ { :get => "/irb/1" }.should route_to(:controller => "irb", :action => "show", :id => "1")
+ end
+
+ it "recognizes and generates #edit" do
+ { :get => "/irb/1/edit" }.should route_to(:controller => "irb", :action => "edit", :id => "1")
+ end
+
+ it "recognizes and generates #create" do
+ { :post => "/irb" }.should route_to(:controller => "irb", :action => "create")
+ end
+
+ it "recognizes and generates #update" do
+ { :put => "/irb/1" }.should route_to(:controller => "irb", :action => "update", :id => "1")
+ end
+
+ it "recognizes and generates #destroy" do
+ { :delete => "/irb/1" }.should route_to(:controller => "irb", :action => "destroy", :id => "1")
+ end
+
+ end
+end
diff --git a/TryRuby/spec/spec_helper.rb b/TryRuby/spec/spec_helper.rb
new file mode 100644
index 0000000..9b8b02c
--- /dev/null
+++ b/TryRuby/spec/spec_helper.rb
@@ -0,0 +1,27 @@
+# This file is copied to spec/ when you run 'rails generate rspec:install'
+ENV["RAILS_ENV"] ||= 'test'
+require File.expand_path("../../config/environment", __FILE__)
+require 'rspec/rails'
+
+# Requires supporting ruby files with custom matchers and macros, etc,
+# in spec/support/ and its subdirectories.
+Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
+
+RSpec.configure do |config|
+ # == Mock Framework
+ #
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
+ #
+ # config.mock_with :mocha
+ # config.mock_with :flexmock
+ # config.mock_with :rr
+ config.mock_with :rspec
+
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
+
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
+ # examples within a transaction, remove the following line or assign false
+ # instead of true.
+ config.use_transactional_fixtures = true
+end
diff --git a/TryRuby/spec/views/irb/edit.html.erb_spec.rb b/TryRuby/spec/views/irb/edit.html.erb_spec.rb
new file mode 100644
index 0000000..5b63112
--- /dev/null
+++ b/TryRuby/spec/views/irb/edit.html.erb_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe "irb/edit.html.erb" do
+ before(:each) do
+ @irb = assign(:irb, stub_model(Irb))
+ end
+
+ it "renders the edit irb form" do
+ render
+
+ # Run the generator again with the --webrat flag if you want to use webrat matchers
+ assert_select "form", :action => irb_path(@irb), :method => "post" do
+ end
+ end
+end
diff --git a/TryRuby/spec/views/irb/index.html.erb_spec.rb b/TryRuby/spec/views/irb/index.html.erb_spec.rb
new file mode 100644
index 0000000..193368a
--- /dev/null
+++ b/TryRuby/spec/views/irb/index.html.erb_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe "irb/index.html.erb" do
+ before(:each) do
+ assign(:irb, [
+ stub_model(Irb),
+ stub_model(Irb)
+ ])
+ end
+
+ it "renders a list of irb" do
+ render
+ end
+end
diff --git a/TryRuby/spec/views/irb/new.html.erb_spec.rb b/TryRuby/spec/views/irb/new.html.erb_spec.rb
new file mode 100644
index 0000000..ca7c0ad
--- /dev/null
+++ b/TryRuby/spec/views/irb/new.html.erb_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe "irb/new.html.erb" do
+ before(:each) do
+ assign(:irb, stub_model(Irb).as_new_record)
+ end
+
+ it "renders new irb form" do
+ render
+
+ # Run the generator again with the --webrat flag if you want to use webrat matchers
+ assert_select "form", :action => irb_path, :method => "post" do
+ end
+ end
+end
diff --git a/TryRuby/spec/views/irb/show.html.erb_spec.rb b/TryRuby/spec/views/irb/show.html.erb_spec.rb
new file mode 100644
index 0000000..9a12d28
--- /dev/null
+++ b/TryRuby/spec/views/irb/show.html.erb_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe "irb/show.html.erb" do
+ before(:each) do
+ @irb = assign(:irb, stub_model(Irb))
+ end
+
+ it "renders attributes in " do
+ render
+ end
+end
diff --git a/TryRuby/vendor/plugins/.gitkeep b/TryRuby/vendor/plugins/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/public/images/header.png b/public/images/header.png
index 1a6a3f243063ba8e2a93933357efbd3174f581ae..d6b9aa0f3ce2e738d3d12c68b52c78175322741c 100644
GIT binary patch
literal 51873
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