Решение на Трета задача от Камен Станев

Обратно към всички решения

Към профила на Камен Станев

Резултати

  • 6 точки от тестове
  • 1 бонус точка
  • 7 точки общо
  • 44 успешни тест(а)
  • 0 неуспешни тест(а)

Код

module RBFS
module Serializer
def serialize_named_object(name)
serialized_data = serialize
"#{name}:#{serialized_data.size}:#{serialized_data}"
end
def serialize_hash(hash)
serialized_data = hash.map do |name, object|
object.serialize_named_object(name)
end.join
"#{hash.size}:#{serialized_data}"
end
end
class File
include Serializer
attr_accessor :data
def initialize(data = nil)
@data = data
end
def data_type
case @data
when NilClass then :nil
when String then :string
when Symbol then :symbol
when Fixnum, Float then :number
when FalseClass, TrueClass then :boolean
end
end
def serialize
"#{data_type}:#{@data}"
end
def self.parse(string_data)
type, data = string_data.split(':', 2)
new FileParser.string_to_object(type.to_sym, data)
end
end
class Directory
include Serializer
attr_reader :files
attr_reader :directories
def initialize
@files = {}
@directories = {}
end
def add_file(name, file)
@files[name] = file
end
def add_directory(name, directory = Directory.new)
@directories[name] = directory
end
def [](name)
@directories[name] || @files[name]
end
def serialize
serialize_hash(@files) + serialize_hash(@directories)
end
def self.parse(data)
directory = new
DirectoryParser.new(data, directory).run
directory
end
end
class FileParser
def self.string_to_object(type, string)
case type
when :nil then nil
when :string then string.to_s
when :symbol then string.to_sym
when :number then string_to_number string
when :boolean then string == 'true'
end
end
def self.string_to_number(str)
if str.include? '.'
str.to_f
else
str.to_i
end
end
end
class DirectoryParser
def initialize(string, directory)
@string = string
@directory = directory
end
def run
each(File) do |name, object|
@directory.add_file(name, object)
end
each(Directory) do |name, object|
@directory.add_directory(name, object)
end
end
def each(type_class)
hash_size, @string = @string.split(':', 2)
hash_size.to_i.times do
yield parse_named_object(type_class)
end
end
def parse_named_object(type_class)
name, size, @string = @string.split(':', 3)
size = size.to_i
current_string, @string = [@string[0..size - 1], @string[size..-1]]
[name, type_class.parse(current_string)]
end
end
end

Лог от изпълнението

............................................

Finished in 0.04257 seconds
44 examples, 0 failures

История (2 версии и 1 коментар)

Камен обнови решението на 02.11.2014 23:15 (преди над 9 години)

+module RBFS
+ module Serializer
+ def serialize_named_object(name)
+ serialized_data = serialize
+ "#{name}:#{serialized_data.size}:#{serialized_data}"
+ end
+
+ def serialize_hash(hash)
+ res = "#{hash.size}:"
+ res << hash.map { |name, obj| obj.serialize_named_object(name) }.join
+ end
+ end
+
+ module Parser
+ def parse_named_object(data)
+ (name, data) = data.split(':', 2)
+ [name, parse_object_with_size(data)]
+ end
+
+ def parse_object_with_size(data)
+ (size, data) = data.split(':', 2)
+ size = size.to_i
+ rest_data = data.size > size ? data[size..-1] : nil
+ [parse(data[0..size-1]), rest_data]
+ end
+
+ def parse_hash(data, type_class)
+ (hash_size, data) = data.split ':', 2
+ hash_size.to_i.times do
+ (name, (file, data)) = type_class.parse_named_object(data)
+ yield(name, file)
+ end
+ data
+ end
+ end
+
+ class File
+ include Serializer
+ extend Parser
+
+ attr_accessor :data
+
+ def initialize(data = nil)
+ @data = data
+ end
+
+ def data_type
+ case @data
+ when NilClass then :nil
+ when String then :string
+ when Symbol then :symbol
+ when Fixnum, Float then :number
+ when FalseClass, TrueClass then :boolean
+ end
+ end
+
+ def serialize
+ "#{data_type}:#{@data}"
+ end
+
+ def File.parse(string_data)
+ (type, data) = string_data.split ':', 2
+ new object_from_sym(type.to_sym, data)
+ end
+
+ def File.object_from_sym(type, data)
+ case type
+ when :nil then nil
+ when :string then data.to_s
+ when :symbol then data.to_sym
+ when :number then number_from_string data
+ when :boolean then data == 'true'
+ end
+ end
+
+ def File.number_from_string(str)
+ if str.include? '.'
+ str.to_f
+ else
+ str.to_i
+ end
+ end
+ end
+
+ class Directory
+ include Serializer
+ extend Parser
+
+ attr_reader :files
+ attr_reader :directories
+
+ def initialize
+ @files = {}
+ @directories = {}
+ end
+
+ def add_file(name, file)
+ @files[name] = file
+ end
+
+ def add_directory(name, directory = Directory.new)
+ @directories[name] = directory
+ end
+
+ def [](name)
+ @directories[name] or @files[name]
+ end
+
+ def serialize
+ serialize_hash(@files) + serialize_hash(@directories)
+ end
+
+ def Directory.parse(data)
+ dir = new
+
+ data = parse_hash(data, File, &dir.method(:add_file))
+ parse_hash(data, Directory, &dir.method(:add_directory))
+
+ dir
+ end
+ end
+end

