tag:blogger.com,1999:blog-90755254807130278282024-08-30T18:34:23.798+01:00Orban Botond's BlogA blog about software development.Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comBlogger36125tag:blogger.com,1999:blog-9075525480713027828.post-57405967926462184022017-05-08T18:58:00.001+01:002017-05-08T18:58:22.169+01:00RSpec before hooksI assume that everybody is familiar with the famous <a href="https://www.ruby-lang.org/en/">ruby</a> testing framework <a href="http://rspec.info/">rspec</a>.<br />
<br />
Let's say that we have this spec structure:<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">require 'rails_helper'</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">describe 'search_index' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> context 'entity A' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> let(!setup_a){ create :a}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> before do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.delete! rescue nil</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.create</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.import</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> context 'entity B' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> let(!setup_b){ create :b}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> before do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.delete! rescue nil</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.create</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.import</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">end</span></div>
<div>
<br /></div>
<div>
As we see the before blocks are the same. Some might be tempted to move to a common place.</div>
<div>
Let's do this:</div>
<div>
<br /></div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">require 'rails_helper'</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">describe 'search_index' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> before do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.delete! rescue nil</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.create</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.import</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> context 'entity A' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> context 'entity B' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">end</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
At this moment we experience a surprise the the tests are not passing...<br />
If we take a closer look we can easily identify the spot.<br />
The import in the before blocks was run before the setup_b and setup_a was called. So there was nothing to import therefore our expectations run on an empty index.<br />
<br />
In this particular case it was much more beneficial to leave the common code where it is.<br />
But wait... Aren't we dealing with a ruby code? Yes of corse. This means that the content of the before can be extracted into a function then called before any context.<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">require 'rails_helper'</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">def reinitialise_the_index</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.delete! rescue nil</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.create</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> SearchIndex.import</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">describe 'search_index' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> context 'entity A' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> let(!setup_a){ create :a}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> before do</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> reinitialise_the_index</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> context 'entity B' do</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> let(!setup_b){ create :b}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> before do</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> reinitialise_the_index</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> end</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">end</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
Much simpler isn't it?<br />
Happy coding :)<br />
<br /></div>
<div>
<br /></div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-40008805399756848912015-06-05T14:55:00.001+01:002015-06-05T14:55:24.752+01:00Available at ToptalHi,<br />
<br />
I would like to announce that I joined <a href="http://toptal.com/">total</a>.<br />
<br />
I am available for hire at:<br />
<a href="http://www.toptal.com/ruby-on-rails#efficient-professional-web-development">http://www.toptal.com/ruby-on-rails#efficient-professional-web-development</a><br />
<br />
<br />Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-24766914327742128412015-05-06T17:32:00.001+01:002015-05-06T17:32:18.112+01:00The benefit of the squeel gem over standard active record <h3>
The benefit of the <a href="https://github.com/activerecord-hackery/squeel">squeel gem</a> over the standard active record</h3>
Let's try this query:<br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;">Field.where(id: 10).unscope(where: :id)</span><br />
<div>
It results in the below SQL:</div>
<div>
<div style="background-color: black; color: #00f900; font-family: 'Andale Mono';">
"SELECT \"fields\".* FROM \"fields\"</div>
</div>
<br />
Let try to unscope something a bit more difficult:<br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;">Field.where('id > 10').unscope(where: :id)</span><br />
In this case the unscoping didn't work as we intended:<br />
<div style="background-color: black; color: #00f900; font-family: 'Andale Mono';">
SELECT \"fields\".* FROM \"fields\" WHERE (id > 10)</div>
<br />
<br />
Let's try now by using <a href="https://github.com/activerecord-hackery/squeel">squeel gem</a>:<br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;">Field.where{id > 10}.unscope(where: :id).to_sql</span><br />
Now the unstopping works just as we intended:<br />
<div style="background-color: black; color: #00f900; font-family: 'Andale Mono';">
"SELECT \"fields\".* FROM \"fields\""</div>
<br />
Enjoy :)Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-17011804873886028182015-01-16T09:41:00.002+00:002015-01-16T09:51:07.644+00:00<h3>
Testing the ActiveRecord models using Rspec</h3>
Usually the skeleton of my models spec looks like this:<br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">require 'spec_helper'</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">describe Car do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> </span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'class hierarchy' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> <a href="#class_hierarchy">#Here comes the class hierarchy specification</a><span id="goog_641505213"></span><span id="goog_641505214"></span><a href="https://www.blogger.com/"></a></span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'fields' do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> <a href="#fields">#test the enumeration of used fields</a></span></div>
<div>
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span></span></div>
</div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'assotiations' do</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> <a href="#assotiations">#Here comes the enumeration of associations</a></span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'validations' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> <a href="#validations">#Here comes the validation of models</a></span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span></span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'callbacks' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> <a href="#callbacks">#Specs for callbacks</a></span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span><br />
<div>
<span style="background-color: #d9ead3;"><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;"> </span></span></div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'methods' do</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span style="background-color: #d9ead3;"> <a href="#methods">#specs for methods</a></span></span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span><br />
<br />
<b><a name="class_hierarchy">Testing the class hierarchy:</a></b><br />
Since the model could include modules which affect the functionality I consider it necessary to assert on them.<br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">describe Car do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'class hierarchy' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify {expect(subject.class).to be < ActiveRecord::Base}</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify{expect(subject).to be_kind_of(Elasticsearch::Model)}</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify{expect(subject).to be_kind_of(Elasticsearch::Model::Callbacks)}</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace;"><span style="background-color: #d9ead3;">end</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span style="background-color: #d9ead3;"><br /></span></span></div>
<b><a name="fields">Testing the fields:</a></b><br />
I often encountered errors when the model was expected to have a field and that field was missing. So I always assert on the used fields:<br />
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">describe Car do</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> context 'fields' do</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> specify {expect(subject).to respond_to(:name)}</span><br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<div>
<div style="margin: 0px;">
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify {expect(subject).to respond_to(:filter)}</span></div>
</div>
</div>
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;">end</span></div>
<div>
<br />
<b><a name="assotiations">Testing the associations:</a></b><br />
I test the presence of the correct associations using the '<a href="https://github.com/thoughtbot/shoulda-matchers">shoulda-matchers</a>' gem.<br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; margin: 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<br /></div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">describe Car do</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> context 'assotiations' do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify { expect(subject).to belong_to(:user) }</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify { expect(subject).to belong_to(:manufacturer) }</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span>
<b><a name="validations">Testing the validations:</a></b><br />
I usually test the validations of the fields using the '<a href="https://github.com/thoughtbot/shoulda-matchers">shoulda-matchers</a>' gem.<br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"><br /></span>
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;">describe Car do</span><br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<div style="margin: 0px;">
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> context 'validations' do</span></div>
</div>
</div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify { expect(subject).to validate_uniqueness_of(:filter).scoped_to(:manufacturer_id)}</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span>
<b><a name="callbacks">Testing the callbacks:</a></b><br />
My callback test usually are like this:<br />
<div>
<br /></div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">describe Car do</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> context 'validations' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'before_destroy' do</span><br />
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> specify 'call destroy like callback'do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expect(equipment).to receive(:destroy_likes)</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> equipment.destroy</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> end</span></span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span></div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<div style="margin: 0px;">
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div style="margin: 0px;">
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span></div>
<div style="margin: 0px;">
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span></div>
<b><a name="methods">Testing the methods:</a></b><br />
Usually I test the methods by calling it the asserting that all the necessary changes are made. I do this for all the execution paths.<br />
<div>
<br /></div>
</div>
</div>
<div>
</div>
</div>
<br /></div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-53625651925216834252015-01-15T15:06:00.000+00:002015-01-15T15:06:47.550+00:00<h3>
Api Development in Ruby On Rails</h3>
Recently I wrote a series of blog posts about the best practices of API development in RoR and how to develop API in general.<br />
<br />
Let me summarize those posts:<br />
<br />
<ul>
<li><a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails-i-met.html">General introduction</a></li>
<li><a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails.html">Presenting the Entities</a></li>
<li><a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails-nested.html">Performance Optimisation</a></li>
<li><a href="http://orbanbotond.blogspot.com/2015/01/specing-api-when-you-cover-your-api.html">Test your API from A-Z</a></li>
<li><a href="http://orbanbotond.blogspot.com/2015/01/api-development-restfull-vs-facebook.html">Organising your routes in API</a></li>
<li><a href="http://orbanbotond.blogspot.com/2015/01/api-development-in-rails-error-path-in.html">Error Handling</a></li>
</ul>
<br />
Happy API development :)<br />
<br />Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-31944001820211169912015-01-15T14:55:00.001+00:002015-01-15T14:56:35.227+00:00<h3>
API development in Rails error path</h3>
<div>
<br /></div>
In my series of of <a href="http://orbanbotond.blogspot.ro/search/label/API">API development</a> I haven't covered the error handling.<br />
<br />
Let me share my experience with you about what I have learned about proper error handling.<br />
I call <a href="http://en.wikipedia.org/wiki/Exception_handling">error path</a> the case when the user can't achieve what he wants.<br />
<br />
This case can happen by the following reasons:<br />
<br />
<ul>
<li>The user calls an invalid url</li>
<li>The user addresses a missing resource</li>
<li>The user misses a mandatory parameter</li>
<li>The uses passes a wrong parameter</li>
<li>Some other internal error occurs</li>
<li>...</li>
</ul>
<br />
Since thousands of calls can be made daily or hourly against the API there is no way to stop the server and debug it. So all the information related to the erroneous call must be recorded. Basically all the information to reproduce the error must be recorded. I call this ApiCallAudit.<br />
<br />
Such an ApiCallAudit must contain:<br />
<br />
<ul>
<li>All the incoming parameters</li>
<li>The type of call (GET, POST, DELETE, ...)</li>
<li>cookies</li>
<li>backtrace</li>
<li>created_at</li>
</ul>
<div>
I added some additional fields to it for filtering purposes:</div>
<br />
<div>
<ul>
<li>level. It serves to quickly determine the possible source of errors. It could be parameter_error, unexpected_error, parameter_missing_error, entity_missing_error</li>
</ul>
</div>
<div>
<ul>
<li>status. It is a default error message.</li>
<li>code. An error code which uniquely identifies the error.</li>
</ul>
<div>
In the beginning the error code was missing from my design and the mobile clients were using the default error message. This has some disadvantages:</div>
</div>
<div>
<ul>
<li>The mobile <a href="http://en.wikipedia.org/wiki/User_interface">UI</a> is usually developed in a different codebase. And the server side error message modification is not possible. Specially if the same chunk of server side code is serving many applications.</li>
<li>-The language used on the mobile <a href="http://en.wikipedia.org/wiki/User_interface">UI</a> can vary. For example the <a href="http://en.wikipedia.org/wiki/User_experience">UX</a> developer can decide to use:</li>
</ul>
<ol><ol>
<li>"You haven't provided the group" - First person complaining style.</li>
<li>"The group is missing" - Passive objective style</li>
<li>"Please provide a group" - Proactive gentle style</li>
<li>"Select a group!" - Imperative style</li>
<li>...</li>
</ol>
</ol>
In order to formulate the sentences which reflects the application mood the mobile developer needs to interpret the returned error based on its error_code and formulate its own corresponding message.</div>
<div>
<br /></div>
<div>
Happy coding :)</div>
<div>
<br /></div>
<div>
<br /></div>
<br />Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-92160529535071613382015-01-15T13:42:00.001+00:002015-01-15T13:44:40.620+00:00<h3>
API Development Restfull vs Facebook style</h3>
<div>
<br /></div>
In the last months I developed mainly API's using <a href="https://github.com/intridea/grape">grape</a> gem and their <a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails-i-met.html">related gems</a>.<br />
<br />
In this blog post I will express my opinion how to organise the API which manages the entities.<br />
<br />
Generally speaking I like the <a href="http://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> concept and I organize my API to conform that way. For cases when there are no association among the entities this philosophy is good enough and clear.<br />
<br />
However I like the API to reflect the associations when we are dealing with <a href="http://guides.rubyonrails.org/association_basics.html#the-has-many-association">has_many</a> or <a href="http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association">has_and_belongs_to_many</a> associations. Just like in case of <a href="https://developers.facebook.com/docs/graph-api">Facebook Graph API</a>. In these cases I find that style more intuitive and I am following that style whenever I deal with associations.<br />
<br />
Examples:<br />
<br />
Lets suppose that cars and manufacturers can be reviewed.<br />
<br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">class Car < ActiveRecord::Base</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> has_many :reviews, as: :reviewable</span></span><br />
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"><br /></span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">class Manufacturer < ActiveRecord::Base</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> has_many :reviews, as: :reviewable</span></span></div>
<div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"><br /></span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">class Review < ActiveRecord::Base</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> belongs_to :user</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> belongs_to :reviewable, polymorphic: true</span></span></div>
</div>
<div>
<br /></div>
<div>
In this case the review API would be:</div>
<div>
<ul>
<li>creation of reviews:</li>
</ul>
</div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> POST /api/cars/{id}/<id>reviews</id></span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> POST /api/manufacturers/{id}/<id>reviews</id></span></span></div>
<div>
<ul>
<li>retrieval of reviews:</li>
</ul>
</div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> GET /api/cars/{id}/<id>reviews</id></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> GET /api/manufacturers/{id}<id>/reviews</id></span></span></div>
</div>
<div>
<ul>
<li>deletion of reviews:</li>
</ul>
</div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> DELETE /api/reviews/{id}</span></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
In the classic restful style the review creation would be:</div>
<div>
<br /></div>
<div>
<div>
<span style="background-color: #d0e0e3;"><span style="font-family: Courier New, Courier, monospace;"> params do</span></span></div>
<div>
<span style="background-color: #d0e0e3;"><span style="font-family: Courier New, Courier, monospace;"> requires :comment, type: String, desc: "The comment"</span></span></div>
<div>
<span style="background-color: #d0e0e3;"><span style="font-family: Courier New, Courier, monospace;"> requires :reviewable_id, type: Integer, desc: "The id of a reviewable entity"</span></span></div>
<div>
<div>
<span style="background-color: #d0e0e3;"><span style="font-family: Courier New, Courier, monospace;"> requires :reviewable_type, type: String, desc: "The type of a reviewable entity"</span></span></div>
</div>
<div>
<span style="background-color: #d0e0e3;"><span style="font-family: Courier New, Courier, monospace;"> end</span></span></div>
</div>
<div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> POST /api/reviews</span></span></div>
</div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"><br /></span></span></div>
<div>
Both solutions have pros and cons. The restful style is more DRY. The Facebook Graph Api style relieves more information about the associations ergo it is more intuitive and easier to use.</div>
<div>
<br /></div>
<div>
Happy coding :)</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-19126464952520621762015-01-02T16:36:00.000+00:002015-01-15T15:05:41.219+00:00<h3>
<a href="http://orbanbotond.blogspot.com/2015/01/specing-api-when-you-cover-your-api.html">Specing the API</a></h3>
<div>
<br /></div>
<div>
<b>When you cover your API with specs the first rule is to cover everything. </b><br />
Since the API can be called by third parties you need to be sure what is happening in every case. You must be able to reproduce every scenario any time and you must have the same results. :) Does it seems scientific? Well... Indeed it is.<br />
<br />
<div>
Let me show you some examples:</div>
<div>
-You might need to check that only the calls with developer key can access the API. In the opposite case the response is 401, it is still a JSON and maybe an audit log is created.</div>
<div>
-You might need to check that the user is authenticated. If not then the response is 401, it is still a JSON and again an audit log is created.</div>
<div>
-If some of the parameters are missing or wrong then the response is 400, it is still a JSON and an audit log is created by logging all the incoming parameters.</div>
<div>
-If everything is alright then the response is 200/201, the response is a JSON and the entities are massaged <i>as needed by that specific case</i>.</div>
<br />
<b>You also need to keep your specs DRY</b><br />
This will reduce maintenance effort of the test code and it will keep your specs more readable and you will have easy time to add new features and you will feel more happy and in control.<br />
<br />
As you can see the above steps like checking the response code, checking if the response format is JSON, checking that the audit log is created are repetitive tasks and can be <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>-ed with <a href="https://www.relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples">rspecs shared examples</a>. The only specific thing which changes from one api endpoint to another is "the entities are massaged <i>as needed by that specific case</i>".<br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">RSpec.shared_examples "returning 401" do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify "returns 401" do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> api_call params, developer_header</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expect(response.status).to eq(401)</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">RSpec.shared_examples "being JSON" do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> specify 'returns JSON' do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> api_call params, developer_header</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expect { JSON.parse(response.body) }.not_to raise_error</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> end</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">end</span></span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">...</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span>
Then in your specs you can use these shared examples in particular cases:<br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">context '/API/learn_by_playing/' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> def api_call *params</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> get "/api/learn_by_playing", *params</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> let(:api_key) { create :apikey }</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> let(:developer_header) { {'Authorization' => api_key.token} }</span></span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'GET' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> let(:required_params) do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> {</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> :first_name => 'Botond',</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> :last_name => "Orban"</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> let(:params) { required_params }</span><br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> it_behaves_like 'restricted for developers'</span><br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'wrong parameters' do</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> required_params.keys.each do |s|</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'when the #{s} parameter is blank' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> let(:params) { required_params.merge({s => ''}) }</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> </span><span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> it_behaves_like 'returning 400'</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> it_behaves_like 'being JSON'</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> it_behaves_like 'creating an audit log'</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'when the #{s} parameter is missing' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> let(:params) { required_params.except(s) }</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> </span><span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> it_behaves_like 'returning 400'</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> it_behaves_like 'being JSON'</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> it_behaves_like 'creating an audit log'</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'valid params' do</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify '...whatever you need to really assert for in this particular case...' do</span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> api_call params, d</span><span style="font-family: Courier New, Courier, monospace;">eveloper_header</span></span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expect ...</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span></div>
<div>
<br /></div>
<div>
As you can see from the above example using Rspec.shared examples a lot of repetitive lines from your Rspec can be thrown out.<br />
<br />
On one of my projects I managed to reduce the specs size by 20% and raise the visibility a lot :) I can't measure and therefore I can't express in numbers what "a lot" means but everybody was satisfied and pleased with the result.</div>
<div>
<br />
Professional DRY hacking ;)<br />
<br /></div>
</div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-72091131839849555252014-12-29T12:17:00.004+00:002014-12-29T12:21:29.910+00:00<h3>
<a href="http://orbanbotond.blogspot.com/2014/12/use-logger.html">Use logger.debug rather than puts!</a></h3>
It is often tempting to use puts in the ruby code to debug the informations.<br />
<br />
I like logging instead because:<br />
-allows the filtering capability<br />
-allows formatting abilities<br />
-with a little tweak it takes the same amount of effort than puts<br />
-it is an already invented and well tested system<br />
<br />
It is much better in short. So whenever is a logger system available I use that.<br />
<br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">logger.debug 'doing this and that'</span></span><br />
<br />
It seem to me too much to type at first, therefore I created a sublime <a href="https://gist.github.com/orbanbotond/3ebb0cb44c4b666567b2#file-logger">logger snippet</a> which accelerated typing. I created another one for those cases where I need to implicitly reference the Rails.logger.<br />
<div>
<br /></div>
<div>
;)</div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-58904487502973112522014-12-28T23:38:00.000+00:002014-12-28T23:39:37.257+00:00<h2>
<span style="font-size: large;"><a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails-nested.html">API Development in Ruby On Rails, Nested Entities</a></span></h2>
There are times when there is one-to-many association amongst the entities. And the mobile developer needs to show all of them in one screen. In these cases it is very easy to fell into the bad habit to execute n+1 queries from mobile side toward the server or from the server to the sql-server.<br />
<br />
Here are the entities:<br />
<br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">class DetailedGameEntity < Grape::Entity</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> ...</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expose :reviews, using: ReviewEntity do |game, options|</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> game.reviews</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> end</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">end</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"><br /></span></span>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">class ReviewPresenter < Grape::Entity</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expose :user_id, as: :owner_id</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expose :comment</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expose :rating</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"><br /></span></span>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> expose :image_urls do |review, options|</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> review.images.map{|image| image.image}</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> end</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">end</span></span><br />
<div>
<br /></div>
<div>
They are designed to return everything the mobile screen needs. The games contains their reviews. And the reviews contain their images. So in one query the whole mobile screen can be populated.</div>
<div>
<br /></div>
Let's see how a programmer in a hurry develops all this API:<br />
<br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">games = Game.actual.limit(500)</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">present games, with: DetailedGameEntity</span><br />
<div>
<br /></div>
<div>
By developing the API this way the call will result in these queries:</div>
<div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">...</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> Review Load (0.5ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."reviewable_id" = $1 AND "reviews"."reviewable_type" = $2 [["reviewable_id", 2359], ["reviewable_type", "Game"]]</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> Review Load (0.4ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."reviewable_id" = $1 AND "reviews"."reviewable_type" = $2 [["reviewable_id", 2358], ["reviewable_type", "Game"]]</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">...</span></span></div>
</div>
<div>
<br /></div>
<div>
As you can see for each returned game game another select is executed to fetch the belonging reviews. The same is done to fetch the belonging user and to fetch the images belonging to the review. This is a very ineffective way because as the review count grows more and more queries need to be executed. This problem is called n+1 query problem.</div>
<div>
<br /></div>
<div>
I do this in my code in these cases:</div>
<div>
<br /></div>
<div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">games = Game.actual.limit(500)</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">games = games.includes(reviews: [:images, :user] )</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">present games, with: DetailedGameEntity</span></span></div>
</div>
<div>
<br /></div>
<div>
The above code will do this sql query:</div>
<div>
<br /></div>
<div>
<div style="background-color: black; color: #00f900; font-family: 'Andale Mono'; font-size: 20px;">
<span style="color: #34bbc7; font-variant-ligatures: no-common-ligatures;">Review Load (1.0ms)</span> SELECT "reviews".* FROM "reviews" WHERE "reviews"."reviewable_type" = 'Schedule' AND "reviews"."reviewable_id" IN (2195, 2198, 2197, 9567, 9572, 9573, 9574, 9575, 9576, 9571, 9570, 9569, 9568, 2196, 2204, 2210, 2211, ...</div>
</div>
<div>
<br /></div>
<div>
So, the underlying Rails code will execute only one query per entity.</div>
<div>
<br /></div>
<div>
It is nice, isn't it?</div>
<div>
<br /></div>
<div>
Please use it ;)</div>
<div>
<br /></div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-74621305159404488522014-12-27T16:22:00.002+00:002014-12-27T16:57:26.148+00:00<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: large; line-height: 18px;"><a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails.html">API Development in Ruby On Rails, Entities</a></span><br />
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 18px;"><br /></span>
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;">In my <a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails-i-met.html">last blog</a> post I presented in general the features I like the most about the <a href="https://github.com/intridea/grape">grape</a> gem. Now I will present the <a href="https://github.com/intridea/grape-entity">grape entities</a> which are related to the presentation of the returned data. I like these entities a lot because they are an <a href="http://en.wikipedia.org/wiki/Object-oriented_programming">OO</a> way to present data and they help a lot to keep my presentation layer <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>.</span><br />
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;"><br /></span>
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white;"><span style="line-height: 18px;">The last statement of every grape call is returned to the caller as JSON.</span></span></span><br />
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white;"><span style="line-height: 18px;">Despite this simple efficiency I like to have more control over the returned data and I use the grape entities to format the returned data </span></span></span><span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;">wherever I can.</span><span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;"> I can also </span><a href="http://grape-entity-matchers/" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;">rspec</a><span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;"> them and I am doing it extensively because I am a Test Driven Guy ;)</span><br />
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 18px;"><br /></span>
<span style="background-color: white;"><span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="line-height: 18px;">Let me present some examples of entities their usage and how do I spec them:</span></span></span><br />
<br />
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white; line-height: 18px;">Here is the spec for the entity:</span></span><br />
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white; line-height: 18px;"><br /></span></span>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">describe GameEntity do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> describe 'fields' do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> subject(:subject) { GameEntity }</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> it { is_expected.to represent(:id) }</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> it { is_expected.to represent(:user_id).as(:owner_id) }</span></span><br />
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">...</span></span></div>
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white;"><span style="line-height: 18px;"><br /></span></span></span>
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white;"><span style="line-height: 18px;"><br /></span></span></span>
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white;"><span style="line-height: 18px;">Here is the entity itself:</span></span></span><br />
<span style="color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="background-color: white;"><span style="line-height: 18px;"><br /></span></span></span>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">class GameEntity < Grape::Entity</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expose :id</span><br />
<span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;"> expose :user_id, as: :owner_id</span><br />
<div>
...</div>
<div>
<br /></div>
<div>
The part of the grape API:</div>
<div>
<br /></div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> namespace :games do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> desc "Retrieve all the games"</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> params do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> optional :include_reviews, type: Boolean, default: true, desc: 'Accepts: true/false.'</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> get do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> games = Game.actual.limit(500)</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> present games, with: GameEntity</span></div>
</div>
<div>
<br /></div>
<div>
This was simple so far. But I can use a more sophisticated GameEntity in case I want to present the included reviews. In all other cases I will use my old simple GameEntity to present the Game.</div>
<div>
<br /></div>
<div>
Let's see that case too:</div>
<div>
<br /></div>
<div>
I modify the grape API to this:<br />
<br /></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> ...</span></div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> if params[:include_reviews]</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> games = games.includes(:reviews)</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> present games, with: DetailedGameEntity</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> else</span><br />
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> </span><span style="background-color: #d9ead3; font-family: 'Courier New', Courier, monospace;">present games, with: GameEntity</span></div>
</div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<br /></div>
<div>
I also spec the new entity:<br />
<br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;">describe DetailedGameEntity do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> describe 'special fields' do</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> subject(:detailed_game) { DetailedGameEntity(game).as_json }</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> specify { expect(detailed_game[:reviews]).to be_an(Array) }</span></span><br />
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace;"> end</span></span></div>
<div>
<br /></div>
<div>
Of course the above was a very simple example.</div>
<div>
<br /></div>
<div>
In real world scenarios we can find ourselves that we are reusing the entities like this:</div>
<div>
<br /></div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">class DetailedGameEntity < Grape::Entity</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expose :game, using: GameEntity do |game, options|</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> game</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expose :venue, using: VenueEntity do |game, options|</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> game.the_venue</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expose :visitor_team, using: TeamEntity do |game, options|</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> game.visitor_team</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expose :host_team, using: TeamEntity do |game, options|</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> game.host_team</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;">As you can see we are keeping our entity codebase <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a> by calling the TeamEntity twice in the DetailedGameEntity. This is a typical use of <a href="http://en.wikipedia.org/wiki/Delegation_(programming)">delegation</a> of the Entities.</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;">Let me show a more complex example of our entities:</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;">The usage of the entities:</span></div>
<div>
<span style="font-family: Georgia, Times New Roman, serif;"><br /></span></div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> desc "Retrieve all the reviews belonging to a team"</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> get do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> ...</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> present reviews, :with => ReviewPresenter, user: current_user</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
</div>
<div>
<br /></div>
<div>
The entity itself:</div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> class ReviewPresenter < Grape::Entity</span></div>
</div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> expose :current_user_likes do |review, options|</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> review.likes.pluck(:user_id).include? options[:user].id if options[:user].present?</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
</div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<br /></div>
<div>
And finally the spec for the entity:</div>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">describe ReviewPresenter do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> describe 'special fields' do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'without passing a user' do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> subject(:presented_review) { ReviewPresenter.new(image.review).as_json }</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify { expect(presented_review[:current_user_likes]).to be_nil }</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> subject(:presented_review) { ReviewPresenter.new(image.review, user: current_user).as_json }</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> ...</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> context 'user is the current_user' do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> specify { expect(presented_review[:current_user_likes]).to eq(true) }</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> ...</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;"> end</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace;">end</span></div>
</div>
<div>
<span style="background-color: #d9ead3;"><br /></span></div>
<div>
<span style="background-color: white; font-family: Georgia, Times New Roman, serif;">What I was doing here is to populate the field 'current_user_likes'</span></div>
<div>
to true or false depending if the current_user likes or not the presented review.</div>
<div>
<br /></div>
<div>
As you can see the presentation layer of any grape API could became more DRY and versatile by using the grape entities.</div>
<div>
<br /></div>
<div>
Enjoy using them ;)</div>
<div>
<br /></div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-90965272746096527362014-12-26T14:34:00.001+00:002014-12-27T15:40:05.864+00:00<a href="http://orbanbotond.blogspot.com/2014/12/api-development-in-ruby-on-rails-i-met.html"><span style="font-size: large;">API Development in Ruby On Rails</span></a><br />
<br />
I met a technology and a set of gems which were designed to develop any API in <a href="http://rubyonrails.org/">Ruby On Rails</a> about half years ago.<br />
<br />
The set of gems were:<br />
<br />
<ul>
<li>'<a href="https://github.com/intridea/grape">grape</a>'</li>
<li>'<a href="https://github.com/intridea/grape-entity">grape-entity</a>'</li>
<li>'<a href="http://grape-swagger/">grape-swagger</a>'</li>
<li>'<a href="https://github.com/kendrikat/grape-swagger-ui">grape-swagger-ui</a>'</li>
</ul>
<br />
At first glance it seemed that it adds complexity to the Rails application compared to standard Rails <a href="http://www.w3schools.com/json/">JSON</a> returns. After I played a bit with it I suddenly realized that its <a href="http://en.wikipedia.org/wiki/Application_programming_interface">API</a> formulating <a href="http://en.wikipedia.org/wiki/Domain-specific_language">DSL</a> is much more superior. So I started to like it :)<br />
<br />
Let me list the elements of DSL I like most:<br />
<br />
<ul>
<li>ability to mount API endpoints. This can be very handy if I want to unmount certain unused parts of the API.</li>
</ul>
<br />
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace; font-size: x-small;"> mount Caesars::Ping</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace; font-size: x-small;"> mount Caesars::PingProtected</span></div>
<br />
<ul>
<li>ability to describe the API endpoint. This description will appear in the <a href="http://swagger.io/">Swagger documentation</a>. It is a help for the API user.</li>
</ul>
<ul>
<li>ability to specify the parameters (type, mandatory or required).</li>
</ul>
<div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace; font-size: x-small;"> params do</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace; font-size: x-small;"> optional :user_ids, desc: "JSON array of the user ids to be deleted."</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace; font-size: x-small;"> requires :group_id, type: Integer, desc: "The id of the group"</span></div>
<div>
<span style="background-color: #d9ead3; font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></div>
</div>
<div>
<br /></div>
<div>
By specifying the type our parameters will be converted automagically to the specified type. It is easier this way for us developers to write our core logic.</div>
<ul>
<li>a complex DSL to organize the API hierarchy and use route params. I consider this the most powerful feature. I can easily organize the API to look more like <a href="https://developers.facebook.com/docs/graph-api">Facebook graph API</a> or <a href="http://www.developers.meethue.com/">Philips Hue API</a> than standard Rails <a href="http://en.wikipedia.org/wiki/Representational_state_transfer">restfull</a> API.</li>
</ul>
<div>
<div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> namespace :teams do</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> route_param :id do</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> desc 'Retrieve all games belonging to teams'</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> get 'scores' do</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> #DO logic here</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></span></div>
<div>
<span style="background-color: #d9ead3;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> end</span></span></div>
</div>
</div>
<div>
<br /></div>
<div>
The grape features are many more including versioning. Take a look for a complete description at <a href="https://github.com/intridea/grape">grape</a> site ;)</div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-69804006545023087872014-12-25T23:35:00.000+00:002014-12-25T23:35:13.472+00:00Hi,<br />
<br />
I have news for you. My daughter had been born in august :) With Her time passed very fast.<br />
I even missed the new macbook release date which was in July. But I don't feel disappointed because according to the <a href="http://www.macrumors.com/roundup/macbook-pro/">rumors</a> the newer MacBook will have a much <a href="http://www.macrumors.com/2014/07/09/broadwell-early-to-mid-2015/">better</a> CPU. So I will wait for the next generation of MacBook pro :)<br />
<br />
Here is a picture about us:<br />
<iframe allowfullscreen="" frameborder="0" height="75" mozallowfullscreen="" msallowfullscreen="" oallowfullscreen="" src="https://www.flickr.com/photos/64959092@N08/15918904858/player/" webkitallowfullscreen="" width="75"></iframe>
<br />
<br />Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-32863443416137801402013-04-01T19:53:00.001+01:002013-04-01T19:54:25.645+01:00Editing the code with the speed of thoughtHi<br />
<br />
I changed my code editor from <a href="http://blog.macromates.com/2011/textmate-2-0-alpha/">Textmate2</a> to <a href="http://www.sublimetext.com/">Sublime</a>.
A friend of mine told me about it and I was so curious that I needed to try it. I started to love it right in the first moment mainly because of the existence of the command palette and its modular feature approach and the multiple editing feature.<br />
<br />
The <a href="http://docs.sublimetext.info/en/latest/reference/command_palette.html">command palette</a> was for me like some sort of help combined with a quicksearch-autosuggest menu. When I thought to something I would do I typed in and if I had luck then the feature was already there. If not then I needed to search for a suitable package which did the desired functionality.<br />
I saw a huge potential in this approach because I always wanted to edit my code like the code editor would be a extension of my body. In other words I wanted to edit code with the speed of my thought.<br />
By default the desired functions were not there. I needed to search for some packages and even edit those packages to achieve my desires. I wanted to toggle between quote types 'a', "a". I wanted to convert a string to a symbol and vice versa "a", :a. I wanted to toggle the do end blocks with the braces blocks. I wanted to convert amongst sneak_case, pascalCase, dash-case, dot.case, slash/separated<br />
<br />
So by using it continouosly for more than half year I installed these packages:<br />
<br />
<ul>
<li><b><a href="https://github.com/jdc0589/CaseConversion">Case Conversion</a></b></li>
</ul>
For converting each other from any of them:<br />
<div>
<ul><ul>
<li>"sneak_case"</li>
<li>"pascalCase"</li>
<li>"dash-case"</li>
<li>"dot.case"</li>
<li>"slash/separated"</li>
</ul>
<li><b>Clipboard History</b> - for using multiple history feature</li>
<li><b>CoffeeScript</b> - syntax for coffee script</li>
<li><b>Cucumber</b> - syntax for cucumber</li>
<li><b>Eco</b> - syntax for eco template</li>
<li><b>ERB Insert And Toggle Command</b></li>
</ul>
For toggling amongs these<br />
<ul><ul>
<li><%= %> </li>
<li><%- -%></li>
<li><% %></li>
<li><%= -%></li>
<li><%# %></li>
</ul>
<li><b>Gist</b> - I like to store my snippets on gist and access them from sublime</li>
<li><b>Missing Palette Commands</b></li>
<li><b>Plain Tasks</b> - For storing my tasks in plain text format but nicely formated, like the below list</li>
<ul>
<li>✔ task 1 @done (13-04-01 21:28)</li>
<li> ☐ implement sorting</li>
</ul>
<li><b>RSpec</b> - syntax for my specs. I am a TDD advocate. It saves me a lot of time.</li>
<li><b>Ruby Block Converter</b> - for toggling amongst {} block and do end blocks.</li>
<li><b>SFTP</b> - for editing remote files.</li>
<li><b>Toggle Symbol To String </b>for toggling between "symbol"and :symbol</li>
<li><b>Toggle Quotes</b> for toggling between 'string' and "string"</li>
<li><b>Advanced New File</b> for creating deeple nested directories when I place a file into them.</li>
<li><b>Nettuts + Fetch </b>for fetching the latest jquery.js or jquery-ui.js or backbone.js libraries.</li>
</ul>
<br />
Often the commands were missing from some of these packages. So when I searched for 'toggle block' then no result was shown however I wanted to convert a block from "{}" style to "do end" style. This was a bottleneck in my speedy coding... In these cases I needed to open the package add the command to the Default.sublime-commands file and then make a pull request. This way if I eventually forgot the key combination of a feature I could search for it in a command palette.<br />
<br />
May I ask any developer who makes a sublime package to add this file to the package and enlist the commands there.<br />
There is the multiple editing feature I like the most... :)<br />
And it is a very fast editor.<br />
I also like the snippet feature of it.<br />
<br />
Overall all I can say s that it is a very handy code editor and it worth the money for buying it and spending time to learn it ;)</div>
Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-31258648696378733132012-06-16T22:29:00.000+01:002012-06-16T22:29:23.910+01:00Leting your vital javascript functions to do their job when the content changesHi Fellow Programmers<br />
<br />
In this post I will show you one way to let your pages work as intended even if they are manipulated dynamically.<br />
<br />
<b>The problem</b>: When the content of the page changes dynamically, those items are not listened by those javascript functions which were initialized at the time when the page loaded.<br />
<br />
At the very first steps every programmer reinitializes only those handlers which are needed by the dynamically reloaded partial. This has some disadvantages. One of them is that if the initialization changes then it is very painful to track back the partials where the initialization need to be changed. Plus, the whole thing need a babysitting process...<br />
<br />
I don't want that...<br />
<br />
What I want is a structured way, in which is agile and robust enough to handle these issues... :)<br />
<br />
Let me show you by code:
<pre class="brush: ruby">
window.problemOrNot = {
vital_functions: [
->
$(".cancel").click ->
$('.cancelable_remove').remove()
$('.cancelable_show').show()
]
run_vital_functions: ->
$.each problemOrNot.vital_functions, ->
@()
}
jQuery ->
problemOrNot.run_vital_functions()
</pre>
And then in your partial, which obviously modifies your dom:
<pre class="brush: ruby">
$('.comments').append("<li class='rounded cancelable_remove'><%=j render :partial => 'form' %></li>");
...
...
...
$('#comment_operations').hide();
problemOrNot.run_vital_functions();
</pre>
That's it...
The first code was in cofee script, and the partial was a rails .js.erb partial. :)
EnjoyOrban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-17596904214692409522012-06-07T14:15:00.000+01:002012-06-07T14:16:00.855+01:00Full Text Search Feature Of PostgresqlHi,
<br />
<br />
I was using mysql for my development for a long-long time. But I decided to try postgresql as I watched <a href="http://railscasts.com/episodes/343-full-text-search-in-postgresql">Ryan Bates screencast</a> which showed the fullt text search capabilities of postgresql.<br />
<br />
I was motivated to try <a href="http://www.postgresql.org/download/">postgresql</a> since heroku <a href="https://addons.heroku.com/heroku-postgresql">added a new addon</a> which enables any application to use the <a href="https://addons.heroku.com/heroku-postgresql">lastest(9.1.13) postgresql.</a> And as a bonus the capacity of that DB is unlimited... However I can hardly believe that it will remain free forever...<br />
<br />
So...<br />
I created a new DB on my heroku account for my application and I followed <a href="https://devcenter.heroku.com/articles/migrating-data-between-plans">the migration manual</a> and voila. It took me about no more than 15 minutes to transition to a new DB.<br />
<br />
It was a nice experience. :)Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-61515481356850563712012-06-05T11:52:00.001+01:002012-06-05T11:52:51.351+01:00Navigating from controllers to .haml type views in TextmateHi,
I made a fix which enables your <a href="http://macromates.com/">Textmate</a> to navigate from your controller to your <a href="http://haml.info/">haml</a> type views, using the <a href="https://github.com/orbanbotond/ruby-on-rails.tmbundle">ruby on rails bundle</a>.
EnjoyOrban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-18211436937467684502012-06-04T09:55:00.000+01:002012-06-05T12:56:44.314+01:00Advanced git featuresHi everybody,<br />
<br />
I am using git as source control for a long time ago, and I am happy with what it offers.
I presume that the basic git commands are clear for everyone. So in this post I will write about some more advanced git commands which aren't used so frequently but they are invaluable when they are needed.<br />
<br />
<br />
<code><pre>
-Nice Git log which contains a lot of usefull info:
git log --graph --all --decorate=full --abbrev-commit --pretty=short
-Delete the branch 'staging' on remote 'staging':
git push staging :staging
-push the branch "newfeature" to the remote "origin":
git push origin newfeature
-create a remote branch:
git push :refs/heads/new_feature_name
-list remote branches:
git branch -r
-diff between two branches:
git diff <branchone>..<another branch>
-create a branch wich tracks a remote branch:
git checkout --track -b <new_feature_name> <origin>/<new_feature_name>
-track a remote branch:
git branch --set-upstream <local_branch> <remote>/<remote_branch>
-push to a remote:
git push <remote> <local branch name>:<remote branch to push into>
-configuring to push automatically to a certain remote branch, without specifying the local branch and the remote branch:
git config remote.<remoteName>.push <localBranchName>:<remoteBranchName>
</pre>
</code>
<br />
Enjoy and easy your life :)Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-35777092896329934472012-05-23T11:37:00.000+01:002012-06-05T11:53:35.140+01:00Installing Ruby On Rails in windows machines<br />
I dedicate this post to my friends who can't afford to buy a mac or not experienced enough to work on Linux machines<br />
<br />
In my virtual box I tried to reproduce the installation of Ruby on Rails on Windows XP.<br />
Because the framework evolved from my last windows installation I realized that some things have become simpler to install and additional things needed to be installed.<br />
<br />
Here are the steps which are needed to get a ready to enjoy Ruby On Rails environment on Windows machines:<br />
<br />
Download ruby:<br />
http://rubyinstaller.org/<br />
Install it. (The default instalation directory will be C:\Ruby193)<br />
<br />
Download devkit for ruby form this location:<br />
https://github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe<br />
Install it to c:/Webkit (important that the directory can't contain spaces)<br />
Put C:\Ruby193\bin and c:\Devkit\bin in your path. (My computer -> properties -> 'environmental variables')<br />
<br />
Run these commands from a newly opened command prompt:<br />
cd c:\Devkit<br />
ruby dk.rb init<br />
ruby dk.rb review<br />
ruby dk.rb install<br />
<br />
Now you can install rails by doing "gem install rails". (If you previously tried to install anything without having the devkit installed then please empty your ruby gems directories. Usually they are here: C:\Ruby193\lib\ruby\gems\1.9.1)<br />
<br />
gem update --system<br />
gem install rubygems-update<br />
<br />
rails new hello_worldOrban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-76189224577736008752011-10-06T17:04:00.007+01:002012-06-05T11:53:44.573+01:00Normalize you DB<div style="text-align: center;"><br /></div><div style="text-align: left;">Hi everyone,</div><div><br /></div><div>In the last couple of days I had the opportunity to normalize a DB. :) </div><div style="text-align: left;"><br /></div><div>Let me show you a screenshot about a single table:</div><div><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi82lfuQWmQ9Fibu2XaUJehQHabI630x0mvI3DPXFHc1MxHXZnDJpGO-n2VSd2OcW5ErNxJv2QBe0__xEk5-cFqZ6dM-13mi2l9MN6jDh06Vx3rLzYDWFB8BhDP3OHOt9GRfusEyeyJQkA/s320/Rows.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 209px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5660458238341906482" /><div style="text-align: center;"><br /></div></div><div>As you see a lot of data is repeated across the rows. Therefore, I had some bad thoughts about those who let the DB become like this in the beginning. I didn't wanted to do it. As the moments were passing away I let the challenge to excite me. At the end I did it.</div><div><br /></div><div>Before doing anything the total number of rows in the db was above 100000 and the size of the table was 10,5 MBytes.</div><div>My first step was to extract the name of the components into another table and reference them.</div><div>This way the size of the table decreased to 8.5 MBytes. And the new table size was 1.5 Mbyte. I earned 0.5 Mbyte, not too much, but the database was much more cleaner.</div><div>My second step was to transform the check/change data represented by a string into their numerical representations, this way enabling the database to be internationalized. By doing this the table size decrease to 6.2 MBytes. That means 72% of its original size and the data can be internationalized and we have all the benefits of a normalized DB schema (that means no update problems and a joy to work with).</div><div><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBUU3JVku4yrmTFaVtBSVJno9LKh8nVRo73va8NiT5ue46sPJ0pn6Bf2MMsXE2CH65e6Xw8kzCFeSuZOaKKsCAC-oJjpzAWiXjh5eukfScO64Xr3_sP5-82El5Hpt9cw7Xt_DpNMpZ2L0/s320/After.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 35px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5660464090816424402" /></div><div>I also measured the speed of joins operations. Because of the normalization the same data retrieval was increased by 3 times. I put an index onto foreign keys. And the speed was just like before. :)</div><div><br /></div>Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-210252098263164452011-10-06T10:52:00.006+01:002012-06-05T11:54:23.420+01:00Design your User InterfaceHello everybody,<br /><br />I have just finished the book "<a href="http://www.amazon.com/Design-Everyday-Things-Donald-Norman/dp/0385267746">The Design Of Everyday Things</a>".<br />It's a great book written by Donald Norman. The guy who wrote "<a href="http://www.amazon.com/Invisible-Computer-Products-Information-Appliances/dp/0262640414/ref=sr_1_1?s=books&ie=UTF8&qid=1317915639&sr=1-1">The invisible computer</a>", who worked for the Apple Computer's Advanced Technology Group.<br /><br />In this book he talks about many-many interesting things:<br />-The constraints and strength of our short term & long term memory.<br />-The slips and mistakes our brain makes, while we do things.<br />-How can we make use of our cultural knowledge and knowledge in the world while design.<br />-The importance of natural mapping between controls and our actions.<br />-Interpretation of a system state, and our mental model about the system.<br />-The (missing) evolution of product line. The evaluation of the already selled products and the importance of debriefing then evolving the product to a better one.<br /><br />He also describes how primitive were the computers at time of writing. <div>And he gives us some hints how he imagines the mobile phones should easy our everyday tasks. I had the impression the he was envisioning the nowadays iPhones. He also describes how a graphical user interface would help us to be more effective with the computer interaction. I often had the feeling that he was describing a vision which is very similar about nowadays iPads, iPhones or Microsoft Surface.</div><div><br /></div><div>I was also symphatizing with him because I remember times when I needed to setup my Mouse and CD driver in the config.sys in order to use those devices. Nowadays everything is much more easier. Especially that, I am a Macintosh user I can spend my time focusing on relevant tasks, without even realizing that I am working.</div><div><br /></div><div>I finished the book yesterday. After I put the book back onto my shelf I started to browse the new Apple products... They are just amazing. I was especially amazed by the new <a href="http://www.apple.com/iphone/features/#siri">Siri</a> feature in the iPhone. It remembers me to the "Computer" from Startreak. It is a good example that if we can imagine something then we can sooner or later achieve it. Another feature I imagined even myself and waited a lot is the iCloud. I tried the beta version by downloading Ethics from Aristotle on my Macbook and the book instantly appeared on my iphone too. :) Definetely it enhances our usability experience.</div><div><br /></div><div>The next day, other news were expecting me. Sad news. The founder and main dreamer, envisioner and technology pioneer Steve Jobs died. So good journey Steve and rest in peace.</div><div><br /></div><div><br /></div>Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-26829210105013097742011-07-18T21:14:00.007+01:002011-09-23T17:36:05.074+01:00sqlite log filteringHi,<br /><br />Presumably a lot of you are developing rails applications using sqlite.<br />In this case the log is spamed by some pesky sql statements every time our app accesses the db.<br /><br/><br /><pre class="" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><br /> SQL (0.6ms) SELECT name<br /> FROM sqlite_master<br /> WHERE type = 'table' AND NOT name = 'sqlite_sequence'<br /> SQL (0.6ms) SELECT name<br /> FROM sqlite_master<br /> WHERE type = 'table' AND NOT name = 'sqlite_sequence'<br />... <br /> SQL (0.6ms) SELECT name<br /> FROM sqlite_master<br /> WHERE type = 'table' AND NOT name = 'sqlite_sequence'<br /> SQL (0.6ms) SELECT name<br /> FROM sqlite_master<br /> WHERE type = 'table' AND NOT name = 'sqlite_sequence'<br />...<br /></pre><br /><br/><br />These messages bother me because they have nothing to do with my application logic, they are only needed by the sqlite driver to access the db structure.<br /><br/><br />Therefore I developed a small ruby gem called <a href='https://rubygems.org/gems/sqlite_log_filter'>sqlite_log_filter</a> which filters these messages out. The source can be found on <a href="https://github.com/orbanbotond/sqlite_log_filter">github</a><br /><br/><br />All you need to do is to put it in your gemfile<br /><pre class="brush: ruby" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><br />gem 'sqlite_log_filter'<br /></pre><br /><br />Then bundle it<br /><pre class="brush: ruby" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><br />bundle install<br /></pre><br /><br />Hopefully it helps :)Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-15619757790533825322011-06-21T21:53:00.007+01:002012-06-05T11:54:34.734+01:00Test (Behaviour) Driven DevelopmentRecently I saw a pretty nice post:<br /><p>"If you see anyone pushing code to Rails without tests, ping me and I will gladly revert. In 2011, this should be unacceptable."</p><br /><p>I remember times when I had a project which was developed test driven at my former place of work. Of course the Test Driven initiator was me, and by time the teammates embraced the methodology and it managed to be a very successful project.</p><br /><p>The most exciting thing was that I never had to debug; the tests always showed me the exact the location of the problem -if there was any.<br />Another almost unbelievable fact was that days before releases we didn't have any unfinished tasks. We were down on the streets having a Budweiser or limonade and we were chatting. What I mean here is that the test batteries saved us so much time that we never had to work overtime. We were just shipping the product confidently days before the planned release. I also remember some sales meetings where this project was showed to the customers as a success story. I also remember one of the questions one customer asked: "Then why don't you write software this way every time?" and the answer was "Because some customers simply dictate to not write any test at all..." Well everyone in the meeting laughed. But we all knew that this decision would cause any customer to throw out hundreds of thousand of dollars and hundreds of hours of overtime for the developers without mentioning the moral downgrade for every involved party. Maybe we were laughing at that unintelligent stereotype.<br />I also remember one important fact while writing this. In the initial project schedule the testing time wasn't calculated. We simply adopted the test first and then the benefits showed themself over and over again. We couldn't run out of the schedule... :) Almost unbelievable but it was true. <br /></p><br /><p>After the project has been finished I have tried to introduce this methodology in as many projects as I could. Unfortunately there were some projects where the mass was so frozen that my attempts were totally ignored. Sometimes the reason for ignorance was simply the lack of infrastructure. Fortunately the infrastructure is not a problem in Ruby on Rails. The community is also mostly test-centered.</p><br /><p>I cannot emphasize enough for every developer in any domain to embrace this Test First thinking. There are so many benefits of it that many books have been written about it. And who knows, maybe you who will embrace this approach, will have some similar success story to tell the world</p>Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-11808287319691606642011-06-20T13:18:00.012+01:002012-06-05T11:54:45.595+01:00autotest, zentest, autotest-rails, autotest-rails-pure: which gems to choose?<p>It was a little bit confusing for me which "autotest like" gems to choose when I want to autotest my rails application.<br />Well... There are some alternatives depending which testing framework I choose <a href="http://rspec.info/">rspec</a> or <a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">Test::Unit</a>, also there are alternatives if I want to generate the tests for my controller actions automatically with zentest or I just want to write them in some other way.<br /></p><br /><p><br />Zentest was the first tool in this domain with a lot of features. Then <a href="http://rubygems.org/gems/autotest-standalone">autotest-standalone</a> was factored out from it. There is also an empty gem called <a href="http://rubygems.org/gems/autotest">Autotest</a> which is dependent on Zentest.<br /></p><br /><p>Because the main idea is to simply map the lib and test directories, in order to test rails application we need to tell the rails specific mappings. This is done by the <a href="">autotest-rails</a> and <a href="">rspec-rails</a> and <a href="">autotest-rails-pure</a> gems. So let's walk through each of the alternatives</p><br /><h2>Test::Unit</h2><br /><p>In this case the rails specific mappings are done by the <a href="http://rubygems.org/gems/autotest-rails-pure">autotest-rails-pure</a> or <a href="http://rubygems.org/gems/autotest-rails">autotest-rails</a> gems.<br />The code excerpt which makes the mapping for the controllers is:<br /><pre class="brush: ruby" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><br /> add_mapping %r%^app/controllers/application_controller\.rb$% do |_, m|<br /> files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%<br /> end<br /></pre><br /></p><br /><p>For the core autotest we can use the <a href="http://rubygems.org/gems/autotest-standalone">autotest-standalone</a> or the feature rich old Zentest.<br /><p><br /><br /><h2>Rspec</h2><br /><p>The rails specific mapping is done by the <a href="https://github.com/rspec/rspec-rails">rspec-rails</a> in case we opt for using rspec</p><br /><p><br />Here is an excerpt wich does the mapping for the controllers:<br /><pre class="brush: ruby" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"> add_mapping(%r%^app/controllers/(.*)\.rb$%) { |_, m|<br /> if m[1] == "application"<br /> files_matching %r%^spec/controllers/.*_spec\.rb$%<br /> else<br /> ["spec/controllers/#{m[1]}_spec.rb"]<br /> end<br /> }<br /></pre><br /></p><br /><p><br />So there are a plenty of alternatives we can choose from and it is good to know which opportunities are available. We have all we need :) <br /></p>Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.comtag:blogger.com,1999:blog-9075525480713027828.post-41096334227341097032011-06-11T12:49:00.019+01:002011-09-23T18:24:56.737+01:00Making the Factory Girl to support PaperclipHi Rubysts,<br /><br /><a href="https://github.com/thoughtbot/paperclip">Paperclip</a> and <a href="https://github.com/thoughtbot/factory_girl_rails">Factory Girl Rails</a> both of them are my loved gems.<br />Unfortunately Factory Girl Rails can't create the model if the model had been Papercliped. Well at least not in Rails 3.0.7. But the good news is that it can be solved by adding a few lines of Ruby code to our factory.<br /><br />This is my model:<br /><pre class="brush: ruby" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><br />class Club < ActiveRecord::Base<br /> has_many :dancers<br /> has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "100x100>" }<br /> validates_attachment_presence :photo<br /> validates_attachment_size :photo, :less_than => 1.megabytes<br />end<br /></pre><br />So in order to use the factory with ease we need to tell our factory to upload some pictures into our model.<br />Therefore let me show you an example how to write the factory which makes the two gems to co-work:<br /><pre class="brush: ruby" style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><br />include ActionDispatch::TestProcess<br /><br />class Club<br /> attr_accessor :photo_file_name<br /> attr_accessor :photo_file_size<br /> has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "100x100>" },<br /> :url => "/test_can_be_deleted/:attachment/:id/:style/:filename",<br /> :path => ":rails_root/public/test_can_be_deleted/:attachment/:id/:style/:filename"<br />end<br /><br />Factory.define :club do |c|<br /> c.title 'Limpex'<br /> c.sefurl 'Limpasm'<br /> c.name 'Limpasm'<br /> c.metadescription 'good etc'<br /> c.content '<p> a good place</p><p>the icecream is prety damn good</p>'<br /> c.state 0<br /> c.city 'Gheorgheni'<br /> c.email 'some@somplexx.com'<br /> c.photo { fixture_file_upload( 'spec/factories/test.png', 'image/png') }<br />end<br /></pre><br /><br />As you can observe I have just reopened my model and have redefined the paperclip attachment to point to a different path in order to store the dev and test mode attachments in different places.<br /><br />That's it. :)Orban Botondhttp://www.blogger.com/profile/15174135306127563444noreply@blogger.com