2016-10-26 1 views
1

도서 클럽 앱을 만들고 있습니다. 사용자는 다른 사람들과 함께 읽고 싶은 책을 업로드 할 수 있습니다. Google 도서 API에서 도서 정보를 가져오고 현재 컨트롤러에서 API 호출을 받았습니다. 나는 이것이 추악하고 unrailsy이지만 모델에서 작동하게하는 데 문제가 있다는 것을 압니다. 이 클리너를 만드는 가장 좋은 리팩터로 무엇을 제안 하시겠습니까?컨트롤러에서 레일로 모델로 API 호출 이동

new.html.erb의 -from 책

<%= form_for :book, url: books_path do |f| %> 
    <fieldset> 
     <h1 class="text-center">Add A Book</h1> 

     <div class="form-group"> 
     <label class="col-md-4 control-label" for="name">Title</label> 
     <div class="col-md-8"> 
      <%= f.text_field :title, required: true, class: "form-control" %><br> 
     </div> 
     </div> 

     <div class="form-group"> 
     <label class="col-md-4 control-label" for="genre">Genre</label> 
     <div class="col-md-8"> 
      <%= f.select :genre, [["Sci-fi", "Sci-fi"], ["Fantasy", "Fantasy"], ["Comic", "Comic"], ["Manga", "Manga"]], class: "form-control" %> 
     </div> 
     </div> 

     <div class="form-group"> 
     <div class="col-md-12"> 
      <%= f.submit "Create Book", class: "btn btn-success" %> 
     </div> 
     </div> 

    </fieldset> 
    <% end %> 

books_controller.rb

def create 
    @user = current_user 
    find_book 
    redirect_to root_path 
end 

require "openssl" 
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE 

def find_book 
    tempBook = params[:book][:title] 
    tempGenre = params[:book][:genre] 
    url = "https://www.googleapis.com/books/v1/volumes?q=" + tempBook + "&key=secret_key" 
    uri = URI(url) 
    response = Net::HTTP.get(uri) 
    book_data = JSON.parse(response)  
    b = Book.new 
    b.user_id = @user.id 
    b.title = book_data["items"][0]["volumeInfo"]["title"] 
    b.genre = tempGenre 
    b.author = book_data["items"][0]["volumeInfo"]["authors"][0] 
    b.publisher = book_data["items"][0]["volumeInfo"]["publisher"] 
    b.publication_date = book_data["items"][0]["volumeInfo"]["publishedDate"] 
    b.synopsis = book_data["items"][0]["volumeInfo"]["description"] 
    b.image = book_data["items"][0]["volumeInfo"]["imageLinks"]["thumbnail"] 
    @book = b.save 
end 

book.rb

class Book < ActiveRecord::Base 
    belongs_to :user 
    has_many :reviews 

    def set_user(user) 
    self.user_id = user.id 
    self.save  
    end 

end 

그것은이 방식으로 작동하지만, 못생긴 내가해야 내 열쇠를 숨기고 대신 열어 둡니다.

모델에 함수를 추가하고 제목과 장르를 show 메서드에서 변수로 선언했지만 모델에 전달되지 않아 작동하지 않았습니다.

감사합니다.

다음 코드는 시도했지만 작동하지 않았습니다. @tempBook은 nil이므로 모델을 엉망으로 만든다. 변수를 가져 오기 전에 모델이 실행되고 있다고 가정하고 있습니까?

는 book.rb

class Book < ActiveRecord::Base 
    belongs_to :user 
    has_many :reviews 

    def set_user(user) 
    self.user_id = user.id 
    self.save  
    end 

    require "net/http" 
    require "json" 

    require "openssl" 
    OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE 

    def self.find_book 
    url = "https://www.googleapis.com/books/v1/volumes?q=" + @tempBook + "&key=SECRET_KEY" 
    uri = URI(url) 
    response = Net::HTTP.get(uri) 
    book_data = JSON.parse(response)  
    b = Book.new 
    b.user_id = @user.id 
    b.title = book_data["items"][0]["volumeInfo"]["title"] 
    b.genre = @tempGenre 
    b.author = book_data["items"][0]["volumeInfo"]["authors"][0] 
    b.publisher = book_data["items"][0]["volumeInfo"]["publisher"] 
    b.publication_date = book_data["items"][0]["volumeInfo"]["publishedDate"] 
    b.synopsis = book_data["items"][0]["volumeInfo"]["description"] 
    b.image = book_data["items"][0]["volumeInfo"]["imageLinks"]["thumbnail"] 
    @book = b.save 
    end 

