Общи въпроси за Ruby

  1. Здравейте, правя тази тема, защото ми се видя недостатъчно подходящо да питам в останалите теми, а и като цяло май няма такава тема.

    Предлагам тук да задаваме общи въпроси относно руби - синтаксис, въпроси по изпълнение и т.н.

    Давам (реален!) пример:

    Predicate методите е хубаво да завършват на ? Супер. Как пишем тернарен if в ruby използвайки такъв метод (онзи, краткия if)? Не ми приема result = generated? 'a' : 'b', нито пък със 2 въпросителни, нито пък с result = generated? == true ? 'a' : 'b'.

    И като цяло, кой е най-якия начин да направим прост conditional assignment на 1 ред без да използваме многоредовия if-else?

    Благодаря предварително.

  2. Имам въпрос (предимно насочен към Стефан и Митьо, но също и към по-опитните с Ruby от мен колеги):

    Лоша практика ли е предефинирането на #method_missing и използването му по един или друг начин в логиката на даден код?

  3. @Николай, да, method_missing е по-скоро code smell. Тоест, ползва се само в краен случай. Третирай го така. Това важи и за всички други инструменти за метапрограмиране. Всичко останало се чете и разбира много по-лесно. Това не означава, че не се ползва от време на време, обаче. Засега не мисля, че има за какво да го ползвате :)

    @Христо, не мисля, че това последното може да се запише във втория вариант. Ползвай първия, с hashrocket-а.

  4. @Николай Моя отговор за #method_missing е зависи. Трябва да покажеш за какво го предефинираш и да ти кажем дали е адекватно. В някои ситуации са прави, но те са такива, в които не можеш да минеш без него.

  5. Искам да ви попитам нещо от лекцията за "Тестове,TDD и MiniTest".Става дума за gem-a mynyml/watchr ,ще ми се някой да ми каже как да го пусна за да видя как работи?На лекцията има даден пример:

    watch('rat.rb') do system 'clear' success = system 'ruby rat.rb'

    if success puts 'OK' else puts "Tests failed ;(" end end

    Аз използвам Notepad++ за писане на сорс код и го изпълнявам през Command Prompt with Ruby.

    Ако ,да речем, имам някакъв прост код във файла new6.rb и искам да го тествам/компилирам за нещо.Просто не успявам да разбера ,каква е разликата между ползването на нещо като watchr и стартирането на кода през Command Prompt.Реално и там ми казва,къде има грешка,на кой ред и каква.

  6. Разликата е, че когато ползваш watchr кода се изпълнява автоматично, когато запишеш файла. Обикновено без watchr трябва да запишеш файла и да го изпълниш ръчно в cmd. Т.е. правиш 2 неща, вместо едно.

    Отделно за домашните да кажем, аз го ползвам освен да ми рънва тестовете, а и за да ми пуска skeptic на кода.

  7. Watchr следи даден файл за промяна и изпълнява даден скрипт. В твоя примера който ти си дал watchr следи rat.rb и ако има промяна изълнява това system 'clear' success = system 'ruby rat.rb' if success puts 'OK' else puts "Tests failed ;(" end Този скрипт може да бъде какъвто си поискаш, и това Е готино :sunglasses:!

    Аз примерно, съм си направил скрипта, така че да ми изпълнява файл с minitest-ове и да ми отпечатва резултата в едно балонче на екрана. За балончето ползвам libnotify-bin. Това е една линукс програма, с която можеш да си изпращаш съобщения към екрана. Тъй като си с notepad++ предполагам ползваш windows, ако искаш някаква подобна функционалност, мисля че growl for windows може би ще ти свърши работа. Ей тук съм качил примерен watchr скрипт, който работи с libnotify-bin. С growl би трябвало да е нещо подобно - трябва със system да изпълниш някаква growl команда, която да ти отпечата резултата от тестовете.

    Това е едно примерно приложение на watchr за autotesting, не е задължително да го позлваш по този начин :trollface:.

  8. @Георги Георгиев Тоест,в един файл си пиша кода на програмата (rat.rt в случая),в друг пиша някакви тестове свързани с него(tests.rb),а в третият съм написал watch.rb(Където на мястото на system "send-notify -t 1000...",съм сложил system(growlnotify -m Test successful)).какво точно прави watch освен да изкара изскачащо съобщение и как всъщност го пускам него?Защото ,когато отида в command prompt И напиша ruby tests.rt там си ми ги изкарва тези неща свързани с: test,assertions,failures,skips Но когато дам ruby watch.rb - Undefined method 'watch'

    П.С.интересно ми е когато сме проверили в нашият test.rb ,примерно, assert_equal 4,2+2 ,а не сме проверили примерно: assert_equal 6,3+3 това значи ли ,че ако в същинският код rat.rt решим да извършим операцията събиране 3+3 и го присвоим на някаква променлива,това ще даде грешка или assert_equal просто прави проверка на работата на метода в неговата същина,тоест събиране на цели числа?

  9. За да заредиш watchr скрипта (в случая watch.rb) изпълняваш : watchr watch.rb След това, при всяко презаписване на наблюдавания файл, ще се извършвaт операциите описани в скрипта.

    Не ти разбирам послеписа. Според мен assert_equal просто устойностява първия израз, устойностява втория и проверя дали са равни. Ти сам се грижиш за това дали са ти обективни тестовете и дали покриват цяло свойство, или само някакъв отделен случай.

  10. @Георги Георгиев В послеписа ми исках да попитам дали ако проверявам в теста ми : assert_equal 4,2+2 същото ли е с assert_equal 6,3+3 ?Просто ми е малко странно,че проверяваме точно това или то си до самостоятелно мнение на програмиста,който си пише кода,смисъл за какво иска той да го тества.

    И още по-ясно,ако имам метод който 'събира 2 комплексни числа' ,на мен ми се ще да го пробвам как би работил ако събирам комплексно число с реално(имагинерната му част е 0). Тоест,аз знам,че кодът ми трябва да изпълнява някаква операция-събиране на комплексни числа,но ми се ще да го тествам как би работил,ако второто събираемо е реално число.И затова в теста правя проверка.

    п.п А като стартираm watchr watch.rb Ми казва : Use RBconfig instead of obsolete and depricated Config.

  11. Представи си, че съм дефинирал събиране между всеки две числа да връща 4. Тогава очевидно първия assertion ще бъде правилен, а втория грешен => имам проблем със събирането. Факт - винаги връщам 4.

    Тестовете ти си избираш какви да са. За комплексните, щом искаш да тестваш събиране на комплексно число с реално, просто си избираш числа и правиш assertion. Примерно (3 + 2i) + 1 да ти е (4 + 2i). Твойте стойности могат да са други, въпроса е че тестваш свойството комплексно + реално. Use RBconfig instead of obsolete and depricated Config Това не би трябвало да пречи на фунционирането на watchr.

  12. Какво се случва тук? Объркан съм. [1] pry(main)> h = Hash.new [] => {} [2] pry(main)> h[:foo].push :bar => [:bar] [3] pry(main)> h => {} [4] pry(main)> h[:foo] => [:bar] [5] pry(main)> h[:foo] = h[:foo] => [:bar] [6] pry(main)> h => {:foo=>[:bar]} Редакция: Уф, аз всъщност променям обекта по подразбиране, а не стойността в таблицата: [7] pry(main)> h[:qux] => [:bar] [8] pry(main)> h[12] => [:bar] [9] pry(main)> p = Hash.new { |hash, key| hash[key] = Array.new } => {} [10] pry(main)> p[:foo].push :bar => [:bar] [11] pry(main)> p[:foo] => [:bar] [12] pry(main)> p[:qux] => [] [13] pry(main)> p => {:foo=>[:bar], :qux=>[]} [14] pry(main)> p[:baz] => [] [15] pry(main)> p => {:foo=>[:bar], :qux=>[], :baz=>[]}

  13. Здравейте, За много години! Покрай проекта ми за курса (Sinatra web приложение) се сблъсках със следният проблем: Трябва да изпълня даден код (да речем че съм го натъпкал във #foo) само веднъж при стартиране на приложението. Отначало пробвах да го плесна в един файл, който да го require-на в main.rb

    main.rb: require './at_start.rb' #Some other code at_start.rb: foo Но не стана - методът се изпълнява при всяко извикване на блок. След това пробвах с глобална променлива по този начин:

    at_start.rb: foo unless $started $started = true Но отново ударих на камък. Отделно че и двата варианта не ме кефят особено - require-вам файл, защото разчитам че ще се изпълни само веднъж, а не защото да речем там съм дефинирал някакъв клас. Т.е. ползвам require за нещо, което нормален човек не би очаквал. Някой може ли да предложи свестен начин да си реша проблема?

  14. @Валентин, явно се случва нещо по-различно в твоя проект, което пропускаш да кажеш. Трябва ни повече контекст, а най-добре целия код на проекта и описание откъде минава изпълнението на нещата, за да удариш проблема.

    Няма проблем кодът на проекта да е публичен (вж. секция "Version control" в указанията за проекти).

  15. @Митьо, прав си че скривам нещо, но всъщност проблема се оказа че не е в кода. Помъчих нещата още малко, всичко изглеждаше ок, но не работеше. Счупих си главата още няколко пъти, кодът стана ужасен, бях сложил сума ти кръпки и накрая намерих ето този въпрос в StackOverflow

    Оказа се че (използвам Shotgun) сървъра се рестартира на всяка заявка и на практика нещата работят - наистина се изпълняват само при стартиране на сървъра, просто сървъра се стартира постоянно (т.е. при всяка нова post/get заявка). След като го стартирах с "ruby main.rb" вместо "shotgun -p 4567 main.rb" нещата си дойдоха на мястото.

    А иначе идеята ми беше че ми трябва background нишка, която всяка минута получава данни от NetControll-a, обработва ги и (евентуално) предприема някаква дейност. Естествено на мен ми трябва да пусна само 1 такава нишка, а не при всяко викане на някое url да пускам нишка и процесора да се чуди какво става.

  16. class Klass
      def initialize()
        @variable = 0
      end
      attr_accessor :variable
      def method
        variable += 1
      end
    end
    
    >> Klass.new.method
    NoMethodError: undefined method `+' for nil:NilClass from (pry):7:in `method'
    

    Защо? А ако тялото на method е @variable += 1 минава, както се очаква.

    П.С. май writer-a се вика вместо reader-a

  17. Всеки път, когато направиш присвояване от типа foo = bar, първо се създава локална променлива foo с начална стойност nil. След това евентуално изразът отдясно се оценява и присвояването се извършва.

    Какво се случва в описания пример:
    Създава се локална променлива variable и тя получава стойност nil. След това Руби гледа какво става отдясно на оператора за присвояване и вижда nil + 1, защото локалната променлива variable е с приоритет пред едноименния метод, дефиниран от attr_accessor. Естествено, следва NoMethodError.

    Всичко се дължи на начина, по който се извършва присвояването. Ето защо:
    irb(main):001:0> foo = foo => nil
    Нещо повече: irb(main):002:0> if false irb(main):003:1> foo = 1 irb(main):004:1> end => nil

    И малко ЗАБАВЕН СПАМ

  18. В шеста задача оператора <=> трябва да хвърля изключения, ако обектите не са сравними.

    Аз доколкото разбрах в тези случай <=> би трябвало да връща nil . Аз ли не съм разбрал или в задачата е така за упражнението?

  19. Да, май аз малко неясно съм се изразил. Имах предвит дали правилно съм разбрал конвенцията, че трябва да се връща nil и дали се спазва или на практика често се среща и да се хвърлят изключения

  20. @Пламен и @Илиян, в задачата е така и за упражнение, и защото на мен така ми се е сторило по-подходящо. Има една добра практика, която казва, че ако нещо е грешка на програмиста, по-добре да хвърлим изключение за него и да оставим програмата да гръмне, за да може програмистът да го види и да си оправи кода. Според мен, сравнение на пари с различна валута е проблем и да се хвърли подходящо изключение за това е по-важно, отколкото да се спази конвенцията за <=>, затова съм направил условието така.

Трябва да сте влезли в системата, за да може да отговаряте на теми.