Решение на Трета задача от Мая Терзиева

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

Към профила на Мая Терзиева

Резултати

  • 4 точки от тестове
  • 0 бонус точки
  • 4 точки общо
  • 29 успешни тест(а)
  • 15 неуспешни тест(а)

Код

module RBFS
class File
DATA_TYPES = {
NilClass => :nil,
String => :string,
Symbol => :symbol,
Fixnum => :number,
Bignum => :number,
Float => :number,
TrueClass => :boolean,
FalseClass => :boolean,
}.freeze
attr_reader :data_type
attr_accessor :data
def self.parse(string_data)
File.new eval(string_data)
end
def initialize(data = nil)
load_data(data)
end
def data=(data)
load_data(data)
end
def serialize()
"#{@data_type}:#{@data.to_s}"
end
private
def load_data(data)
@data = data
@data_type = DATA_TYPES[data.class]
end
end
class Directory
attr_reader :files, :directories
def self.parse(string_data)
#TODO
end
def initialize()
@files = {}
@directories = {}
end
def add_directory(name, directory = nil)
directory = Directory.new unless directory
@directories[name] = directory
end
def add_file(name, file)
@files[name] = file
end
def [](name)
directory = directories[name]
directory or files[name]
end
def serialize
#TODO
end
end
end

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

....F...FFFFFF..........F..FF..F..F..F..F..F

Failures:

  1) RBFS Directory without files can be serialized
     Failure/Error: expect(directory.serialize).to eq '0:0:'
       
       expected: "0:0:"
            got: nil
       
       (compared using ==)
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:11:in `block (4 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) RBFS Directory serialization #serialize can serialize
     Failure/Error: expect(directory.serialize).to eq simple_serialized_string
       
       expected: "2:README:19:string:Hello world!spec.rb:20:string:describe RBFS1:rbfs:4:0:0:"
            got: nil
       
       (compared using ==)
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:81:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) RBFS Directory serialization #serialize can serialize multiple directories recursively
     Failure/Error: expect(directory.serialize).to eq recursive_serialized_string
       
       expected: "2:README:19:string:Hello world!spec.rb:20:string:describe RBFS2:rbfs:64:1:solution.rb:13:symbol:hidden1:spec:24:1:test:12:boolean:true0:sample:4:0:0:"
            got: nil
       
       (compared using ==)
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:95:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) RBFS Directory serialization ::parse can parse empty directories
     Failure/Error: expect(parsed_directory.files      ).to eq({})
     NoMethodError:
       undefined method `files' for nil:NilClass
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:103:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) RBFS Directory serialization ::parse can parse directories with files
     Failure/Error: expect(parsed_directory.files.size     ).to eq    2
     NoMethodError:
       undefined method `files' for nil:NilClass
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:110:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) RBFS Directory serialization ::parse can parse directory trees without files
     Failure/Error: expect(parsed_directory['dir1']        ).to be_an RBFS::Directory
     NoMethodError:
       undefined method `[]' for nil:NilClass
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:119:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) RBFS Directory serialization ::parse can parse directories recursively
     Failure/Error: expect(parsed_directory.files.size     ).to eq 2
     NoMethodError:
       undefined method `files' for nil:NilClass
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:127:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  8) RBFS File data type nil can be parsed
     Failure/Error: file = RBFS::File.parse('nil:')
     SyntaxError:
       (eval):1: syntax error, unexpected ':', expecting end-of-input
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:230:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  9) RBFS File data type string can be parsed
     Failure/Error: file = RBFS::File.parse('string:Hey there')
     SyntaxError:
       (eval):1: syntax error, unexpected tIDENTIFIER, expecting end-of-input
       string:Hey there
                       ^
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:248:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  10) RBFS File data type string can parse a string with colons
     Failure/Error: file = RBFS::File.parse('string:Hay :)')
     SyntaxError:
       (eval):1: syntax error, unexpected ':', expecting end-of-input
       string:Hay :)
                   ^
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:255:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  11) RBFS File data type symbol can be parsed
     Failure/Error: file = RBFS::File.parse('symbol:hello')
     NoMethodError:
       undefined method `symbol' for RBFS::File:Class
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:274:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  12) RBFS File data type number can be parsed
     Failure/Error: file = RBFS::File.parse('number:1234')
     SyntaxError:
       (eval):1: syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
       number:1234
                  ^
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:293:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  13) RBFS File data type float number can be parsed
     Failure/Error: file = RBFS::File.parse('number:3.14')
     SyntaxError:
       (eval):1: syntax error, unexpected tFLOAT, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
       number:3.14
                  ^
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:312:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  14) RBFS File data type boolean true can be parsed
     Failure/Error: file = RBFS::File.parse('boolean:true')
     NoMethodError:
       undefined method `boolean' for RBFS::File:Class
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:332:in `block (6 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  15) RBFS File data type boolean false can be parsed
     Failure/Error: file = RBFS::File.parse('boolean:false')
     NoMethodError:
       undefined method `boolean' for RBFS::File:Class
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `eval'
     # /tmp/d20141111-26053-1ja8tqq/solution.rb:18:in `parse'
     # /tmp/d20141111-26053-1ja8tqq/spec.rb:351:in `block (6 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.0533 seconds
44 examples, 15 failures

Failed examples:

rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:10 # RBFS Directory without files can be serialized
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:76 # RBFS Directory serialization #serialize can serialize
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:84 # RBFS Directory serialization #serialize can serialize multiple directories recursively
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:100 # RBFS Directory serialization ::parse can parse empty directories
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:107 # RBFS Directory serialization ::parse can parse directories with files
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:116 # RBFS Directory serialization ::parse can parse directory trees without files
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:124 # RBFS Directory serialization ::parse can parse directories recursively
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:229 # RBFS File data type nil can be parsed
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:247 # RBFS File data type string can be parsed
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:254 # RBFS File data type string can parse a string with colons
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:273 # RBFS File data type symbol can be parsed
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:292 # RBFS File data type number can be parsed
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:311 # RBFS File data type float number can be parsed
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:331 # RBFS File data type boolean true can be parsed
rspec /tmp/d20141111-26053-1ja8tqq/spec.rb:350 # RBFS File data type boolean false can be parsed

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

