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'에 해당되는 글 40건
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/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)
2009/02/23 10:02

루비로 작성한 프로그램에 대해 문서를 제출할 일이 생겼다. RDOC을 이용하면 간단하게 HTML, XML, chm 형식의 문서를 뽑아 낼 수 있다. 주석을 충분히 달아 놓기만 했다면...

RDOC을 이용해서 문서화할 수 있도록 주석을 다는 법에 대해 알아보자.

RDOC 주석을 위한 특별한 시작은 없다. 클래스나 메쏘드 위의 주석은 모두 rdoc에 의해 해석된다. 주석의 작성 형식은 전체적으로 간단하고 실용적이며, 위키의 포맷과 비슷하다. 어렵지 않다.

리스트 표현

숫자, *, - 로 시작되는 문장은 리스트로 표현된다.

  1. # * List item 1
  2. # * List item 2

list

[] 는 다음 처럼 단어 설명에 사용하면 좋다.

  1. # [고앙이]   small domestic animal 

cat

인덴트

스페이스 두개로 인덴트가 표현된다. 리스트 안에 리스트를 표현하고 싶으면 다음과 같이 할 수 있다.

  1. #
  2. # - list 1
  3. #   - list 1.1
  4. #     - list 1.1.1

indent 

 

강조
  • <b> </b> 는 글씨를 진하게 한다.
  • <tt> </tt> 는 글씨를 typewriter 체로 한다.
  • <em> </em> 은 글씨를 이택릭체로 한다.
  • = 로 시작하면 헤드라인을 보여준다.
  1. # <em>text</em>
  2. # <b>text</b>
  3. # <tt>text</tt>
  4. # = Level One Heading 
  5. # == Level Two Heading
  6. # === Level Three Heading
  7. # ==== Level Four Heading

text 

rdoc에 포함시키지 않기

주석중 rdoc에 포함시키기 원치 않은 부분이 있다면 #-- 를 이용할 수 있다. #-- 이하의 부분은 rdoc에 포함되지 않는다. 다시 포함시키고 싶다면 #++를 이용한다.

  1. # This is RDOC document.
  2. #--
  3. # Not included in RDOC from here
  4. #++
  5. # Now included again

상수를 포함시키고 싶지 않다면 다음과 같이 #:nodoc: 를 이용한다.

  1. OsInfo = { #:nodoc:
  2.                 "Linux" => "Linux", #:nodoc:  
  3.                  "Unknown" => "Unknown" #:nodoc:
  4. } #:nodoc:

#:doc:  은 반대로 포함되지 않는 부분을 포함시킨다. 특정한 private method를 주석에 포함시키고 싶을 때 유용하다.

하이퍼링크

http, ftp, mailto, www 로 시작되는 문장은 알아서 하이퍼링크로 치환시켜 준다. 또한 이미지 URL의 경우는 알아서 <img> 태그로 변환시켜 준다.
클래스나 메쏘드 이름과 같은 단어는 알아서 하이퍼링크를 생성해 준다. 굿!

참조

http://rdoc.sourceforge.net/doc/index.html

2008/09/08 08:34





출처 : 스토리큐

'스토리큐' 카테고리의 다른 글

DOM 개요  (0) 2008/09/17
Metaprogramming javascript  (0) 2008/09/17
Rails 배포에 대해  (0) 2008/09/08
Fireq : 스토리큐를 위한 파이어폭스 확장기능  (0) 2008/09/04
2008 베이징 올림픽 개막식  (0) 2008/08/13
최우 운우도화첩  (0) 2008/07/31
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하기

 

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

2008/06/30 16:54

SWF upload 플래시는 cookie의 값을 서버로 제대로 전송해 주지 못한다. 무슨 내용인지 궁금하면 다음을 읽어보라.

http://swfupload.org/forum/generaldiscussion/383

IE가 아닌 다른 브라우저에서는 IE의 쿠키값을 보낸다는 듯 하다.

이유야 어찌됬든 파일 업로드는 사용자가 로그인한 상태에서 행해지는 것이 대부분일테고, 그리고 사용자의 로그인을 restful authentication의 login_required 같은 before_filter에 의해 보장받고 싶다면, SWFUpload를 이용한 업로드는 문제가 있다.

