BLOG main image
분류 전체보기 (239)
Rails (65)
Ruby (34)
이야기 (40)
스토리큐 (61)
그 밖에.. (30)
C# (6)
작은아이의 생각
agiletalk's me2DAY
[rails] Growl4Rails
美소년 ㅇㅅㅇ씨의 一日
마사키군의 생각
ayukawa's me2DAY
작은아이의 생각
agiletalk's me2DAY
[Google App Engine] 나의 첫번..
머드초보의 블로그
53,979 Visitors up to today!
Today 43 hit, Yesterday 58 hit

 SUBSCRIBE

'Rails'에 해당되는 글 65건
2009/08/20 06:57

will_paginator gem은 페이지네이션을 너무나 간편하게 만들어준다. 데이터를 찾기 위해서는 find대신 paginate 메소드를 사용하기만 하면 되며, view에서는 will_paginate 라는 helper method를 이용해 pagination을 간단히 구현할 수 있다.

  1. # controller
  2. @posts = Post.paginate :page => params[:page]
  3. # view
  4. <%= will_paginate @posts %>

will_paginate 메소드는 똑똑하게 다음과 같은 식의 페이지네이터를 만들어낸다.

image

이 때 1,2,3과 같은 페이지네이터 숫자에 사용되는 URL은 현재 페이지의 URL과 기본형이 같다. 같은 protocol, host, port, controller, action, id를 사용하고 파라미터로 붙는 :page 파라미터만 적절히 변경된다. 즉 이 페이지가 http://app.com/posts 라고 하면 2 페이지의 link는 http://app.com/posts?page=2가 된다. 때때로 이 페이지네이터의 기본 URL을 페이지의 URL과 다르게 사용하고 싶은 경우가 있다. 이 때는 will_paginate method의 :params 옵션을 이용할 수 있다.

예를 들어 현재 페이지의 URL이 http://app.com/community 이고, 페이지네이터에서는 http://app.com/posts?page=2 와 같은 식의 URL을 사용하고 싶은 경우,

  1. will_paginate @posts, :params => {:controller=>”posts”}

이런 식으로 할 수 있다.

만약 posts를 routes.rb에 resouces로 정의해 놓았다면(RESTful), 그래서 :controller와 :action 에 어떤 것을 써야할지 잘 모르겠다면, hash_for_XXX_url 메소드를 이용할 수 있다.

  1. will_paginate @posts, :params => hash_for_posts_path

이번엔 host 와 port도 바꾸어야 할 필요가 있다면, 다음과 같이 :host 파라미터를 이용한다.

  1. will_paginate @posts, :params => hash_for_posts_path.merge(:host => "aaa", :only_path=>false)

only_path를 false로 해야만 host를 포함한 URL이 사용된다.

will_paginate의 소스를 따라가다 보면 :params 의 내용이 그대로 ActionView::Helpers::UrlHelper#url_for 로 넘어가고 있음을 알 수 있다.

2009/08/14 09:37

Controller에서 요청을 처리하고 난 후 다른 페이지로 이동시키기 위해서 redirect_to를 사용한다. 예를 들어 post를 삭제하는 요청을 처리하고 목록으로 보여주는 페이지로 redirect시키기 위해서는

  1. redirect_to posts_path

와 같은 식이다. redirect_to 의 파라미터로는 url string 혹은 url을 위한 options들이 사용될 수 있다. 그리고 :back 이라는 파라미터도 사용할 수 있다.

  1. redirect_to :back

:back은 요청이 들어온 페이지로 이동시킨다. 이는 여러 페이지에서 요청이 오는 경우 요청을 처리하고 그 페이지로 보낼 때 사용하면 유용하다. 예를 들어, post를 삭제하는 버튼이 있는 페이지가 여러 개가 있고, post를 삭제한 후 그 이전 페이지로 이동하고 싶은 경우 사용할 수 있다.

그 이전 페이지의 url은 어떻게 알 수 있을까? 그것은 HTTP header의 HTTP_REFERER를 참조해서 알 수 있다. redirec_to의 구현을 보면 이 referer를 이용하는 것을 알 수 있다. redirect_to :back은 다음과 동일한 의미다.

  1. redirect_to request.referer
2009/06/16 08:29

어떤 정보가 저장되었을 때, 혹은 저장에 실패했을 때, 사용자에게 성공/실패 메시지를 보여주기 위해 사용하는 한 가지 방법은 flash message이다. controller에서 다음과 같은 식으로 flash message를 저장한다.

  1. flash[:notice] = “Logged in successfully!”

그리고 view에서 flash message를 다음과 같이 사용할 수 있다.

  1. <%= flash[:notice] %>

flash 메시지는 cookie에 저장되어 다음 요청까지 남아 있기 때문에 redirect 요청을 사용할 때 특히 유용하다.