Здравей :)

Отделил модули за парсването и сериализацията по доста интересен начин. :) Харесват ми имената на методите в тях. Eто и няколко коментара:

  • В serialize_hash ще е по-добре, ако си запишеш резултата от map-a някъде и после използваш интерполация, вместо да конкатенираш низове.
  • При "разпакетирането" може да изпуснеш най-външните скоби около променливите
  • data.split(':', 2) vs data.split ':', 2 - избери си едното от двете и го ползвай него - не и двете. Препоръчвам ти да използваш първия вариант.
  • Защо ти е проверката data.size > size? data.size не може да е по-малко от size, а ако са равни - отрязването, което правиш ще ти върне празен низ.
  • Името на object_from_sym е подвеждащо. Очаквах да "превръща" символ в друг обект.
  • &dir.method(:add_file) vs { |name, file| directory.add_file(name, file) }. Интересно е, че си използвал method, но според мен това е малък wtf момент. Сякаш е по-лесно четимо с блоковия синтаксис. Освен това dir е лошо име на променлива, както и str, obj и т.н.

Можеш ли да помислиш как може да махнеш нуждата функциите, които парсват да ти връщат и останалата част от низа? Ако направиш парсера клас и пазиш вътрешно състояние?

Камен обнови решението на 05.11.2014 21:29 (преди над 9 години)

module RBFS
module Serializer
def serialize_named_object(name)
serialized_data = serialize
"#{name}:#{serialized_data.size}:#{serialized_data}"
end
def serialize_hash(hash)
- res = "#{hash.size}:"
- res << hash.map { |name, obj| obj.serialize_named_object(name) }.join
+ serialized_data = hash.map do |name, object|
+ object.serialize_named_object(name)
+ end.join
+ "#{hash.size}:#{serialized_data}"
end
end
- module Parser
- def parse_named_object(data)
- (name, data) = data.split(':', 2)
- [name, parse_object_with_size(data)]
- end
-
- def parse_object_with_size(data)
- (size, data) = data.split(':', 2)
- size = size.to_i
- rest_data = data.size > size ? data[size..-1] : nil
- [parse(data[0..size-1]), rest_data]
- end
-
- def parse_hash(data, type_class)
- (hash_size, data) = data.split ':', 2
- hash_size.to_i.times do
- (name, (file, data)) = type_class.parse_named_object(data)
- yield(name, file)
- end
- data
- end
- end
-
class File
include Serializer
- extend Parser
attr_accessor :data
def initialize(data = nil)
@data = data
end
def data_type
case @data
when NilClass then :nil
when String then :string
when Symbol then :symbol
when Fixnum, Float then :number
when FalseClass, TrueClass then :boolean
end
end
def serialize
"#{data_type}:#{@data}"
end
- def File.parse(string_data)
- (type, data) = string_data.split ':', 2
- new object_from_sym(type.to_sym, data)
- end
+ def self.parse(string_data)
+ type, data = string_data.split(':', 2)
- def File.object_from_sym(type, data)
- case type
- when :nil then nil
- when :string then data.to_s
- when :symbol then data.to_sym
- when :number then number_from_string data
- when :boolean then data == 'true'
- end
+ new FileParser.string_to_object(type.to_sym, data)
end
- def File.number_from_string(str)
- if str.include? '.'
- str.to_f
- else
- str.to_i
- end
- end
end
class Directory
include Serializer
- extend Parser
attr_reader :files
attr_reader :directories
def initialize
@files = {}
@directories = {}
end
def add_file(name, file)
@files[name] = file
end
def add_directory(name, directory = Directory.new)
@directories[name] = directory
end
def [](name)
- @directories[name] or @files[name]
+ @directories[name] || @files[name]
end
def serialize
serialize_hash(@files) + serialize_hash(@directories)
end
- def Directory.parse(data)
- dir = new
+ def self.parse(data)
+ directory = new
- data = parse_hash(data, File, &dir.method(:add_file))
- parse_hash(data, Directory, &dir.method(:add_directory))
+ DirectoryParser.new(data, directory).run
- dir
+ directory
+ end
+ end
+
+ class FileParser
+ def self.string_to_object(type, string)
+ case type
+ when :nil then nil
+ when :string then string.to_s
+ when :symbol then string.to_sym
+ when :number then string_to_number string
+ when :boolean then string == 'true'
+ end
+ end
+
+ def self.string_to_number(str)
+ if str.include? '.'
+ str.to_f
+ else
+ str.to_i
+ end
+ end
+ end
+
+ class DirectoryParser
+ def initialize(string, directory)
+ @string = string
+ @directory = directory
+ end
+
+ def run
+ each(File) do |name, object|
+ @directory.add_file(name, object)
+ end
+ each(Directory) do |name, object|
+ @directory.add_directory(name, object)
+ end
+ end
+
+ def each(type_class)
+ hash_size, @string = @string.split(':', 2)
+ hash_size.to_i.times do
+ yield parse_named_object(type_class)
+ end
+ end
+
+ def parse_named_object(type_class)
+ name, size, @string = @string.split(':', 3)
+ size = size.to_i
+ current_string, @string = [@string[0..size - 1], @string[size..-1]]
+ [name, type_class.parse(current_string)]
end
end
end