잊지 않겠습니다.

[업어온 글 : 출처 http://kousenit.wordpress.com/2006/0...rvice-clients/

Yesterday I was in a Barnes and Noble bookstore and I started browsing through the Ruby Cookbook by Carlson and Richardson.

Quick aside: I still can’t believe how quickly Barnes and Noble has descended from a decent bookstore into practically garbage, especially as far as software development is concerned. The number of development books is down to a tiny fraction of that a few years ago, certainly no more than 5%. I was so happy when a BN opened in Glastonbury near my (old) house and unfortunately it’s still the closest bookstore to me, but now it’s just depressing. Even leaving aside the drop in selection, as Malcolm Gladwell points out in The Tipping Point, context matters. If nothing else, the managers of the store in Glastonbury could help themselves considerably if they would just oil the stupid door hinges (it’s been literally years since this was done) and clean the bathrooms every once in a while. If there was a Borders anywhere near them I’d never go in that store again.

Bruce Tate in his book Beyond Java points out that Java developers rarely look outside of Java because the field keeps moving so quickly they feel they have to run as fast as they can just to keep up. I really understand that feeling. At the end of December of 2005 I decided I was going to learn Ruby and Rails. I spent the next couple of months really digging into them and made a lot of progress. Ruby is sufficiently different from Java that it’s not at all an easy transition, though, despite what Rubyists say, and I learned just enough to get stuck on a regular basis.

Eventually I had to go back to Java, partly because I understand it and partly because it’s still paying the bills. I really need to become good at Hibernate, Spring, JSF, Tapestry, etc, and they all take time. Consequently, around the middle of March I switched gears and began really digging into Hibernate again, as I’ve mentioned here on several occasions.

Still, the allure of Ruby is strong. I think I may be past the newbie stage, but I’m hardly any good at it yet. On a scale from 1 to 10, I’d give myself about a 4, and that mostly based on reading rather than experience. I did read the pickaxe book (Programming Ruby by Dave Thomas) and R4R (Ruby for Rails by David Black) as well as the RoR books Agile Web Development with Rails (Dave Thomas again, among others) and a couple of the O’Reilly Rough Cuts. In other words, I’ve read the background material but haven’t yet gone through more than a couple of the Ten Canonical Errors in each technology.

Anyway, I mentioned here that about a week ago I decided to write a quick web service client for my Rensselaer students. I used the Amazon web service, connected with the URL class in Java, and parsed the resulting DOM tree (not fun). So yesterday I was browsing through the Ruby Cookbook, as I said, and found this code from Recipe 16.1:

require 'amazon/search'

$AWS_KEY = 'Your AWS key goes here' # See below.
def price_books(keyword)
  req = Amazon::Search::Request.new($AWS_KEY)
  req.keyword_search(keyword, 'books', Amazon::Search::LIGHT) do |product|
    newp = product.our_price || 'Not available'
    usedp = product.used_price || 'not available'
    puts "#{product.product_name}: #{newp} new, #{usedp} used."
  end
end

price_books(’ruby cookbook’)Wow. It depends on a third party library, but I found that easy to download and install. I tested it out using my RadRails editor and it worked like a charm, with all the product information already converted into a Product class.

Even more amazing is this little snippet from Recipe 16.7, Using a WSDL File to Make SOAP Calls Easier:

require 'soap/wsdlDriver'
wsdl = 'http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl'
driver = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver

puts "Stock price: %.2f" % driver.getQuote('TR')

Holy cow. That’s three lines, really, and you can invoke a web service without even having to download the WSDL file. I wrapped it in a class and tried various web services at Xmethods.net and it worked without a problem. Not only that, but the “soap/wsdlDriver” library is built into Ruby 1.8.4.

I don’t even need a comment here. I simply have to find time to build up some experience with Ruby, and then go back to Rails, definitely by the time the next version of the AWDR book comes out. I purchase the “beta book” version from the Pragmatic Programmers already, but I think the hard copy becomes available in the Fall.

Posted by Y2K
,

Database에서의 각각의 객체간의 관계의 정의

belongs_to

1:N 관계에서 1을 나타낸다.

has_many

1:N 관계에서 N을 나타낸다.

class Slide < ActiveRecord::Base
  belongs_to :slideshow
  belongs_to :photo
end
class Photo < ActiveRecord::Base
  has_many :slides
  validates_presence_of :filename
end

  

class Slideshow < ActiveRecord::Base
  has_many :slides
end

  

has_one

1:1 관계를 나타낸다.

belogs_to, has_one을 이용 pair로 구성

  • belogs_to : 기본 Key가 있는 Table
  • has_one : 외래키가 있는 Table

  

Posted by Y2K
,

ActiveRecord - Rails application과 Database 간의 연동을 담당.

   

ActiveRecord
  • Record 객체를 통해서 Database Table을 조작
  • 각각의 Record는 Database의 각각의 row와 대응 : create, read, update, delete 가능

  

 차이점  장점

 Rails는 Database table에 정의된 Column을 기반으로 하여 속성을 자동으로 추가한다.

Rails는 내부 언어로 관계 관리와 검증을 한다.

Rails 작명 규약은 DB의 특수한 필드를 자동으로 찾아낸다.

개발자는 속성을 지정할 때, DB만을 추가하면 된다. 

Rails 개발자는 관계와 모델 기반의 검증을 코드 생성없이 프레임워크 자체적으로 할 수 있도록 선언할 수 있다.

Rails 개발자는 주키나 후보키를 따로 설정할 필요가 없다.

   

OR Mappting
  • Datatabe Table과 object를 각각 생성.
  • Java Framework 에서 주로 사용되는 방법
  • 여러 종류의 Database Schema 지원
  • setting 파일의 복잡
  • 코드의 중복 가능

   

ActiveRecord on Rails
  • Setting대신 규약 : 규약에 의하여 Database Schema를 가져오는 것이 가능하다.
  • MetaProgramming
  • Mapping 언어 : Ruby를 이용하여 또 다른 언어를 만든다.

  

Rails 에서의 Active Record Model object 생성
  1. config/databasel.yml 수정
  2. Model 객체의 생성 (ruby script/generate model Photo)
  3. migration의 수정 - DB의 column의 설정(ruby에서 db의 table의 생성 및 수정 가능)
  4. migration의 실행 (rake db:migrate)

class CreatePhotos < ActiveRecord::Migration
  def self.up #DB Table의 생성
    create_table :photos do |photo|
      photo.column "filename", :string
    end
  end

  def self.down #DB Table의 삭제
    drop_table :photos
  end
end

   

Active Record Class
Class와 Table 이름
  • Active Record Class의 영문 복수형을 Database table 이름으로 간주하여 Table을 찾고, 만들어준다.
식별자
  • "id" 라는 column을 자동으로 생성하고 primary key로 만들어준다.
  • "id"는 정수형이며, auto-increase 속성을 갖는다.
외래키
  • "<classname>_id" 형태로 지어지게 된다.
표기 방법
  • Active Record class는 모두 Camel casing에 따라 지어지게 된다.
  • 공백은 모두 '_'로 채워지게 된다.

  

Rails Console

Database와 Model간에 유기적인 결합을 만들어준다.

  • DB에 연결
  • app/model에 있는 Active Record Class들을 불러온다.
  • Database와 Model간에 DB 작업을 포함하여 유기적인 작업을 할 수 있도록 하여준다.

photo = Photo.new
photo.filename = 'dog.jpg'
photo.save


photo = Photo.find_by_filename('dog.jpg')

  

Finder
  • find_by_sql : 직접 SQL Query 문을 입력하여 검색
  • find_all : 모든 항목 얻어오기
  • find_by_<column>(and...) : 항목으로 얻어오기

  

Transaction

: Record에 대한 Transaction 구현 역시 완벽.

def transfer(from, to, ammount)
  Account.transaction do
    from.debit(amount)
    to.creadit(amount)
  end
end
Posted by Y2K
,

Rails의 장점

  1. Meta Programming : 프로그램이 다른 프로그램 코드를 만들어주는 기법.
  2. Active Record : DB에 객체를 저장하는 Active record framwork를 도입
  3. less setting : .NET 에서의 web.config 등의 설정 파일들이 거의 필요 없게 된다.
  4. auto unittest code : meta programming을 통해 생성된 코드는 모두 단위 테스트를 갖는다.
  5. development, test, service 환경의 완벽 제공

   

Rails Project의 구조

  • app : application component를 담고 있다. model, view, controller에 해당되는 각각의 Directory가 존재한다.
  • components : model, view, controller 들의 작은 application이 component화 되어 저장되어 있다.
  • config : rails application에 대한 설정 코드 값을 가지고 있다.
  • db : db를 access 하는 model 객체를 가지고 있다.
  • doc : 자동으로 RubyDoc을 생성시킨다.
  • lib : 외부 library를 제외한 모든 라이브러리들이 저장
  • log : Error log의 자동 저장 folder
  • public : javascirpt, image, css, html 파일과 같이 변하지 않는 정적 web resource를 넣어두는 디랙토리
  • script : rails application에서 사용하는 tool과 scirpt를 담고 있다.
  • test : test code가 위치
  • tmp : 임시 directory

   

Rails Project의 시작

rails chapter-1

: chatper-1이라는 directory가 생성되고, 그에 대한 모든 파일들이 generate

   

Controller and View Create

ruby script/generate controller Greeting index

controller와 view가 모두 생성된다.

   

Example Controller and View

class GreetingController < ApplicationController

  def index
    @age = 8
    @table = {"headings" =>["addend", "addend", "sum"],
                    "body" => [[1,1,2], [1,2,3], [1,2,4]] }
  end
end

   

<h1>Simple expression</h1>
<p> Tommy is <%= @age %> old </p>

<h1>Simple Expression</h1>
<% for i in 1..5 %>
  <p> Heading number <%= i %> </p>
<% end %>


<h1>A simple table</h1>
<table>
  <tr>
    <% @table["headings"].each do |head| %>
      <td>
        <b><%= head %></b>
      </td>
    <% end %>
  </tr>
  <% @table["body"].each do |row| %>
    <tr>
      <% row.each do |col| %>
        <td>
          <%= col%>
        </td>
      <% end %>         
    </tr>
  <% end %>
</table> 

Posted by Y2K
,

Test::Unit Framework

  • 개별 테스트를 표현하는 방법 제공
  • 테스트를 구조화하는 프레임 워크를 제공
  • 테스트를 수행하기 위한 유연한 방법의 제공

   

Test code의 추가

  • requrie 'test/unit'
  • test case < Test::Unit::TestCase 의 하위 Class로 움직여야지 된다.
  • 반드시 Test를 포함하는 method는 test로 시작하는 method로 구성되어야지 된다.
  • setup과 teardown이 사용 가능하다. (Pytho이나 C#에서의 경우와 동일하다.)

   

TestCode의 위치

roman/lib/roman.rb

roman/test/test_roman.rb

위와 비슷한 case로 만들어주고, 실행 위치를 상위 디랙토리에서 실행시켜주게 되면, library의 로드시에도 아무런 문제가 없이 동작이 가능하게 된다.

   

Posted by Y2K
,

프로그램의 서로 다른 영역을 '동시에' 실행할 수 있도록 프로그램을 구성하는 두가지 기본 방법을 제공

  • Multy Threading : 한 프로그램 안에서 협업이 가능한 일을 분리해서 실행
  • Multy Process : 작업을 서로 다른 프로그램으로 분리

Ruby Thread

  • Thread.new 호출과 함께 새로운 Thread를 생성
  • Thread.join으로 각 Thread의 종결을 기다림 : Thread.join의 호출이 되지 않고 Ruby 프로그램이 종료되면 모든 Thread가 종결된다. - python과 동일하다.
  • Thread에서 실행될 코드를 적어서 실행하여 준다.
  • Thread.current : 현재 Thread
  • Thread.list : 모든 Thread의 List를 얻어낼 수 있다.
  • Thread.prioirty를 이용해서 Thread의 우선 순위를 조정 가능하다.
  • Ruby Thread는 일종의 Hash와 동일하게 사용가능하고 이는 []=를 이용해서 요소를 쓰고, []를 이용해서 요소를 얻어낼 수 있다.

 

 require 'net/http'

pages = %w{ www.rubycentral.com slashdot.org www.google.com }
threads = []

for page_to_fetch in pages
    threads << Thread.new(page_to_fetch) do |url|
        h = Net::HTTP.new(url, 80)
        puts "Fetching : #{url}"
        resp = h.get('/', nil)
        puts "Got #{url}: #{resp.message}"
    end
end

threads.each { |thr| thr.join } 

 

count = 0
threads = []

10.times do |i| 
    threads[i] = Thread.new do
        sleep(rand(0.1))
        Thread.current["mycount"] = count
        count += 1
    end
end

threads.each { |t| t.join; print t["mycount"], ", " }
puts "count = #{count}"

 

Thread and Exception

 Debug Flag, abort_on_exception flag에 따라서 Thread에서의 Exception이 같이 설정된다.

 

Thread Schedule Control

  • Thread#stop : Thread를 stop
  • Thread#run : 특정 Thread를 실행하도록 제어
  • Thread.new에서 Thread.stop으로 넣어지면, 정지된 상태에서의 Thread로 생성된다.
class Chaser
    attr_reader :count
    def initialize(name)
        @name = name
        @count = 0
    end

    def chase(other)
        while @count < 5
            while @count - other.count > 1
                Thread.pass
            end
            @count += 1
            print "#@name : #{count} \n"
        end
    end
end

c1 = Chaser.new("A")
c2 = Chaser.new("B")

threads = [
    Thread.new{ Thread.stop; c1.chase(c2) },
    Thread.new{ Thread.stop; c2.chase(c1) }
    ]
start_index = rand(2)
threads[start_index].run
threads[1 - start_index].run
threads.each{ |t| t.join }    

 : Thread의 동작 제어의 경우에는 전에 사용되던 내용과 거의 유사 -> python, C# 등과 거의 유사하다는 생각이 든다.

 

Thread-Critical

  • 전역적인 Thread-critical 을 사용한다.
  • Thread.critical=true 로 설정시켜준다.
  • Monitor, Mutex_m, Sync  라이브러리를 이용하면, 다른 방법을 이용해서 얻는 것이 가능하다.  

 

Monitor
  • Monitor class를 상속 받아서 synchronize 를 이용 
  • MonitorMixin을 사용 
require 'monitor'

class Counter < Monitor
    attr_reader :count
    def initialize
        @count = 0
        super
    end

    def tick
        synchronize do
            @count += 1
            print @count
        end
    end
end

c = Counter.new
t1 = Thread.new { 10000.times { c.tick() } } 
t2 = Thread.new { 1000.times { c.tick() } }

t1.join ; t2.join

print c.count
 
 require 'monitor'

class Counter
    attr_reader :count
    include MonitorMixin
    
    def initialize
        @count = 0
        super
    end

    def tick
        synchronize do
            @count += 1
            print @count
        end
    end
end

c = Counter.new
t1 = Thread.new { 10000.times { c.tick() } } 
t2 = Thread.new { 1000.times { c.tick() } }

t1.join ; t2.join

print c.count

 

class의 Monitor 만들기
require 'monitor'

class Counter
    attr_reader :count
    
    def initialize
        @count = 0
        super
    end

    def tick
        @count += 1
        print @count
    end
end

c = Counter.new
c.extend(MonitorMixin)

t1 = Thread.new { 10000.times { c.synchronize{ c.tick() } } }
t2 = Thread.new { 1000.times { c.synchronize{ c.tick() } } } t1.join ; t2.join print c.count


module의 extend를 이용해서 MonitorMixin을 include 시켜서 만들어준다.

 

 

Multi-Process

Create New Process

명령을 실행하고 기다리기 (execute and waiting..)

  • ``을 이용
  • Kernel.system 이용

 

exec("sort testfile > output.txt") if fork.nil?

# 내부에서는 자식 Process에서 돌아가게 된다.

Process.wait #자식의 Process를 기다리는 상태

 

 


Posted by Y2K
,
Ruby에서의 standard io method
  1. standard IO method : gets, open, print, printf, putc, puts, readline, readlines, test
  2. IO class 이용 : ruby class 이용

   

IO class
  • Child Class : File, BasicSocket etc...
  • 기본적인 사용방법은 거의 동일하다.
  • 잘짜여진 객체에 대한 논의를 해볼 만 하다.

File IO Class

File Open/Close

file = File.new("testfile", "r")
 # File Process...
file.close

File의 auto close and exception handling

File.Open("testfile", "r") do |file|
 #File Process...
end

File Read/Write

기본적으로 C에서의 기본 IO와 비슷하다. - 이래서 C가 중요한가 보다. -_-

  • file.gets : file에서 one lline을 읽는다.
  • file.get : file에서 one character를 읽는다.

   

읽기를 위한 반복자

IO Stream에서 데이터를 읽기 위해서 사용

  • each_byte : byte 단위로의 return
  • each_line : line 단위로 이용
File.open("testfile") do |file|
 file.each_byte { |ch| putc ch; print "." }
end
File.open("testfile") do |file_data|
 file_data.each_line { |line| print line }
end
C++ iosteam을 이용한 file write
  • <<을 이용한 file write 가능
  • 이는 array, string 등에도 같이 이용 가능하다. [너무나 신기하다. -.-]
write_file = File.open("test_write_file", "w")
write_file << "ykyoon OK"
write_file.close

  

문자열을 이용한 IO
  • 문자열이 파일에 있지 않고, 명령어 인수나 SOAP 서비스를 통한 데이터인 경우에는 문자열 자체를 IO로 사용가능하다.
  • StringIO객체를 이용해서 해결 가능하다.

require 'stringio'

file_data = File.new("vs2005_development.ini")
str_io = StringIO.new("string_io")

file_data.each_line do |line|
    str_io << line
end

print str_io.string

  

Communicate with Network
  • network와의 대화 역시 모두 IO에서 기본적으로 파생되어서 해결 가능하다.
  • Socket Class 들을 이용한 IO를 사용 가능하다.

require 'socket'

client = TCPSocket.open('127.0.0.1', 'finger')
client.send("mysql", 0)

puts client.readlines
client.close

require 'open-uri'

open('http://www.pragmaticprogrammer.com') do |f|
    puts f.read.scan(/<img src="(.*?)"/m).uniq
end

Posted by Y2K
,
  • Module은 Namespaces를 제공해서 이름이 충돌되는 것을 막아준다.
  • 이름은 Mixin 기능을 구현하는 데 사용된다.

모듈의 정의

  • 모듈 상수의 이름은 클래스 상수와 같이 첫문자를 대문자로 한다.
  • 메서드 선언 또한 비슷.

module Moral
    VERY_BAD = 0
    BAD = 1

    def Moral.sin(badness)
        return 'Moral sin : '
    end
end

module Trig
    PI = 3.1415
    def Trig.sin(x)
        return 'sin value '
    end

    def Trig.cos(x)
        return 'cos value '
    end

end

require 'trig'
require 'moral'

y = Trig.sin(Trig::PI / 4)
print y

wrongdoing = Moral.sin(Moral::VERY_BAD)
print wrongdoing

   

MIX-IN

  • Module과 Class는 다르게 움직인다.
  • Module에서는 instance를 가질 수 없게 된다.
  • Module은 Class가 아닌다.
  • Class에서 Module이 포함되게 될 경우에는 Module의 기능을 상속받게 되고, Class의 Instance Method와 동일하게 동작하게 된다.
  • include문을 이용해서 Module을 포함 할 수 있다.

include

  • include문은 ruby에서 선언이 되고, instance를 사용할 때는 아무런 일을 하지 않는다.
  • module의 method를 호출하는 일이 생기게 되면, 그때 추가가 된다.
  • class에 module의 인스턴스 method를 복사하는 것이 아니라, include는 class에 포함될 module에 대한 참조를 얻게 된다.
  • module의 참조를 얻기 때문에, module의 공통 변수나 모듈의 method를 수정하게 될 경우에는 모든 class에서 사용되는 module의 함수가 변경되게 된다.

다른 파일의 참조

requrie

해당 파일을 단 한번만 로드된다.

load

    실행될 때마다 해당 이름 파일을 가진 소스 코드는 포함된다.

Posted by Y2K
,

Error처리는 다른 언어와 기본적으로 비슷하게 throw..catch 방법을 이용한다.
Ruby에서의 예외 계층을 이용해서, 사용하게 된다. (Python에서의 Error 처리 방법과 유사하다.)
begin~rescue~end 구문으로 이용한다.

op_file = File.Open(opfile_name, "w")
begin
    while data = socket.read(512)
        op_file.write(data)
    end
rescue SystemCallError
    $stderr.print "IO failed : " + $!
    op_file.close
    File.delete(op_filename)
    raise
end

 

에러의 발생 방법

1. 단순히 현재의 예외($!)를 다시 발생
raise 
2. RuntimeError 예외를 새로 만들고, 문자열을 넘기는 경우
raise "Error Occur!!" 
3. 예외 Class를 지정해주는 경우
raise InterfaceException, "Keyboard Fail", caller

예외 class를 만들어주고, 그에 대한 메세지를 정한 후에 추적 stack을 정해준다. 일반적으로 Kernel.caller를 통해서 stack의 추적이 이루어진다.

 

Posted by Y2K
,
Command Expand

(`)또는 %x로 시작하는 구분자 형식을 이용하면 사용중인 기반 운영체제에 의해 command로 실행된다.

 

조건적 실행

논리 표현식

Ruby에서의 true : nil이 아니고 false가 아니면 모두가 true로 된다. 따라서 숫자 0나 길이가 0인 문자열 역시 true로 해석된다.

defined?, And(&&), Or(||), Not(!)
defined?

정의되지 않을때 nil이 반환되며, 그렇지 않은 경우 매개 변수에 대한 설명을 반환한다.

eql?

수신자와 매개변수가 서로 같은 타잎이며, 같은 값을 갖는 경우 참이 된다.

1==1.0 : true
1.eql?(1.0) : false

equal?

수신자와 매개변수가 같은 객체 ID를 가질 경우 true로 된다.

 

논리 표현식

if, elsif, unless

마치 python과 비슷하게도 사용 가능하고, 문법에서의 자유도가 매우 높은 것을 알 수 있다.
구문 변경자로도 사용 가능하며, 이는 조건문을 일반 구문에 끝에 붙여서 사용가능할 수 있게 해준다.

if song.artist == "Gillespie" : handle = "Dizzy"
elsif song.artist == "Parker" then handle = "Bird"
else handle = "unknown"
end
mon, day, year = $1, $2, $3 if date ~= /(\d\d)-(\d\d)-(\d\d)/
Case

if 구문의 연결 형태와 유사

case..when구문으로의 사용

문자열이나 숫자보다는 Expression에서의 true가 적용될 때 해당

while, until

while 구문과 until구문은 반대로 동작하는 것으로 해당

file = File.Open("ordinal")
while line = file.gets
   puts(line) if line = ~/third/ .. ~ /fifth/
end

 

반복자

: Ruby에서는 for loop구문이 없다. for loop구문보다 더욱더 강력하고 편한 방법으로 수행이 가능하다.

0.upto(9) { |x| print x, " " }
0.step(12, 3) { |x| print x, " " }

: Ruby에서의 for loop는 each method에 응답하는 객체와 같이 사용된다.

 

songlist.each do |song|
    song.play
end

변수의 유효 범위, 반복문, 블록

  1. while, until, for에서는 새로운 변수 범위가 생성되지 않는다.
  2. loop, each에서는 새로운 변수 범위가 생성된다.
  3. {} 형식 변수의 경우에도 역시 새로운 변수 범위로서 생성된다. 
Posted by Y2K
,