2012-12-20 2 views
3

미리 사과 드리겠습니다. 긴 질문이 될 것입니다.레일 : 컨트롤러가 모델을 올바르게 업데이트하지 않습니다.

짧은 버전 :

은 내가 date, start_timeend_time을 가지고 회의 모델을 가지고있다. 이것들은 시간 객체입니다. 물론 사용자가 입력해야하는 고통이 있습니다. 따라서 저장하기 전에 Chronic으로 구문 분석되는 문자열을 허용하기 위해 가상 속성을 사용하고 있습니다.

나는 폼에서 이러한 가상 속성을 받아 모델에 전달하는 일반 바닐라 레일 컨트롤러를 사용합니다. 내가 예상대로 설정 예를 params[:meeting][:date_string]를 들어, 컨트롤러가 양식에서 올바른 매개 변수를 수신하는 것을 확인했습니다

def create 
    @meeting = @member.meetings.build(params[:meeting]) 
    if @meeting.save 
    redirect_to member_meetings_path(@member), :notice => "Meeting Added" 
    else 
    render :new 
    end 
end 

def update 
    @meeting = @member.meetings.find(params[:id]) 
    if @meeting.update_attributes(params[:meeting]) 
    redirect_to member_meetings_path(@member), :notice => "Meeting Updated" 
    else 
    render :new 
    end 
end 

다음은 컨트롤러입니다.

문제 :

에 생성은 날짜가 올바르게 설정됩니다,하지만 시간은 UTC에서 설정 한 2000 년에 할당되며, 프론트 엔드에 현지 시간으로 표시되지 않습니다.

업데이트시 날짜가 업데이트되지 않습니다. 시간은 2000-01-01 동안 UTC로 업데이트되지만 유지됩니다. 버전 제가이 모든 것을 나타내는 괜찮은 테스트 커버리지 모델 계층에서 작동이되는이 슈퍼 기괴한하게 무엇

긴. 여기

# DEPENDENCIES require 'chronic' class Meeting < ActiveRecord::Base # MASS ASSIGNMENT PROTECTION attr_accessible :name, :location, :description, :contact_id, :member_id, :time_zone, :date, :start_time, :end_time, :date_string, :start_time_string, :end_time_string # RELATIONSHIPS belongs_to :member belongs_to :contact # CALLBACKS before_save :parse_time # Time IO Formatting attr_writer :date_string, :start_time_string, :end_time_string # Display time as string, year optional def date_string(year=true) if date str = "%B %e" str += ", %Y" if year date.strftime(str).gsub(' ',' ') else "" end end # Display time as string, AM/PM optional def start_time_string(meridian=true) if start_time str = "%l:%M" str += " %p" if meridian start_time.strftime(str).lstrip else "" end end # Display time as string, AM/PM optional def end_time_string(meridian=true) if end_time str = "%l:%M" str += " %p" if meridian end_time.strftime(str).lstrip else "" end end # Display Date and Time for Front-End def time date.year == Date.today.year ? y = false : y = true start_time.meridian != end_time.meridian ? m = true : m = false [date_string(y),'; ',start_time_string(m),' - ',end_time_string].join end private # Time Input Processing, called in `before_save` def parse_time set_time_zone self.date ||= @date_string ? Chronic.parse(@date_string).to_date : Date.today self.start_time = Chronic.parse @start_time_string, :now => self.date self.end_time = Chronic.parse @end_time_string, :now => self.date end def set_time_zone if time_zone Time.zone = time_zone elsif member && member.time_zone Time.zone = member.time_zone end Chronic.time_class = Time.zone end end 

은 사양입니다 : 여기

모델이다. 독점적으로 parse_time 콜백을 테스트하려면 실제로 레코드를 만들거나 업데이트하지 않을 때마다이 테스트에서 @meeting.send(:parse_time)을 호출합니다.

require "minitest_helper" 