세션 정보가 제대로 전해지지 않기에 login_required는 실패를 할 테고, 업로드 액션에 도달하지도 못하고 끝이 날 것이다.

 

이를 해결하는 방법 중 하나는 session 정보를 업로드 요청의 파라미터로 보내서 그것으로부터 session 정보를 복구해내는 것이다. Upload files with SWFUpload 를 보면 session 정보를 URI의 query_string에 전하고, class CGI::Session 과 class CGI::Session::CookieStore 를 해킹해서 해결했다. 이 방법은 잘 동작한다. 이렇게 해도 별 무리가 없을 듯하지만...

어떤 이유 (- 모든 요청에 대해 query_string을 조사하는 것이 꺼림직하다. -) 에서든지 session 정보를 query_string이 아닌 POST 파라미터로 보내고 싶다면 다음과 같은 방법을 사용할 수도 있다.

 

여기서는 sesion 정보를 POST 파라미터로 전하고 action의 before_filter를 이용해서 session 정보를 복구하도록 하는 방법을 소개한다.

 

1. flash의 upload 요청에 session 값을 POST 파라미터로 전해준다.

다음과 같이 swfupload가 post_parameter 에 session_id를 전해주도록 한다.

  1. new SWFUpload({
  2. post_params: {"_app_session_id" : <%= session.session_id %>},
  3. ...

% Rails 2.1에서는 session_id 에 '\n' 이 포함되어 있다. 왜 그런거지..? 그래서 javascript 에러가 발생한다. session.session_id.gsub("\n", "\\n") 이렇게 해주니까 잘 되더라.(이.. 깊이없는 땜빵....음냐..)

 

2. before_filter를 이용해서 파라미터에서 session 정보를 얻도록 한다.

SWFUpload를 이용해서 업로드를 수행하고자 하는 action을 retrieve_session_from_param 로 보호하도록 한다. retrieve_session_from_param은 params로 부터 session을 복구할 것이다. 주의할 것은 retrieve_session_from_param 가 login_require 보다 먼저 실행되어야 한다.

  1. class PhotosController < ApplicationController
  2.   before_filter :retrieve_session_from_param, :only=>[:create]
  3.   before_filter :login_required

 

2.1. retrieve_session_from_param

parameter로 부터 session 정보를 얻어오는 함수 retrieve_session_from_param을 app/controllers/application.rb에 추가한다.

  1. def retrieve_session_from_param
      session_key = ActionController::Base.session[0][:session_key]
      return if params[session_key].blank?

      self.session = CGI::Session.new(request,
        'database_manager' => CGI::Session.const_get((:cookie_store).to_s.camelize),
        'new_session' => false,
        'session_key' => session_key,
        'secret' => ActionController::Base.session[0][:secret],
        'session_data' => params[session_key],
        'session_id' => params[session_key] )   
    end

code 설명

sesson_key 가 params에 있으면 params[:session_key]로부터 session을 만들도록 하자. session_key는 environment에서 config.action_controller.session 에 적어준 그 값이다.

params[:session_key] 있다면 그로부터 session을 생성해서 controller의 session값과 바꿔치기하자. 이 때 database_manager는 CookieStore를 이용한다. 그리고 session_data라는 파라미터도 전해주는데, 이 값은 CookieStore 인스턴스를 만들때 그대로 전해질 것이다.

 

2.2. CGI::Session::CookieStore 의 해킹
CGI::Session::CookieStore가 session 정보를 session_data 에서 우선적으로 읽어올 수 있도록 하자.

config/initializers/swf_upload.rb를 만들어 다음 내용을 추가한다.(좋지 않아 보인다..)

  1. class CGI::Session::CookieStore
      alias original_initialize initialize
  2.   alias original_read_cookie read_cookie
      def initialize(session, options = {})
        @session_data = options['session_data']
        original_initialize(session, options)
      end

      def read_cookie
        @session_data || original_read_cookie
      end
    end

code의 설명

 

options['session_data'] 를 파라미터로 받으면 이 값을 @session_data 에 저장하고 있다가 read_cookie 시에 return하도록 한다. 그러면 session을 cookie에서가 아닌 입력받은 파라미터(options['session_data'])에서 얻을 수 있다.

 

 

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