이와 같이 flash message는 주로 공지(notice)나 에러(error) 같은 메시지를 위해 사용된다. 이런 메시지들은 전 페이지에 걸쳐 발생한다. 이들을 각 view에서 보여주는 것은 번거로울 뿐만 아니라 view마다 보여지는 방식이 다르다면 사이트를 산만하게 보이게 할 수도 있다.  일관된 방식으로 보여주는 것은 개발에도 편리하고 일관성도 유지할 수 있다. 예를 들어 ‘항상 오른쪽 상단에 공지 메시지가 보여지게 된다’ 와 같은 정책을 갖는 것이다. 이는 다음과 같이 layout에서 flash message를 보여주는 방법으로 간단히 구현할 수 있다.

  1. # layout
  2. <div id=’contents’>
  3.   <%= flash_messages %>
  4.   <%= yield  %>
  5. </div>
  6. # application_helper
  7. def flash_messages
  8.   if(flash[:notice])
  9.     content_tag(:div, flash[:notice], :class=>”notice”)
  10.   end
  11. end

이 방법은 편하기는 하지만 멋지지는 않다. jGrowl을 사용해서 좀 더 멋지게 보여지도록 하자.

jGrowl은 MAC의 growl 알림 시스템에 영감을 받은 jquery 플러그인이다. 메시지가 발생하면 투명한 메시지 박스가 보여진다. 세련된 느낌의 그 박스를 보고 있자면 기분이 좋다. 여기 데모가 있다.

jGrowl의 사용은 간단하다. jGrowl을 다운 받아서 설치(jquery.js, jquery.jgrowl.js, jquery.jgrowl.css 파일을 추가)한 후 다음과 같이 사용할 수 있다.

  1. $.jGrowl("hello jGrowl");

이제 아래와 같이 flash message들을 jGrwol을 통해 보여지도록 하자.

1

application_helper의 flash_message를 수정해서 jGrowl을 통해 메시지를 보여지도록 한다.

  1. module ApplicationHelper
      def flash_messages
        messages = []
        %w(notice error).each do |type|
          if flash[type.to_sym]
            messages << """
      $('#flash_container').jGrowl('#{flash[type.to_sym]}',
      {
        header: '#{type.upcase}',
        theme: '#{type.downcase}'
      });
            """
          end
        end
        javascript_tag """
    $(function(){
      #{messages.join}
    });
        """
      end
  2. end

flash_container 밑에 jGrowl 메시지들이 들어가도록 했다. header는 메시지의 처음에 나오는 제목이다. theme를 지정하면 메시지 element의 CSS class가 theme로 설정된다.
이제 flash_container를 layout 파일의 적당한 위치에 넣자.

  1. <div id="content">
  2.   <div id="flash_container"></div>
  3.   <%= flash_messages %>
  4.   <%= yield  %>
  5. </div>

그리고 css를 다음과 같이 주어 오른쪽 상단에 메시지들이 보여지도록 하자.

  1. div#content {
      position: relative;
    }
    div#flash_container {
      position: absolute;
      right: 5px;
    }

error 와 notice의 CSS를 설정해서 조금 더 보기 좋게 할 수도 있다.

  1. div.jGrowl-notification.error div.header {
      background: transparent url(/images/icon_error.png) no-repeat left;
      padding-left: 20px;
      color:red;
    }
    div.jGrowl-notification.notice div.header {
      background: transparent url(/images/icon_info.png) no-repeat left;
      padding-left: 20px;
      color:#009BC7;
    }
2009/06/11 21:56

authlogic은 restful_authentication처럼 rails에서 사용자의 인증을 처리해주는 gem이다. restful_authentication보다 더 깔끔하다는 평을 받고 있다.

authlogic의 내부에서는 I18n을 고려하고 있기 때문에 locale data(yml 파일)만 잘 설정해 주면 손쉽게 한글화를 시킬 수 있다. I18n에 대해서는 [Rails] I18n을 이용하기 를 참조.
다음과 같은 번역을 ko.yml 파일에 추가하자.

  1.   authlogic:
        error_messages:
          login_blank: "은 비워두면 안됩니다"
          login_not_found: "을 찾을 수 없습니다"
          login_invalid: "은 영어 문자와 숫자, 그리고 .-_@ 만 사용할 수 있습니다"
          consecutive_failed_logins_limit_exceeded: "연속되는 로그인 실패로 인해 계정이 사용중지 되었습니다"
          email_invalid: "은 올바르지 않은 이메일 형식입니다"
          password_blank: "은 비워두면 안됩니다"
          password_invalid: "은 올바른 형식이 아닙니다"
          not_active: "계정이 사용중지되어 사용할 수 없습니다"
          not_confirmed: "계정이 아직 활성화되지 않았습니다"
          not_approved: "계정이 아직 승인되지 않았습니다"
          no_authentication_details: "로그인 정보를 입력하지 않았습니다"
        models:
          user_session: "사용자 세션"
        attributes:
          user_session:
            login: "로그인 ID"
            email: "이메일"
            password: "비밀번호"
            remember_me: "아이디 기억하기"

끝.

2009/06/11 21:46