end 

는 books_controller.rb 코드를 단순화하고 더 쉽게 읽을 수 있도록하기 위해

def create 
    @tempBook = params[:book][:title] 
    @tempGenre = params[:book][:genre] 
    @user = current_user 
    Book.find_book 
    redirect_to root_path 
    end 
+0

"작동하지 않음"에 대한 자세한 정보를 제공해 주실 수 있습니까? 시도한 코드를 보여줄 수 있습니까? 또한 관찰 한 내용을 알려주십시오 (정확히 오류 메시지는 무엇입니까?)? 우리는 실제 코드를 보게되면 더 잘 작동하도록 도와 줄 수 있습니다. (참고 : 서식을 편집 할 때 무서워하고 따라하기가 어렵습니다. 질문을 편집하여 거기에 넣으십시오.) –

+0

아, 예 ... 그래서'@ tempbook'이 작동하지 않는 이유는 그들이 무엇인지 알지 못한다면'@ variable's를 사용해서는 안된다는 것입니다;) 컨트롤러를 통해 뷰에 변수를 전달할 때 유용합니다. 실제 메소드 인수 만 사용해야합니다. 나는 이것을 내 대답에 추가 할 것이다. –

+0

나는 범위 밖에서 변수를 이동하기 위해 인스턴스 변수를 사용했지만 전역 변수가 아닌 해당 클래스에서 여전히 범위가 있다고 생각 했습니까? 하지만 그래, 범위의 개념은 여전히 ​​내가 망쳐 놓은 것입니다 – nwimmer123

답변

2

1 단계 :

book_data = book_data["items"][0]["volumeInfo"] 
# then you can write the shorter version: 
b.title = book_data["title"] # etc 

2 단계 : 비밀 키는 종종 최고 플랫폼에 환경 변수를 설정하는 방법은 환경 변수 Google을 참조하십시오.

사용하는 방법은 다음과 같습니다

# Note that I've also added 'string interpolation' here 
# (google that phrase to find out more) 
url = "https://www.googleapis.com/books/v1/volumes?q=#{tempBook}&key=#{ENV['BOOK_FIND_SECRET_KEY']}" 

참고 :이가 .. 좀 더 도움이,하지만 당신은 그것을 시도 할 때 무엇이 ​​잘못되었는지에 대한 자세한 내용을 필요로하고 싶습니다 변경할 수있는 작은 것들 모델에서 (질문에 대한 코멘트 참조).

3 단계 @variables은 사용하지 않아야합니다 (필요한 경우 제외).

@variables은 특별합니다. 컨트롤러에서 뷰에 값을 전달하기 위해 컨트롤러에서 사용합니다. 컨트롤러에서 뷰로의 데이터 마술 핸드 오버가 있기 때문입니다. 하지만 나머지 루비 코드에서는 데이터를 메소드에 실제 인수로 전달하는 것이 좋습니다. 예를 들어 도서 모델에 find_book이라는 메소드가있는 경우 @tempBook 대신에 book_name 또는 그와 비슷한 인수가 있어야합니다. 나는 또한 그 하나의 큰 방법을 작은 것들로 분해 할 것입니다 - 각각은 하나의 특정한 것을합니다.여기에 당신의 기존 코드를 수정하는 예입니다 : 컨트롤러 지금

# Note: generally it's accepted practice to put these includes 
# at the top of the file outside of the class definition 
require "net/http" 
require "json" 

require "openssl" 