describe Meeting do 
    before do 
    @meeting = Meeting.new 
    end 

    describe "accepting dates in natural language" do 
    it "should recognize months and days" do 
     @meeting.date_string = 'December 17' 
     @meeting.send(:parse_time) 
     @meeting.date.must_equal Date.new(Time.now.year,12,17) 
    end 

    it "should assume a start time is today" do 
     @meeting.start_time_string = '1pm' 
     @meeting.send(:parse_time) 
     @meeting.start_time.must_equal Time.zone.local(Date.today.year,Date.today.month,Date.today.day, 13,0,0) 
    end 

    it "should assume an end time is today" do 
     @meeting.end_time_string = '3:30' 
     @meeting.send(:parse_time) 
     @meeting.end_time.must_equal Time.zone.local(Date.today.year,Date.today.month,Date.today.day, 15,30,0) 
    end 

    it "should set start time to the given date" do 
     @meeting.date = Date.new(Time.now.year,12,1) 
     @meeting.start_time_string = '4:30 pm' 
     @meeting.send(:parse_time) 
     @meeting.start_time.must_equal Time.zone.local(Time.now.year,12,1,16,30) 
    end 

    it "should set end time to the given date" do 
     @meeting.date = Date.new(Time.now.year,12,1) 
     @meeting.end_time_string = '6pm' 
     @meeting.send(:parse_time) 
     @meeting.end_time.must_equal Time.zone.local(Time.now.year,12,1,18,0) 
    end 
    end 

    describe "displaying time" do 
    before do 
     @meeting.date = Date.new(Date.today.year,12,1) 
     @meeting.start_time = Time.new(Date.today.year,12,1,16,30) 
     @meeting.end_time = Time.new(Date.today.year,12,1,18,0) 
    end 

    it "should print a friendly time" do 
     @meeting.time.must_equal "December 1; 4:30 - 6:00 PM" 
    end 
    end 

    describe "displaying if nil" do 
    it "should handle nil date" do 
     @meeting.date_string.must_equal "" 
    end 

    it "should handle nil start_time" do 
     @meeting.start_time_string.must_equal "" 
    end 

    it "should handle nil end_time" do 
     @meeting.end_time_string.must_equal "" 
    end 
    end 

    describe "time zones" do 
    before do 
     @meeting.assign_attributes(
     time_zone: 'Central Time (US & Canada)', 
     date_string: "December 1, #{Time.now.year}", 
     start_time_string: "4:30 PM", 
     end_time_string: "6:00 PM" 
    ) 
     @meeting.save 
    end 

    it "should set meeting start times in the given time zone" do 
     Time.zone = 'Central Time (US & Canada)' 
     @meeting.start_time.must_equal Time.zone.local(Time.now.year,12,1,16,30) 
    end 

    it "should set the correct UTC offset" do 
     @meeting.start_time.utc_offset.must_equal -(6*60*60) 
    end 

    after do 
     @meeting.destroy 
    end 
    end 

    describe "updating" do 
    before do 
     @m = Meeting.create(
     time_zone: 'Central Time (US & Canada)', 
     date_string: "December 1, #{Time.now.year}", 
     start_time_string: "4:30 PM", 
     end_time_string: "6:00 PM" 
    ) 
     @m.update_attributes start_time_string: '2pm', end_time_string: '3pm' 
     Time.zone = 'Central Time (US & Canada)' 
    end 

    it "should update start time via mass assignment" do 
     @m.start_time.must_equal Time.zone.local(Time.now.year,12,1,14,00) 
    end 

    it "should update end time via mass assignment" do 
     @m.end_time.must_equal Time.zone.local(Time.now.year,12,1,15,00) 
    end 

    after do 
     @m.destroy 
    end 
    end 

end 

나중에 테스트 방법에서 대량 할당을 통해 레코드를 만들고 업데이트하는 작업을 특별히 혼합하여 예상대로 작동하는지 확인했습니다. 모든 테스트가 통과합니다.

나는 다음에 어떤 통찰력을 주셔서 감사합니다 :

  1. 하지 않는 이유는 컨트롤러 # 업데이트 작업의 날짜 업데이트?

  2. 왜 설정 한 날짜로부터 시간을 얻지 못하는 걸까요? 이것은 모델 및 스펙에서 작동하지만 컨트롤러를 통해 양식을 통해 제출 된 경우에는 작동하지 않습니다.

  3. 시간대가 양식에서 전달되는 시간대로 설정되지 않는 이유는 무엇입니까? 다시 말하지만,이 스펙은 통과합니다. 컨트롤러의 문제점은 무엇입니까?

  4. 시간대가 프런트 엔드의 시간대에 표시되지 않는 이유는 무엇입니까?