Rails 2.3을 보면 config 밑에 locales라는 디렉토리가 보인다. 이것이 무엇일까?
Rails 2.2 부터 Internationalization(I18n) 이 activesupport에 포함되었다. I18n을 이용하면 비교적 쉽게 한글/영문을 동시에 지원하는 서비스를 만들 수 있다.

목표

rails 2.3을 이용해서 한글과 영문을 동시에 지원하는 서비스 만들기.
한글 페이지의 url은 http://site.com/ko/users 와 같은 식이고 영문 페이지는 http://site.com/en/users와 같은 식이다.

구현

우선 environment.rb를 열어서 기본으로 사용하는 언어에 대해 설정을 해 주자.

  1. config.i18n.default_locale = :ko
  2. config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'ko', '*.yml')]
  3. config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'en', '*.yml')]

기본으로 사용하는 언어는 한국어다. 그리고 한국어에 대한 파일은 config/locales/ko 디렉토리에 yml 형식으로 들어 있다.

rails에서 사용하는 기본적인 것들, 즉 시간 같은 것들에 대한 번역은 Locale data for Ruby on Rails I18n 에서 찾아볼 수 있다. 여기서 ko.yml 파일을 복사해서 config/locales/ko 디렉토리에 추가하자. en.yml 파일은 없는데, rails에 영문에 대한 것은 디폴트로 포함되어 있다.

authlogic 같은 인증 관련 gem을 통해서 user model을 만들고, ko 및 en 디렉토리에 user.yml 파일을 생성한다. 파일을 생성한 후에는 서버를 재시작해 주어야 한다. 그러나 파일이 수정될 때마다 재시작 해줄 필요는 없다. 개발환경에서는 재시작하지 않아도 파일 내용의 수정이 반영 된다.

activerecord

user.yml 파일에 activerecord에서 참조할 내용들을 적어주자.

  1. ko:
      activerecord:
        models:
          user: "사용자"
        attributes:
          user:
            login: "로그인 ID" 
            email: "이메일"

이렇게 함으로써 activerecord에서 발생하는 validation error가 한글로 번역되어서 보인다. 조사에 약간 문제가 있지만..

clip_image001

  view 와 controller

view와 controller에서는 다음과 같이 t를 이용해서 i18n을 사용할 수 있다. model에서는 I18n.t 을 이용한다.

  1. flash[:notice] = "Login successful!"

대신

  1. flash[:notice] = t "login.success"

라고 해주면 된다.

그리고 user.yml에 다음과 같이 적어준다.

  1. ko:
  2.   login:
  3.     :success: "로그인 성공"
시간 포맷

시간 포맷은 다음과 같이 l 을 이용해서 현지화를 시킬 수 있다.

  1. l Time.now, :format => :short

ko.yml 에 선언된 형식에 의해 시간포맷이 출력된다.

  1. time:
      formats:
        default: "%Y/%m/%d %H:%M:%S"
        short: "%y/%m/%d %H:%M"
I18n.translate

I18.translate(I18.t) 메소드에 대해서 좀 더 자세히 알아보자.

yml 파일이 다음과 같을 때, 

  1. hello: “안녕하세요”
  2. hi: “안녕”
  3. user:
  4.   name: “이름”
  5.   hello: “{{name}}님 안녕하세요”
  6.   total:
  7.     zero: "no one"
  8.     one: "1 person"
  9.     other: "{{size}} people"

배열울 파라미터로 주면 배열의 항목을 번역해 준다.

  1. I18n.t [:hello, :hi]
  2. => [“안녕하세요”, “안녕”]

depth가 있는 경우 다 써줘도 되고, scope을 이용해도 된다.

  1. I18n.t "user.name"
  2. I18n.t :name, :scope => [:user]

interpolation이 가능하다.

  1. I18n.t "user.hello", :name => user.name

count을 주면 영리하게 복수화를 시킨다.

  1. I18n.t "user.total", :count => User.size, :size=>User.size

요청 URL에 locale 정보 포함시키기

http://site.com/en/users 와 같은 식으로 locale정보가 url에 포함되도록 하자. 그리고 이 locale 정보에 맞도록 rails가 응답하도록 하자.

우선 route.rb에 path_prefix를 이용해서 locale의 패턴을 지정해 준다.

  1. map.resources :users, :path_prefix => '/:locale'

root에 대해서도 locale을 지정할 수 있도록 해 준다. 지정하지 않으면 ko를 사용한다.

  1. map.locale_root '/:locale', :controller => "records"
  2. map.root :controller => "records", :locale=>"ko"

그리고 application_controller.rb에 before_filter를 하나 만들어 준다.

  1. before_filter :set_locale
    private
      def set_locale
        I18n.locale = params[:locale] if params[:locale]
      end

마법처럼 간단하다.

참고 페이지

rails cast : http://railscasts.com/episodes/138-i18n

아주 잘 되어있는 문서화 : http://guides.rubyonrails.org/i18n.html