class Book < ActiveRecord::Base 
    # Store the secret key in this constant so you don't have to 
    # keep typing the ENV-var name each time you use it 
    SECRET_KEY = ENV['BOOK_FETCH_SECRET_KEY'] 

    belongs_to :user 
    has_many :reviews 

    # I don't know what this is, so I've left it... 
    # but it probably shoudln't go here... 
    OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE 

    # Make the book-url into a method - abstract out this complexity 
    # so the code for the main find_book method is much easier to read. 
    # Notice how we pass in the book_name as an argument 
    def self.book_url(book_name) 
    "https://www.googleapis.com/books/v1/volumes?q=#{book_name}&key=#{SECRET_KEY}" 
    end 

    # Fetching the book via the URL is kind of a stand-alone thing to do 
    # so it's easy to turn it into a method - that just accepts the 
    # book title, and returns the parsed book data 
    def self.fetch_book_data(book_name) 
    uri = URI(book_url(book_name)) 
    response = Net::HTTP.get(uri) 
    JSON.parse(response)  
    end 

    # and here's what's left for find_book 
    # note how we name and use the arguments 
    def self.find_book(user, book_name, genre) 
    book_data = fetch_book_data(book_name) 
    the_book = book_data["items"][0]["volumeInfo"] 
    b = Book.new 
    b.user_id = user.id 
    b.title = the_book["title"] 
    b.genre = genre 
    b.author = the_book["authors"][0] 
    b.publisher = the_book["publisher"] 
    b.publication_date = the_book["publishedDate"] 
    b.synopsis = the_book["description"] 
    b.image = the_book["imageLinks"]["thumbnail"] 
    b.save 
    # return the new book at the end of the method 
    b 
    end  
end 

, 우리는 매우 간단하게이 코드를 호출 할 수 있습니다

def find_book 
    # We just call the method on Book - passing in our params as the arguments 
    @book = Book.find_book(current_user, params[:book][:title], params[:book][:genre]) 
end 

참고 : 나는 정말 GoogleBook을 갖는 다른 대답의 생각처럼 클래스를 가져 오면 클래스를 가져 와서 코드를 fetch_book_data (또는 이와 동등한 코드)에 넣을 수 있습니다.

+0

그 작은 변화는 그것을 많이 청소합니다, 감사합니다! – nwimmer123

+1

환경 변수를 사용하는 방법을 파악하기 시작 했으므로 자세한 내용을 읽어야합니다. – nwimmer123

+0

'OpenSSL :: SSL :: VERIFY_PEER = OpenSSL :: SSL :: VERIFY_NONE'은 보안 인증서 또는 무엇인가 잘못 되었기 때문에 내가 발견 한 해킹입니다. 정확히 무엇을 잊어 버렸지 만, API 호출을 망쳐 놓고 그 라인이 그것을 수정합니다 . 나는 아마 문제를 해결해야하지만 그걸로 돌아 가지 못했다 ... – nwimmer123

3

lib 디렉토리에있는 클래스에 Google 코드를 추출해야합니다. 그 클래스 인스턴스의 인스턴스를 책 모델에 넣은 다음 거기에서 find_book 메서드를 호출하십시오. API의 구성 키를 yml 파일에 넣을 수 있습니다.

#lib/google_book.rb 
class GoogleBook 
    def initialize(info) 
    end 

    def find_book 
    url = "https://www.googleapis.com/books/v1/volumes?q=" + tempBook + "&key=secret_key" 
    uri = URI(url) 
    response = Net::HTTP.get(uri) 
    book_data = JSON.parse(response) 
    #rest of code 
    end 
end 

#book.rb 
class Book < ActiveRecord::Base 
    belongs_to :user 
    has_many :reviews 

    def set_user(user) 
    self.user_id = user.id 
    self.save  
    end 

    def retrieve_google_book 
    google_book = GoogleBook.new(some_params) 
    google_book.find_book 
    end 

end 
+0

나는 데이터베이스 제품이 모델에 들어가야한다는 인상하에 있었고 lib를 전혀 사용하지 않았다. 초기 제목 값을 가져 오는 index.html의 양식 데이터에 액세스 할 수 있습니까? 아니면 그 값을 다른 방식으로 저장해야합니까? 감사! – nwimmer123

+0

또한 create 메소드에서'retrieve_google_book'을 호출하지 않아도됩니까? 'Book.retrieve_google_book'과 같습니다.시도했을 때 GoogleBook이 초기화되지 않은 상수라고 알았지 만 – nwimmer123

+0

예. 컨트롤러에서 여전히 'retrieve_google_book' 메서드를 호출해야합니다. 요점은 클래스 안에'API '를 캡슐화하여 코드가 외부 서비스 호출로 흩어져 있지 않도록하려는 것입니다. 그런 식으로 사용하는'API '를 변경하거나 급격한 변화가있는 경우 수정/업데이트하기 위해 한 곳만 이동하면됩니다. lib 폴더는 사람들이 일반적으로 범용 클래스를 저장하는 곳으로, 프로젝트 정크 그릴의 약간으로 변하는 경향이있어서 거기에 넣은 것과 징계를받습니다. –

관련 문제