├── .gitignore ├── 2015-02-24 └── .keep ├── 2015-03-31 ├── .keep └── ruby_extensions.pdf ├── 2015-05-26 └── Marketing Yousrelf.pdf ├── 2015-12-22 └── bruno_sutic_ruby_struct.rb ├── 2016-01-28 └── ruby-best-friend.pdf ├── 2016-02-25 ├── docker_for_ruby_devs.pdf └── docker_for_ruby_devs_no_comments.pdf ├── 2016-04-28 ├── stubs_mocks_spies.pdf └── stubs_mocks_spies_no_comment.pdf ├── 2016-06-02 └── http_clients.pdf ├── 2016-06-30 └── microservices.pdf ├── 2016-09-26 ├── blazingfastmina.pdf └── rails_associations.key ├── 2016-10-25 ├── dox.pdf └── steganography.pdf ├── 2016-11-29 └── extreme_programming.pdf ├── 2016-12-20 ├── sidekiq_enterprise.pdf └── unused.pdf ├── 2017-01-24 ├── ansible_for_rubyists.pdf └── services_with_grpc.pdf ├── 2017-03-28 ├── graphql-with_presenter_notes.pdf ├── graphql.pdf ├── graphql_server_side_talks-with_presenter_notes.pdf ├── graphql_server_side_talks.pdf └── modeling_a_solid_database.pdf ├── 2017-04-25 └── caching_with_ruby_on_rails.pdf ├── 2017-05-30 └── action_cable.pdf ├── 2017-06-27 └── architecture_for_scaling_iot_devices.md ├── 2017-11-28 ├── helix-with_presenter_notes.pdf └── helix.pdf ├── 2018-02-28 ├── rabbitmq_is_more_than_a_sidekiq_replacement-with_presenter_notes.pdf └── rabbitmq_is_more_than_a_sidekiq_replacement.pdf ├── 2018-09-27 ├── reform.pdf └── sort_vs_sort_by.rb ├── 2022-06-15 └── rubyzg_hotwired.pdf ├── 2022-09-13 └── building-api-with-grape.pdf ├── 2022-11-08 └── value-objects-in-ruby-and-rails.pdf ├── 2023-02-28 ├── background_jobs_with_rails.pdf ├── rspec-intro.md └── rspec-intro.pdf ├── 2023-10-24 ├── typed-ruby.key └── typed-ruby.pdf ├── 2023-11-21 ├── dissecting_service_objects.pdf └── linkok.com_making_a_webapp_the_hard_way.pdf ├── 2024-01-24 ├── Deconstructing_Action_Cable.pdf └── Model_anti_patterns_slides.pptx ├── 2024-02-28 ├── Ruby-constants-lookup.key └── devex_and_lsps_ruby.pdf ├── 2025-02-19 └── code_as_design.pdf └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /2015-02-24/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2015-02-24/.keep -------------------------------------------------------------------------------- /2015-03-31/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2015-03-31/.keep -------------------------------------------------------------------------------- /2015-03-31/ruby_extensions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2015-03-31/ruby_extensions.pdf -------------------------------------------------------------------------------- /2015-05-26/Marketing Yousrelf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2015-05-26/Marketing Yousrelf.pdf -------------------------------------------------------------------------------- /2015-12-22/bruno_sutic_ruby_struct.rb: -------------------------------------------------------------------------------- 1 | # Ruby Struct class 2 | 3 | # Using bare Struct {{{ 4 | 5 | # Struct returns a class (!!) 6 | struct = Struct.new(:id, :name) # => # 7 | struct.is_a? Class # => true 8 | 9 | # Instantiating a new struct class 10 | instance = struct.new(123, "John Wayne") # => # 11 | instance.id # => 123 12 | instance.name # => "John Wayne" 13 | 14 | instance.name = "Clint Eastwood" # => "Clint Eastwood" 15 | instance.name # => "Clint Eastwood" 16 | 17 | instance[:name] = "Charles Bronson" # => "Charles Bronson" 18 | instance.name # => "Charles Bronson" 19 | 20 | # Thoughts: 21 | # - yea.. but why? 22 | # - instantiating twice is un-Ruby 23 | 24 | # }}} 25 | # Subclassing struct {{{ 26 | 27 | # ** Ruby idiom ** 28 | class Cowboy < Struct.new(:id, :name) 29 | def says 30 | "My id is more than #{id}" # => "My id is more than Infinity" 31 | end 32 | end 33 | 34 | chuck = Cowboy.new(234, "Chuck Norris") # => # 35 | chuck.id = Float::INFINITY # => Infinity 36 | chuck.says # => "My id is more than Infinity" 37 | 38 | # the equivalent class 39 | 40 | class Cowboy2 41 | 42 | attr_accessor :id, :name # => nil 43 | 44 | def initialize(id, name) 45 | @id = id 46 | @name = name 47 | end 48 | 49 | def says 50 | "My id is more than #{id}" 51 | end 52 | 53 | end 54 | 55 | 56 | # Ok.. but still, why? Arguments: 57 | 58 | # - elegant and succinct, accessor defined in a single place 59 | # - struct states *nothing* happens in an initializer 60 | # - faster than writing the initializer 61 | # - it's an idiom you'll see in many, many gems 62 | # - using it feels good, try it 63 | 64 | # }}} 65 | # Better usage with a constant {{{ 66 | 67 | class Gun < Struct.new(:name, :size) 68 | # ... 69 | end 70 | 71 | # Downside: anonymous class in ancestor chain 72 | Gun.ancestors # => [Gun, #, Struct, Enumerable, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject] 73 | 74 | # Solution: 75 | Pistol = Struct.new(:bullets) do # => Struct 76 | def shoot 77 | "bang" 78 | end 79 | end # => Pistol 80 | 81 | Pistol.ancestors # => [Pistol, Struct, Enumerable, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject] 82 | 83 | # What really happens there? 84 | 85 | struct = Struct.new(:foo) do # => Struct 86 | def something 87 | end 88 | end # => # 89 | 90 | struct.is_a? Class # => true 91 | struct.name # => nil 92 | 93 | # Interesting (assignment happens on the object on the right) 94 | Something = struct # => Something 95 | struct.name # => "Something" 96 | 97 | # }}} 98 | # Is it really better? {{{ 99 | 100 | # Is using better syntax really better? 101 | Rifle = Struct.new(:model) do # => Struct 102 | def shoot 103 | "ka-bang" 104 | end 105 | end # => Rifle 106 | 107 | # Downsides: 108 | # - a lot of devs don't know what it is 109 | # - editor issues (ctags doesn't recognize new class) 110 | # - not pretty, defining methods in a block? 111 | 112 | # Suggestion: 113 | # - "improved" syntax for ruby gems 114 | # - "classic" syntax for your application code 115 | 116 | # }}} 117 | 118 | # vim: fdm=marker 119 | -------------------------------------------------------------------------------- /2016-01-28/ruby-best-friend.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-01-28/ruby-best-friend.pdf -------------------------------------------------------------------------------- /2016-02-25/docker_for_ruby_devs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-02-25/docker_for_ruby_devs.pdf -------------------------------------------------------------------------------- /2016-02-25/docker_for_ruby_devs_no_comments.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-02-25/docker_for_ruby_devs_no_comments.pdf -------------------------------------------------------------------------------- /2016-04-28/stubs_mocks_spies.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-04-28/stubs_mocks_spies.pdf -------------------------------------------------------------------------------- /2016-04-28/stubs_mocks_spies_no_comment.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-04-28/stubs_mocks_spies_no_comment.pdf -------------------------------------------------------------------------------- /2016-06-02/http_clients.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-06-02/http_clients.pdf -------------------------------------------------------------------------------- /2016-06-30/microservices.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-06-30/microservices.pdf -------------------------------------------------------------------------------- /2016-09-26/blazingfastmina.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-09-26/blazingfastmina.pdf -------------------------------------------------------------------------------- /2016-09-26/rails_associations.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-09-26/rails_associations.key -------------------------------------------------------------------------------- /2016-10-25/dox.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-10-25/dox.pdf -------------------------------------------------------------------------------- /2016-10-25/steganography.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-10-25/steganography.pdf -------------------------------------------------------------------------------- /2016-11-29/extreme_programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-11-29/extreme_programming.pdf -------------------------------------------------------------------------------- /2016-12-20/sidekiq_enterprise.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-12-20/sidekiq_enterprise.pdf -------------------------------------------------------------------------------- /2016-12-20/unused.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2016-12-20/unused.pdf -------------------------------------------------------------------------------- /2017-01-24/ansible_for_rubyists.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-01-24/ansible_for_rubyists.pdf -------------------------------------------------------------------------------- /2017-01-24/services_with_grpc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-01-24/services_with_grpc.pdf -------------------------------------------------------------------------------- /2017-03-28/graphql-with_presenter_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-03-28/graphql-with_presenter_notes.pdf -------------------------------------------------------------------------------- /2017-03-28/graphql.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-03-28/graphql.pdf -------------------------------------------------------------------------------- /2017-03-28/graphql_server_side_talks-with_presenter_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-03-28/graphql_server_side_talks-with_presenter_notes.pdf -------------------------------------------------------------------------------- /2017-03-28/graphql_server_side_talks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-03-28/graphql_server_side_talks.pdf -------------------------------------------------------------------------------- /2017-03-28/modeling_a_solid_database.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-03-28/modeling_a_solid_database.pdf -------------------------------------------------------------------------------- /2017-04-25/caching_with_ruby_on_rails.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-04-25/caching_with_ruby_on_rails.pdf -------------------------------------------------------------------------------- /2017-05-30/action_cable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-05-30/action_cable.pdf -------------------------------------------------------------------------------- /2017-06-27/architecture_for_scaling_iot_devices.md: -------------------------------------------------------------------------------- 1 | Outline 2 | ======= 3 | 4 | 5 | * architecture 6 | * design 7 | * coding 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Problem(s) 38 | ========== 39 | Problem 40 | ------- 41 | - scaling IoT device connections 42 | 43 | 44 | 45 | +------------+ +------------+ 46 | | Controller | ====> | | 47 | +------------+ | | 48 | ... | Server | 49 | +------------+ | | 50 | | Controller | =====> | | 51 | +------------+ +------------+ 52 | 53 | 54 | 55 | 56 | - Limit 1024 connects 57 | 58 | - Things slow down at 100 connections 59 | 60 | - A big $$ customer :) 61 | o 500 new devices this year 62 | 63 | - Very strict requirements: downtime or slowness is NOT 64 | an option (otherwise less revenue for the customer) 65 | 66 | 67 | *Other problems* 68 | 69 | - dirty design 70 | - crappy C code 71 | - manually assembled JSON 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | Easy solution 84 | ------------- 85 | 86 | - Scale the existing solution 87 | 88 | 'foo.mycompany.com' 89 | +------------+ +-------------+ 90 | | Controller | ====> | | 91 | +------------+ | | 92 | ... | Server 1 | 93 | +------------+ | | 94 | | Controller | =====> | | 95 | +------------+ +-------------+ 96 | 97 | 98 | 99 | 'bar.mycompany.com' 100 | +------------+ +-------------+ 101 | | Controller | ====> | | 102 | +------------+ | | 103 | ... | Server N | 104 | +------------+ | | 105 | | Controller | =====> | | 106 | +------------+ +-------------+ 107 | 108 | 109 | - Hostnames are *hardcoded* on controllers. Changing is hard. 110 | 111 | - Need to have a single interface (single URL). 112 | Radical simplicity where things are hard to change. 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | Proper solution 121 | --------------- 122 | 123 | - rethink everything 124 | 125 | - full rewrite 126 | 127 | 128 | The rest of the speech is about this. 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | Architecture 154 | ============ 155 | High level 156 | ---------- 157 | 158 | 159 | 160 | 161 | 162 | 'foo.mycompany.com' 163 | +------------+ +--------------+ +----------------+ +--------------+ +---------------+ 164 | | Controller | ==> | | ==> | Driver Process | <===> | | | | 165 | +------------+ | Load | +----------------+ | Vendor | | Main app | 166 | ..(n).. | balancer | +----------------+ | app | <==> | | 167 | +------------+ | (single URL) | ==> | Driver Process | <===> | (with DB) | | (web API, | 168 | | Controller | ==> | | +----------------+ | | | admin UI) | 169 | +------------+ +--------------+ ..(n).. +--------------+ +---------------+ 170 | +----------------+ / 171 | ==> | Driver Process | <===/ 172 | +----------------+ 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | Process level 195 | ------------- 196 | 197 | * notes 198 | 199 | Driver Process Vendor app 200 | (40 in prod) 201 | +-----------------------------------+ +----------------------------+ 202 | | | | | 203 | | Ruby layer | GRPC (2 way) | Rails | 204 | | <==========//==========> <== GRPC ==> Main app 205 | | | | | 206 | | Event | | - Event processing | 207 | | & command | | - Events to DB | 208 | | processing <==========//=========== | 209 | | | Redis pub/sub | | 210 | | | (1 way) | | 211 | | events commands | | | 212 | | ^ | | +----------------------------+ 213 | |~~~~~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~| 214 | | | v | 215 | | C lang layer | 216 | port x <=> | 217 | | libmpl.a | 218 | | scp_net.h | 219 | | ... | 220 | +-----------------------------------+ 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | Design 236 | ====== 237 | 238 | Driver process 239 | 240 | +-----------------------------------------------------------------------------------+ 241 | | C layer / Ruby layer | 242 | | \ +----------------------------------------------+ | 243 | | / | Event processing thread | | 244 | | \ | | | GRPC 245 | | scpGetMessage() ============> Driver::API.fetch_event >================//========> vendor app 246 | | / | | | 247 | | \ | | | 248 | | / +----------------------------------------------+ | 249 | | \ | 250 | | / | 251 | | \ | 252 | | libmpl.a / +----------------------------------------------+ | 253 | | scp_net.h \ | Command sending thread 1 | | 254 | port x <=> ... / | | | 255 | | \ | <======== redis.subscribe(controller.serial_num) 256 | | / | Driver::API.send_command(command) | | 257 | | \ | v | | 258 | | / +-|--------------------------------------------+ | 259 | | \ | . | 260 | | scpConfigCommand() <===========+ . | 261 | | / | . | 262 | | \ +-|--------------------------------------------+ | 263 | | / | | Command sending thread 20 | | 264 | | \ | ^ | | 265 | | / | Driver::API.send_command(command) <======== redis.subscribe(controller.serial_num) 266 | | \ | | | 267 | | / | | | 268 | | \ +----------------------------------------------+ | 269 | | / | 270 | | \ | 271 | | / | 272 | | \ | 273 | +-----------------------------------------------------------------------------------+ 274 | 275 | 276 | 277 | 278 | 279 | Coding 280 | ====== 281 | Intro 282 | ----- 283 | - C extension 284 | 285 | 286 | - App is plain ruby (memory requirements) 287 | o 30 Mb RAM per process (in QA) 288 | 289 | 290 | - Gems used (4): 291 | o dotenv for env vars (3 of them) 292 | o grpc interprocess communication 293 | o redis interprocess communication 294 | o sentry-raven exception reporting 295 | 296 | 297 | - Raw thread manipulation 298 | Is not as hard as it seems 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | Ruby FFI 318 | -------- 319 | 320 | - Super easy to start with 321 | 322 | - Everything written in Ruby, NO C code, no compiling! 323 | 324 | - BUT, C structs and enums need to be all 325 | precisely specified/written in Ruby 326 | 327 | * Our experience 328 | - lots of segfaults 329 | - writing ~1000 LOC of Ruby code for C structs :( 330 | - risk for production 331 | 332 | * Lessons learned 333 | - great for just calling C functions 334 | - avoid if you have lot of C structs 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | Ruby C extensions 355 | ----------------- 356 | 357 | - Mature technology 358 | 359 | - Requires compilation and writing C code 360 | 361 | - Works with statically compiled libraries example: 'libfoo.a' 362 | (FFI works only with dynamically linked libraries) 363 | 364 | * Our experience 365 | - super stable after the compilation and C-code debugging phase 366 | - Gets the job done 367 | - Can work with Ruby threads (FFI can't) 368 | 369 | * Lessons learned 370 | - If not sure, use C extensions 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | How do you build a long-running ruby process? 392 | --------------------------------------------- 393 | - use a loop 394 | 395 | class EventLoop 396 | def start 397 | loop do 398 | event = Driver.fetch_event 399 | handle event 400 | end 401 | end 402 | end 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | C extensions and Ruby threads bug 428 | --------------------------------- 429 | 430 | * 2 bugs 431 | (show Design pic) 432 | 433 | 434 | - requires wrapping "interruptable" blocking C function with 435 | 436 | `rb_thread_call_without_gvl()` 437 | 438 | and passing appropriate flags as args 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | Changing process name 465 | --------------------- 466 | 467 | - just write to '$0' 468 | 469 | class ProgramName 470 | def self.update(name) 471 | $0 = name 472 | end 473 | end 474 | 475 | 476 | - benefit: exposing internal process info 477 | 478 | $ ps aux | grep ... 479 | mercury-driver v4.6.1.212 port: 5001 sequences: 0 controllers: 480 | mercury-driver v4.6.1.212 port: 5002 sequences: 7 controllers: 61364 481 | mercury-driver v4.6.1.212 port: 5003 sequences: 3 controllers: 12345, 67890 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | Lesson learned when working with threads 503 | ---------------------------------------- 504 | 505 | 506 | - class state is SHARED across threads 507 | 508 | class Foo 509 | def self.bar=(value) 510 | @bar = value 511 | end 512 | 513 | def self.bar 514 | @bar 515 | end 516 | end 517 | 518 | 519 | - use thread local variables instead 520 | 521 | class Foo 522 | def self.bar=(value) 523 | Thread.current[:bar] = value 524 | end 525 | 526 | def self.bar 527 | Thread.current[:bar] 528 | end 529 | end 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | Questions ? 543 | =========== 544 | -------------------------------------------------------------------------------- /2017-11-28/helix-with_presenter_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-11-28/helix-with_presenter_notes.pdf -------------------------------------------------------------------------------- /2017-11-28/helix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2017-11-28/helix.pdf -------------------------------------------------------------------------------- /2018-02-28/rabbitmq_is_more_than_a_sidekiq_replacement-with_presenter_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2018-02-28/rabbitmq_is_more_than_a_sidekiq_replacement-with_presenter_notes.pdf -------------------------------------------------------------------------------- /2018-02-28/rabbitmq_is_more_than_a_sidekiq_replacement.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2018-02-28/rabbitmq_is_more_than_a_sidekiq_replacement.pdf -------------------------------------------------------------------------------- /2018-09-27/reform.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2018-09-27/reform.pdf -------------------------------------------------------------------------------- /2018-09-27/sort_vs_sort_by.rb: -------------------------------------------------------------------------------- 1 | require "benchmark" 2 | 3 | ###################################### 4 | # # 5 | # Enumerable - #sort & #sort_by # 6 | # # 7 | # Performance and usage comparison # 8 | # # 9 | ###################################### 10 | 11 | # 12 | # *---------------------* 13 | # | How #sort_by works? | 14 | # *---------------------* 15 | # 16 | # Prerequisite in order to understand benchmarks below is understanding how 17 | # #sort_by works. 18 | # 19 | # General steps are: 20 | # 1. execute block for each element in a collection 21 | # 2. create a temporary array; use block return values as keys of a new array 22 | # 23 | # [ 24 | # [:key_1,:element_1], --> 1st tuple 25 | # [:key_2,:element_2], --> 2nd tuple 26 | # . 27 | # . 28 | # [:key_n,:element_n], --> nth tuple 29 | # ] 30 | # 3. sort tuples using their keys 31 | # 4. extract elements from the sorted array 32 | # 5. put extracted elements in a resulting array 33 | # 34 | 35 | # 36 | # *---------------------* 37 | # | TIP - method naming | 38 | # *---------------------* 39 | # 40 | # #sort_by uses "_by" suffix in the name in order to indicate that a return 41 | # value from the block will be used to sort original elements of the collection. 42 | # 43 | # Think about it! :) 44 | # 45 | 46 | # 47 | # *-----------* 48 | # | block API | 49 | # *-----------* 50 | # 51 | # #sort takes two arguments in the block, while #sort_by takes one. This 52 | # denotes different usage. 53 | # 54 | 55 | # 56 | # *----------------* 57 | # | 1. Simple sort | 58 | # *----------------* 59 | # 60 | # Sorting integers from one, to one million. 61 | # 62 | 63 | Benchmark.bm(10) do |run| 64 | unordered_numbers = (1..1_000_000).to_a.shuffle 65 | 66 | run.report("Sort") { unordered_numbers.sort } 67 | run.report("Sory by") { unordered_numbers.sort_by { |e| e } } 68 | end 69 | 70 | # 71 | # Result - #sort is faster 72 | # ------------------------ 73 | # 74 | # user system total real 75 | # Sort 0.210000 0.010000 0.220000 ( 0.221937) 76 | # Sory by 1.150000 0.010000 1.160000 ( 1.260971) 77 | # 78 | # Interpretation 79 | # -------------- 80 | # 81 | # #sort is faster because performing #<=> on integers is a cheap operation, 82 | # whereas #sort_by has to create a temporary array with keys identical to 83 | # their values first and then sort it out. 84 | # 85 | # In this case, creating #sort_by temporary array is more expensive then 86 | # executing #sort blocks. 87 | # 88 | 89 | # 90 | # *-----------------* 91 | # | 2. Complex sort | 92 | # *-----------------* 93 | # 94 | # Sorting ten thousand files by their modification time. 95 | # 96 | 97 | Benchmark.bm(10) do |run| 98 | test_files = Dir["./test_files/*"] 99 | 100 | run.report("Sort") do 101 | test_files.sort { |f1, f2| File.new(f1).mtime <=> File.new(f2).mtime } 102 | end 103 | 104 | run.report("Sort by") do 105 | test_files.sort_by { |f| File.new(f).mtime } 106 | end 107 | end 108 | 109 | # 110 | # Result - #sort_by is faster 111 | # --------------------------- 112 | # 113 | # user system total real 114 | # Sort 1.960000 3.810000 5.770000 ( 6.117591) 115 | # Sort by 0.120000 0.170000 0.290000 ( 0.302010) 116 | # 117 | # Interpretation 118 | # -------------- 119 | # 120 | # #sort_by is faster because block execution is cheap and sorting a temporary 121 | # array is straightforward. Meanwhile, #sort has to instantiate two File objects, 122 | # call #mtime on each, perform comparison using spaceship operator and 123 | # do it N - 1 times (where N is the number of collection elements). 124 | # 125 | # In this case, creating #sort_by temporary array and sorting its elements is 126 | # cheaper then executing #sort blocks. 127 | # 128 | 129 | # 130 | # *-------------------* 131 | # | When to use which | 132 | # *-------------------* 133 | # 134 | # USE SORT when you have to compare two objects and perform simple computation. 135 | # Such as comparing via **spaceship operator**. 136 | # 137 | # USE SORT_BY when computation is expensive, or would be if #sort was used. 138 | # 139 | -------------------------------------------------------------------------------- /2022-06-15/rubyzg_hotwired.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2022-06-15/rubyzg_hotwired.pdf -------------------------------------------------------------------------------- /2022-09-13/building-api-with-grape.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2022-09-13/building-api-with-grape.pdf -------------------------------------------------------------------------------- /2022-11-08/value-objects-in-ruby-and-rails.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2022-11-08/value-objects-in-ruby-and-rails.pdf -------------------------------------------------------------------------------- /2023-02-28/background_jobs_with_rails.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2023-02-28/background_jobs_with_rails.pdf -------------------------------------------------------------------------------- /2023-02-28/rspec-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | theme: rose-pine-moon 4 | --- 5 | 6 | # How to start testing your Rails app with RSpec 7 | #### 5 Tips and Tricks 8 | #### 4 Gems that go well with RSpec 9 | 10 | --- 11 | 12 | 13 | # Unit testing 14 | 15 | ```ruby 16 | class User < ApplicationRecord 17 | def admin?(account) 18 | role.include? :admin && has_account_access? account 19 | end 20 | end 21 | ``` 22 | 23 | --- 24 | 25 | # Unit testing 26 | 27 | ```ruby 28 | # Example! 29 | RSpec.describe User, type: :model do 30 | describe "#admin?" do 31 | subject { user.admin?(account) } 32 | let(:user) { create(:user, role: :admin) } 33 | let(:account) { create(:account) } 34 | 35 | before do 36 | allow(user).to \ 37 | receive(:has_account_access?).with(account).and_return(account_access) 38 | end 39 | 40 | context "when the user doesn't have access to the account" do 41 | let(:account_access) { false } 42 | 43 | it { is_expected.to eq false } 44 | end 45 | 46 | context "when the user has access to the account" do 47 | let(:account_access) { true } 48 | 49 | it "returns true" do 50 | expect(subject).to eq(true) 51 | end 52 | end 53 | end 54 | end 55 | ``` 56 | 57 | --- 58 | 59 | #### describe 60 | - `#` instance methods, 61 | `.` class methods or 62 | nothing 63 | 64 | 65 | #### context 66 | - `with, without or when` 67 | - `before, after and during` 68 | - `if, unless and for` 69 | 70 | --- 71 | 72 | # `before` block 73 | 74 | ```ruby 75 | before :each do 76 | end 77 | ``` 78 | 79 | --- 80 | 81 | # let 82 | 83 | - helper method 84 | - memoized return value 85 | - lazy-evaluated 86 | 87 | --- 88 | 89 | # let 90 | 91 | ```ruby 92 | RSpec.describe User, type: :model do 93 | describe "#admin?" do 94 | subject { user.admin?(account) } 95 | 96 | before do 97 | allow(user).to \ 98 | receive(:has_account_access?).with(account).and_return(account_access) 99 | end 100 | 101 | context "when the user doesn't have access to the account" do 102 | it "returns false" do 103 | user = create(:user) 104 | account = create(:account, role: :admin) 105 | account_access = false 106 | 107 | expect(subject).to eq(false) 108 | end 109 | end 110 | 111 | context "when the user has access to the account" do 112 | it "returns true" do 113 | user = create(:user) 114 | account = create(:account, role: :admin) 115 | account_access = true 116 | 117 | expect(subject).to eq(true) 118 | end 119 | end 120 | end 121 | end 122 | ``` 123 | 124 | --- 125 | 126 | # let 127 | 128 | ```ruby 129 | RSpec.describe User, type: :model do 130 | describe "#admin?" do 131 | subject { user.admin?(account) } 132 | 133 | before do 134 | @user = create(:user) 135 | @account = create(:account) 136 | 137 | allow(user).to \ 138 | receive(:has_account_access?).with(account).and_return(account_access) 139 | end 140 | 141 | context "when the user doesn't have access to the account" do 142 | let(:account_access) { false } 143 | 144 | it "returns false" do 145 | account_access = false 146 | 147 | expect(subject).to eq(false) 148 | end 149 | end 150 | 151 | context "when the user has access to the account" do 152 | let(:account_access) { true } 153 | 154 | it "returns true" do 155 | account_access = true 156 | 157 | expect(subject).to eq(true) 158 | end 159 | end 160 | end 161 | end 162 | ``` 163 | 164 | --- 165 | 166 | # let 167 | 168 | ```ruby 169 | # False positive example. 170 | 171 | ... 172 | before do 173 | @complicated_query = BusinessLogic.complicated_dataset # And it actually returns some dataset value and not `nil`. 174 | end 175 | 176 | it "returns `nil` when something" do 177 | expect(@complicated_qery).to eq(nil) 178 | end 179 | ``` 180 | 181 | --- 182 | 183 | # Unit testing 184 | 185 | ```ruby 186 | # Example! 187 | RSpec.describe User, type: :model do 188 | describe "#admin?" do 189 | subject { user.admin?(account) } 190 | let(:user) { create(:user, role: :admin) } 191 | let(:account) { create(:account) } 192 | 193 | before do 194 | allow(user).to \ 195 | receive(:has_account_access?).with(account).and_return(account_access) 196 | end 197 | 198 | context "when the user doesn't have access to the account" do 199 | let(:account_access) { false } 200 | 201 | it { is_expected.to eq false } 202 | end 203 | 204 | context "when the user has access to the account" do 205 | let(:account_access) { true } 206 | 207 | it "returns true" do 208 | expect(subject).to eq(true) 209 | end 210 | end 211 | end 212 | end 213 | ``` 214 | 215 | --- 216 | 217 | # subject 218 | 219 | Explicit not named subject. 220 | 221 | ```ruby 222 | subject { user.admin?(account) } 223 | ``` 224 | 225 | Explicit named subject 226 | 227 | ```ruby 228 | subject(:is_user_admin) { user.admin?(account) } 229 | ``` 230 | 231 | --- 232 | 233 | # One-liner syntax - GOOD 234 | 235 | ```ruby 236 | ... 237 | 238 | it { is_expected.to eq false } 239 | ``` 240 | 241 | --- 242 | 243 | # One-liner syntax - BAD 244 | 245 | ```ruby 246 | ... 247 | 248 | it "returns true" do 249 | expect(subject).to eq(true) 250 | end 251 | ``` 252 | 253 | --- 254 | 255 | # Testing controllers 256 | 257 | - type: :controller 258 | - type :request 259 | 260 | --- 261 | 262 | # Integration tests 263 | 264 | ```ruby 265 | RSpec.describe "login", type: :request do 266 | describe "POST /login" do 267 | it "signs in the user" do 268 | post login_path, params: {username: "mirko", password: "Go0DPa55w0rD"} 269 | 270 | expect(response).to be_successful 271 | expect(response.body).to include("Welcome mirko to the app") 272 | end 273 | end 274 | end 275 | ``` 276 | 277 | --- 278 | 279 | # Tips and tricks 1 280 | 281 | ```ruby 282 | def login 283 | result = LoginUser.call(session_params) 284 | 285 | if result.success? 286 | session[:user_token] = result.token 287 | redirect_to result.user 288 | else 289 | flash.now[:message] = t(result.message) 290 | render :new 291 | end 292 | end 293 | ``` 294 | 295 | --- 296 | 297 | # Tips and tricks 1 298 | 299 | #### Stub everything you can 300 | 301 | ```ruby 302 | RSpec.describe "login", type: :request do 303 | describe "POST /login" do 304 | let(:user) { create(:user, username: "mirko", password: "Go0DPa55w0rD") } 305 | let(:user_interactor) do 306 | OpenStruct.new(result: result_value, token: "1234") 307 | end 308 | let(:session_params) do 309 | { username: user.username, password: user.password } 310 | end 311 | 312 | before do 313 | allow(LoginUser).to receive(:call).with(session_params).and_return(user_interactor) 314 | end 315 | 316 | context "when success" do 317 | let(:result_value) { true } 318 | 319 | it "signs in the user" do 320 | post login_path, params: session_params 321 | 322 | expect(LoginUser).to have_received(:call).with(session_params) 323 | expect(response).to be_successful 324 | expect(response.body).to include("Welcome mirko to the app") 325 | end 326 | end 327 | end 328 | end 329 | ``` 330 | 331 | # TODO: nothing 332 | 333 | --- 334 | 335 | # Tips and tricks 2 336 | 337 | #### Do not save to the database if you do not have to 338 | 339 | 340 | #### BAD 341 | ```ruby 342 | let(:user) { create(:user, username: "mirko", password: "Go0DPa55w0rD") } 343 | ``` 344 | 345 | #### Good 346 | 347 | ```ruby 348 | let(:user) { instance_double(User, username: "mirko", password: "Go0DPa55w0rD") } 349 | ``` 350 | 351 | --- 352 | 353 | # Tips and tricks 3 354 | 355 | #### `anything` general matcher 356 | 357 | ```ruby 358 | before do 359 | allow(ExampleClass).to receive(:call).with(id: 12, name: anything) 360 | end 361 | ``` 362 | 363 | --- 364 | 365 | # Tips and tricks 4 366 | 367 | #### `aggregate_failures` 368 | 369 | ```ruby 370 | it "signs in the user" do 371 | post login_path, params: session_params 372 | 373 | expect(response).to be_successful 374 | expect(response.body).to include("Welcome mirko to the app") 375 | expect(LoginUser).to have_received(:call).with(session_params) 376 | end 377 | ``` 378 | 379 | --- 380 | 381 | # Tips and tricks 4 382 | 383 | ```ruby 384 | it "signs in the user", aggregate_failures: true do 385 | post login_path, params: session_params 386 | 387 | expect(response).to be_successful 388 | expect(response.body).to include("Welcome mirko to the app") 389 | expect(LoginUser).to have_received(:call).with(session_params) 390 | end 391 | ``` 392 | 393 | --- 394 | 395 | # Tips and tricks 4 396 | 397 | ```ruby 398 | it "signs in the user" do 399 | post login_path, params: session_params 400 | 401 | expect(LoginUser).to have_received(:call).with(session_params) 402 | 403 | aggregate_failures "request response" do 404 | expect(response).to be_successful 405 | expect(response.body).to include("Welcome mirko to the app") 406 | end 407 | end 408 | ``` 409 | 410 | --- 411 | 412 | # Tips and tricks 5 413 | 414 | Running specific spec, context or description. 415 | 416 | 1. With the line number 417 | 418 | ``` 419 | bundle exec rspec spec/example_spec.rb:18 420 | ``` 421 | 422 | 2. With regex 423 | 424 | ``` 425 | bundle exec rspec spec/example_spec.rb -e "a part or a full text from the spec description" 426 | ``` 427 | 428 | --- 429 | 430 | # Tips and tricks 5 431 | 432 | 3. With tags 433 | 434 | ```ruby 435 | describe "tagged specs" do 436 | it "focused example that I'm just developing", :focus => true do; end 437 | it "special example", :focus => 'special' do; end 438 | it "untagged example" do; end 439 | end 440 | 441 | rspec spec --tag focus # Runs specs that have :focus => true 442 | rspec spec --tag focus:special # Run specs that have :focus => special 443 | rspec spec --tag focus ~skip # Run tests except those with :focus => true 444 | ``` 445 | 446 | --- 447 | 448 | # Gems that go well with RSpec 1 449 | 450 | #### FactoryBot 451 | 452 | Factory 453 | 454 | ```ruby 455 | FactoryBot.define do 456 | factory :user do 457 | username { "Test name" } 458 | password { "Go0DPa55w0rD" } 459 | 460 | trait :admin do 461 | role { :admin } 462 | end 463 | end 464 | end 465 | ``` 466 | 467 | 468 | 469 | --- 470 | 471 | # Gems that go well with RSpec 1 472 | 473 | #### FactoryBot 474 | 475 | Usage 476 | 477 | ```ruby 478 | create(:user) 479 | 480 | # or 481 | 482 | create(:user, username: "mirko") 483 | 484 | # or 485 | 486 | create(:user, :admin) 487 | ``` 488 | 489 | --- 490 | 491 | 492 | # Gems that go well with RSpec 2 493 | 494 | #### Faker 495 | 496 | ```ruby 497 | FactoryBot.define do 498 | factory :user do 499 | username { Faker::Internet.username } 500 | password { Faker::Internet.password } 501 | end 502 | end 503 | ``` 504 | 505 | --- 506 | 507 | # Gems that go well with RSpec 3 508 | 509 | #### Timecop 510 | 511 | ```ruby 512 | describe "some set of tests to mock" do 513 | before do 514 | Timecop.freeze(Time.local(1990)) 515 | end 516 | 517 | after do 518 | Timecop.return 519 | end 520 | 521 | it "should do blah blah blah" do 522 | end 523 | end 524 | ``` 525 | 526 | It is similar for `travel` method. 527 | 528 | 529 | --- 530 | 531 | # Gems that go well with RSpec 4 532 | 533 | #### VCR 534 | 535 | ``` 536 | ... 537 | 538 | it do 539 | VCR.use_cassette("file_to_save_the_request_to") do 540 | get "http..." 541 | end 542 | end 543 | ``` 544 | -------------------------------------------------------------------------------- /2023-02-28/rspec-intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2023-02-28/rspec-intro.pdf -------------------------------------------------------------------------------- /2023-10-24/typed-ruby.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2023-10-24/typed-ruby.key -------------------------------------------------------------------------------- /2023-10-24/typed-ruby.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2023-10-24/typed-ruby.pdf -------------------------------------------------------------------------------- /2023-11-21/dissecting_service_objects.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2023-11-21/dissecting_service_objects.pdf -------------------------------------------------------------------------------- /2023-11-21/linkok.com_making_a_webapp_the_hard_way.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2023-11-21/linkok.com_making_a_webapp_the_hard_way.pdf -------------------------------------------------------------------------------- /2024-01-24/Deconstructing_Action_Cable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2024-01-24/Deconstructing_Action_Cable.pdf -------------------------------------------------------------------------------- /2024-01-24/Model_anti_patterns_slides.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2024-01-24/Model_anti_patterns_slides.pptx -------------------------------------------------------------------------------- /2024-02-28/Ruby-constants-lookup.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2024-02-28/Ruby-constants-lookup.key -------------------------------------------------------------------------------- /2024-02-28/devex_and_lsps_ruby.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2024-02-28/devex_and_lsps_ruby.pdf -------------------------------------------------------------------------------- /2025-02-19/code_as_design.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyzg/slides/43d4aedbe6bf713e0fad42cf1bbc9cc94931419e/2025-02-19/code_as_design.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slides 2 | ## 2025-02-19 3 | [Juraj Sulimanović](https://github.com/Juraj-Sulimanovic) - [Code as Design](2025-02-19/code_as_design.pdf) 4 | 5 | ## 2024-02-28 6 | [Marko Matotan](https://github.com/mmatotan) - [State of developer experience tools and LSPs in Ruby](2024-02-28/devex_and_lsps_ruby.pdf) 7 | 8 | [Mateo Vukušić](https://github.com/mateo9) - [Ruby Constant Lookup](2024-02-28/Ruby-constants-lookup.key) 9 | 10 | ## 2024-01-24 11 | [Dominik Mačković](https://github.com/DominikMackovic) - [Model antipatterns in Rails](2024-01-24/Model_anti_patterns_slides.pptx) 12 | 13 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Deconstructing Action Cable](2024-01-24/Deconstructing_Action_Cable.pdf) 14 | 15 | ## 2023-11-21 16 | [Bruno Sutic](https://brunosutic.com) - [linkok.com - making a webapp the hard way](2023-11-21/linkok.com_making_a_webapp_the_hard_way.pdf) 17 | 18 | [Juraj Sulimanović](https://github.com/Juraj-Sulimanovic) - [Dissecting Service Objects](2023-11-21/dissecting_service_objects.pdf) 19 | 20 | ## 2023-10-24 21 | [Miha Rekar](https://mr.si/) - [What's new in Rails?](https://speakerdeck.com/mrfoto/whats-new-in-rails) 22 | 23 | [Radan Skorić](https://radanskoric.com/) - [Typed Ruby](2023-10-24/typed-ruby.pdf) ([keynote file](2023-10-24/typed-ruby.key)) 24 | 25 | ## 2023-02-28 26 | 27 | [Rino Kovačević](https://github.com/Rinoku) - [Background jobs with Rails](2023-02-28/background_jobs_with_rails.pdf) 28 | 29 | [Mirko Cerovac](https://github.com/MirkoC) - [How to start testing your Rails app with RSpec](2023-02-28/rspec-intro.pdf) 30 | 31 | ## 2022-11-08 32 | 33 | [Vlado Cingel](https://github.com/vlado) - [Value Objects in Ruby (and Rails)](2022-11-08/value-objects-in-ruby-and-rails.pdf) 34 | 35 | ## 2022-09-13 36 | 37 | [Fabien Loup](https://github.com/norydev) - [Building an API with Grape](2022-09-13/building-api-with-grape.pdf) 38 | 39 | ## 2022-06-15 40 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Hotwired](2022-06-15/rubyzg_hotwired.pdf) 41 | 42 | 43 | ## 2019-12-12 44 | 45 | [Vlado Cingel](https://github.com/vlado) - [Organising complex SQL queries in Rails](https://vlado.github.io/slides/2019-12-12-organising-complex-sql-queries-in-rails/#/) 46 | 47 | ## 2019-09-27 48 | 49 | [Dario Daić](https://github.com/ringz3n) - [#sort VS #sort_by](2018-09-27/sort_vs_sort_by.rb) 50 | 51 | [Piotr Misiurek](https://github.com/PiotrMisiurek) - [Reform](2018-09-27/reform.pdf) 52 | 53 | ## 2018-05-29 54 | 55 | [Zoran Majstorović](https://github.com/zmajstor) - [Microservices with RabbitMQ](https://speakerdeck.com/zmajstor/microservices-with-rabbitmq) 56 | 57 | ## 2018-03-27 58 | 59 | [Radan Skorić](https://radanskoric.com/) - [Stumbling off the Rails](https://radanskoric.com/talks/files/stumbling_off_the_rails.pdf) 60 | 61 | ## 2018-02-27 62 | 63 | [Janko Marohnić](https://github.com/janko-m) - [Dynamic Routing in Ruby](https://speakerdeck.com/janko_m/dynamic-routing-in-ruby) 64 | 65 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [RabbitMQ is more than a Sidekiq replacement](2018-02-28/rabbitmq_is_more_than_a_sidekiq_replacement.pdf) [[with notes](2018-02-28/rabbitmq_is_more_than_a_sidekiq_replacement-with_presenter_notes.pdf)] 66 | 67 | ## 2017-11-28 68 | 69 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Helix](2017-11-28/helix.pdf) [[with notes](2017-11-28/helix-with_presenter_notes.pdf)] 70 | 71 | ## 2017-10-26 72 | 73 | [Janko Marohnić](https://github.com/janko-m) - [A Look into HTTP.rb (And why you shouldn't use Net::HTTP)](https://speakerdeck.com/janko_m/a-look-into-http-dot-rb-and-why-you-shouldnt-use-net-http) 74 | 75 | ## 2017-06-27 76 | 77 | [Bruno Sutic](https://github.com/bruno-) - [Architecture for scaling IoT devices](2017-06-27/architecture_for_scaling_iot_devices.md) 78 | 79 | ## 2017-05-30 80 | 81 | [Nika Jukić](https://github.com/nikajukic) - [Diving into ActionCable](2017-05-30/action_cable.pdf) 82 | 83 | 84 | [Dario Daic](https://github.com/ringz3n) - [RSpec Mocks - practical test object nomenclature](http://slides.com/da1chy/rspec-mocks#/) 85 | 86 | ## 2017-04-25 87 | 88 | [Nenad Nikolic](https://github.com/nikone) - [Caching with Ruby on Rails](2017-04-25/caching_with_ruby_on_rails.pdf) 89 | 90 | [Vedran Hrncic](https://github.com/v3dr4n) - [Going off the rails](http://slides.com/vrabac/going-off-the-rails) 91 | 92 | ## 2017-03-28 93 | 94 | [Zoran Majstorovic](https://github.com/zmajstor) - [Modeling a Solid Database](2017-03-28/modeling_a_solid_database.pdf) 95 | 96 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [GraphQL](2017-03-28/graphql.pdf) 97 | 98 | ## 2017-02-21 99 | 100 | [Vedran Hrncic](https://github.com/vr4b4c) - [Subclassing Modules](http://slides.com/vrabac/subclassing-modules) 101 | 102 | [Damir Svrtan](https://github.com/DamirSvrtan) - [Building Ruby Bots on AWS Lambda](https://speakerdeck.com/damirsvrtan/building-ruby-bots-on-aws-lambda) 103 | 104 | ## 2017-01-24 105 | 106 | [Bruno Sutic](https://github.com/bruno-) - [Services with gRPC](2017-01-24/services_with_grpc.pdf) 107 | 108 | [Tomislav Mikulin](https://github.com/MrKindle85) - [Ansible for Rubyists](2017-01-24/ansible_for_rubyists.pdf) 109 | 110 | ## 2016-12-20 111 | 112 | [Bruno Sutic](https://github.com/bruno-) - [Sidekiq Enterprise](2016-12-20/sidekiq_enterprise.pdf) 113 | 114 | [Dario Daic](https://github.com/ringz3n) - [Pry plugin: pry-globs](https://github.com/da1chy/pry-globs) 115 | 116 | [Nika Jukić](https://github.com/nikajukic) - [Killing dead code with Unused](2016-12-20/unused.pdf) 117 | 118 | ## 2016-11-29 119 | 120 | [Radan Skorić](https://radanskoric.com/) - ["Extreme Programming Explained" explained](2016-11-29/extreme_programming.pdf) 121 | 122 | [Dario Daić](https://github.com/ringz3n) - [Action Pack Variants](https://gist.github.com/da1chy/a93068c5cac286044a500b35005e9841) 123 | 124 | ## 2016-10-25 125 | 126 | [Melita Kokot](https://github.com/melcha) - [Dox](2016-10-25/dox.pdf) 127 | 128 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Steganography](2016-10-25/steganography.pdf) 129 | 130 | ## 2016-26-09 131 | 132 | [Stjepan Hadjić](https://github.com/d4be4st) - [Blazingfast deployment with Mina](2016-09-26/blazingfastmina.pdf) 133 | 134 | [Tomislav Mikulin](https://github.com/MrKindle85) - [Rails associations](2016-09-26/rails_associations.key) 135 | 136 | ## 2016-06-30 137 | 138 | [Marko Ćilimković](https://github.com/cilim) - [Trailblazer: a New Architecture for Rails](https://www.emaze.com/@AZIFWRRR/rails-trailblazer) 139 | 140 | [Radan Skorić](https://radanskoric.com/) - [**** microservices](2016-06-30/microservices.pdf) 141 | 142 | ## 2016-06-02 143 | 144 | [Zoran Majstorovic](https://github.com/zmajstor) - [Comparing Ruby HTTP Clients](2016-06-02/http_clients.pdf) 145 | 146 | ## 2016-04-28 147 | 148 | [Dario Daic](https://github.com/ringz3n) - [Pry like a gentleman/lady!](http://slides.com/dariodaic/repl_pry#/) 149 | 150 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Stubs, mocks and spies](2016-04-28/stubs_mocks_spies.pdf) 151 | 152 | [Radan Skorić](https://radanskoric.com/) - [Build a DSL for fun and profit (live coding)](https://radanskoric.com//radanskoric.github.io/tree/master/talks/building_dsls) 153 | 154 | ## 2016-03-31 155 | 156 | [Janko Marohnić](https://github.com/janko-m) - [Improve file uploads with Shrine](https://speakerdeck.com/janko_m/shrine-file-upload-toolkit-for-ruby) 157 | 158 | [Ante Barać](https://github.com/abarac) - Ruby on Rails na skliskom terenu 159 | 160 | ## 2016-02-25 161 | 162 | [Dario Daic](https://github.com/ringz3n) - [Ruby objects with a telephone](http://slides.com/dariodaic/ruby_objects_with_a_telephone#/) 163 | 164 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Docker for Rubyists](2016-02-25/docker_for_ruby_devs.pdf) 165 | 166 | ## 2016-01-28 167 | 168 | [Filip Defar](https://github.com/dabrorius) - [Ruby still wants to be your best friend](https://github.com/rubyzg/slides/blob/master/2016-01-28/ruby-best-friend.pdf) 169 | 170 | [Vlado Cingel](https://github.com/vlado) - [How to deploy Rails on Windows, NOT!](https://vlado.github.io/slides/2016-01-28-RubyZG-how-to-deploy-rails-on-windows-not/#/) 171 | 172 | ## 2015-12-22 173 | 174 | [Nikola Šantić](https://github.com/shnikola) - [Čovječe, gdje mi je konstanta?](https://slides.com/nikolasantic/rails-autoloading) 175 | 176 | [Bruno Sutić](https://github.com/bruno-) - [Struct klasa](2015-12-22/bruno_sutic_ruby_struct.rb) 177 | 178 | ## 2015-11-24 179 | 180 | [Nikica Jokic](https://github.com/neektza) - Demistyfied Sidekiq 181 | 182 | [Vedran Hrncic](https://github.com/vr4b4c) - Sketching object model using common sense 183 | 184 | ## 2015-10-27 185 | 186 | [Ivan Nemytchenko](https://github.com/inem) - [Stop being a Rails developer](https://www.slideshare.net/secret/4k5WrmRcpnfiQ5) 187 | 188 | [Dario Daic](https://github.com/ringz3n) - [Enumerators - Are they worth it?](http://slides.com/dariodaic/enumerators#/1) 189 | 190 | ## 2015-09-29 191 | 192 | [Jan Varljen](https://github.com/janvarljen) - [ActiveRecord Views](http://slides.com/janvarljen/activerecordviews) 193 | 194 | ## 2015-06-30 195 | 196 | [Stjepan Hadjić](https://github.com/d4be4st) - [MySQL -> PostgreSQL](http://slides.com/stjepanhadjic/mysql-postgresql) 197 | 198 | [Bruno Sutić](https://github.com/bruno-) - [Readline + irb/pry = \<3](https://speakerdeck.com/brunosutic/pry-equals-3) 199 | 200 | ## 2015-05-26 201 | 202 | [Berislav Babic](https://github.com/berislavbabic) - [Marketing Yourself](https://github.com/rubyzg/slides/blob/master/2015-05-26/Marketing%20Yousrelf.pdf) 203 | 204 | [Vlado Cingel](https://github.com/vlado) - [Elasticsearch On Rails](http://vlado.github.io/slides/2015-05-26-RubyZG-elasticsearch-on-rails/#/) 205 | 206 | ## 2015-04-28 207 | 208 | [Orest Kulik](https://github.com/okulik) – [ELK Stack](https://github.com/okulik/elastic-rubyzg) 209 | 210 | [Zoran Majstorović](https://github.com/zmajstor) – [CryptoRuby 101](https://speakerdeck.com/zmajstor/cryptoruby-101) 211 | 212 | [Janko Marohnić](https://github.com/janko-m) – [Sequel](https://speakerdeck.com/janko_m/sequel) 213 | 214 | ## 2015-03-31 215 | 216 | [Filip Defar](https://github.com/dabrorius) - [How to replace Ruby meetup speakers with robots](http://www.refactorit.co/talks/robots/) 217 | 218 | [Stanko Krtalić Rusendić](https://github.com/monorkin) - [Ruby Extensions](https://github.com/rubyzg/slides/blob/master/2015-03-31/ruby_extensions.pdf) 219 | 220 | ## 2015-02-24 221 | 222 | [Damir Svrtan](https://github.com/DamirSvrtan) - [Has anyone seen puts?](http://slides.com/damirsvrtan/ruby-talks-1-9#/) 223 | 224 | [Radan Skorić](https://radanskoric.com/) - [Rails spring cleaning](https://radanskoric.com/talks/large_refactoring) 225 | 226 | ## 2015-01-27 227 | 228 | [Bruno Sutić](https://github.com/bruno-) - [Capistrano as a deployment tool](https://speakerdeck.com/brunosutic/capistrano-vs-mina-capistrano-demo-talk) 229 | 230 | [Stjepan Hadjić](https://github.com/d4be4st) - [Mina as a deployment tool](http://slides.com/stjepanhadjic/mina/#/) 231 | 232 | ## 2014-12-23 233 | 234 | [Brian Underwood](https://github.com/cheerfulstoic) - [Neo4j and Ruby](http://www.brian-underwood.codes/asciidoc-slides/content/presentation/neo4j-ruby/) 235 | 236 | [Nikica Jokić](https://github.com/neektza) - [WebMachine](https://speakerdeck.com/neektza/building-well-defined-apis-part-1-proper-http) 237 | 238 | [Željko Filipin](https://github.com/zeljkofilipin) - [Basic usage of RuboCop](http://filipin.eu/rubocop/) 239 | 240 | ## 2014-10-28 241 | 242 | [Radan Skorić](https://radanskoric.com/) - [Where to put the business logic?](https://radanskoric.com/talks/ror_business_logic) 243 | 244 | [Bruno Sutić](https://github.com/bruno-) - [Arel](https://speakerdeck.com/brunosutic/introduction-to-arel) 245 | 246 | ## 2014-10-01 247 | 248 | [Damir Svrtan](https://github.com/DamirSvrtan) - [Building a bidirectional web framework in Ruby](http://slides.com/damirsvrtan/bidirectional-ruby-framework#) 249 | 250 | [Bruno Sutić](https://github.com/bruno-) - [Deploying Rails apps with Capistrano](https://speakerdeck.com/brunosutic/deploying-rails-apps-with-capistrano) 251 | 252 | [Jan Varljen](https://github.com/janvarljen) - [Continuous Integration process with Ruby On Rails](http://slides.com/janvarljen/rubyonrailsci#/) 253 | 254 | ## 2014-05-21 255 | 256 | [Janko Marohnić](https://github.com/janko-m) - [Rails Engines](https://speakerdeck.com/janko_m/rails-engines) 257 | 258 | ## 2014-04-22 259 | 260 | [Hrvoje Šimić](https://github.com/shime) - [Hipster::Test part II](https://github.com/shime/hipster_test) 261 | 262 | ## 2014-03-21 263 | 264 | [Vlado Cingel](https://github.com/vlado) - [Rails DB info](http://vlado.github.io/slides/2014-03-21-RubyZG-rails-db-info) 265 | 266 | [Hrvoje Šimić](https://github.com/shime) - [Hipster::Test](https://github.com/shime/hipster_test) 267 | 268 | [Nikica Jokić](https://github.com/neektza) - [Actor based concurrency](https://github.com/neektza/actor_demo) 269 | --------------------------------------------------------------------------------