2009/04/05 19:52

rails를 구동하는 서버가 리눅스이고, 리눅스에 PuTTY를 통해서 접속해서 작업을 한다.

rails log를 vi로 보거나 console를 이용하다보면 한글이 깨져서 나온다.

PuTTY의 설정을 변경해서 한글이 제대로 나오도록 해 주자.

우선 PuTTY Configuration 의 Window/Appearance 에서 폰트를 Fixedsys로 정하자. 다른 폰트를 사용해도 되나 스크립트가 꼭 한글이어야 한다.

 font.jpg

그리고 Window/Translation에서 Character set translation on received data 를 UTF-8로 설정한다. 터미널로 들어오는 데이타의 문자셋이 UTF-8인 것으로 생각하겠다는 뜻.

translation.jpg

 

마지막으로 설정을 저장하자.

이것으로 끝이다.

이 글은 스프링노트에서 작성되었습니다.

2009/02/25 14:24

갯수가 많은 상품의 목록을 보여줄 때 쓰는 유용한 방법 중 하나가 페이지네이션이다. 한페이지에 적당한 갯수를 보여주고, 페이지 하단에 다른 페이지로 가는 링크를 걸어서 전체 목록을 조회할 수 있도록 한다. ruby on rails를 사용한다면 will_pagination을 이용해서 간단하게 페이지네이션을 구현할 수 있다.

어떤 페이지에 보여줄 목록을 가져오기 위해서는 페이지 번호와 한페이지에 보여줄 갯수만 있으면 된다. 예를 들어 boxes라는 테이블 목록을 한 페이지에 25개씩 보여준다고 하자. 만약 세번째 페이지를 생성시간 순서로 보여주고 싶다면 SQL 쿼리는 다음과 같다.

  1. SELECT * FROM boxes ORDER BY created_at LIMIT 75, 25;
인덱스

위 쿼리는 아주 잘 동작하지만 작은 문제가 있다. boxes 테이블 항목의 갯수가 많아지면 많아질 수록 쿼리의 수행 속도도 느려진다. 이점을 보완하기 위해 created_at에 인덱스를 걸어 둔다. 그러면 검색속도가 빨라진다.

목록의 갯수가 아주 많다면?

그래도 여전히 문제가 있다. 목록의 갯수가 아주 많아졌다고 생각해 보자. 갯수가 100만개쯤 되었다고 가정하자. 첫 페이지에 대해서는 빠른 응답을 보인다. 그러나 마지막 페이지를 요청하면 오랜 시간 동안 기다려야 한다. 왜 그럴까? 4만번째 페이지를 요청하는 쿼리는 다음과 같다.

  1. SELECT * FROM boxes ORDER BY created_at LIMIT 1000000, 25;

이 요청은 생성된 시간이 백만번째 부터 25개의 box를 가져와라라는 쿼리다. 백만번째 생성된 box가 무엇인지만 알면 인덱스에 의해 잘 정렬된 테이블에서 그 후 25개만 가져오면 된다. 그런데 이 백만번째라는 것이 문제다. 백만번째가 무엇인지 단번에 알 수가 없다. 백만번째 생성된 box를 찾아내기 위해서는 백만번 동안 테이블을 조사 해야 한다. 인덱스가 걸려 있다 해도 마찬가지다. 첫번째, 두번째... 이렇게 순차적으로 조사해 나가는 수밖에 없다. 그래서 시간이 오래 걸린다.

4만번째 페이지라고?

한편으로 생각해보면, 이렇게 많은 내용을 페이지네이션으로 한다는 것은 별 의미가 없다. 32423번쨰 페이지를 어떻게 찾아갈 것이며, 그곳에 찾아가서 무엇을 기대할 수 있을까?  페이지가 1000개가 넘어가면 그저 '많은 페이지가 있구나' 라는 생각이 들 뿐이다. 이렇게 자료가 많다면 페이지네이션이 아닌 다른 조회 시스템을 마련하는 것이 더 낫다. 한가지 예는 성능좋은 검색시스템이 될 수 도 있고, 월별로 조회하는 페이지를 따로 둘 수도 있다. 좀 더 현실적인 문제는 40000번째까지 페이지 링크를 만들어 놓으면 봇들이 돌아다니면서 페이지들을 죄다 누르고 다닌다.

페이지의 수를 제한하자.

페이지네이션을 꼭 사용해야 한다면 최대 페이지의 수를 제한하는 것도 한가지 좋은 방법이다. 자료의 갯수가 아무리 많더라도 페이지네이션으로 보여줄 때는 100페이지만 보여주는 것이다. 만약 최대 페이지를 100으로 제한하고 싶다면 총 항목의 갯수를 가져오는 SQL 쿼리는 다음과 같다.

  1. SELECT COUNT(*) as total FROM (SELECT id FROM boxes LIMIT 2500) s;

