Blogged by Ujihisa. Standard methods of programming and thoughts including Clojure, Vim, LLVM, Haskell, Ruby and Mathematics written by a Japanese programmer. github/ujihisa

Monday, December 13, 2010

Búsqueda/Instalación Librerías RubyGems Fácilmente

¿Cómo se puede explorar y tratar de librerías RubyGems? Hay una gran cantidad de joyas como las estrellas en el cielo. Si lo desea, estar de pie sobre los hombros de gigantes.

Una escena típica de cuando usted está buscando una librería que cumple su trabajo sería como el siguiente, por ejemplo, que busca una librería que está relacionado con DataMapper.

$ gem search dm

No consigues nada porque se le olvidó especificar -r opción.

$ gem search dm -r

Usted obtiene demasiados resultados, porque hay demasiadas joyas que incluyen "dm" en el nombre, por ejemplo "admin" algo.

De todos modos, que finalmente encontró una buena librería cuyo nombre es "dm-is-persistent_state_machine".

 $ gem install dm-is-persistent_states_machine

Este failes porque el nombre de la librería correcta es "dm-is-persistent_state_machine", mientras que ha escrito "dm-is-persistent_states_machine". Sí, eso es muy difícil de escribir correctamente cuando el nombre de la librería es muy larga. Puede ser difícil si el nombre no está en su lengua materna. (*1)

unite-gem

Una solución es usar un plugin Unite unite-gem. Después de instalar este plugin, basta con ejecutar

 :Unite gem

y el tipo nada. Verá los siguientes resultados.

y se puede reducir el número de candidatos.

a continuación, simplemente pulse <Cr>... el proceso de instalación se produjo. Usted no tiene que introducir el nombre completo de la librería.

Nota

  • (*1): por ejemplo, muchas personas trataron de tipo "nokogiri", pero mal escrita como "nokogirl". Por último los autores Nokogiri realizó una joya alias "nokogirl" y todo el mundo se puso feliz. Este blog es sólo givin una versión más general de la solución.

How To Write And Show Multibyte Characters With Snap Framework

I'm playing with Snap these days. Snap is a web framework for Haskell.

The default template of application code will be like below

