Камен обнови решението на 02.11.2014 23:15 (преди около 10 години)
+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)
vsdata.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
и т.н.
Можеш ли да помислиш как може да махнеш нуждата функциите, които парсват да ти връщат и останалата част от низа? Ако направиш парсера клас и пазиш вътрешно състояние?