will_pagination을 사용한다면 총 페이지 갯수를 지정하기 위해 paginate의 옵션에 :total_entries 를 추가할 수 있다.

  1. res= ActiveRecord::Base.connection.execute("SELECT COUNT(*) as total FROM (SELECT id FROM boxes LIMIT 2500) s;")
  2. total = 0;
  3. res.each_hash { |r| total = r["total"].to_i }
  4. Box.pagination(:page=>3, :total_entries=>total)
2008/09/05 00:57

파이어 폭스의 확장기능을 개발하면서 참조할만한 것들, 알게 된 것들을 모아 놓았다. 개발자를 위한 문서들의 한글화가 잘 되어 있어서 좋았다.

 

시작하기

파이어폭스 확장기능 만들기 에는 개괄적인 설명이 있다.

XUL로 만드는 firefox Extention 도 많은 도움이 된다.

이 슬라이드도 좋은 시작점이 될 것이다.

 

firefox 확장기능은 .xpi 확장자를 갖는 zip 압축 파일이다. MyExt.zip 를 다운 받아서 압축을 풀어보자. 이 디렉토리 구조가 확장기능의 구조이다.

 

install.rdf 파일

파이어폭스 확장기능을 설명하는 파일이다. 버젼, 개발자, 이름과 같은 여러 정보들이 포함된다. 자세한 내용은 intall.rdf 를 참조한다.

 

chrome.manifest 파일

확장기능이 사용하는 chrome과 overlay 에 대한 설정이 있는 파일이다. 자세한 내용은 Manifest files 를 참조.

 

Chrome 에 대해서

Chrome 에 대한 설명이다. 한글로 설명하기 애매해서 영문 버젼을 인용한다.

Chrome is the user interface parts of the application window that are outside of a window's content area.