{-# LANGUAGE OverloadedStrings #-}
module Main where

import           Control.Applicative
import           Snap.Types
import           Snap.Util.FileServe
import           Text.Templating.Heist
import           Text.Templating.Heist.TemplateDirectory

import           Glue
import           Server


main :: IO ()
main = do
    td <- newTemplateDirectory' "templates" emptyTemplateState
    quickServer $ templateHandler td defaultReloadHandler $ \ts ->
        ifTop (writeBS "hello") <|>
        route [ ("foo", writeBS "bar")
              , ("echo/:echoparam", echoHandler)
              ] <|>
        templateServe ts <|>
        dir "static" (fileServe ".")


echoHandler :: Snap ()
echoHandler = do
    param <- getParam "echoparam"
    maybe (writeBS "must specify echo/param in URL")
          writeBS param

See the following line in main function:

        ifTop (writeBS "hello") <|>

This just shows a text written in ByteString "hello" to browser.

$ cabal install
$ {your application name} 3000
$ curl http://localhost:3000
hello

You can change the message "hello" to something and show the message on browser, but if the text is non-ascii like utf-8, you have to change other places as well. See the following patch:

diff --git snapfib.cabal snapfib.cabal
index d7a98fe..35b5c92 100644
--- snapfib.cabal
+++ snapfib.cabal
@@ -28,7 +28,8 @@ Executable snapfib
     text,
     containers,
     MonadCatchIO-transformers,
-    filepath >= 1.1 && <1.2
+    filepath >= 1.1 && <1.2,
+    utf8-string

   if impl(ghc >= 6.12.0)
     ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2
diff --git src/Main.hs src/Main.hs
index d5b24c4..036b2db 100644
--- src/Main.hs
+++ src/Main.hs
@@ -9,13 +9,14 @@ import           Text.Templating.Heist.TemplateDirectory

 import           Glue
 import           Server
+import           Data.Text


 main :: IO ()
 main = do
     td <- newTemplateDirectory' "templates" emptyTemplateState
     quickServer $ templateHandler td defaultReloadHandler $ \ts ->
-        ifTop (writeBS "hello") <|>
+        ifTop (writeText $ pack "こんにちはこんにちは!") <|>
         route [ ("foo", writeBS "bar")
               , ("echo/:echoparam", echoHandler)
               ] <|>

The function writeBS in Snap receives ByteString and shows the message to browser as a response. This cannot handle UTF-8 strings for some reason, so instead of using writeBS with ByteString you have to use writeText with Text represented message. You can convert into Text with Data.Text.pack function.

See also:

Wednesday, December 8, 2010

Ruby-like Object-oriented Notation in Haskell

This article doesn't discuss about something practical but about something experimental.

import Prelude hiding ((.))

send :: Object -&gt; Method -&gt; Object
send (Fixnum value) (Method "to_s") = Str $ show value
send _ _ = undefined

(.) = send

data Object = Fixnum Int | Str String deriving Show
data Method = Method String deriving Show

main = print $ x.to_s
  where
    x = Fixnum 10
    to_s = Method "to_s"

See the line in main function definition: print $ x.to_s. This looks like Ruby.

  • You can hide the definition of Haskell Prelude "." and can define your own function which name is "."; here I defined send first then made an alias of it.
  • Object and sending method into an object are like just a data and a function which receives an argument of a specific type unless you think about class or prototype; or just, say, "performance."

Monday, December 6, 2010

Writing Fibonacci Web Server in Haskell

A practice: implementing a web service which receives a number by URL parameter and returns the corresponding Fibonacci sequence number.

GET
http://localhost:8000/fib/10

returns

fib(10) = 55

I used Snap Framework.

$ mkdir snapfib
$ cd snapfib
$ snap init

This generates an empty snap project files. then

$ cabal install
$ snapfib

builds the codes and installs a command snapfib in your ~/.cabal/bin/snapfib. You may run the app locally with the default port 8000; you may open the default page, hello world, on your browser.

Then implement fib sequence, a utility function, and the controller/view. This allows you to get the Fibonacci sequence number just by URL parameter.

Below is the main routine code from the repository.

{-# LANGUAGE OverloadedStrings #-}
module Main where

import           Control.Applicative
import           Snap.Types
import           Snap.Util.FileServe
import           Text.Templating.Heist
import           Text.Templating.Heist.TemplateDirectory

import           Glue
import           Server
import           Data.ByteString.Char8 (pack, unpack)

fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibInString :: Int -> String
fibInString n = "fib(" ++ show n ++ ") = " ++ show (fibs !! n)

main :: IO ()
main = do
    td <- newTemplateDirectory' "templates" emptyTemplateState
    quickServer $ templateHandler td defaultReloadHandler $ \ts ->
        ifTop (writeBS "hello world") <|>
        route [ ("foo", writeBS "bar")
              , ("fib/:n", fibHandler)
              ] <|>
        templateServe ts <|>
        dir "static" (fileServe ".")


fibHandler :: Snap ()
fibHandler = do
    param <- getParam "n"
    maybe (writeBS "must specify fib/n in URL")
          (writeBS . pack . fibInString . read . unpack) param

(writeBS . pack . fibInString . read . unpack) param is converting param :: Maybe ByteString into Int, getting a Fibonacci sequence number in String, converting into ByteString again, and passing it to writeBS function which is defined in Snap Framework.

It was not difficult or complicated to implement such a simple web service in Haskell as long as you have basic Haskell knowledges like Maybe Monad or String manipulations. The problem was, in my opinion, it took long time to build the web app. Every time you fix your code, you have to wait for the compilation before you access your web service.

Thursday, December 2, 2010

Investing The Methods Of An Object On Unite

How often do you use Object#methods? That's convenient particularly on IRB.

methods on irb

methods on irb

This is also available on Unite.vim. Write the following code on your ~/.vimrc

let s:unite_source = {
      \ 'name': 'evalruby',
      \ 'is_volatile': 1,
      \ 'required_pattern_length': 1,
      \ 'max_candidates': 30,
      \ }

function! s:unite_source.gather_candidates(args, context)
  if a:context.input[-1:] == '.'
    let methods = split(
          \ unite#util#system(printf('ruby -e "puts %s.methods"', a:context.input[:-2])),
          \ "\n")
    call map(methods, printf("'%s' . v:val", a:context.input))
  else
    let methods = [a:context.input]
  endif
  return map(methods, '{
        \ "word": v:val,
        \ "source": "evalruby",
        \ "kind": "command",
        \ "action__command": printf("!ruby -e \"p %s\"", v:val),
        \ }')
endfunction

call unite#define_source(s:unite_source)

Execute :Unite evalruby on your Vim, and write an expression.

unite

unite

Ruby Advent Calendar jp-en: 2010

This entry is for the event Ruby Advent Calendar jp-en: 2010. The previous post was from yhara and the next post will be from authorNari. I'm sure he will write an entry about GC.

I also had written an entry of the event last year http://ujihisa.blogspot.com/2009/11/write-implementation-and-spec-on-same.html. Time flees away without delay.

Tuesday, November 23, 2010

Ruby 1.9.2 in Production with Tatsuhiro Ujihisa

(This is the presentation slides set for vancouver ruby meetup)

Before the talk...

  • two questions
  • easy one and normal one

Question 1

An easy Ruby quiz: How to define a method without using "def" keyword? (only with pure Ruby)

def a
  :something
end
p a #=> :something

Sample answer

self.class.module_eval do
  define_method(:a) do
    :something
  end
end
p a

Or

eval 'def a; :something; end'
p a
  • There's no keyword def but string containing 'def'

I think there are a lot more answers

Question 2

A normal Ruby quiz: How to assign a value to a local variable without using assignment operator "="? (only with pure Ruby)

a = :something
p a #=> :something

Sample answer 1

for a in [:something]; end

p a
  • "Local variable" is very special in Ruby
  • Declaring a new local variable is completely static

One more answer which is only on 1.9

Sample answer 2

/(?<a>).*/ =~ ''
eval 'a=:something'

p a

There are a match operator =~ and a string which contains =, but no assignment operator =.

Ruby 1.9.2 in Production with Tatsuhiro Ujihisa

image

image

This talk contains

  1. What is Ruby 1.9.2?
  2. Differences between 1.8..1.9
  3. Differences between 1.9.1..1.9.2
  4. Differences between 1.9.2..1.9.3

The summary of this talk

"Ruby 1.9.2 makes your code cleaner and easier to maintain."

Ruby versions

  • Ruby 1.8.6
    • Mar 2007
  • Ruby 1.8.7
    • May 2008
  • Ruby 1.9.0
    • Dec 2007
  • Ruby 1.9.1
    • Jan 2009
  • Ruby 1.9.2
    • Aug 2010

Ruby versions

  • Ruby 1.8.6
    • Mar 2007
  • Ruby 1.9.0
    • Dec 2007
    • Rails 2.0
  • Ruby 1.8.7
    • May 2008
  • Ruby 1.9.1
    • Jan 2009
  • Ruby 1.9.2
    • Aug 2010
    • Rails 3.0

Background: Ruby versions

  • All of them are officialy stable version
  • But Ruby 1.9 series didn't look like stable

What's new?

  • Which problems did 1.9 solved?
  • How can you write code easily?
  • -> Examples

Problems and solutions

  • Verbose hash notation
  • Difficulty in process handling
  • etc..

Problem 1

About syntax

Problem 1

JSON is cool. Dictionary in Erlang is cool. Hash in Ruby is...?

JavaScript or Erlang:

{a: 1, b: 2, c: 3}

Ruby:

{:a => 1, :b => 2, :c => 3}

1.9.2 new Hash syntax

This solved the problem

Ruby:

{a: 1, b: 2, c: 3}

This issue mattered particularly in..

Haml, the tool everyone is using, needs a lot of hash.

HTML:

<img src="a.jpg" width="64" height="128"></img>

Haml with ruby variables:

%img{:src => icon_path, :width => icon[:width], :height => icon[:height]}

(the below doesn't work)

%img(src=icon_path width=icon[:width] height=icon[:height])

This issue mattered particularly in..

Using 1.9.2 new hash syntax

%img{src: icon_path, width: icon[:width], height: icon[:height]}

Problem 2

  • About builtin methods

Problem 2

system or `` operator lacked some important functionalities.

  • Ruby is a good shell script (Rakefile!),
  • Ruby has some file/process handling methods,
  • But..
    • You couldn't retrieve the output or error of system
    • You could run a command asynchronously with system with "&", but couldn't kill the process directly
    • You couldn't run a command asynchronously with ``

Example

Start a Sinatra app by a Ruby script and kill the app by the script

in shell script:

#!/bin/sh
ruby sinatra_app.rb &
PID=$!
# something...
kill $PID

in ruby?

Bad solution

system 'ruby sinatra_app &'
  • You cannot get the proccess ID, so you cannot kill the process

Better solution

pid = fork do
  exec 'ruby', 'sinatra_app'
end
# something..
Process.kill 'KILL', pid
  • This doesn't work on NetBSD4 or Windows due to fork()

Best solution

pid = spawn 'ruby', 'sinatra_app'
# something...
Process.kill 'KILL', pid
  • :)
  • spawn =~ {fork + exec} or {system + &}
  • portable

New system and spawn spec

system({'A' => 'b'}, 'c', in: input_io, [:out, :err] => :out)
#=> true/false

spawn(...)
#=> Fixnum

Even more..

open3 standard library

  • Open3.capture2e
  • powered by spawn (read the source!)

Problem 3

  • Local variable shadowing (potantial bug!)

    a = :hello
    [1, 2, 3].each do |a|
      p a
    end
    p a #=> 3 (in 1.8)
    

Problem 4

  • "Most libraries didn't work"
  • Yes it was (particularly on 1.9.1)

now?

Active libraries work for sure!

  • nokogiri
  • rails

Problem 5

Installation

  • install ruby, and then install rubygems, ...

rubygems is builtin!

  • rake as well

Problem 6

Ruby is slow

YARV!

fib 31

  • 1.8.7
    • 7.99sec
  • 1.9.2
    • 0.64sec
  • (jruby)
    • 3.00sec

How to make legacy code 1.9 compatible?

Changes between 1.8 and 1.9

  • String isn't Enumerable
  • when clause doesn't accept colon as semicolon

change between 1.9.1 and 1.9.2

  • $: doesn't have the current dir.
    • require_relative is handy (but long...)

Demo: make a gem library 1.9.2 compatible

  • github gem library
  • github create etc..
  • it's very 1.8 even though it's new
    • 0.1.0 was March 3, 2008
    • 0.4.5 was Oct 25, 2010; after 1.9.2 public release!

The summary of this talk (again)

"Ruby 1.9.2 makes your code cleaner and easier to maintain."

end

thanks!

by

  • Tatsuhiro Ujihisa
  • @ujm
  • HootSuite Media, inc
  • Ruby, Haskell, JavaScript, Vim script, etc..

hootsuite hootsuite

appendix

  • JRuby
    • --1.9 option
    • almost compatible with 1.9 except spawn() or etc

Conditional Operator Associativities

In Ruby:

a = true ? :a : true ? :b : :c
p a

Guess the answer! Yes, as you thought, the answer is :a.

In JavaScript:

var a = true ? 'a' : true ? 'b' : 'c';
alert(a);

Guess the answer! Yes, as you thought, the answer is a.

In Vim script:

let a = 1 ? 'a' : 1 ? 'b' : 'c'
echo a

Guess the answer! Yes, as you thought, the answer is a.

In PHP:

$a = true ? 'a' : true ? 'b' : 'c';
print_r($a);

Guess the answer! No, the answer if "b".

$a = true ? 'a' : (true ? 'b' : 'c');
print_r($a);

The code above is the equivalent code to the examples in Ruby, JS and Vim script. It's impossibly difficult to imagine why the specification is so.

Followers