Мая обнови решението на 05.11.2014 12:52 (преди около 10 години)

+module RBFS
+ class File
+ DATA_TYPES = {
+ NilClass => :nil,
+ String => :string,
+ Symbol => :symbol,
+ Fixnum => :number,
+ Bignum => :number,
+ Float => :number,
+ TrueClass => :boolean,
+ FalseClass => :boolean,
+ }.freeze
+
+ attr_reader :data_type
+ attr_accessor :data
+
+ def self.parse(string_data)
+ File.new eval(string_data)
+ end
+
+ def initialize(data = nil)
+ load_data(data)
+ end
+
+ def data=(data)
+ load_data(data)
+ end
+
+ def serialize()
+ "#{@data_type}:#{@data.to_s}"
+ end
+
+ private
+ def load_data(data)
+ @data = data
+ @data_type = DATA_TYPES[data.class]
+ end
+ end
+
+ class Directory
+ attr_reader :files, :directories
+
+ def self.parse(string_data)
+ #TODO
+ end
+
+ def initialize()
+ @files = {}
+ @directories = {}
+ end
+
+ def add_directory(name, directory = nil)
+ directory = Directory.new unless directory
+ @directories[name] = directory
+ end
+
+ def add_file(name, file)
+ @files[name] = file
+ end
+
+ def [](name)
+ directory = directories[name]
+ directory or files[name]
+ end
+
+ def serialize
+ #TODO
+ end
+ end
+end

Качвам си черновата. Явно ще се бавя още с (де)сериализацията на директории, а вярвам, че ще има забележки и по останалата част.

Между другото, в style guide-а на курса не видях препоръки за подредбата на елементи в дефиницията на клас (но пък засега чета по диагонал и може да съм пропуснала :}). Намерих в style guide-а на Божидар(?) Бацов и се опитах да следвам тях.

Като цяло добре. Няколко забележки:

  • Малко подлъгващо беше да ви кажем, че искаме поле data_type. Имахме предвид, че трябва да може да се отговаря правилно на File#data_type. Сама виждаш, че си минала през доста неща, само и само да не е метод.
  • Кажи НЕ! на eval! Just google eval is evil! Виждам съм eval само два пъти в production код. Един път в python, където имаше много по-лесна и разбираема алтернатива с 'нормалните' средства на езика. Друг път в bash скрипт, където беше довел до един от най-obscure bug-овете ever. Не работи така, както изглежда мислиш, че работи. Дори и да работеше - гарантирано е, че ако го използваш има 99.99% вероятност да можем да напишем тест, който го чупи, след като не сме дали сериозни ограничения какво могат да съдържат string данните.
  • В Directory#add_directory можеше да пишеш направо x = y or z - по-кратко и ясно. Също в Directory#[] - защо не просто directories[name] or files[name]?
  • Не слагай скоби при дефинирането или викането на методи без аргументи.
  • Има по-чист синтаксис за интерполиране на инстанционни променливи.
  • Смята се за прилежно да подравняваш някои структури в езика, когато се срещат на съседни редове. Сред тях са =, then и ключовете на hash-овете.
  • Виж как би следвало да си анотираш TODO-тата. (:

Относно как се подрежда класа - не сме толкова рестриктивни, но като цяло community style guide-а добре отговаря на въпроса. extend, include, константи, accessors, други клас макроси, public, protected, private. За реда на вътрешни класове и класови vs инстанционни методи може да се спори. Това е лично мое мнение.

Здравей :)

Ето малко допълнения от мен, понеже така или иначе бях тръгнал и аз да коментирам:

  • Харесва ми, че си се сетила да freeze-неш хеша. :)
  • Виждам, че си разбрала недостатъка на този начин за проверка на типа - когато класът на обекта ти наследява от някой от изброените. Аз лично не бих използвал този подход точно заради това, пък и ще стане по-кратко с case. В тази конкретна задача не е технически грешен, защото в условието не сме споменали нищо за наследници на класовете и не сме толкова лоши, че да пишем специални тестове за това. :) Просто имай предвид, ако ти се наложи на друго място - да не разчиташ на конкретен клас, а по-скоро на него или негов наследник.
  • eval е лошо нещо, защото изпълнява произволен Ruby код - повече от това, за което го използваш. Например, ако си пуснала тази програма да приема данни от потребители, то някой потребител може да сложи Ruby код, който да ти изтрие всички файлове на харддиска и eval ще го изпълни.
  • В Ruby подразбиращите се стойности на аргументите могат да са всякакви изрази, включително и Directory.new. Може да го използваш за add_directory.

След толкова коментиране очаквам накрая да излезе супер решение! :)

Много ви благодаря!

  • Ех, надявах се да мина тънко с този eval. (: Правило ми е впечатление, че е непрепоръчителен на този курс (а вероятно и като цяло), но не си направих труда да проверя защо.
  • Колкото до очевидните чисти конструкции, които пропускам (и защо, защо не се сещам първо за is_a? и подобни, случи ми се и с предната задача), има някаква кутийка, в която явно мисля, и коментарите ви са ми много ценни за работа срещу това.
  • Скобите при методи без аргументи са някакво OCD + навик от работа с езици, в които типът има значение. Малко трудно правя прехода към duck typing. (:

Страшно ценни съвети още веднъж, благодаря за отделеното внимание!