Toolbars, menu bars, progress bars, and window title bars are all examples of elements that are typically part of the chrome.

  • UI 관련한 파일들(xul, css, js, dtd, ...)이 들어 있는 공간(디렉토리)
  • 예를 들면 확장기능에서는 UI 관련 파일들이 있는 디렉토리를 chrome package라고 할 수 있음. 이 디렉토리를 chrome으로 이용하기 위해서는 chrome.manifest에 다음 같은 방식으로 선언이 필요함.
  1. content sample chrome/chromeFiles/content/
  • 종류는 conent, locale, skin 이렇게 세가지가 있다.
  • 파이어폭스는 Chrome들을 관리하는 Chrome Manager 라는 컴포넌트를 가지고 있다. Chrome Manager는 등록된 Chrome들의 정보를 이용해 사용자 인터페이스를 설치하고 만들어낸다.
  • 파이어폭스에서 Chrome 의 파일에 접근하기 위해서는 Chrome URL를 사용한다. (예: chrome://sample/content/browser.xul)
  • Chrome URL는 다음과 같이 구성된다.

    • "URI_scheme://package_name/parts/path_to_file"
    • chrome://sample/content/browser.xul


locale 사용하기

locale을 사용해서 다양한 언어를 지원하도록 할 수 있다. 자세한 내용은 http://developer.mozilla.org/index.php?title=Ko/Localization 참조.

다음은 한국어와 영어를 지원하는 확장기능을 만드는 과정이다.

ROOT/chrome/locale/en-US/sample.dtd,

ROOT/chrome/locale/ko-KR/sample.dtd 파일을 생성한다.

그리고 chrome.manifest에 등록한다.

  1. locale  sample en-US  chrome/locale/en-US/
  2. locale  sample ko-KR  chrome/locale/ko-KR/

 

이제 Chrome URL(chrome://sample/locale/sample.dtd)를 통해서 dtd 파일에 접근할 수 있다.

locale을 사용하는 XUL 파일, sample.xul에 다음과 같이 선언을 해 준다.

  1. <!DOCTYPE window SYSTEM "chrome://sample/locale/sample.dtd">

 

sample.dtd에 필요한 locale 정보를 작성한다.

  1. <!ENTITY registerLabel "등록">

sample.xul에서는 이렇게 사용할 수 있다.

  1. <caption label="&registerLabel;"/>

 

xul에 대해서

XUL Tutorial 을 살펴보면 기본적인 것들에 대해서 알 수 있을 것이다.

더 자세한 것을 원한다면 http://developer.mozilla.org/ko/XUL 를 보자.

XUL을 이용해서 간단한 테스트를 하고 싶다면 live XUL editor 를 이용해 보자.

 

overlays

overlay를 이용하면 이미 존재하고 있는 파이어폭스 UI를 변경할 수 있다. 예를 들면 파이어폭스의 상태바에 확장기능의 아이콘을 추가 및 삭제할 수 있다.

overlay의 예

다음은 help메뉴에 'online' 이라는 항목을 추가하는 overlay

  1. <overlay id="sample_overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  2.  <menu id="help-menu">
      <menupopup id="help-popup">
       <menuitem id="help-online" label="online"/>
      </menupopup>
     </menu>
    </overlay>

'help-menu' 이라는 id를 갖는 메뉴에 online 이라는 menuitem을 추한다. 만약 'help-menu'라는 id가 없으면 아무것도 하지 않는다.

 

toolbar 에 넣을 수 있는 아이콘을 추가하고 싶은 경우

  1.  <toolbarpalette id="BrowserToolbarPalette">
      <toolbarbutton id="sample-button" image="chrome://sample/content/icons/toolbar-button.png" >
      </toolbarbutton>
     </toolbarpalette>

statusbar에 아이콘을 추가하고 싶은 경우

  1. <statusbar id="status-bar">
      <statusbarpanel class="statusbarpanel-iconic" id="sample-status" src="chrome://sample/content/icons/toolbar-button.png" />
    </statusbar>

도구 메뉴에 추가하고 싶은 경우

  1. <menupopup id="menu_ToolsPopup">
     <menuitem label="sample" insertafter="devToolsSeparator" oncommand="alert('why?');" />
    </menupopup>

 

사이드바 만들기

http://developer.mozilla.org/index.php?title=En/Creating_a_Firefox_sidebar

http://developer.mozilla.org/en/Code_snippets/Sidebar

 

broadcaster 와 observe는 무엇인가?

http://developer.mozilla.org/En/XUL_Tutorial/Broadcasters_and_Observers

 

browser.js 에 있는 toggleSidebar() 함수를 이용해서 사이드바를 보이고 사라지게 할 수 있다.

 

Tips

firefox 의 주소창에 about:config을 치면 firefox의 환경설정(preference)를 볼 수 있음.

xpath 사용하기 : http://developer.mozilla.org/Ko/Using_XPath

DOM inspector를 이용하면 XUL 의 DOM 구조를 쉽게 확인할 수 있다.

 

개발중 테스트하기

기본 프로파일 디렉토리로 이동한다. 윈도우에서는 아마도 "C:\Documents and Settings\...\Application Data\Mozilla\Firefox\Profiles\xxxxx.default" 일 것임.

  1. extentions 폴더로 이동해서 파일을 하나 생성한다. 파일 이름은 확장기능의 id로 한다. 파일의 내용은 확장기능이 위치하고 있는 디렉토리 이름이다.
    예) c:\sample\
  2. firefox를 재시작하면 개발중인 확장기능을 도구>부가기능 메뉴을 통해서 볼 수 있다.
  3. chrome URI를 통해서 개발중인 xul파일에 접근할 수 있다. chrome URI를 이용할 수도 있지만, xul 파일을 드래그 앤 드롭으로 파이어폭스에 가져다 놓아도 된다. chrome URI 보다 드래그 드롭으로 하는 것이 더 좋은데, 왜냐하면 변경된 내용이 새로고침했을 때 바로 적용되기 때문이다.

 

프로파일에 대해서는 [firefox] profile 이용하기 참조.

 

배포하기

zip 으로 압축한 후 확장자를 xpi로 바꾼다. 압축할 때는 알집을 사용하지 말고 마우스 오른 버튼 클릭해서 나오는 메뉴의 보내기 > 압축폴더 를 이용하도록 하자. 알집으로 하면 안된다.

.xpi 파일을 부가기능 목록에 드래그 앤 드랍 하면 확장기능이 설치된다. 이렇게 테스트가 마쳐졌으면 http://addons.mozilla.org/developers/ 페이지에 확장기능을 등록한다. 정식으로 공개되기까지는 테스트가 필요하다.

 

 

이 글은 스프링노트에서 작성되었습니다.

2008/07/25 08:08

Rails로 애플리케이션을 개발하는 데 있어서 Rspec은 참 유용하다. 개인적으로 Rspec을 생각하면 10 Ruby On Rails Plugins You Should Be Using 에서 평가한 다음 문장이 제일 먼저 생각난다.

Rspec - All the cool kids are using it these days...

쿨한 녀석이 되봐야지..^^

 

fixture를 사용할 때 어려운 점들

model을 테스트 할 때 보통 사용하는 것이 fixture이다. yaml로 된 fixture에 테스트 데이타를 넣어 놓고 그것으로 테스트를 수행한다. 그러면 rspec이 시작되면서 fixture를 db에 넣어놓고 테스트를 수행한다. 멋진 일이다. 그런데 이 fixture에 대해서 몇 가지 아쉬운 점이 있다.

첫번째는 애플리케이션이 커지면서 fixture가 점점 복잡해진다. 예를 들어 User 라는 모델에 대해 fixture는 user.yaml 하나만 두고 fixure가 필요한 모든 테스트들이 user.yaml을 사용하는데, 따라서 user.yaml은 점점 복잡해진다. 때때로 새로운 테스트를 위해 추가하는 정보가 기존의 테스트에 영향을 미치기도 한다. 

두번째 문제는 테스트를 수행할 때마다 fixture의 정보를 db에 넣기 때문에 테스트 수행 속도가 느리다. fixture가 커질수록, 또 자주 사용할수록 테스트는 느려진다.

세번째는 fixture가 테스트 코드 밖에 있기 때문에 테스트 코드를 작성하면서 fixture내용을 참조하기 어렵다. 테스트를 작성하면서 매번 무슨 내용이 fixture에 있는지 확인을 하는 일은 은근히 귀찮다.

네번째는 fixture가 yaml로 되어있다보니 fixture를 관리하기가 어렵다.(그런가? yaml은 쉬운데.. 어렵다의 기준은 "ruby를 사용할 때보다"이다.)

 

fixture senarios 플러그인

내게는 첫번째 문제(커져버린 fixture의 관리가 힘들다)가 참기 힘들었다. 이것을 해결하기 위한 좋은 방법이 있었으니, fixture senarios 플러그인 이다.

This plugin allows you to create ‘scenarios’ which are collections of fixtures and ruby files that represent a context against which you can run tests.

오~ 좋다. 이 플러그인은 fixture 파일들을 테스트 시나리오 별로 관리할 수 있게 해 준다. 그래서 user.yaml 이라는 fixture가 fixtures 디렉토리에 하나만 존재하는 대신, 검색을 위해서 fixtures/search에 하나, 사용자 로그인을 위해서 fixtures/user에 하나, 이런식으로 시나리오 별로 여러 개 있을 수 있다. 그리고 그 시나리오별 fixture들를 테스트에서 읽어 들이기 위해서는 senario :search 이런식으로 하면 된다.

fixture senarios 플러그인과 더불어 fixture senarios builder 플러그인은 순수 ruby코드를 이용해서 시나리오별 fixture들을 동적으로 생성할 수 있도록 해준다. fixtures 디렉토리의 senarios.rb 를 만들어서 시나리오 별로 어떤 fixture를 만들지 지정해 줄 수 있다. 예를 들면 다음과 같다.

  1. scenario :blog do
      %w( Tom Chris Kevin ).each_with_index do |user, index|
        User.create! :name => user, :banned => index.odd?
      end
    end

그러면 테스트가 수행되기 전 우선 senarios.rb를 참조해서 시나리오 별 fixture들이 생기게 될 것이다. 오! 너무 좋다. 이제 네번째 문제인 yaml을 직접 수정하면서 관리할 필요도 없어졌다. 

 

model_stubbing 플러그인

model_stubbing 플러그인을 소개하기 전에 '앞의 두 플러그인을 이용해도 충분히 만족할 수 있었을 텐데..' 라는 생각을 해 본다. 그러나 이 두 플러그인의 존재를 ruby flow 에 올라온 model_stubbing에 관한 글 을 따라가 보다 알게 되었기에, 아직 한번도 사용해 보지 않았다. model_stubbing 플러그인은 위 기능 더하기 알파를 제공해 준다.

model_stubbing 플러그인의 REAME를 보면 다음과 같이 소개하고 있는 것을 볼 수 있다.

Creates in-memory versions of models for testing. This attempts to solve a few problems with ActiveRecord Fixtures:
   Speed - There's no hit to the database and no cleanup between tests.
   Flexibility - You can define a global set of stubs, or define custom ones for specific tests/specs

테스트들 사이에 db에 접근하지 않기 때문에 빠르다. 그리고 테스트를 위한 데이터들을 테스트 코드 바로 위에 위치시킬 수 있다.

 

음.. db에 접근하지 않는다는 말이 정확히 뭘 뜻하는지 모르겠으나..

model_stubbing은 describe 블록 안에 정의된 stub들을 db에 한번 넣는다. 그리고 그 블록안의 테스트들을 모두 수행한다. fixture를 사용할 때와 똑같이 느리다. 만약 db에 데이터가 있을 필요가 없는 테스트라면 :insert => false 옵션을 주어서 db에 넣지 않을 수도 있다. find 같은 db 접근을 필요로 하는 메쏘드를 사용하는 메쏘드는 db에 데이타가 있어야 정확한 테스트를 할 수 있다. 그런 메쏘드들을 테스트하는 것이 아니라면 :insert => false로 해서 db에 넣지 않는 것이 좋겠다.

 

테스트를 위한 데이터들이 테스트 코드 안에 있는 것은 정말 큰 장점이 된다. 한발 더 나아가서는 일반적으로 사용되는 Model들을 stub.rb 혹은 시나리오 별로 senario.rb 에 모아놓고, 필요할 때마다 이것들을 include 해서 사용할 수도 있다. 그리고 테스트만을 위한 세부적인 내용을 테스트 코드 위에다 재정의하는 것이다. 매우 이해하기 편하고, 유지보수도 쉽다.

 

설치하기

다음과 같이 설치하자.(주소를.. 영..찾기 힘들어서야...원..)

  1. ruby script\plugin install http://ar-code.svn.engineyard.com/plugins/model_stubbing

model_stubbing github 에서 직접 다운 받아서 복사해 줘도 좋겠다.(귀찮아서...뭐..영..)

 

사용하기

test를 수행하고자 하는 model의 spec을 다음과 같은 식으로 작성할 수 있다.

  1. describe Agent do
      define_models do
  2.     time 2008, 7, 1, 11, 20
        model AgentGroup do
          stub :animal, :name => 'animal', :created_at => current_time
        end  
        model Agent do
          stub :name => 'agent', :agent_group => all_stubs(:animal_agent_group)
          stub :tiger, :name =>"tiger"
          stub :monkey, :name =>"tiger"
        end
      end

      it "should do something" do
  3.    agents(:tiger).agent_group.should == agent_groups(:animal)
  4.   end
  5. end

매우 직관적이어서 그다지 설명할 것은 없고,

time을 정하면 current_time 이 지정된 시간이 된다. 나이스!

model에서 stub을 할 때, 첫번째 파라미터가 stub의 이름이 된다. 그것을 fixture를 사용할 때처럼 agents(:tiger) 이런 식으로 가져올 수 있다. 첫 번째 파라미터가 생략되어 있는 경우 그것은 default stub이 된다. default stub은 tiger 같이 이름을 가진 stub들의 default값이 된다. default stub은agents(:default) 혹은 stubs(:agent) 로 가져올 수 있다. stub을 가져올 수 있는 다른 방법은 all_stubs(NAME_MODEL)을 이용하는 것인데, stub을 선언할 때는 이를 사용해야 한다.

object mother 패턴을 구현했기에 stub을 간단하게 재정의할 수 있다. 예를 들어 테스트 중 tiger stub의 이름이 "cat" 이길 원한다면(비유..참..)

  1. it "should do something"
  2.   agents(:tiger, :name=>"cat").name.should == "cat"
  3. end

라고 사용할 수 있다. 이것도 나이스!

만약 stub의 내용이 db에 들어가는 것이 필요하지 않다면 define_model에 :insert => false 옵션을 줄 수 있다.

  1. define_models :insert => false do

 

쓰다가 발생한 이상한 일들
  • spec_server를 사용하고 있다면 define_models 블록이 수정된 후에는 spec_server를 재시작 시켜 주어야 한다. 안 그러면 키가 중복된다며 에러를 발생시킨다.
  • type 을 이용해서 단일 테이블 상속을 사용하고 있는 경우 상위 클래스가 꼭 model에 선언되어 있어야 한다.
    예를 들어 events가 단일 테이블 상속을 사용하고 있는 테이블이고, 그리고 NetworkEvent가 Event로 부터 상속받고 있다면,
  1. class NetworkEvent < Event
    end

NetworkEvent의 model을 정의할 때 Event도 꼭 있어야 한다. 안그러면 뭔가 에러가 난다.

  1. define_models do
  2.   model Event do
      end   
      model NetworkEvent do
        stub :type=>"NetworkEvent"
      end
  3. end

 

써보니

아주 맘에 든다.

 

진이가 요새 코골면서 잔다. 고양이가 코코는건 반칙 아닌가..? 아픈건가..?

P5040114.JPG

 

이 글은 스프링노트에서 작성되었습니다.

2008/06/30 16:54

swf upload

SWFUpload 는 flash/javascript를 이용해서 file upload를 쉽게 구현할 수 있도록 해 준다.

다음과 같은 장점을 갖는다.

  • 여러개의 파일을 선택하고 업로드 할 수 있다.
  • 파일을 선택하면서 file type에 대한 검사를 할 수 있다.
  • 파일을 선택하면서 file 크기에 대한 검사를 할 수 있다.
  • 파일 업로드 상태(프로그레스)를 구현하기 쉽다.
  • javascript/flash가 사용불가능한 경우에는 기존의 방법(HTML)을 사용할 수 있다.(Degrades gracefully)

 

다운로드를 받아서 내용을 풀어보면, 잘 정리된 문서와 풍부한 예제 코드들을 볼 수 있다. 비록 php로 작성되어 있지만, 기능을 살펴보는 데 있어서는 부족함이 없을 것이다.

 

rails에서 swf upload사용하기

문서화가 잘 되어 있어서 사용하기는 많이 어렵지 않을 것이다.

내가 필요했던 API들을 간단히 정리한 문서가 여기 있다.

flash가 가지고 있는 cookie 버그를 해결한 문서 도 도움이 될 것이다.

업로드를 사용하는 요청에 respond_to 문법을 사용하면 제대로 동작하지 않을 수도 있는데, 그것은 SWFUpload 의 업로드 요청이

  1. HTTP_ACCEPT = "text/*"

이기 때문이다. 가장 간단한 해결책은 mime_type.rb에 다음을 추가하는 것일 듯 싶다.(이상하지만..)

  1. Mime::Type.register "text/*", :flash

그러면 response_to 에서 다음과 같이 사용할 수 있다.

  1. format.flash { render :text => @sbox.id.to_s, :layout=>false }

 

참조

swf upload를 사용해서 file upload하기

 

이 글은 스프링노트에서 작성되었습니다.

prev"" #1 #2 #3 #4 #5 ... #7 next