Process incoming asciicast with AsciicastCreator

This commit is contained in:
Marcin Kulik 2013-08-04 22:53:25 +02:00
parent 86a4662fe5
commit 59e31baa1b
8 changed files with 143 additions and 125 deletions

View File

@ -1,15 +1,13 @@
class Api::AsciicastsController < ApplicationController class Api::AsciicastsController < ApplicationController
skip_before_filter :verify_authenticity_token skip_before_filter :verify_authenticity_token
def create def create
ac = Asciicast.new(params[:asciicast]) asciicast = AsciicastCreator.new.create(params[:asciicast])
render :text => asciicast_url(asciicast), :status => :created,
if ac.save :location => asciicast
SnapshotWorker.perform_async(ac.id) rescue ActiveRecord::RecordInvalid => e
render :text => asciicast_url(ac), :status => :created, :location => ac render :nothing => true, :status => 422
else
render :text => ac.errors.full_messages, :status => 422
end
end end
end end

View File

@ -29,8 +29,7 @@ class Asciicast < ActiveRecord::Base
before_create :assign_user, :unless => :user before_create :assign_user, :unless => :user
attr_accessible :meta, :stdout, :stdout_timing, :stdin, :stdin_timing, attr_accessible :title, :description, :time_compression
:title, :description, :time_compression
def self.assign_user(user_token, user) def self.assign_user(user_token, user)
where(:user_id => nil, :user_token => user_token). where(:user_id => nil, :user_token => user_token).
@ -42,22 +41,6 @@ class Asciicast < ActiveRecord::Base
Digest::MD5.hexdigest timestamps.join('/') Digest::MD5.hexdigest timestamps.join('/')
end end
def meta=(file)
data = JSON.parse(file.tempfile.read)
self.username = data['username']
self.user_token = data['user_token']
self.duration = data['duration']
self.recorded_at = data['recorded_at']
self.title = data['title']
self.command = data['command']
self.shell = data['shell']
self.uname = data['uname']
self.terminal_lines = data['term']['lines']
self.terminal_columns = data['term']['columns']
self.terminal_type = data['term']['type']
end
def assign_user def assign_user
if user_token.present? if user_token.present?
if ut = UserToken.find_by_token(user_token) if ut = UserToken.find_by_token(user_token)

View File

@ -0,0 +1,39 @@
class AsciicastCreator
def create(attributes)
attributes = prepare_attributes(attributes)
Asciicast.create!(attributes, :without_protection => true).tap do |asciicast|
SnapshotWorker.perform_async(asciicast.id)
end
end
private
def prepare_attributes(attributes)
meta = parse_meta_file(attributes[:meta])
{
:stdout_data => attributes[:stdout],
:stdout_timing => attributes[:stdout_timing],
:stdin_data => attributes[:stdin],
:stdin_timing => attributes[:stdin_timing],
:username => meta['username'],
:user_token => meta['user_token'],
:duration => meta['duration'],
:recorded_at => meta['recorded_at'],
:title => meta['title'],
:command => meta['command'],
:shell => meta['shell'],
:uname => meta['uname'],
:terminal_lines => meta['term']['lines'],
:terminal_columns => meta['term']['columns'],
:terminal_type => meta['term']['type'],
}
end
def parse_meta_file(file)
JSON.parse(file.read)
end
end

View File

@ -3,57 +3,49 @@ require 'spec_helper'
describe Api::AsciicastsController do describe Api::AsciicastsController do
describe '#create' do describe '#create' do
let(:creator) { double('creator') }
let(:attributes) { { 'foo' => 'bar' } }
before do
allow(AsciicastCreator).to receive(:new).with(no_args()) { creator }
end
context 'when the creator returns an asciicast' do
let(:asciicast) { stub_model(Asciicast, :id => 666) } let(:asciicast) { stub_model(Asciicast, :id => 666) }
before do before do
new = asciicast allow(creator).to receive(:create).with(attributes) { asciicast }
Asciicast.should_receive(:new).and_return(new) post :create, :asciicast => attributes
end end
context 'when save succeeds' do it 'returns the status 201' do
expect(response.status).to eq(201)
end
it 'returns the URL of created asciicast as the content body' do
expect(response.body).to eq(asciicast_url(asciicast))
end
end
context 'when the creator raises ActiveRecord::RecordInvalid' do
let(:asciicast) { double('asciicast', :errors => errors) }
let(:errors) { double('errors', :full_messages => full_messages) }
let(:full_messages) { ['This is invalid'] }
before do before do
asciicast.stub(:save => true) allow(creator).to receive(:create).with(attributes).
and_raise(ActiveRecord::RecordInvalid.new(asciicast))
post :create, :asciicast => attributes
end end
it 'enqueues snapshot capture' do it 'returns the status 422' do
post :create expect(response.status).to eq(422)
SnapshotWorker.should have_queued_job(asciicast.id)
end end
it 'returns status 201' do it 'returns the full error messages as the content body' do
post :create expect(response.body).to be_blank
response.status.should == 201
end end
it 'returns URL of created asciicast as content body' do
post :create
response.body.should == asciicast_url(asciicast)
end end
end end
context 'when save fails' do
before do
asciicast.stub(:save => false)
end
it 'returns status 422' do
post :create
response.status.should == 422
end
it 'returns full error messages as content body' do
full_messages = double.to_s
errors = double('errors', :full_messages => full_messages)
asciicast.should_receive(:errors).and_return(errors)
post :create
response.body.should == full_messages
end
end
end
end end

