2011-11-23 21:30:09 +00:00
|
|
|
class Asciicast < ActiveRecord::Base
|
2013-11-18 13:29:18 +00:00
|
|
|
|
|
|
|
ORDER_MODES = { recency: 'created_at', popularity: 'views_count' }
|
2012-12-18 20:38:49 +00:00
|
|
|
|
2013-08-04 20:39:55 +00:00
|
|
|
mount_uploader :stdin_data, StdinDataUploader
|
|
|
|
mount_uploader :stdin_timing, StdinTimingUploader
|
|
|
|
mount_uploader :stdout_data, StdoutDataUploader
|
|
|
|
mount_uploader :stdout_timing, StdoutTimingUploader
|
2013-09-20 21:21:25 +00:00
|
|
|
mount_uploader :stdout_frames, StdoutFramesUploader
|
2015-02-27 13:58:04 +00:00
|
|
|
mount_uploader :file, AsciicastUploader
|
2015-03-27 11:35:34 +00:00
|
|
|
mount_uploader :image, ImageUploader
|
2011-11-23 22:22:14 +00:00
|
|
|
|
2014-08-30 17:38:47 +00:00
|
|
|
serialize :snapshot, ActiveSupportJsonProxy
|
2013-07-08 22:55:31 +00:00
|
|
|
|
2015-03-11 09:47:02 +00:00
|
|
|
belongs_to :user
|
|
|
|
has_many :comments, -> { order(:created_at) }, dependent: :destroy
|
|
|
|
has_many :likes, dependent: :destroy
|
|
|
|
|
2015-05-08 09:52:42 +00:00
|
|
|
validates :user, :duration, presence: true
|
2015-02-27 13:58:04 +00:00
|
|
|
validates :stdout_data, :stdout_timing, presence: true, unless: :file
|
|
|
|
validates :file, presence: true, unless: :stdout_data
|
2014-11-27 12:30:08 +00:00
|
|
|
validates :snapshot_at, numericality: { greater_than: 0, allow_blank: true }
|
2015-05-08 09:52:42 +00:00
|
|
|
validates :terminal_columns, presence: true, numericality: { less_than_or_equal_to: 1000 }
|
|
|
|
validates :terminal_lines, presence: true, numericality: { less_than_or_equal_to: 500 }
|
2012-01-14 20:26:07 +00:00
|
|
|
|
2013-11-17 15:49:46 +00:00
|
|
|
scope :featured, -> { where(featured: true) }
|
|
|
|
scope :by_recency, -> { order("created_at DESC") }
|
2013-11-28 22:17:02 +00:00
|
|
|
scope :by_random, -> { order("RANDOM()") }
|
2014-11-15 19:07:16 +00:00
|
|
|
scope :non_private, -> { where(private: false) }
|
|
|
|
scope :homepage_latest, -> { non_private.by_recency.limit(6).includes(:user) }
|
|
|
|
scope :homepage_featured, -> { non_private.featured.by_random.limit(6).includes(:user) }
|
2012-12-09 20:22:12 +00:00
|
|
|
|
2014-11-15 17:46:46 +00:00
|
|
|
before_create :generate_secret_token
|
|
|
|
|
2015-04-26 13:30:42 +00:00
|
|
|
def self.find_by_id_or_secret_token!(thing)
|
|
|
|
if thing.size == 25
|
|
|
|
find_by_secret_token!(thing)
|
|
|
|
else
|
|
|
|
non_private.find(thing)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-08-22 18:00:31 +00:00
|
|
|
def self.cache_key
|
|
|
|
timestamps = scoped.select(:updated_at).map { |o| o.updated_at.to_i }
|
|
|
|
Digest::MD5.hexdigest timestamps.join('/')
|
|
|
|
end
|
|
|
|
|
2013-11-18 13:29:18 +00:00
|
|
|
def self.paginate(page, per_page)
|
|
|
|
page(page).per(per_page)
|
|
|
|
end
|
|
|
|
|
2014-01-17 16:21:22 +00:00
|
|
|
def self.for_category_ordered(category, order, page = nil, per_page = nil)
|
2015-04-26 13:54:23 +00:00
|
|
|
collection = non_private
|
2013-11-18 13:29:18 +00:00
|
|
|
|
|
|
|
if category == :featured
|
|
|
|
collection = collection.featured
|
|
|
|
end
|
|
|
|
|
2015-04-24 11:40:28 +00:00
|
|
|
collection = collection.order("#{ORDER_MODES.fetch(order)} DESC")
|
2014-01-17 16:21:22 +00:00
|
|
|
|
|
|
|
if page
|
|
|
|
collection = collection.paginate(page, per_page)
|
|
|
|
end
|
|
|
|
|
|
|
|
collection
|
2013-11-18 13:29:18 +00:00
|
|
|
end
|
|
|
|
|
2015-04-20 10:43:37 +00:00
|
|
|
def title=(value)
|
|
|
|
value ? super(value.strip[0...255]) : super
|
|
|
|
end
|
|
|
|
|
|
|
|
def command=(value)
|
|
|
|
value ? super(value.strip[0...255]) : super
|
|
|
|
end
|
|
|
|
|
2014-11-15 17:46:46 +00:00
|
|
|
def self.generate_secret_token
|
2014-11-15 23:41:14 +00:00
|
|
|
SecureRandom.hex.to_i(16).to_s(36).rjust(25, '0')
|
2014-11-15 17:46:46 +00:00
|
|
|
end
|
|
|
|
|
2015-04-26 13:30:42 +00:00
|
|
|
def to_param
|
|
|
|
if private?
|
|
|
|
secret_token
|
|
|
|
else
|
|
|
|
id.to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-08-04 20:43:36 +00:00
|
|
|
def stdout
|
2015-02-28 17:39:31 +00:00
|
|
|
return @stdout if @stdout
|
|
|
|
@stdout = Stdout::Buffered.new(get_stdout)
|
2013-09-10 19:29:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def with_terminal
|
|
|
|
terminal = Terminal.new(terminal_columns, terminal_lines)
|
|
|
|
yield(terminal)
|
|
|
|
ensure
|
2013-09-14 11:13:58 +00:00
|
|
|
terminal.release if terminal
|
2013-08-04 20:43:36 +00:00
|
|
|
end
|
|
|
|
|
2014-06-30 21:26:25 +00:00
|
|
|
def theme
|
|
|
|
theme_name.presence && Theme.for_name(theme_name)
|
|
|
|
end
|
|
|
|
|
2015-03-27 11:35:34 +00:00
|
|
|
def image_filename
|
|
|
|
"#{image_hash}.png"
|
|
|
|
end
|
|
|
|
|
|
|
|
def image_stale?
|
|
|
|
!image.file || (image.file.filename != image_filename)
|
|
|
|
end
|
|
|
|
|
2014-11-15 18:30:05 +00:00
|
|
|
def owner?(user)
|
|
|
|
user && self.user == user
|
|
|
|
end
|
|
|
|
|
2015-02-28 17:39:31 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def get_stdout
|
|
|
|
if version == 0
|
|
|
|
Stdout::MultiFile.new(stdout_data.decompressed_path,
|
|
|
|
stdout_timing.decompressed_path)
|
|
|
|
else
|
2015-03-02 10:53:56 +00:00
|
|
|
Stdout::SingleFile.new(file.url)
|
2015-02-28 17:39:31 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-03-27 11:35:34 +00:00
|
|
|
def image_hash
|
2015-03-27 17:33:50 +00:00
|
|
|
version = 2 # version of screenshot, increment to force regeneration
|
2015-03-27 11:35:34 +00:00
|
|
|
input = "#{version}/#{id}/#{snapshot_at}"
|
|
|
|
Digest::SHA1.hexdigest(input)
|
|
|
|
end
|
|
|
|
|
2014-11-15 17:46:46 +00:00
|
|
|
def generate_secret_token
|
|
|
|
begin
|
|
|
|
self.secret_token = self.class.generate_secret_token
|
|
|
|
end while self.class.exists?(secret_token: secret_token)
|
|
|
|
end
|
|
|
|
|
2011-11-23 21:30:09 +00:00
|
|
|
end
|