요즘은 코코아, 루비, 그리고 jquery에 꽂혀 있다.
꽂혀만 있고 공부는 잘 안한다. ...

업무 중에 시간이 좀 남아서 일과 직접적으로 연관되는 jquery와 루비가 공부 후보로 올라왔는데
낮에 jquery를 처음 써보면서 공부를 약간 했었기 땜에 남는 저녁시간은 레일스를 보기로 했다.

액티브서포트부터 시작해서 내키는 데까지 코드를 쭉 훑어보기로 했다.
눈으로만 훑으면 나중에 다 까먹으니까 간단히 정리만 해 두기로 했다.

abc 순으로 제일 위에 있는 b*.rb 들을 봤다. a로 시작하는 파일은 없었다.

lib/buffered_logger.rb

  1. module ActiveSupport
      # Inspired by the buffered logger idea by Ezra
      class BufferedLogger
        module Severity
          DEBUG   = 0
          INFO    = 1
          WARN    = 2
          ERROR   = 3
          FATAL   = 4
          UNKNOWN = 5
        end
        include Severity
  2. ...

class 안에 module 선언해서 enum처럼? 혹은 C의 define처럼? 활용한다. 오오.

 

  1. module ActiveSupport
      # Inspired by the buffered logger idea by Ezra
      class BufferedLogger
  2. ...
  3.     for severity in Severity.constants
          class_eval <<-EOT, __FILE__, __LINE__
            def #{severity.downcase}(message = nil, progname = nil, &block)
              add(#{severity}, message, progname, &block)
            end

            def #{severity.downcase}?
              #{severity} >= @level
            end
          EOT
        end
  4. ...

메서드 선언을 class_eval로 하는 코드.

logger.warn, logger.info, logger.warn? logger.info? 등의 메서드를 정의한다. 심플한 코드라 나중에 쉽게 참조하기 좋을듯.

 

  1.     # Set the auto-flush period. Set to true to flush after every log message,
        # to an integer to flush every N messages, or to false, nil, or zero to
        # never auto-flush. If you turn auto-flushing off, be sure to regularly
        # flush the log yourself -- it will eat up memory until you do.
        def auto_flushing=(period)
          @auto_flushing =
            case period
            when true;                1
            when false, nil, 0;       MAX_BUFFER_SIZE
            when Integer;             period
            else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
            end
        end

auto_flushing= 은 period를 인자로 받는 것 같지만 boolean 값도 받아서 처리한다.
메서드는 한가지 일을 해야 하지만, 한 종류의 인자를 받을 필요는 없다. 하튼 자기 할 일만 잘하면 된다. 개발자 헛갈리지 않게.

 

이같은 코드는 생성자에도 존재하는데 바로 @log를 생성하는 부분.

  1.     def initialize(log, level = DEBUG)
          @level         = level
          @buffer        = []
          @auto_flushing = 1
          @no_block = false

          if log.respond_to?(:write)
            @log = log
          elsif File.exist?(log)
            @log = open(log, (File::WRONLY | File::APPEND))
            @log.sync = true
          else
            FileUtils.mkdir_p(File.dirname(log))
            @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
            @log.sync = true
            @log.write("# Logfile created on %s" % [Time.now.to_s])
          end
        end

write가 있으면 그냥 쓴다. 왜냐면 우린 @log.write만 쓸 꺼니까..
그렇지 않으면 파일 이름으로 간주하고, 찾아본다. 그래도 없으면 디렉토리+파일 이름으로 간주하고 디렉토리도 만들고 찾는다.
파일이 없는 경우 이 로그 파일은 새 것이므로 맨 윗줄에 주석까지 달아주는 센스를 보여준다.

후에 write외에 다른 메서드를 쓸 일이 생기는데... 바로 close할 때다.

  1.     def close
          flush
          @log.close if @log.respond_to?(:close)
          @log = nil
        end

@log가 IO 객체? 라면 아마 close를 모두 가지고 있을 것이다. 위위 코드에서 두번째와 세번째 경우는 모두 close를 가지고 있을 것이다.
하지만 직접 구현한 객체라면? 이 객체는 write는 있는데 close는 없다면?

그런 경우에 위 close 메서드의 두번째 줄 같은 코드가 필요하다.
duck type을 이용하여 @log를 생성하여 쓰고... close는 있으면 하고 없으면 말고.
아.. 아름답다. (라고 세뇌한다.-_-)

 

오늘은 비도 오고 하니까 이만

 

References

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

Posted by 나이누