15
spec/fixtures/meta.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"command": "/bin/bash",
"duration": 11.146430015563965,
"recorded_at": "Thu, 25 Jul 2013 20:08:57 +0000",
"shell": "/bin/zsh",
"term": {
"columns": 96,
"lines": 26,
"type": "screen-256color"
},
"title": "bashing :)",
"uname": "Linux 3.9.9-302.fc19.x86_64 #1 SMP Sat Jul 6 13:41:07 UTC 2013 x86_64",
"user_token": "f33e6188-f53c-11e2-abf4-84a6c827e88b",
"username": "kill"
}

View File

@ -71,70 +71,6 @@ describe Asciicast do
end end
end end
describe '#meta=' do
let(:asciicast) { stub_model(Asciicast) }
let(:username) { 'username' }
let(:user_token) { 'token' }
let(:duration) { 123.456 }
let(:recorded_at) { Time.now.to_s }
let(:title) { 'title' }
let(:command) { '/bin/command' }
let(:shell) { '/bin/shell' }
let(:uname) { 'OS' }
let(:terminal_lines) { 29 }
let(:terminal_columns) { 97 }
let(:terminal_type) { 'xterm-lolz' }
it 'assigns attributes properly' do
data = {
:username => username,
:user_token => user_token,
:duration => duration,
:recorded_at => recorded_at,
:title => title,
:command => command,
:shell => shell,
:uname => uname,
:term => {
:lines => terminal_lines,
:columns => terminal_columns,
:type => terminal_type,
}
}
json = data.to_json
tempfile = stub('tempfile', :read => json)
json_file = stub('file', :tempfile => tempfile)
asciicast.meta = json_file
asciicast.username.should == username
asciicast.user_token.should == user_token
asciicast.duration.should == duration
asciicast.recorded_at.should == recorded_at
asciicast.title.should == title
asciicast.command.should == command
asciicast.shell.should == shell
asciicast.uname.should == uname
asciicast.terminal_lines.should == terminal_lines
asciicast.terminal_columns.should == terminal_columns
asciicast.terminal_type.should == terminal_type
end
end
describe '#snapshot' do
let(:asciicast) { Asciicast.new }
let(:snapshot) {
Snapshot.new([
SnapshotLine.new([
SnapshotFragment.new('foo', { :fg => 1 })
])
])
}
it 'is empty Snapshot instance initially' do
expect(asciicast.snapshot).to eq(Snapshot.new)
end
describe '#stdout' do describe '#stdout' do
let(:stdout) { double('stdout') } let(:stdout) { double('stdout') }

View File

@ -0,0 +1,56 @@
require 'spec_helper'
describe AsciicastCreator do
let(:creator) { AsciicastCreator.new }
describe '#create' do
let(:meta_file) { fixture_file_upload('spec/fixtures/meta.json', 'application/json') }
let(:stdout_data_file) { double('stdout_data_file') }
let(:stdout_timing_file) { double('stdout_timing_file') }
let(:asciicast) { stub_model(Asciicast, :id => 666) }
subject {
creator.create(
:meta => meta_file,
:stdout => stdout_data_file,
:stdout_timing => stdout_timing_file
)
}
before do
allow(Asciicast).to receive(:create!) { asciicast }
end
it 'calls Asciicast.create! with proper attributes' do
subject
expect(Asciicast).to have_received(:create!).with({
:stdout_data => stdout_data_file,
:stdout_timing => stdout_timing_file,
:stdin_data => nil,
:stdin_timing => nil,
:username => 'kill',
:user_token => 'f33e6188-f53c-11e2-abf4-84a6c827e88b',
:duration => 11.146430015563965,
:recorded_at => 'Thu, 25 Jul 2013 20:08:57 +0000',
:title => 'bashing :)',
:command => '/bin/bash',
:shell => '/bin/zsh',
:uname => 'Linux 3.9.9-302.fc19.x86_64 #1 SMP Sat Jul 6 13:41:07 UTC 2013 x86_64',
:terminal_columns => 96,
:terminal_lines => 26,
:terminal_type => 'screen-256color'
}, { :without_protection => true })
end
it 'enqueues snapshot capture job' do
subject
expect(SnapshotWorker).to have_queued_job(asciicast.id)
end
it 'returns the created asciicast' do
expect(subject).to be(asciicast)
end
end
end

View File

@ -10,8 +10,7 @@ module AsciiIo
end end
def load_asciicast(id) def load_asciicast(id)
FactoryGirl.create( AsciicastCreator.new.create(
:asciicast,
:meta => uploaded_file( :meta => uploaded_file(
"spec/fixtures/asciicasts/#{id}/meta.json", "spec/fixtures/asciicasts/#{id}/meta.json",
'application/json' 'application/json'