-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
1333 lines (961 loc) · 67.4 KB
/
atom.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
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[My Little Blog]]></title>
<link href="http://laptite.github.io/atom.xml" rel="self"/>
<link href="http://laptite.github.io/"/>
<updated>2014-02-27T22:11:16-05:00</updated>
<id>http://laptite.github.io/</id>
<author>
<name><![CDATA[Your Name]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Deep Nested Attributes Using Has_many Through Join Model]]></title>
<link href="http://laptite.github.io/blog/2014/02/26/deep-nesting-with-has-many-through-and-a-join-model/"/>
<updated>2014-02-26T21:34:22-05:00</updated>
<id>http://laptite.github.io/blog/2014/02/26/deep-nesting-with-has-many-through-and-a-join-model</id>
<content type="html"><![CDATA[<p>(rails 3.2.15)</p>
<p>It’s been almost six months since I graduated from the Flatiron School. It’s hard to believe that I haven’t written a blog in just as long. Every time I overcame a challenge I kept telling myself that I should write a blog, but never got around to it.</p>
<p>But this. This problem was a tough nut to crack. I found hints of what to do on StackOverflow and countless articles, but three sources (appended at the end) were most useful. So, definitely check those out.</p>
<p><strong>SCENARIO:</strong> My company had a very long and convoluted controller. The desire was to simplify it in anticipation for a version update. My task was to wipe the controller clean, redesign the corresponding section, and aim for simple CRUD. But I couldn’t get nested_attributes to work. So I broke the hash into pieces and wrote controller methods to get my CRUD actions to work in time for the release. It was shorter and cleaner than the previous version, and handled all validations beautifully. But it just seemed wrong to end up with a fat controller that doesn’t take advantage of the magic that is Rails (wink). I’ve sanitized the code a bit to keep it generic, but it works just the same.</p>
<p><strong>Problems this blog addresses:</strong></p>
<pre><code>- Two-level deep nesting
- Parent-Child validation (Bi-Directional Association)
- Assigning multiple drop-down selections to nested object (Join Model Association)
- Multiple drop-downs from same collection: the last drop-down selection overwrites the first
- Join table with has_and_belongs_to_many association not working with nested_attributes
- Nested attributes works only on create action and not on update
</code></pre>
<p><strong>VERSION 1: THE INITIAL CODE THAT DOESN’T AUTOMAGICALLY WORK</strong></p>
<p>I setup my form using fields_for and accepts_nested_attributes_for following the Rails documentation. Here’s a high-level breakdown:</p>
<pre><code>Car > Make > Pricing
> Feature ( Select: #{feature_type_id: 1 } ) <-- Color
> Feature ( Select: #{feature_type_id: 2)} ) <-- Body Type (2-door, 4-door...)
</code></pre>
<p>Here’s how controller and associations are originally setup in the models, and what the resulting hash looks like:</p>
<pre><code>class CarsController < ApplicationController
def new
@car = Car.new
make = @car.makes.build
make.pricings.build
end
def create
@car = Car.new(params[:car])
if @car.save
redirect_to edit_car_path(@car), notice: "Car created successfully"
else
redirect_to new_car_path(@car), notice: "Car could not be created"
end
end
end
Models
class Car < ActiveRecord::Base
attr_accessible :name, :model_xx, :makes_attributes
has_many :makes
has_many :pricings, through: :makes
has_many :features, through: :makes
has_many :feature_types, through: :features
accepts_nested_attributes_for :makes, allow_destroy: :true
class Make < ActiveRecord::Base
attr_accessible :car_id, :vin, :pricings_attributes, :features_attributes
belongs_to :car, touch: true
has_many :pricings, dependent: :destroy
has_many :feature_types, through: :features
has_and_belongs_to_many :features, join_table: :features_makes
accepts_nested_attributes_for :makes, allow_destroy: true
class Pricing < ActiveRecord::Base
attr_accessible :make_id, :price, :currency
belongs_to :make, touch: true
has_and_belongs_to_many :makes, join_table: :pricings_makes
validates :make_id, :currency, :price, presence: true
class FeatureType < ActiveRecord::Base
attr_accessible :name
has_many :features, dependent: :destroy
validates :name, presence: true
end
class Feature < ActiveRecord::Base
attr_accessible :name, :feature_type_id
belongs_to :feature_type
has_and_belongs_to_many :makes, join_table: :features_makes
validates :name, presence: true
end
form.haml
= form_for @car do |f|
= f.label :car_name
= f.text_field :name
= f.label :model_code
= f.text_field :model_xx
= f.fields_for :makes do |v|
= v.label :vin
= v.text_field :vin
= v.fields_for :features do |o|
= o.label :id, "Color"
= o.collection_select :id, Feature.where(feature_type_id: 1).order(:name), :id, :name, { prompt: "Select Color" }
= o.label :id, "Body Type"
= o.collection_select :id, Feature.where(feature_type_id: 2).order(:name), :id, :name, { prompt: "Select BodyType" }
= v.fields_for :pricings do |p|
= p.label :price
= p.select :currency, Pricing.all.collect{ |p| [currency_symbol(p.currency), p.currency] }, { include_blank: "Currency" }, { class: "selectpicker", data: {style: "btn-primary currency-dropdown"} }
= p.text_field :price
params:
"car"=>{"name"=>"delano", "model_code"=>"dx",
"makes_attributes"=>{"0"=>{"vin"=>"vin98765",
"pricings_attributes"=>{"0"=>{"currency"=>"usd", "price"=>"100000"}},
"features_attributes"=> {"0"=>{"id"=>"6"}, "1"=>{"id"=>"4"}}
}}
}
</code></pre>
<p>In a perfect world, clicking submit triggers Car.create(params[:car]) and the car auto-magically creates not only itself, but also all of its makes and associations in one fell swoop.</p>
<p>In reality, the car cannot be created for a number of reasons:</p>
<pre><code>- Car and Make cannot be saved simultaneously because:
- Car is not valid because there's no make_id
- Make is not valid because there's no car_id
- Pricing is not valid because there's no make_id
- Make doesn't know what to do with the features hash
- Last feature selection over-writes the first selection
</code></pre>
<p><strong>BI-DIRECTIONAL ASSOCIATION: Parent-Child Validation for Nested Attributes</strong></p>
<p>What’s causing this error:</p>
<pre><code>ActiveRecord::RecordNotFound: Couldn't find [table] with ID=1 for ... with ID=
</code></pre>
<p>By default, Active Record (AR) creates separate in-memory copies of object data. You need to specify to Rails that there’s an inverse relationship between models to optimize object loading. If you don’t, AR creates objects with no association to each other. By specifying an inverse relationship, you’re telling AR that this Pricing belongs to this make, which belongs to this car. In addition to telling AR how to load the objects, you also need to set the :autosave feature to tell it that you want to save the members simultaneously. By default this is set to false.</p>
<p>You should also set validations down the line to require each object and specify any dependencies that need to be destroyed along with the parent (unless you want orphaned objects littering your database). Finally, you should add a variant hidden field if you forgot to include it earlier. This facilitates bi-directional relationships through the variant.</p>
<pre><code>class Car < ActiveRecord::Base
...
has_many :makes, inverse_of: :car, autosave: true, dependent: :destroy
validates :makes, presence: true
accepts_nested_attributes_for :makes, allow_destroy: :true, reject_if: :all_blank
...
end
class Make < ActiveRecord::Base
...
has_many :pricings, inverse_of: :make, autosave: true, dependent: :destroy
validates :car, presence: true
accepts_nested_attributes_for :features, :pricings, allow_destroy: true
...
end
class Pricing < ActiveRecord::Base
...
validates :make, presence: true
...
end
form.haml
= form_for @car do |f|
...
= f.fields_for :makes do |v|
= v.hidden_field :id
</code></pre>
<p><strong>ASSIGNING DROP-DOWN SELECTIONS TO NESTED ATTRIBUTE VIA JOIN TABLE</strong></p>
<p>This is a complex issue because not only are we trying to assign two sub-sets of the same collection (color and body-type) per make, but we’re attempting to do it through a join table.</p>
<p>What’s causing this error:</p>
<pre><code>[...]Controller# (ActionView::Template::Error) "Unknown primary key for [table] in model [...]"
</code></pre>
<p>The reason has_many :through associations allow you to do all sorts of things through join tables that you wouldn’t ordinarily be able to with HABTM (such as callbacks and validations), is because there’s a primary key to reference each joined record. If all you’re doing is joining tables and don’t need any special behavior, then it’s quickest to use HABTM and create a join table. That’s what we had, but we needed more.</p>
<p><strong>JOIN MODEL ASSOCIATION: Using has_many :through on a join table</strong></p>
<p>Since we are assigning features before a make_id exists, has_many associations to the join table need to be written so as to allow parent-child validation through bi-directional association. This is done following the same logic as employed in the previous example.</p>
<p>Note that Rails needs a primary key to manage has_many :through associations on join tables. Since my join table already exists, I run a migration to add a primary key to the join table and use first: true to make it the first column. Finally, I update my form builder and nested_attributes on my form to reflect the association to the join table.</p>
<p>Notice that the feature value for each drop-down is simultaneously built along with the join record. Instantiating one of each FeatureType when building the form: (a) takes care of the last drop-down over-writing the first, and (b) gives us a handy new join record to link through to each of our features.</p>
<pre><code>class AddIdToFeaturesMakes < ActiveRecord::Migration
def change
add_column :features_makes, :id, :primary_key, first: true
end
end
class Make < ActiveRecord::Base
...
has_many :features, through: :features_makes, source: :feature
has_many :features_makes, inverse_of: :make, autosave: true, dependent: :destroy
...
end
class FeaturesMakes < ActiveRecord::Base
attr_accessible :make_id, :feature_id, :feature_attributes
belongs_to :make
belongs_to :feature
validates :make, :feature, presence: true
end
class Feature < ActiveRecord::Base
...
has_many :makes, through: :features_makes
has_many :features_makes, inverse_of: :feature, autosave: true, dependent: :destroy
...
end
CarsController.rb
def new
@car = Car.new
make = @car.makes.build
make.pricings.build
2.times { |n| make.features_makes.build.build_feature(feature_type_id: n+1) }
end
form.haml
= v.fields_for :features_makes do |ov|
- features = ::Feature.where(feature_type_id: ov.object.feature.feature_type_id)
= ov.label :feature_id, "#{ov.object.feature.feature_type.name}"
= ov.collection_select "feature_id", features.order(:name), :id, :name, { prompt: "Select #{ov.object.feature.feature_type.name}" }, { class: "selectpicker select-block", data: {style: "btn-primary"} }
</code></pre>
<p><strong>Why do nested attributes work on #create but not on #update?</strong></p>
<p>If for any reason you decide to leave the primary key out, it is absolutely possible to assign feature values to a new make. Through trial and error – trying to get this to work – I discovered that you can use has_many :through without adding a primary key. You can tell Rails what primary key you want it to use, which can be an array (or composite):</p>
<pre><code>class Featuresmake < ActiveRecord::Base
attr_accessible :make_id, :feature_id, :feature_attributes
self.primary_key = [:make_id, :feature_id] <-- [ Setting primary key on a join table ]
...
end
</code></pre>
<p>But – while possible – what’s the point? It’s a waste of real estate AND you don’t get the full benefit that you get with a primary key. The biggest impediment is that you won’t be able to update drop-down selections. Instead you’ll end up with two new drop-downs anytime you try to update color or body-type for an existing make. The reason for this is that Rails will instantiate a new record when it can’t find a primary key. So, there you go.</p>
<p><strong>FINAL VERSION: THE CODE THAT AUTOMAGICALLY WORKS</strong></p>
<p> JOIN MODEL ASSOCIATION: Add primary key to join table</p>
<pre><code>class AddIdToFeaturesmakes < ActiveRecord::Migration
def change
add_column :features_makes, :id, :primary_key, first: true
end
end
</code></pre>
<p> FORM BUILDER: Build instances for desired associations</p>
<pre><code>CarsController.new
def new
@car = Car.new
make = @car.makes.build
make.pricings.build
2.times { |n| make.features_makes.build.build_feature(feature_type_id: n+1) }
end
</code></pre>
<p> BI-DIRECTIONAL ASSOCIATION: Specify parent-child relationship</p>
<pre><code>class Car < ActiveRecord::Base
attr_accessible :model, :description, :model_code, :makes_attributes
has_many :makes, inverse_of: :car, autosave: true, dependent: :destroy
has_many :feature_types, through: :features
has_many :features, through: :makes
has_many :pricings, through: :makes
validates :makes, presence: true <--[ Validates presence of inverse_of object ]
accepts_nested_attributes_for :makes, allow_destroy: :true, reject_if: :all_blank
(...)
end
class Make < ActiveRecord::Base
attr_accessible :name, :car_id, :model_code, :pricings_attributes, :features_attributes
belongs_to :car, touch: true
has_many :pricings, inverse_of: :make, autosave: true, dependent: :destroy
has_many :features_makes, inverse_of: :make, autosave: true, dependent: :destroy
has_many :images, through: :features
has_many :feature_types, through: :features
has_many :features, through: :features_makes, source: :feature ### replaces habtm
validates :car, presence: true <--[ Validates presence of inverse_of object ]
accepts_nested_attributes_for :features_makes, :pricings, allow_destroy: true
(...)
end
class Pricing < ActiveRecord::Base
attr_accessible :make_id, :currency, :price
belongs_to :make, touch: true
validates :make, presence: true <--[ Validates presence of inverse_of object ]
validates :currency, presence: true
(...)
end
class FeatureType < ActiveRecord::Base
attr_accessible :name
has_many :features, dependent: :destroy
validates :name, presence: true
end
class Feature < ActiveRecord::Base
attr_accessible :name, :presentation, :feature_type_id
belongs_to :feature_type
has_many :features_makes, inverse_of: :feature, autosave: true, dependent: :destroy
has_many :makes, through: :features_makes
validates :name, presence: true
(...)
end
class FeaturesMakes < ActiveRecord::Base
attr_accessible :make_id, :feature_id, :feature_attributes
belongs_to :make
belongs_to :feature
validates :make, :feature, presence: true <--[ Validates presence of inverse_of object ]
end
</code></pre>
<p> INPUT FORM</p>
<pre><code>form.haml
= form_for @car do |f|
= f.label :car_model
= f.text_field :model
= f.label :model_code
= f.text_field :model_code
= f.fields_for :makes do |v|
= v.hidden_field :id
= v.label :vin
= v.text_field :vin
= v.fields_for :features_makes do |ov|
- features = ::Feature.where(feature_type_id: ov.object.feature.feature_type_id)
= ov.label :feature_id, "#{ov.object.feature.feature_type.name}"
= ov.collection_select "feature_id", features.order(:name), :id, :name, { prompt: "Select #{ov.object.feature.feature_type.name}" }, { class: "selectpicker select-block", data: {style: "btn-primary"} }
= v.fields_for :pricings do |p|
= p.label :price
= p.select :currency, Pricing.all.collect{ |p| [currency_symbol(p.currency), p.currency] }, { include_blank: "Currency" }, { class: "selectpicker", data: {style: "btn-primary currency-dropdown"} }
= p.text_field :price
</code></pre>
<p> PARAMS</p>
<pre><code>"car"=>{"name"=>"delano", "model_code"=>"dx",
"makes_attributes"=>{"0"=>{"vin"=>"vin98765",
"features_makes_attributes"=>{"0"=>{"feature_id"=>"6"}, "1"=>{"feature_id"=>"4"}},
"pricings_attributes"=>{"0"=>{"currency"=>"usd", "price"=>"100000"}}}}}
</code></pre>
<p>Sources:<br>
<a href="http://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through">http://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through</a>
<a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html</a>
<a href="http://www.sitepoint.com/complex-rails-forms-with-nested-attributes">http://www.sitepoint.com/complex-rails-forms-with-nested-attributes</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Ruby & Me: Include vs Other Includes]]></title>
<link href="http://laptite.github.io/blog/2013/09/03/include-includes-include-included-include/"/>
<updated>2013-09-03T23:01:00-04:00</updated>
<id>http://laptite.github.io/blog/2013/09/03/include-includes-include-included-include</id>
<content type="html"><![CDATA[<p><img class="left" src="http://laptite.github.io/images/way.jpg" width="298" height="235" title="[Pixel Img]" >
The beautiful thing about ruby is that it wants to be expressive and legible – it aims to mimic human language. Unfortunately human language is not that consistent. Words are not used the same way all the time because usage is often driven by context. The good news is that Ruby makes some allowances for this and provides different ways (aka methods) to help us be more explicit in our programming.</p>
<p>For example, ‘map’ is a method that performs the same job as ‘collect’, and there are times when you might find it preferable to specify that values are being collected from a block vs. when they are being mapped. The truth is though that ‘map’ has been historically used in functional programming, and might sometimes be used to please the habitual self over semantic purpose.</p>
<p>So there we have it. Po-TAY-toh Po-TAH-toh… but this is America and we say Po-TAY-toh! Seriously though, it’s best to use whatever language helps you and others easily read your code. But what happens when terms are the same, or overlap between Ruby and Rails?</p>
<p>I was recently asked to explain what <strong>include</strong> does in Rails, and the first thing that came to mind was the ruby method include?(obj) – a boolean method that returns true if the given object is present in self:</p>
<pre><code>grades = ["A", "C+", "B-"]
grades.include?("A") #=> true
grades.include?("F") #=> false
</code></pre>
<p>But ‘include?’‘ is a ruby method, and the question specified Rails. Hm. Well, I’ve used ‘include’ in Ruby to mix in module instance methods, but I simply haven’t had to use it in Rails. So later in the day I did what any self-respecting nerd would do, and looked its usage up in RailsGuide. I also asked my classmates and instructor the same question. Sample responses included: like where in Rails? in a mixin? a module? in eager loading? what do you mean?</p>
<p>As Tony the Tiger says: grrreat.</p>
<p>This post briefly explores the various uses of a similarly named method between Ruby (1.9.3) and Rails (3.2.13), using the variations of “include” as an example. The include method is not by any means the most exciting one, but it is one that is commonly used in different contexts.</p>
<p>In Ruby, many methods such as <strong>include?</strong> can be similarly applied to various contexts with slight variations:</p>
<pre><code>array.include?(true_if_this_object_is_present_in_array)
[1,2,3,4].include?(2) #=> true
[1,2,3,4].include?(5) #=> false
hash.include?(true_if_this_key_is_present_in_hash)
{:name => "Bob", :age => 25}.include?(:name) #=> true
{:name => "Bob", :age => 25}.include?(:gender) #=> false
"string".include?(true_if_this_string_or_character_is_present_in_string)
"hello".include?("el") #=> true
"hello".include?("pi") #=> false
(range "this".."that").include?(true_if_this_object_is_an_element_of_range)
(34..76).include?(38) #=> true
(34..76).include?(14) #=> false
environment_variable.include?(true_if_there_is_an_environment_variable_with_this_name)
PATH = "/Users/LittleApple/.rvm/gems/ruby-1.9.3-p429/bin"
ENV['PATH'].include?('bin') #=> true
ENV['PATH'].include?('BigApple') #=> false
module.include?(true_if_this_module_is_present_in_the_module_or_one_of_the_module's_ancestors)
module Granny
end
class Mama
include Granny
end
class Baby < Mama
end
Mama.include?(Granny) #=> true
Baby.include?(Granny) #=> true
Granny.include?(Granny) #=> false
</code></pre>
<p>There are Public Class methods, Private Class methods, Module methods, Enumerable methods, and so on. This leads to some additional variations on the usage of ‘include’ in Ruby:</p>
<pre><code>Module.include(module, ...)
#=> used to mix module constants, methods, and variables into the Module or class instances.
Module.included_modules
#=> returns an array list of modules that are included in Module.
Module.included(other_module)
Module Hello
def Hello.included(other_module)
(...)
end
end
Module
include Hello
end
#=> callback is invoked when the (receiver) is included in another module or class.
</code></pre>
<p>Moving onto Rails.</p>
<p>ActiveRecord in Rails is an object relational mapping system that lets you retrieve objects from your database using queries that execute as SQL, without actually having to write raw SQL. Includes is one of the many finder methods available in ActiveRecord that helps minimize the number of queries run against your database.</p>
<p>With the includes method, all associations can be specified in advance by passing arguments. This act of preloading is also known as eager loading, and is how ActiveRecord tackles N+1 – a problem that occurs with associations. When you load the parent object and start loading one child or association at a time, you end up having to run N+1 queries. Pre-loading associations minimizes the number of queries by way of its specificity:</p>
<pre><code>School.includes("students").where(first_name: 'Ryan', students: {status: 'accepted'}).count
</code></pre>
<p>The includes method can come in handy when you want to display the same information in multiple views but don’t want to bog down performance by repeatedly running the same query. To provide even more flexibility, Rails has an :include option for those times that a different finder method might be better suited to your query, but you still want to eager load associations.</p>
<p>Since Rails runs on the Ruby programming language, the various ‘include’ ruby class and module methods can be used to respectively drive object behavior or refer to modules, mix-ins or concerns.</p>
<p>So there you have it. The many wonderful uses of the term ‘include’.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Ruby & Me: Converting Roman Numerals to Arabic]]></title>
<link href="http://laptite.github.io/blog/2013/08/24/numerals/"/>
<updated>2013-08-24T11:45:00-04:00</updated>
<id>http://laptite.github.io/blog/2013/08/24/numerals</id>
<content type="html"><![CDATA[<p><img class="left" src="http://laptite.github.io/images/FS_grad.jpg" width="256" height="192" title="[Pixel Img]" ></p>
<p>Yesterday was a big day. I graduated from the FlatironSchool 12-week program (yay! applause).</p>
<p>It’s been an intense race to pack in as much knowledge as possible in a short time-frame. As a parting gift, we were given an appropriate signifier that we are now versatile web application developers: a swiss army knife.</p>
<p>In recent weeks, it’s been difficult not to notice time ticking away against our task list: <br>
– Absorb knowledge<br>
– Complete web application for ScienceFair<br>
– Absorb knowledge<br>
– Practice ruby problems in preparation for Interviews<br>
– Absorb knowledge<br>
– Ask for 11th-hour lectures to make sure we understand topic xyz<br>
– Absorb knowledge<br></p>
<p>In the end, as a coping mechanism, my peers and I decided that it was best to look at graduation not as the end of the road but as a new beginning. I’m happy to say that I’ve made it through the program. I’m also happy to report that I’ve largely absorbed most of my newfound knowledge.</p>
<p>The harrowing schedule has made it difficult for many of us to keep up with our blogs, but I’m ready and raring to go. So, I’ve decided to start a new segment called ‘Ruby & Me’ to a) force me to review my notes, b) test my understanding of basic concepts, and c) document my thought process as I work through problems. The immediate result may not be pretty, but this is what the result looks like short of refactoring. In the words of our Dean, Avi Flombaum: Make it work – Make it right – Make it fast. Then and only then: refactor.</p>
<p><strong>Problem: Convert roman numerals to arabic</strong></p>
<p>I’ve learned the hard way that it’s paralyzing to look too far ahead. Small successes are productive and feel great. So, I start with the following pseudo-code:</p>
<pre><code># Convert roman numerals to arabic:
# I, V, X, L, C, D, M
# NumTranslator.new("V") return arabic numeral value 5
</code></pre>
<p>My first goal is to create a class in which I can instantiate a new object and successfully pass my roman numeral between methods. By the way, I’ve also learned the hard way that too much coding without testing is a recipe for disaster. The last thing I want is to get bogged down debugging my code, but I’ve done it before… a lot. So, let’s see how this works out:</p>
<pre><code>class NumTranslator
def initialize(roman_numeral)
@numeral = roman_numeral
end
def translator
@numeral = "V"
end
end
puts NumTranslator.new("V").translator
return value #=> V
</code></pre>
<p>My first test passes. Awesome! Now I want to setup a simple translator that deals with all single-digit roman numerals:</p>
<pre><code>def translator
case @numeral
when "I" then 1
when "V" then 5
when "X" then 10
when "L" then 50
when "C" then 100
when "D" then 500
when "M" then 1000
else
puts "Please enter a roman numeral"
end
end
puts NumTranslator.new("V").translator
return value #=> 5
</code></pre>
<p>Second test passes. Cool. Now I want to make sure that my full translation table works so I modify my call to accept user input:</p>
<pre><code>puts "Enter roman numeral to convert to arabic numeral: "
print NumTranslator.new(gets.chomp).translator
</code></pre>
<p>At this point I realize that I should probably help guard my entries from my lightning fingers.</p>
<pre><code>def initialize(roman_numeral)
@numeral = roman_numeral.upcase
end
</code></pre>
<p>Sweet, now I can mistakenly enter a lowercase letter and it will automatically be upcased.</p>
<p>Now comes the tough part. Translation is easy at a single digit level, but if I enter a multiple digit roman numeral, I get the following response: “Please enter a roman numeral”. It’s time to alter my method so that I can deal with multiple letter roman numerals. I know that adjacent numerals sum to a total value e.g. “III” = 3. I also know that there are exceptions but I choose to ignore those for now.</p>
<p>Since each digit within a roman numeral translates to a value, I decide to update my translator method to split the input string and see if I can get “III” to return 3. I decide to use the collect method because I want to add each letter to an array. This will allow me to iterate through and add up all the values.</p>
<pre><code>def translator
@numeral = @numeral.split('').collect do |letter|
case letter
when "I" then 1
when "V" then 5
when "X" then 10
when "L" then 50
when "C" then 100
when "D" then 500
when "M" then 1000
else
puts "Please enter a roman numeral"
end
end
@numeral.inject(:+)
end
(...)
gets.chomp #=> "III"
return value #=> 3
</code></pre>
<p>Excellent! It works. It’s time to get happy:</p>
<pre><code>gets.chomp #=> "XI"
return value #=> 11
</code></pre>
<p>Yippee!</p>
<pre><code>gets.chomp #=> "LV"
return value #=> 55
</code></pre>
<p>Hoorah!</p>
<pre><code>gets.chomp #=> "XX"
return value #=> 20
</code></pre>
<p>Woopdeedoo!</p>
<pre><code>gets.chomp #=> "IV"
return value #=> 6
</code></pre>
<p>Sigh. Ok, enough prolonging the inevitable. Good thing I’ve learned to manipulate my expectations: Yay! Yay! Yay! Waaaah!</p>
<p>Time for more pseudo-code:</p>
<pre><code># Translator method assumes each single-digit character is unique
# It splits and translates each character into a number, and adds it to an array
# Exceptions must be converted to a single-digit string and added back to the roman numeral
# Exceptions: "IV" = "4" / "IX" = "9" / "XL" = "F" (forty) / "XC" = "N" (ninety)
</code></pre>
<p>The translator method is already crazy long, so it seems appropriate to create a separate method to detect exceptions. I decide to start with “IV” and “IX” to keep things simple.</p>
<pre><code>def detect_exception
if @numeral.include?("IV")
@numeral = @numeral.split("IV").push("4").inject(:+)
elsif @numeral.include?("IX")
@numeral = @numeral.split("IX").push("9").inject(:+)
else
@numeral
end
end
</code></pre>
<p>Let’s test this real quick:</p>
<pre><code>puts "Enter roman numeral to convert to arabic numeral: "
print NumTranslator.new(gets.chomp).detect_exception
gets.chomp #=> "XXIV"
return value #=> "XX4"
</code></pre>
<p>Cool, that works.</p>
<pre><code>puts "Enter roman numeral to convert to arabic numeral: "
print NumTranslator.new(gets.chomp).detect_exception
gets.chomp #=> "LXIX"
return value #=> "LX9"
</code></pre>
<p>Awesome. Now I need to figure out how to expand this to include the other exceptions. I find that I can’t just add these as additional elsif statements because there seems to be confusion with overlapping numerals, so I try to nest them:</p>
<pre><code>def detect_exception
if @numeral.include?("IV")
@numeral = @numeral.split("IV").push("4").inject(:+)
if @numeral.include?("XL")
@numeral = @numeral.split("XL").push("F").inject(:+)
elsif @numeral.include?("XC")
@numeral = @numeral.split("XC").push("N").inject(:+)
else
@numeral
end
elsif @numeral.include?("IX")
@numeral = @numeral.split("IX").push("9").inject(:+)
if @numeral.include?("XL")
@numeral = @numeral.split("XL").push("F").inject(:+)
elsif @numeral.include?("XC")
@numeral = @numeral.split("XC").push("N").inject(:+)
else
@numeral
end
elsif @numeral.include?("XL")
@numeral = @numeral.split("XL").push("F").inject(:+)
elsif @numeral.include?("XC")
@numeral = @numeral.split("XC").push("N").inject(:+)
else
@numeral
end
end
</code></pre>
<p>It’s butt ugly, but it works. Only a single line needs to be added to the translator method to call this new method to detect exceptions:</p>
<pre><code>def translator
@numeral = self.detect_exception
@numeral = @numeral.split('').collect do |letter|
(...)
end
end
</code></pre>
<p>Excellent. Now we just need to test that everything works:</p>
<pre><code>puts "Enter roman numeral to convert to arabic numeral: "
print NumTranslator.new(gets.chomp).translator
gets.chomp #=> "MMMDXCLXIX"
return value #=> "3669"
</code></pre>
<p>I can hardly contain myself. I run about fifteen different number combinations to make sure that everything works. Sweet! Hopefully, I didn’t leave out any exceptions.</p>
<p>UPDATE: Special thanks to Ed for pointing out a problem with my nested conditions under detect_exception, and for suggesting saving on real estate by using a hashmap instead of a case statement.</p>
<p>Here’s the final code:</p>
<pre><code>class NumTranslator
def initialize(roman_numeral)
@numeral = roman_numeral.upcase
end
def detect_exception
if @numeral.include?("IV")
@numeral = @numeral.split("IV").push("4").inject(:+)
if @numeral.include?("XL")
@numeral = @numeral.split("XL").push("F").inject(:+)
elsif @numeral.include?("XC")
@numeral = @numeral.split("XC").push("N").inject(:+)
else
@numeral
end
elsif @numeral.include?("IX")
@numeral = @numeral.split("IX").push("9").inject(:+)
if @numeral.include?("XL")
@numeral = @numeral.split("XL").push("F").inject(:+)
elsif @numeral.include?("XC")
@numeral = @numeral.split("XC").push("N").inject(:+)
else
@numeral
end
elsif @numeral.include?("XL")
@numeral = @numeral.split("XL").push("F").inject(:+)
elsif @numeral.include?("XC")
@numeral = @numeral.split("XC").push("N").inject(:+)
else
@numeral
end
end
def translator
hashmap = {"I"=>1,"4"=>4,"V"=>5,"9"=>9,"X"=>10,"F"=>40,"L"=>50,"N"=>90,"C"=>100,"D"=>500,"M"=>1000}
@numeral = self.detect_exception
@numeral = @numeral.split('').collect do |numeral|
hashmap[numeral]
end
@numeral.inject(:+)
end
end
puts "Enter roman numeral to convert to arabic numeral: "
puts NumTranslator.new(gets.chomp).translator
</code></pre>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Rails Deployment to Digital Ocean Ubuntu via Capistrano]]></title>
<link href="http://laptite.github.io/blog/2013/08/13/rails-deployment-to-digital-ocean-ubuntu-via-capistrano/"/>
<updated>2013-08-13T18:58:00-04:00</updated>
<id>http://laptite.github.io/blog/2013/08/13/rails-deployment-to-digital-ocean-ubuntu-via-capistrano</id>
<content type="html"><![CDATA[<p><img class="left" src="http://laptite.github.io/images/holy-cow.png" width="194" height="187" title="[Pixel Img]" >
We are in the 11th week of Flatiron School. It has been a whirlwind of learning, and it feels great to finally deploy our first Rails app today. Deployment was painful, so I’m posting this walk-through in hopes that it might save someone some deployment time. My team started this process with high hopes of crushing it in 45 minutes. Instead we ran into all sorts of annoying problems to debug for the next 3 hours (ugh!).</p>
<p>Some hurdles that were overcome and that are discussed in this post are: asset pre-compilation errors, Sunspot solr gem headaches, Database not seeding, and image_tag ‘isn’t precompiled’ error message.</p>
<p>This walk-through builds on an excellent tutorial by guest speaker <a href="https://github.com/spikegrobstein/flatironschool-deployment_lecture/blob/master/lecture.md">Spike Grobstein</a>. My intent is not to plagiarize any of that work, but instead to add in some steps that were needed for me to get our application deployed and working.</p>
<p>We were provided the following information by our TA Blake: (a) Digital Ocean droplet to serve our app, and (b) credentials (IP Address / Username: root / Password). He then sent us packing to work on our own because he’s a meanie (just kidding). One skill you’ll sharpen while in this program is your ability to walk in the dark. Not to worry though. There is light to be found for those who persevere. Anyway, I digress.</p>
<p>Before you start anything you’ll want to make sure that all working git branches are up-to-date, reviewed, merged and pushed up to master accordingly. This may not be a big concern if you’re working solo, but a critical step for our team of four sharing workload.</p>
<p>Open terminal, cd into your Rails app and let’s get ready to rumble.</p>
<p><strong>PHASE 1: Connect to your spankin’ new server</strong><br>
<em>In the code base a hashtag # represents a comment, so don’t type it – ok?</em></p>
<p>Connect to your new server via SSH:</p>
<pre><code>ssh [email protected] # plug your IP address in the place of the X's
</code></pre>
<p>Answer questions politely:</p>
<pre><code>Are you sure you want to continue connecting (yes/no)? # type yes and press enter
</code></pre>
<p>Root is a super user, so you should really create a user for the app since it brings bad mojo on top of 7 years of bad luck to do otherwise – it’s bad practice, so don’t be that person:</p>
<pre><code>useradd -s /bin/bash -G sudo -m outlinked # note: our app is called "outlinked", enter username.
</code></pre>
<p>Then you have to create a password for this user, but you won’t be entering the password right away. You’re going to tell the server which username you want to apply the password to:</p>
<pre><code>passwd outlinked
</code></pre>
<p>When prompted for a password, choose one and confirm it.</p>
<p>Groovy, now exit the server and connect as your alter-ego (aka your new username):</p>
<pre><code>ssh [email protected] # you know what to do with these X's now
</code></pre>
<p>Enter your password, and get ready to install the packages your app will need to run on your Ubuntu server.</p>
<p><strong>PHASE 2: Get your server ready for your awesome application</strong></p>
<p>Update your apt-repository and dependencies with this code:</p>
<pre><code>sudo apt-get update
</code></pre>
<p>Then upgrade all installed packages with this code:</p>
<pre><code>sudo apt-get upgrade
</code></pre>
<p>You’ll be shown a list of packages that need upgrading, and then asked to confirm. Be polite and answer in the affirmative:</p>
<pre><code>Y # press enter
</code></pre>
<p>Install build essential package:</p>
<pre><code>sudo apt-get install build-essential
</code></pre>
<p>Install Ruby packages:</p>
<pre><code>sudo apt-get install ruby1.9.3 sqlite3 libsqlite3-ruby1.9.1 libsqlite3-dev
</code></pre>
<p>Install Git:</p>
<pre><code>sudo apt-get install git
</code></pre>
<p>Install XML libraries (this one is for gems using the stated libs e.g. Nokogiri):</p>
<pre><code>sudo apt-get install libxml2-dev libxslt1-dev
</code></pre>
<p>Install Gem Bundler:</p>
<pre><code>sudo gem install bundler
</code></pre>
<p>Install SQLite3:</p>
<pre><code>sudo gem install sqlite3
</code></pre>
<p>Install nodejs (avoids compile errors with the Javascript framework):</p>
<pre><code>sudo apt-get install nodejs
</code></pre>
<p>Install Capistrano, which you’ll be using to deploy your app:</p>
<pre><code>gem install capistrano
</code></pre>
<p>Install Passenger, which is an nginx module and your Rack webserver:</p>
<pre><code>sudo gem install passenger
sudo passenger-install-nginx-module
sudo apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev
sudo nano /opt/nginx/conf/nginx.conf # configure nginx
# scroll through the nano file looking for #user nobody;
# uncomment that, and change nobody; to www-data;
# keep scrolling and find the location block now
# Replace the whole block:
location / {
root html;
index index.html index.htm;
}
# with:
root /home/USERNAME/APPNAME/current/public;
passenger_enabled on;
# Exit the editor (Ctrl + X)
sudo ln -s /opt/nginx/sbin/nginx /usr/local/sbin/
sudo nginx # Start nginx
(To stop ngingx: sudo nginx -s stop)
</code></pre>
<p>Congratulations, you’re well on your way to making your app dreams a reality!</p>
<p><strong>PHASE 3: Application config files & Deployment</strong></p>
<p>Go ahead an open a new terminal tab so that you can cd back into your application’s repo on your local drive. All Capistrano needs for you to type in the command line to start the deployment process is:</p>
<pre><code>capify .
</code></pre>
<p>Excellent, let’s update a few settings in our application to enable deployment.</p>
<p>Navigate to the config directory, open deploy.rb and edit it as follows:</p>
<pre><code>set :application, "outlinked" # Set application to USERNAME
# Enter your github ssh handle (the https one didn't work for us)
set :repository, "[email protected]:flatiron-school/library-redux.git"
set :user, 'outlinked' # Set user to USERNAME
set :deploy_to, "/home/outlinked/outlinked" # Set deploy_to to /home/USERNAME/APPLICATION
set :user_sudo, false
set :rails_env, "production" # sets your server environment to Production mode
set :scm, :git # sets version control
default_run_options[:pty] = true
role :web, "XXX.XXX.XXX.XXX" # Your HTTP server, Apache/etc
role :app, "XXX.XXX.XXX.XXX" # We made the app role the same as our `Web` server
# We made the database role the same as our our `Web` server
role :db, "XXX.XXX.XXX.XXX", :primary => true # This is where Rails migrations will run
# We're using Passenger mod_rails, so we uncommented this:
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
end
end
</code></pre>
<p>Cool. We’re getting closer to the promise land.</p>
<p>Now go into your Capfile in your application, and uncomment the following:</p>
<pre><code>load 'deploy/assets' # this will precompile your assets every time you deploy
</code></pre>
<p>Oh yeah, don’t forget to add and commit your changes:</p>
<pre><code>git add .
git commit -am "edit deploy.rb"
git push
</code></pre>
<p>My team had to generate a deploy key because our application sits in a private Github repo. If this doesn’t apply to you, keep reading because one day you’ll need to do this. Switch your terminal tab so that you’re back into your server command line and enter this code:</p>
<pre><code>ssh-keygen -t rsa -b 4096 # Leave all requests for passwords blank!
# Hit enter until no more questions are asked.
</code></pre>
<p>Now you’ll need to open your browser and navigate to your repo in Github. Click on ‘Settings’ and then on ‘Deploy key’, and then finally on ‘Add deploy key’. Enter your github password, and go back to your server terminal to retrieve the key to copy/paste into github:</p>
<pre><code>cat ~/.ssh/id_rsa.pub # copy everything from the ssh-rsa to username@host.
# I first opened this file with nano, which only showed a partial key.
# Don't enter a title for the key. Github will use your username.
</code></pre>
<p>Awesome. Now jump back into your other terminal window where you’re in your local repo and run:</p>
<pre><code>cap deploy:setup
</code></pre>
<p>This connects you to your server, prompts you for your password and creates your directory structure on the server. When you cd into your USERNAME directory in your server, you’ll see three sub-directories: current, releases and shared. The current directory is the server equivalent of your application directory in your local directory.</p>
<p>I should mention that a log folder wasn’t immediately available in the current directory. This became an issue when I ran into a problem deploying and I couldn’t get to my production.log to review error messages. I got help from our TA Joe for this, and there was some light-fingered magic going on, so I don’t have exact code to post. My understanding is that the Production.log file in the current directory points via symlink to a log file in the shared directory, which did not exist. You can check this status by doing an <code>ls -l</code> and looking for line items in red.</p>
<p><strong>Guess what? It’s time to deploy – Heck yeah!</strong><br>
So you’ve done like a million steps already and we’re just deploying now. Pat yourself on the back and give yourself a high five.</p>
<p>All your changes are committed, right? Cool. You’re ready to deploy. From your local repo terminal, enter the magical Capistrano command while singing O Sole Mio:</p>
<pre><code>cap deploy
</code></pre>
<p>Open your browser, type in your IP address in the url bar <code><a href="http://">http://</a>[your IP]</code></p>
<p>Celebration time, right? Wrong. This is where error messages plagued me.</p>
<p><strong>Important note:</strong> you know all that stuff you have to do in your local Rails app like bundle install and all that jazz? That doesn’t magically happen in the server. All the routines required in your local drive, you’re going to have to repeat here.</p>
<p><strong>Problem no. 1 – Sunspot gem</strong>
We installed this gem so that users can run searches in our app. This gem was a pain in the ass from day one. It runs a Java server and it’s been a challenge to update every time we changed seats during pair programming. A good rule of thumb is that whatever you’ve had to do to fix problems in your local drive, you’ll likely have to repeat in your server environment. For one you have to install a Java environment:</p>
<pre><code>sudo apt-get install openjdk-6-jdk
</code></pre>
<p>Next you’ll need to cd into your app USERNAME/current and do all that other junk that fixes rake errors:</p>
<pre><code>rails g sunspot_rails:install RAILS_ENV='production'