도움을 주셔서 감사합니다. 나는 몇 시간 동안이 나무에서 숲을 잃어 가고있는 것 같습니다.


하는 업데이트 : AJcodez의 도움을

감사합니다, 나는 몇 가지 문제 보았다

  1. 하는 것은 잘못된 날짜, 감사 AJ를 할당했다! 현재 사용 : 내가 제대로 만성를 사용했다

    if @date_string.present? 
        self.date = Chronic.parse(@date_string).to_date 
    elsif self.date.nil? 
        self.date = Date.today 
    end 
    
  2. , 내 실수는 데이터베이스 계층에 있었다! 데이터베이스의 필드를 time 대신 datetime으로 설정 했으므로 모든 것이 손상됩니다. 이 책을 읽는 모든 이들에게 교훈 : 데이터베이스 필드로 time을 절대 사용하지 마십시오 (정확히 무엇을하는지, 왜 datetime 대신 사용하는지 이해하지 않는 한).

  3. 동일한 문제가 발생하여 datetime으로 필드를 변경하면 문제가 해결됩니다.

  4. 여기에서 문제는 모델에서보기와 시간에 액세스하는 것과 관련이 있습니다. 이 시간 형식 지정 메소드를 도우미로 이동하면 현재 요청 범위에서 호출되므로 올바르게 작동합니다.

감사합니다. AJ! 당신의 제안은 저의 사각 지대를 넘어 섰습니다.

답변

1

여기가 ..

1. 컨트롤러 # 업데이트 동작에서 날짜가 업데이트되지 않는 이유는 무엇입니까?

두 가지 잠재적 인 문제가 있습니다. 네가 날짜를 다시 파싱하지 않는 것 같아.

def update 
    @meeting = @member.meetings.find(params[:id]) 
    @meeting.assign_attributes params[:meeting] 
    @meeting.send :parse_time 
    if @meeting.save 
    ... 

assign_attributes 세트를하지만, 새로운 값을 저장 나던 :이 시도 self.date ||=가 할당되어있는 경우 항상 자신에게 다시 self.date 설정합니다 : http://apidock.com/rails/ActiveRecord/AttributeAssignment/assign_attributes

또한, 귀하의 parse_time 방법으로,이 과제를 사용 . 즉, 거짓이 아닌 한 날짜를 업데이트 할 수 없습니다.


2. 설정 한 날짜로부터 시간을 얻지 못하는 이유는 무엇입니까? 이것은 모델 및 스펙에서 작동하지만 컨트롤러를 통해 양식을 통해 제출 된 경우에는 작동하지 않습니다.

아무 생각없이 Chronic#parse을 사용하고있는 것 같습니다.


3. 시간이 양식에서 전달되는 시간대로 설정되지 않는 이유는 무엇입니까? 다시 말하지만,이 스펙은 통과합니다. 컨트롤러의 문제점은 무엇입니까?

디버그 time_zone을 시도하고 params[:meeting][:time_zone]에 whats가 반환되는지 확인하십시오. 다시 만성적 인 것으로 보입니다.

사이드 노트 : Time#zone=에 잘못된 문자열을 전달하면 오류가 발생하여 오류가 발생합니다. 예를 들어 Time.zone = 'utc' 모두 잘못되었습니다.


4. 시간대가 프런트 엔드의 시간대에 표시되지 않는 이유는 무엇입니까?

Time#in_time_zonehttp://api.rubyonrails.org/classes/Time.html#method-i-in_time_zone을 참조하고 때마다 명시 적으로 시간대 이름을 지정하십시오.

이미이 작업을 수행하고 있지만 데이터베이스에서 UTC로 시간을 명시 적으로 저장하려고 시도했는지 확인한 다음 현지 시간으로 표시하십시오.

+0

고맙습니다.이 질문은 100 % 답변이 아니었지만 디버깅 할 수있는 몇 가지 새로운 방법을 발견하게 도와주었습니다. 어떻게 해결했는지에 대한 몇 가지 노트로 답변을 업데이트했습니다. – Andrew

관련 문제