-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
599 lines (430 loc) · 60.1 KB
/
feed.xml
1
2
3
4
5
6
7
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
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
428
429
430
431
432
433
434
435
436
437
438
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
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.0.1">Jekyll</generator><link href="https://kinnrot.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kinnrot.github.io/" rel="alternate" type="text/html" /><updated>2022-06-29T20:22:42+03:00</updated><id>https://kinnrot.github.io/feed.xml</id><title type="html">cdblg</title><subtitle>Code blog.</subtitle><author><name>Chen Kinnrot</name></author><entry><title type="html">Why focus on why</title><link href="https://kinnrot.github.io/freedom-and-trust/" rel="alternate" type="text/html" title="Why focus on why" /><published>2022-06-18T20:13:46+03:00</published><updated>2022-06-18T20:13:46+03:00</updated><id>https://kinnrot.github.io/freedom-and-trust</id><content type="html" xml:base="https://kinnrot.github.io/freedom-and-trust/"><![CDATA[<p>Around 2 years ago, I started a new management gig. it wasn’t the first, but it was the first with a relatively large team.
One of the most basic things I needed to do as a manager was to bind the team to its mission, and in simple terms: get them super motivated to do their day to day tasks.</p>
<p>I’m a strong preacher for freedom, it gives people a sense of ownership.</p>
<p>Let’s tackle the two edges of freedom:</p>
<h4 id="no-freedom-at-all">No freedom at all</h4>
<p>Someone tells you what to do, how to do it, and when to do it.</p>
<p>” You need to remove some logs related to the checkout process, so please delete lines x and y from file z, make a PR and send it to me “</p>
<p>Well, this is straight forward, anyone can probably do it, you might think it’s a good task for a newcomer. Another perspective is: what message am I sending to this person?
This is how it might look from an employee perspective:</p>
<p>“I don’t trust your judgment and capabilities, so I’m telling you exactly what needs to be done, I don’t even bother to give you the bigger context. You probably won’t learn anything from this task, but that’s the job, and this is what the future looks in this team.
I trust you so little I must personally make sure you did it right, so I must review your code.”</p>
<p>So, as you can see I took it to the extreme, but even if 5% of this thoughts will pop in the employee’s mind, it’s bad.</p>
<h4 id="complete-freedom">Complete freedom</h4>
<p>” We’re paying too much to our logging storage service “</p>
<p>This approach is wide open. no what, when or how, only why. This can go down in one or 2 ways:</p>
<blockquote>
<p>when it’s a junior developer:</p>
<p>“Ohh sh*t I’m clueless, I have no idea what needs to be done, I’m too shy to ask anyone anything, this is scary. Why is there no one to guide me?”</p>
<p>when it’s a senior developer:</p>
<p>“Alright, we’ve got a problem, let’s solve it. I feel trusted and empowered by my manager to solve problems on my own”</p>
</blockquote>
<p>*There’s an even higher degree of freedom, one where you don’t focus the employee on a specific problem, and let them choose it on their own. This is not an industry standard so I’m ignoring it.</p>
<p>So, to the point, freedom is awesome, not for everyone and should be dosed right.
You’re optimizing for independent team members who can do everything. in order to get there, you need to trust them and have high expectations from each team member.</p>
<p>With experienced developers it should be relatively easy, just focus on the “why” and they’ll figure out the rest, or ask nudging questions to get more of the ‘what’. if it’s a feature, a product manager will provide the what. When you focus on the why, you create a strong ownership of the problem. This is good because you never want to get attached to a particular solution, the solution might change tomorrow, it’s a fast moving world. However, the problem probably won’t change so quickly.
When you focus on the why, you give a wider perspective, allowing people to be more creative when it comes to proposing creative solutions. They need to think, and this is exactly why they came to work, and that’s why it’s so motivating.</p>
<p>With less experienced developers, it might be a bit trickier: you want to do the same without getting their frustration level above the tipping point where they feel incapable. You’ll need to do some sensing for each task to understand the proper level of freedom that can be given.</p>
<p>My recommendation is to start with the why, and start an open discussion about the next steps. Uf things don’t evolve, you can push some of the what and how as needed.</p>]]></content><author><name>Chen Kinnrot</name></author><category term="engineering" /><category term="management" /><category term="leadership" /><category term="motivation" /><summary type="html"><![CDATA[Around 2 years ago, I started a new management gig. it wasn’t the first, but it was the first with a relatively large team. One of the most basic things I needed to do as a manager was to bind the team to its mission, and in simple terms: get them super motivated to do their day to day tasks.]]></summary></entry><entry><title type="html">The value of fast feedback loop for developers</title><link href="https://kinnrot.github.io/the-value-of-fast-feedback-loop/" rel="alternate" type="text/html" title="The value of fast feedback loop for developers" /><published>2022-03-03T23:19:59+02:00</published><updated>2022-03-03T23:19:59+02:00</updated><id>https://kinnrot.github.io/the-value-of-fast-feedback-loop</id><content type="html" xml:base="https://kinnrot.github.io/the-value-of-fast-feedback-loop/"><![CDATA[<p>We all wanna get from 0 to 1 (with many trailing zeros), the sooner the better.</p>
<h3 id="so-whats-the-problem">So, what’s the problem?</h3>
<p>You build something and you’re brave enough to put it out there, now what?
Now you start the endless feedback loop cycle, you look at the data, analytics, user recordings, product inputs, and decide whats the next thing that’ll make your product better then ever, it’s an iterative process that never ends. The faster you iterate, the faster you’ll get to your target.</p>
<h3 id="how-fast-can-this-be-done">How fast can this be done?</h3>
<p>It depends on so many things, lets try to focus on the dev part, suppose the product is a ninja with an endless queue of things he wants the R&D department to execute, what does it take? what kind of feedback loop do we need as developers?</p>
<p>The simplest way to explain what need to be done:
“Write the correct code, according to product spec, put it on production.”</p>
<p>Writing the correct code - takes time and that’s fine, what kind of feedback loop do we have when writing code?</p>
<p>It starts with a developer coding on his IDE, gets immediate feedback from it, auto lint/compile issue/warnings etc.
This is the most basic and immediate feedback out there, and it should be seamlessly fast (as fast as typing)</p>
<p>Second layer of feedback are unit tests, fastest way to run your new code, in a neutral environment allowing to identify and fix issues quickly.
This is less basic and immediate, but, an optimal developer experience, allow changing code, running tests almost instantly get results and iterate.
In a world of SOA/Micro-services and remote dev environment, this might not be so fast, but it should. Let’s take an example, company with 200 great developers, each one constantly coding amazing things, writing tests to make sure their code works, after each test written, developer need to wait 30sec to see the test results, lets say each developer write 20 tests a day. 20X30X200=120K seconds = 33.3 hours, meaning every day, almost 2% of productivity go’s on waiting for tests to run. let’s continue to the next loop.</p>
<p>Third layer - running the real product on dev env, mandatory for engineers who make user experience, how much time does it take to make an html/css/js change and see it reflects on the app? it should be instant, if it’s 30 sec, we lost another 2% for the waiting, how much time does it take to spin the env? is it fast? stable?</p>
<blockquote>
<p>Note - each time a developer need to wait more then 1 second, she might lose her focus to something else, which is a multiplier for the waiting time, not to mention the frustration when things doesn’t work locally.</p>
</blockquote>
<p>Forth layer - Pull request, this is an async process, which by definition get’s everything stuck, and a time to work on something else. If we need to move super fast, this process should be synchronized, and conducted f2f right after the pr was created, this is also an iterative process, get comment, fix them, re review.</p>
<p>Fifth layer - QA, Again async process, we wanna get here as ready as possible, to avoid another loop, cause cycles are slow here.</p>
<p>We just went over 5 layers of feedback loop, from the fastest to slowest. now think about it this way, the more iterations we do on the fast layers (1,2,3) the less chances we’ll have many iterations on the slow layers (4-5). To encourage developers to do more iterations, we need a strong infra to allow developers to write and run tests fast, play with their local env, make changes and check them fast. If it’s not fast they’ll do less iterations, and we’ll get to later layers less ready.</p>
<p>So in a hypergrowth company, how to maintain fast feedback loop?</p>
<p>It should start with observability over the metrics.</p>
<ol>
<li>How much time does it take to run a single test?</li>
<li>How much time does it take to spin a new dev env with latest code?</li>
<li>How much time does it take to make a code change on dev/staging/production env and see it live?</li>
<li>How much time does a pr wait in review on average?</li>
</ol>
<p>Most of the time, when you start, everything is super fast(If you are at the beginning and something is slow, I urge you to stop and optimize), and gradually gets slower and slower, you got chunky modules, more dependencies, files, tooling, scripts and the whole shabang. Keeping things optimal when running fast is almost impossible.
At some point you need to stop and think, What feedback cycle is too slow? What can I do about it?</p>
<p>Possible solution is to have a lab mode where you work in isolation from the entire project, have a thin and lean project that has a shell wrapper that simulates the entire app with stubs.</p>
<p>Another one is lot’s of separation and decoupling that allows you to work on relatively small parts of the system that can run fast (for example run a test that loads/compiles 15 files of 1 module, instead of all the 3000 files the project need in order to fully run).</p>
<p>What’s your feedback loop time? what are you doing to improve it? please share on comment!</p>]]></content><author><name>Chen Kinnrot</name></author><category term="engineering" /><category term="startups" /><category term="ci" /><category term="cd" /><summary type="html"><![CDATA[We all wanna get from 0 to 1 (with many trailing zeros), the sooner the better.]]></summary></entry><entry><title type="html">Rails Presentation Objects</title><link href="https://kinnrot.github.io/rails-presentation-objects-guide/" rel="alternate" type="text/html" title="Rails Presentation Objects" /><published>2020-02-23T09:19:37+02:00</published><updated>2020-02-23T09:19:37+02:00</updated><id>https://kinnrot.github.io/rails-presentation-objects-guide</id><content type="html" xml:base="https://kinnrot.github.io/rails-presentation-objects-guide/"><![CDATA[<p>If you ever used any <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC</a> framework, I’m sure you asked yourself more than once, ‘Where should I put this piece of code?’ (If not that’s also fine).
Well, there is no one answer for all problems but, I asked it a lot.</p>
<p>In this post, I’m gonna focus about the view part.</p>
<p>View objects, AKA view models, but any other name is fine (presenter view object, view controller mediator, whatever).</p>
<h2 id="why-do-i-need-it">Why do I need it?</h2>
<p>Well, that’s easy, I got a controller action, need to fetch some data from a few places, Data-base, cache, external API, session, and who knows.
Next, I need to put this data on a view, the view can be in different formats: HTML, JSON, XML, PDF, whatever, it’s the same data, presented in different formats.
this data need to go through some manipulations to be more human friendly. <br />
What I just described is 80% of apps (web, mobile, desktop) scenarios.
Assuming your model is not a <a href="http://www.cqrs.nu/tutorial/cs/03-read-models">read model</a> you can’t just fetch one model/list of models and put it on a view.
You’ll probably have to do some table joins, filters, sorting and a bunch of other things.
So first recommendation is to create a query class for each query you need. The controller can call this query directly.
Next comes the data manipulation this is where the view model comes to the picture. The view model gets data and make it presentable, Why not putting this logic directly in view. cause views should be stupid as possible, they are not classes, they’re hard to debug, so keep them as simple as possible.
And if you have multiple views for the same data, like API and a web view, You’ll find yourself duplicating logic for each view.</p>
<p>There is an issue with directly passing query results to a view model, you are risking with N+1 query performance issues,
cause if you pass an active record model to a view model and view model access a referenced object that wasn’t pre fetched a DB query will run, If you got a list of 50 models,
each with 4 relations not pre-fetched, you got 200 extra queries for a single view, that’s not good.</p>
<p>It’s also considered a bad practice to have data queries in the view layer, but that’s for another post.</p>
<p>To solve this kind of issue you have a few possible solutions,</p>
<ol>
<li>
<p>Do not pass active record models to the view models, use <a href="https://en.wikipedia.org/wiki/Data_transfer_object">data transfer objects</a>, plain poro.</p>
</li>
<li>
<p>Do not pass active record models to the view models, use :attributes method, relation won’t work, but not unexpected queries</p>
</li>
<li>
<p>Make sure you prefetch everything the view needs, and pass the model directly to view model, !danger! if other developers work with you, and they don’t know the consequences.</p>
</li>
<li>
<p>Use a zero relations read model for each view, query it, and send it as is to the view model.</p>
</li>
</ol>
<p>If you don’t wanna implement something on you own, here are some gems that’ll give you some structure and syntactic sugar:</p>
<ul>
<li><a href="https://github.com/drapergem/draper">draper</a> - uses the decorator pattern to wrap active record models, allowing to add view specific methods on top of existing models.</li>
<li><a href="https://github.com/trailblazer/cells">cells</a> - component based approach to encapsulate view parts, and logic to classes.</li>
<li><a href="https://rom-rb.org/">rom</a> - full blown object mapper</li>
<li><a href="https://dry-rb.org/gems/dry-view/0.7/">dry-view</a> - A component based view framework</li>
</ul>
<p>Personally, I choose to write my own PORO, to keep things as simple as possible, I try to pass data, not models, this way my view models layer is not bound to any data store structure.
The query result goes to a view model factory class, that responsible for extracting the relevant data for the view model, and view gets rendered with the view model.</p>
<p>Here is an example:</p>
<p>View model -</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby">
<span class="k">module</span> <span class="nn">ViewModels</span>
<span class="k">class</span> <span class="nc">GeoMap</span>
<span class="nb">attr_reader</span> <span class="ss">:map_data</span>
<span class="k">def</span> <span class="nf">map_options</span>
<span class="p">{</span>
<span class="ss">id: </span><span class="s1">'fans-map'</span><span class="p">,</span>
<span class="ss">width: </span><span class="s1">'100%'</span><span class="p">,</span>
<span class="ss">height: </span><span class="s1">'85%'</span><span class="p">,</span>
<span class="ss">label: </span><span class="s1">'Fans'</span><span class="p">,</span>
<span class="ss">min_value: </span><span class="mi">0</span><span class="p">,</span>
<span class="ss">colors: </span><span class="sx">%w[#e7f7f0 #19B26C]</span>
<span class="p">}</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">map_data</span><span class="p">,</span> <span class="n">view_context</span><span class="p">)</span>
<span class="vi">@map_data</span> <span class="o">=</span> <span class="n">map_data</span>
<span class="vi">@view_context</span> <span class="o">=</span> <span class="n">view_context</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">max_fans</span>
<span class="vi">@map_data</span><span class="p">.</span><span class="nf">max_by</span> <span class="p">{</span> <span class="o">|</span><span class="n">_country</span><span class="p">,</span> <span class="n">fans</span><span class="o">|</span> <span class="n">fans</span> <span class="p">}[</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="vi">@map_data</span><span class="p">.</span><span class="nf">present?</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">max_fans_humanize</span>
<span class="vi">@view_context</span><span class="p">.</span><span class="nf">number_to_human</span><span class="p">(</span><span class="n">max_fans</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">mid_fans_humanize</span>
<span class="vi">@view_context</span><span class="p">.</span><span class="nf">number_to_human</span><span class="p">(</span><span class="n">max_fans</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span> <span class="k">if</span> <span class="n">max_fans</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>View (Written in slim, using chartkick for the geo map) -</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby">
<span class="o">=</span> <span class="n">geo_chart</span> <span class="vi">@geo_map</span><span class="p">.</span><span class="nf">map_data</span><span class="p">,</span> <span class="vi">@geo_map</span><span class="p">.</span><span class="nf">map_options</span>
</code></pre></figure>
<p>Controller -</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby">
<span class="c1"># Map data came from a service object</span>
<span class="vi">@geo_map</span> <span class="o">=</span> <span class="no">GeoMap</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">map_data</span><span class="p">,</span> <span class="n">view_context</span><span class="p">)</span>
</code></pre></figure>
<p><strong>Would love to hear from you, What’s your favorite view objects strategy?</strong></p>]]></content><author><name>Chen Kinnrot</name></author><category term="rails" /><category term="view-models" /><category term="presenter" /><category term="view" /><category term="view-context" /><category term="mvc" /><summary type="html"><![CDATA[If you ever used any MVC framework, I’m sure you asked yourself more than once, ‘Where should I put this piece of code?’ (If not that’s also fine). Well, there is no one answer for all problems but, I asked it a lot. In this post, I’m gonna focus about the view part. View objects, AKA view models, but any other name is fine (presenter view object, view controller mediator, whatever).]]></summary></entry><entry><title type="html">Simulate Click on Google Geo Chart</title><link href="https://kinnrot.github.io/simulate-click-on-google-geo-chart/" rel="alternate" type="text/html" title="Simulate Click on Google Geo Chart" /><published>2020-01-22T21:27:38+02:00</published><updated>2020-01-22T21:27:38+02:00</updated><id>https://kinnrot.github.io/simulate-click-on-google-geo-chart</id><content type="html" xml:base="https://kinnrot.github.io/simulate-click-on-google-geo-chart/"><![CDATA[<p>So I found out it’s not so documented, hopefully google will index this.
I have a page on my system that shows a google geo chart (same as <a href="https://developers.google.com/chart/interactive/docs/gallery/geochart">this</a>)
When clicking on a country on the map lot’s of interesting things happens. And I wanted to implement a ui automation test to cover this use case.</p>
<p>It’s not so easy cause even if you find the right svg path object and try clicking on it you get some interception errors.
So I googled a bit without any luck, but the geo chart documents came to the rescue.</p>
<p>The easiest way is to run some javascript:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">yourChart</span> <span class="o">=</span> <span class="nx">Chartkick</span><span class="p">.</span><span class="nx">charts</span><span class="p">[</span><span class="dl">'</span><span class="s1">counutries-map</span><span class="dl">'</span><span class="p">].</span><span class="nx">chart</span> <span class="c1">// Replace with code that gets your chart</span>
<span class="nx">google</span><span class="p">.</span><span class="nx">visualization</span><span class="p">.</span><span class="nx">events</span><span class="p">.</span><span class="nx">trigger</span><span class="p">(</span><span class="nx">yourChart</span><span class="p">,</span> <span class="dl">'</span><span class="s1">regionClick</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">region</span><span class="p">:</span><span class="dl">'</span><span class="s1">US</span><span class="dl">'</span> <span class="p">});</span></code></pre></figure>
<p>This code triggers an event on the chart object that the United States region was clicked. super simple and easy
I’m using <a href="http://teamcapybara.github.io/capybara/">capybara</a> and it looks like this:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"> <span class="n">page</span><span class="p">.</span><span class="nf">evaluate_script</span><span class="p">(</span><span class="o"><<~</span><span class="no">JS</span><span class="p">)</span><span class="sh">
function(){
var yourChart = Chartkick.charts['counutries-map'].chart // Replace with code that gets your chart
google.visualization.events.trigger(yourChart, 'regionClick', { region:'US' });
}()
</span><span class="no"> JS</span></code></pre></figure>
<p>It’s just injecting a self executing function to the page.</p>]]></content><author><name>Chen Kinnrot</name></author><category term="browser-automation" /><category term="testing" /><category term="capybara" /><category term="maps" /><category term="google-charts" /><summary type="html"><![CDATA[So I found out it’s not so documented, hopefully google will index this. I have a page on my system that shows a google geo chart (same as this) When clicking on a country on the map lot’s of interesting things happens. And I wanted to implement a ui automation test to cover this use case.]]></summary></entry><entry><title type="html">Keep Track on Coverage</title><link href="https://kinnrot.github.io/keep-track-on-coverage/" rel="alternate" type="text/html" title="Keep Track on Coverage" /><published>2020-01-21T17:40:16+02:00</published><updated>2020-01-21T17:40:16+02:00</updated><id>https://kinnrot.github.io/keep-track-on-coverage</id><content type="html" xml:base="https://kinnrot.github.io/keep-track-on-coverage/"><![CDATA[<p>Recently I discovered <a href="https://github.com/danger/danger">Danger</a>, an amazing tool you should check out no matter what.</p>
<p>I can write a ton of use cases that serves me on my day to day work, but let’s start with one which I like a lot.</p>
<p>Do you know how much of your code is covered by tests?</p>
<p>Do you keep track on how your code coverage changes over time ?</p>
<p>I didn’t till I installed a simple danger plugin called <a href="https://github.com/marcelofabri/danger-simplecov_json">‘danger-simplecov_json’</a>, It’s a ruby gem it’s configuration is super easy (check this link to the gem),
Once you configure Danger on your CI or locally, you can see after each build (spec run or whatever you use) the percentage of code coverage.</p>
<p>When I started using this plugin my coverage was somewhere in the 40%, currently I’m on 85%. This plugin motivates you to increase your coverage push new code with tests.</p>
<p>I configured it to run on my CI (I use circle) and add a nicely formatted comment on each opened PR.</p>
<p>It looks like this:</p>
<blockquote>
<p>Code coverage is now at 83.13% (37790/45395 lines)</p>
</blockquote>
<p>It’s a small thing with big impact, try it!</p>]]></content><author><name>Chen Kinnrot</name></author><category term="danger" /><category term="simple-cov" /><category term="code-coverage" /><summary type="html"><![CDATA[Recently I discovered Danger, an amazing tool you should check out no matter what.]]></summary></entry><entry><title type="html">Building your own rails form builder</title><link href="https://kinnrot.github.io/building-your-own-rails-form-builder/" rel="alternate" type="text/html" title="Building your own rails form builder" /><published>2019-08-07T00:00:00+03:00</published><updated>2019-08-07T00:00:00+03:00</updated><id>https://kinnrot.github.io/building-your-own-rails-form-builder</id><content type="html" xml:base="https://kinnrot.github.io/building-your-own-rails-form-builder/"><![CDATA[<p>If you’re building forms with rails, whether you’re using a gem for it or working with pure rails forms, you should know this.</p>
<p>Rails uses form builder to allow you to call all the standard label/input/select methods, the exact class name is</p>
<blockquote>
<p>ActionView::Helpers::FormBuilder</p>
</blockquote>
<p>When calling ‘form_for’ you get a fresh instance of this FormBuilder which allows you to define your form elements.</p>
<p>Lets say you wanna add another reusable form element, for example a form section with title that uses your own custom style/classes.</p>
<p>To achieve that, you should extend the default form builder, just add a new class to your helpers folder lets call it ‘MyFormBuilder’ it looks like this</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sb">`
class MyFormBuilder < ActionView::Helpers::FormBuilder
def section(title, id = "section-</span><span class="si">#{</span><span class="no">SecureRandom</span><span class="p">.</span><span class="nf">uuid</span><span class="si">}</span><span class="sb">")
@template.content_tag(:label, title, { class: 'form-label', id: id }) +
@template.content_tag(:div) do
yield if block_given?
end +
@template.content_tag('hr', '', { class: 'row-divider' })
end
end
`</span></code></pre></figure>
<p>So we defined a form builder with a section method now we just need to tell rails, that when we write in our view</p>
<blockquote>
<p>form_for()…</p>
</blockquote>
<p>to use our freshly coded MyFormBuilder</p>
<p>It’s actually super simple ( but not so documented ), if you’ll look at https://apidock.com/rails/ActionView/Helpers/FormHelper/form_for there’s a section called ‘Customized form builders’ which explains what I just wrote</p>
<p>To make your new form builder the default builder so you won’t have to specify it each time you write form_for you can do one of two options, add a new form_for method with a different name, or override existing form_for.</p>
<p>I prefer adding a new method, so on helpers/application_helper.rb I added the following code:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sb">`
module ApplicationHelper
def my_form_for(name, *args, &block)
options = args.extract_options!
content_tag("div",
form_for(name, *(args << options.merge(:builder => MyFormBuilder)), &block),
:class => "core-form section"
)
end
def my_fields_for(name, classes, *args, &block)
options = args.extract_options!
content_tag("div",
fields_for(name, nil, *(args << options.merge(:builder => MyFormBuilder)), &block),
:class => "core-form section </span><span class="si">#{</span><span class="n">classes</span><span class="si">}</span><span class="sb">"
)
end
end
`</span></code></pre></figure>
<p>Which allows me to use the classic form_for, or my extended my_form_for, and also my_fields_for instead of fields_for when nesting inside existing form.</p>
<p>It’s super useful when you got forms and wanna keep things DRY and managed, I use it to define a standard label with input with error rendering for example.</p>
<p>Hope you like it, please share your thoughts.</p>]]></content><author><name>Chen Kinnrot</name></author><category term="ruby" /><category term="rails" /><category term="form" /><category term="component" /><summary type="html"><![CDATA[If you’re building forms with rails, whether you’re using a gem for it or working with pure rails forms, you should know this.]]></summary></entry><entry><title type="html">Android Lifecycle Aware Modal</title><link href="https://kinnrot.github.io/android-lifecycle-aware-modal/" rel="alternate" type="text/html" title="Android Lifecycle Aware Modal" /><published>2018-10-25T18:43:04+03:00</published><updated>2018-10-25T18:43:04+03:00</updated><id>https://kinnrot.github.io/android-lifecycle-aware-modal</id><content type="html" xml:base="https://kinnrot.github.io/android-lifecycle-aware-modal/"><![CDATA[<p>Sometimes we want to show the user an alert when somethings wrong or we just need to give some extra info,
For example; Ask the user if he is sure he wanna leave the app.</p>
<p>This can be achieved with the following code (runs inside activity):</p>
<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="nc">AlertDialog</span><span class="p">.</span><span class="nc">Builder</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
<span class="p">.</span><span class="nf">setMessage</span><span class="p">(</span><span class="s">"Are you sure you want to exit?"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">setCancelable</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
<span class="p">.</span><span class="nf">setPositiveButton</span><span class="p">(</span><span class="s">"Yes"</span><span class="p">,</span> <span class="k">object</span><span class="p">:</span><span class="nc">DialogInterface</span><span class="p">.</span><span class="nc">OnClickListener</span><span class="p">()</span> <span class="p">{</span>
<span class="k">fun</span> <span class="nf">onClick</span><span class="p">(</span><span class="n">dialog</span><span class="p">:</span><span class="nc">DialogInterface</span><span class="p">,</span> <span class="n">id</span><span class="p">:</span><span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="nd">@YourActivity</span><span class="p">.</span><span class="nf">finish</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">})</span>
<span class="p">.</span><span class="nf">setNegativeButton</span><span class="p">(</span><span class="s">"No"</span><span class="p">,</span> <span class="k">null</span><span class="p">)</span>
<span class="p">.</span><span class="nf">show</span><span class="p">()</span>
</code></pre></figure>
<p>This works fine, buy there is one annoying issue, If user clicks home button while dialog displayed, and go back to the app, the dialog will still be there.
Now your user experience is seeing a question about getting out of the app while he just went in.</p>
<p>To avoid this case, I found an easy solution by making my modal lifecycle aware, If your’e not sure whats lifecycle, consider read about it <a href="https://developer.android.com/topic/libraries/architecture/lifecycle">here</a>.</p>
<p>Every activity/fragment implements the LifecycleOwner interface so lets add the following method:</p>
<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="k">private</span> <span class="k">fun</span> <span class="nf">subscribeToLifecycleEvents</span><span class="p">(</span><span class="n">dialog</span><span class="p">:</span> <span class="nc">Dialog</span><span class="p">,</span> <span class="n">lifecycleOwner</span><span class="p">:</span> <span class="nc">LifecycleOwner</span><span class="p">?,</span> <span class="n">dismissHandler</span><span class="p">:</span> <span class="p">(()</span> <span class="p">-></span> <span class="nc">Unit</span><span class="p">)?</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">val</span> <span class="py">lifecycleObserver</span> <span class="p">=</span> <span class="nc">GenericLifecycleObserver</span> <span class="p">{</span> <span class="n">_</span><span class="p">,</span> <span class="n">event</span> <span class="p">-></span>
<span class="k">if</span> <span class="p">(</span><span class="n">event</span> <span class="p">==</span> <span class="nc">Lifecycle</span><span class="p">.</span><span class="nc">Event</span><span class="p">.</span><span class="nc">ON_PAUSE</span> <span class="p">&&</span> <span class="n">dialog</span><span class="p">.</span><span class="n">isShowing</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dialog</span><span class="p">.</span><span class="nf">dismiss</span><span class="p">()</span>
<span class="n">dismissHandler</span><span class="o">?.</span><span class="nf">invoke</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">lifecycleOwner</span><span class="o">?.</span><span class="nf">apply</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="n">lifecycle</span><span class="p">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="n">lifecycleObserver</span><span class="p">)</span> <span class="p">}</span>
<span class="n">dialog</span><span class="p">.</span><span class="nf">setOnDismissListener</span> <span class="p">{</span> <span class="n">lifecycleOwner</span><span class="o">?.</span><span class="nf">apply</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="n">lifecycle</span><span class="p">.</span><span class="nf">removeObserver</span><span class="p">(</span><span class="n">lifecycleObserver</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>This method make every dialog lifecycle aware, meaning, if we add subscription to the dialog display:</p>
<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"> <span class="kd">val</span> <span class="py">dialog</span> <span class="p">=</span> <span class="nc">AlertDialog</span><span class="p">.</span><span class="nc">Builder</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
<span class="p">.</span><span class="nf">setMessage</span><span class="p">(</span><span class="s">"Are you sure you want to exit?"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">setCancelable</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
<span class="p">.</span><span class="nf">setPositiveButton</span><span class="p">(</span><span class="s">"Yes"</span><span class="p">,</span> <span class="k">object</span><span class="p">:</span><span class="nc">DialogInterface</span><span class="p">.</span><span class="nc">OnClickListener</span><span class="p">()</span> <span class="p">{</span>
<span class="k">fun</span> <span class="nf">onClick</span><span class="p">(</span><span class="n">dialog</span><span class="p">:</span><span class="nc">DialogInterface</span><span class="p">,</span> <span class="n">id</span><span class="p">:</span><span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="nd">@YourActivity</span><span class="p">.</span><span class="nf">finish</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">})</span>
<span class="p">.</span><span class="nf">setNegativeButton</span><span class="p">(</span><span class="s">"No"</span><span class="p">,</span> <span class="k">null</span><span class="p">)</span>
<span class="p">.</span><span class="nf">show</span><span class="p">()</span>
<span class="nf">subscribeToLifecycleEvents</span><span class="p">(</span><span class="n">dialog</span><span class="p">,</span> <span class="k">this</span><span class="p">)</span> <span class="c1">// add subscription</span>
</code></pre></figure>
<p>Every time the lifecycle owner of the modal (fragment activity or custom implementation) is paused, the
dialog will be automatically dismissed.</p>
<p>Lifecycle architecture opens many options related to bind ui interactions with lifecycle which is extremely important for a mobile phone
that can get interrupts like phone calls and messages all the time.</p>]]></content><author><name>Chen Kinnrot</name></author><category term="android" /><category term="arch" /><category term="lifecycle" /><summary type="html"><![CDATA[Sometimes we want to show the user an alert when somethings wrong or we just need to give some extra info, For example; Ask the user if he is sure he wanna leave the app.]]></summary></entry><entry><title type="html">Live Data Pitfall You Should Be Aware Of</title><link href="https://kinnrot.github.io/live-data-pitfall-you-should-be-aware-of/" rel="alternate" type="text/html" title="Live Data Pitfall You Should Be Aware Of" /><published>2018-10-22T08:12:18+03:00</published><updated>2018-10-22T08:12:18+03:00</updated><id>https://kinnrot.github.io/live-data-pitfall-you-should-be-aware-of</id><content type="html" xml:base="https://kinnrot.github.io/live-data-pitfall-you-should-be-aware-of/"><![CDATA[<p>When working with <code class="highlighter-rouge">MutableLiveData</code> you can update the observable value in 2 ways:</p>
<ol>
<li><code class="highlighter-rouge">setValue</code></li>
<li><code class="highlighter-rouge">postValue</code></li>
</ol>
<p>Both will update the live data value as expected as long as your code is running from the main thread.</p>
<p>If you need to update a value from other thread you can use the <code class="highlighter-rouge">postValue</code> which is thread safe, and will make sure to notify observers on main thread.</p>
<p>This is all nice, but be aware!</p>
<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">val</span> <span class="py">foo</span> <span class="p">=</span> <span class="nc">MutableLiveData</span><span class="p"><</span><span class="nc">Boolean</span><span class="p">>()</span>
<span class="c1">// this</span>
<span class="n">foo</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="k">true</span>
<span class="n">foo</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="k">false</span>
<span class="c1">// and that</span>
<span class="n">foo</span><span class="p">.</span><span class="nf">postValue</span><span class="p">(</span><span class="k">true</span><span class="p">)</span>
<span class="n">foo</span><span class="p">.</span><span class="nf">postValue</span><span class="p">(</span><span class="k">false</span><span class="p">)</span></code></pre></figure>
<p>Both this and that sets the live data value to true and then to false, so you probably expect, if observing foo, you’ll get your code to run twice,
once with true and once with false.</p>
<p>Well for the first section with <code class="highlighter-rouge">setValue</code> it works as expected.</p>
<p>For the second section with <code class="highlighter-rouge">postValue</code> you’ll get a surprise, only second call triggers the observer, or is it?</p>
<p>The issue with post value is as follows:</p>
<p>Value is being set immediately in a synchronized code block for thread safety,
but the observers notification is scheduled to execute on main thread via the event loop (with handler)
So whats happening is value changes to true and false but scheduling code occurs only once.</p>
<p>this is the implementation of <code class="highlighter-rouge">postValue</code></p>
<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="k">protected</span> <span class="n">void</span> <span class="nf">postValue</span><span class="p">(</span><span class="nc">T</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
<span class="n">boolean</span> <span class="n">postTask</span><span class="p">;</span>
<span class="nf">synchronized</span> <span class="p">(</span><span class="n">mDataLock</span><span class="p">)</span> <span class="p">{</span>
<span class="n">postTask</span> <span class="p">=</span> <span class="n">mPendingData</span> <span class="p">==</span> <span class="nc">NOT_SET</span><span class="p">;</span>
<span class="n">mPendingData</span> <span class="p">=</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(!</span><span class="n">postTask</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">ArchTaskExecutor</span><span class="p">.</span><span class="nf">getInstance</span><span class="p">().</span><span class="nf">postToMainThread</span><span class="p">(</span><span class="n">mPostValueRunnable</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>As you can <code class="highlighter-rouge">postTask</code> is a flag that say “do I need to schedule a runnable to notify my observers, or did I already do that?”
for the first call <code class="highlighter-rouge">postTask</code> is true, and for the second its false</p>
<p>postValue is good for ui progress reporting for example, cause ui updates only when main thread is available to perform screen updates.
It’s not good when you count on getting notified for each change of the LiveDataValue.</p>
<p>as comment says:</p>
<blockquote>
<p>If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.</p>
</blockquote>
<p>So I recommend, by default, prefer using <code class="highlighter-rouge">setValue</code>, use postValue, only when:</p>
<ol>
<li>you gonna post lots of changes (progress) and you don’t need all of them only last value is relevant.</li>
<li>you not running on the main thread.</li>
</ol>]]></content><author><name>Chen Kinnrot</name></author><category term="android" /><category term="arch" /><category term="lifecycle" /><summary type="html"><![CDATA[When working with MutableLiveData you can update the observable value in 2 ways:]]></summary></entry><entry><title type="html">Choosing the Right WordPress Hosting Service</title><link href="https://kinnrot.github.io/choosing-the-right-wordpress-hosting-service/" rel="alternate" type="text/html" title="Choosing the Right WordPress Hosting Service" /><published>2018-09-10T21:35:12+03:00</published><updated>2018-09-10T21:35:12+03:00</updated><id>https://kinnrot.github.io/choosing-the-right-wordpress-hosting-service</id><content type="html" xml:base="https://kinnrot.github.io/choosing-the-right-wordpress-hosting-service/"><![CDATA[<p>I needed to choose WordPress hosting service for some of clients and decided to do some research to figure out what’s best value for each client needs.</p>
<p>There are many service providers in this area, After lots of googling I decided to focus on the following:</p>
<ol>
<li><a href="https://wordpress.com/create/?aff=9716&cid=958799">WordPress</a></li>
<li><a href="https://www.bluehost.com/track/kinnrot/">BlueHost</a></li>
<li><a href="https://kinsta.com/?utm_source=kinnrot.github.io">Kinsta</a></li>
</ol>
<h2 id="wordpress">WordPress</h2>
<p>If you’re building a new site/blog without any technical knowledge, <a href="https://wordpress.com/create/?aff=9716&cid=958799">WordPress</a> is the right place for you,
owned by the official word press organization offering limited capabilities cheap plans, and more expensive plans with
more options like upload a custom theme, and charging money via paypal.</p>
<p>They also got a free plan, but you won’t get to use your own domain name.</p>
<h2 id="bluehost">BlueHost</h2>
<p>This is the most economy wise solution.</p>
<p>If you have technical knowledge and you have a custom theme, and wanna save money, this is the service for you,
It’s a managed solution with one click WordPress install. <a href="https://www.bluehost.com/track/kinnrot/">BlueHost</a> also offers additional services like seo optimization tool, and backup tool for additional cost.</p>
<h2 id="kinsta">Kinsta</h2>
<p>For those of you who run a serious operation but don’t wanna setup their own dev ops,
and dont have more than 100K monthly visits / don’t mind to pay a lot on your web site,
This is the service for you, you get:</p>
<ul>
<li>One click SSL setup with <a href="https://letsencrypt.org/">let’s encrypt</a></li>
<li>Premium DNS by amazon</li>
<li>Staging environment with one click push to production</li>
<li>One click CDN setup</li>
</ul>
<blockquote>
<p>For those of you who want to dig deeper there are many other providers and guides on how to choose the best one for your needs,
I found <a href="https://www.wpbeginner.com/wordpress-hosting/?utm_source=kinnrot.github.io">this link</a></p>
</blockquote>]]></content><author><name>Chen Kinnrot</name></author><category term="hosting" /><category term="wp" /><category term="wordpress" /><category term="kinsta" /><category term="bluehost" /><category term="letsencrypt" /><summary type="html"><![CDATA[I needed to choose WordPress hosting service for some of clients and decided to do some research to figure out what’s best value for each client needs.]]></summary></entry><entry><title type="html">From jQuery to Stimulus</title><link href="https://kinnrot.github.io/from-jquery-to-stimulus/" rel="alternate" type="text/html" title="From jQuery to Stimulus" /><published>2018-05-22T12:10:10+03:00</published><updated>2018-05-22T12:10:10+03:00</updated><id>https://kinnrot.github.io/from-jquery-to-stimulus</id><content type="html" xml:base="https://kinnrot.github.io/from-jquery-to-stimulus/"><![CDATA[<p>I tried to build an <a href="https://en.wikipedia.org/wiki/Single-page_application">SPA</a> without a shiny client side framework, I wanted to build something fast with good user experience and keeping it as simple as possible.</p>
<p>I decided to take <a href="https://rubyonrails.org/">rails</a>, use <a href="https://github.com/turbolinks/turbolinks">turbolinks</a> and a avoid javascript till its a must.</p>
<p>It didn’t take more than a few hours and I found myself writing javascript. What I needed to do is simple, I had an input with number, and 2 buttons next to it, one to increase values by 1 and on to decrease it looked like this:</p>
<p><button>-</button><span> 0</span><button>+</button></p>
<p>jQuery to the rescue, I had a list of those buttons, so I needed to identify each one, so I added a data-id attr and used it to identify which input I need to update it looked like this:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html">`
<span class="nt"><button</span> <span class="na">data-id=</span><span class="s">"1"</span> <span class="na">name=</span><span class="s">"minus"</span><span class="nt">></span>-<span class="nt"></button></span>
<span class="nt"><input</span> <span class="na">id=</span><span class="s">"1"</span> <span class="na">value=</span><span class="s">"0"</span> <span class="na">readonly=</span><span class="s">"readonly"</span><span class="nt">></span>
<span class="nt"><button</span> <span class="na">data-id=</span><span class="s">"1"</span> <span class="na">name=</span><span class="s">"plus"</span><span class="nt">></span>+<span class="nt"></button></span>
`</code></pre></figure>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// number_componenet.js</span>
<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">NumberComponent</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">button[name="plus"]</span><span class="dl">'</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">id</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">input</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">lastValue</span> <span class="o">=</span> <span class="o">+</span><span class="nx">input</span><span class="p">.</span><span class="nx">val</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">num</span> <span class="o">=</span> <span class="nx">lastValue</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">num</span> <span class="o"><=</span> <span class="mi">20</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">input</span><span class="p">.</span><span class="nx">val</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">button[name="minus"]</span><span class="dl">'</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">id</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">input</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">lastValue</span> <span class="o">=</span> <span class="o">+</span><span class="nx">input</span><span class="p">.</span><span class="nx">val</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">num</span> <span class="o">=</span> <span class="nx">lastValue</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">num</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">input</span><span class="p">.</span><span class="nx">val</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>As You can see I added a bit of client side validation to make sure counter does not go below 0 and above 20.
Now All left to do is to initialize the <code class="highlighter-rouge">NumberComponent</code> when the page loads, so I added it to my packs (I’m using <a href="https://github.com/rails/webpacker">webpacker</a>) and wrote:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">new</span> <span class="nx">GameOrderComponent</span><span class="p">()</span>
<span class="p">});</span></code></pre></figure>
<p>And it works!.</p>
<p>but it’s already a bit of a mess, I need to search for the relevant buttons, I don’t know how it’ll behave when
content of page changes (turbolinks, other dynamic behavior etc.) I though to myslef, “it would be much easier to annotate the relevant html items to describe their behavior”.
But I did not want to make the client super complex, I just want something that’ll help me to model my code without all the hassle.</p>
<p>I did some research (very intensive googling) and found <a href="https://github.com/stimulusjs/stimulus">Stimulus</a>.
Its a library that decouples your javascript from the html like css decouple styling from html, which is pretty nice.</p>
<p>The concepts are pretty simple and familiar, you got:</p>
<ul>
<li>Controller - to group a bunch of html elements in one context (numberController)</li>
<li>Target - to allow access dom element inside the controller (instead of jQuery selector) (input)</li>
<li>Action - to respond to dom element event and act (button click)</li>
</ul>
<p>That’s all, simple huh?</p>
<p>This is how it looks</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html">`
<span class="nt"><div</span> <span class="na">data-controller=</span><span class="s">"number"</span><span class="nt">></span>
<span class="nt"><button</span> <span class="na">data-action=</span><span class="s">"click->number#minusOne"</span><span class="nt">></span>-<span class="nt"></button></span>
<span class="nt"><input</span> <span class="na">data-target=</span><span class="s">"number.input"</span> <span class="na">value=</span><span class="s">"0"</span> <span class="na">readonly=</span><span class="s">"readonly"</span><span class="nt">></span>
<span class="nt"><button</span> <span class="na">data-action=</span><span class="s">"click->number#plusOne"</span><span class="nt">></span>+<span class="nt"></button></span>
<span class="nt"></div></span>
`</code></pre></figure>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// number_controller.js</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">Controller</span><span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">stimulus</span><span class="dl">"</span>
<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="kd">extends</span> <span class="nx">Controller</span> <span class="p">{</span>
<span class="nx">minusOne</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">()</span>
<span class="kd">const</span> <span class="nx">lastValue</span> <span class="o">=</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">input</span><span class="p">.</span><span class="nx">value</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">lastValue</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">input</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">input</span><span class="p">.</span><span class="nx">value</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">plusOne</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">()</span>
<span class="kd">const</span> <span class="nx">lastValue</span> <span class="o">=</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">input</span><span class="p">.</span><span class="nx">value</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">lastValue</span> <span class="o"><</span> <span class="mi">20</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">input</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">input</span><span class="p">.</span><span class="nx">value</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">get</span> <span class="nx">input</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">targets</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>I didn’t write less code but,</p>
<ul>
<li>The code is more readable</li>
<li>No need to initialize the controller, Stimulus take care of that.</li>
<li>You get access to the actual html element, which keeps everything straightforward.</li>
<li>No need to worry about dom changes, Stimulus take care of that.</li>
</ul>
<p>I know that everyone thinks react angular and vue is the s*** but I think its good for large organizations where you have a separate front-end team and back-end team.
When you build something new by yourself or with some friends, there is no good reason to start with client side rendering (unless you got a few million users on day one which is less likely)</p>
<p>There are also small companies with a team of full stackers, who might also consider this approach.</p>
<p>If you got a bit stimulated, you can read a small guide <a href="https://stimulusjs.org/handbook/introduction">here</a></p>]]></content><author><name>Chen Kinnrot</name></author><category term="ruby" /><category term="stimulus" /><category term="rails" /><category term="jquery" /><summary type="html"><![CDATA[I tried to build an SPA without a shiny client side framework, I wanted to build something fast with good user experience and keeping it as simple as possible.]]></summary></entry></feed>