Blog NameBlog subtitlehttp://blog.url.com/2015-06-08T20:00:00-04:00Blog AuthorHitting real angular http service from jasmine test/blog/2015/06/09/hitting-real-angular-http-service-from-jasmine-test.html2015-06-08T20:00:00-04:002015-06-08T20:00:00-04:00Article Author<h1>Hitting real angular http service from jasmine test</h1>
<p>Sometimes you want to use jasmine as a tool for playing with existing web services,
grab some responses and then convert them into the stubs. Unfortunately, from jasmine test
you have access only to mocked version of <strong>$httpBackend</strong> service.</p>
<p>You can use this helper code for accessing real <strong>$httpBackend</strong> service:</p>
<pre class="highlight javascript"><code><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'httpReal'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'ng'</span><span class="p">])</span>
<span class="p">.</span><span class="nx">config</span><span class="p">([</span><span class="s1">'$provide'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$provide</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$provide</span><span class="p">.</span><span class="nx">decorator</span><span class="p">(</span><span class="s1">'$httpBackend'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'ng'</span><span class="p">]).</span><span class="nx">get</span><span class="p">(</span><span class="s1">'$httpBackend'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}])</span>
<span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="s1">'HttpReal'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$rootScope</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">submit</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$rootScope</span><span class="p">.</span><span class="nx">$digest</span><span class="p">();</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="p">);</span>
</code></pre>
<p>This code creates <strong>httpReal</strong> service in which you restore access to original <strong>$httpBackend</strong>.
It also provides a function to flush the request. Otherwise promises from http service call will
never be executed.</p>
<p>Let’s create demo service:</p>
<pre class="highlight javascript"><code>
<span class="kd">var</span> <span class="nx">namespace</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'myModule'</span><span class="p">,</span> <span class="p">[]);</span>
<span class="nx">namespace</span><span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="s1">'MyService'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$http</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">remoteCall</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">$http</span><span class="p">({</span><span class="na">method</span><span class="p">:</span> <span class="s1">'GET'</span><span class="p">,</span> <span class="na">url</span><span class="p">:</span>
<span class="s1">'http://api.openweathermap.org/data/2.5/weather?q=Princeton'</span><span class="p">});</span>
<span class="p">};</span>
<span class="p">});</span>
</code></pre>
<p>and test it:</p>
<pre class="highlight javascript"><code><span class="nx">describe</span><span class="p">(</span><span class="s1">'MyService'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">myService</span><span class="p">,</span> <span class="nx">httpReal</span><span class="p">;</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="nx">module</span><span class="p">(</span><span class="s1">'myModule'</span><span class="p">,</span> <span class="s1">'httpReal'</span><span class="p">));</span>
<span class="nx">beforeEach</span><span class="p">(</span><span class="nx">inject</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">MyService</span><span class="p">,</span> <span class="nx">HttpReal</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">myService</span> <span class="o">=</span> <span class="nx">MyService</span><span class="p">;</span>
<span class="nx">httpReal</span> <span class="o">=</span> <span class="nx">HttpReal</span><span class="p">;</span>
<span class="p">}));</span>
<span class="nx">it</span><span class="p">(</span><span class="s1">'calls success callback and returns valid data'</span><span class="p">,</span>
<span class="kd">function</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">myService</span><span class="p">.</span><span class="nx">remoteCall</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">sys</span><span class="p">.</span><span class="nx">country</span><span class="p">).</span><span class="nx">toEqual</span><span class="p">(</span><span class="s1">'US'</span><span class="p">);</span>
<span class="nx">done</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">httpReal</span><span class="p">.</span><span class="nx">submit</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">);</span>
<span class="nx">it</span><span class="p">(</span><span class="s1">'calls error callback'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">myServiceResults</span> <span class="o">=</span> <span class="nx">createPromise</span><span class="p">({},</span> <span class="kc">false</span><span class="p">);</span>
<span class="nx">spyOn</span><span class="p">(</span><span class="nx">myService</span><span class="p">,</span> <span class="s1">'remoteCall'</span><span class="p">).</span><span class="nx">and</span><span class="p">.</span><span class="nx">returnValue</span><span class="p">(</span><span class="nx">myServiceResults</span><span class="p">);</span>
<span class="nx">myService</span><span class="p">.</span><span class="nx">remoteCall</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="nx">angular</span><span class="p">.</span><span class="nx">noop</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">done</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">httpReal</span><span class="p">.</span><span class="nx">submit</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="kd">function</span> <span class="nx">createPromise</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">q</span><span class="p">;</span>
<span class="nx">inject</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$q</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">q</span> <span class="o">=</span> <span class="nx">$q</span><span class="p">;</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">deferred</span> <span class="o">=</span> <span class="nx">q</span><span class="p">.</span><span class="nx">defer</span><span class="p">();</span>
<span class="k">if</span><span class="p">(</span><span class="nx">success</span> <span class="o">===</span> <span class="kc">undefined</span> <span class="o">||</span> <span class="nx">success</span> <span class="o">==</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">deferred</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="nx">deferred</span><span class="p">.</span><span class="nx">reject</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">deferred</span><span class="p">.</span><span class="nx">promise</span><span class="p">;</span>
</code></pre>
<p>Take a look on how to properly use promises in tests. It uses <a href="http://www.htmlgoodies.com/beyond/javascript/stips/using-jasmine-2.0s-new-done-function-to-test-asynchronous-processes.html"><strong>done</strong></a>
function. In jasmine 2.x they have dropped <strong>runs/waitsFor</strong> functions in favor of the Mocha <strong>done</strong>
callback. You can use it in <strong>beforeEach</strong>, <strong>afterEach</strong> and <strong>it</strong> calls. </p>
Creating configuration files for Ruby programs/blog/2014/11/09/creating_configuration_files_for_ruby_programs.html2014-11-08T19:00:00-05:002014-11-08T19:00:00-05:00Article Author<h1>Creating configuration files for Ruby programs</h1>
<h2>Introduction</h2>
<p>There are different ways to keep configuration information outside of ruby program. You can use
.ini, .xml, .properties, .json, .yml formats to achieve it.</p>
<p>You can find some issues with this approach though. In case when you need to evaluate one property
based on value of another property, it could be very difficult or almost impossible.</p>
<p>Let’s show some examples of external configuration. In <strong>xml format</strong> it could look this way:</p>
<pre class="highlight xml"><code><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span>
<span class="nt"><properties></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"property1"</span> <span class="na">value=</span><span class="s">"value1"</span><span class="nt">/></span>
<span class="nt"><property></span>
<span class="nt"><name></span>property2<span class="nt"></name></span>
<span class="nt"><value></span>value2<span class="nt"></value></span>
<span class="nt"></property></span>
<span class="nt"><property</span> <span class="na">name=</span><span class="s">"property3"</span> <span class="na">value=</span><span class="s">"#{property1}123"</span><span class="nt">/></span>
<span class="nt"></properties></span>
</code></pre>
<p>Question: how to implement reference to another property, e.g. <strong>properties.property1</strong> for <strong>properties.property3</strong>?</p>
<p>Another example in <strong>json format</strong>:</p>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"property1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"value1"</span><span class="p">,</span><span class="w">
</span><span class="s2">"property2"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"property21"</span><span class="p">:</span><span class="w"> </span><span class="s2">"value21"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"property3"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{property1}/123"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<p>We can ask same question here.</p>
<p>Let’s take a look at possible solutions.</p>
<h2>Using text interpolation</h2>
<p>You can use some form of text interpolation to substitute such values based on the knowledge of the context. Look at <a href="https://github.com/shvets/text-interpolator">text_interpolator gem</a> and <a href="http://shvets.github.io/blog/2014/05/17/text_interpolator.html">article</a> about how to use this gem for further details. In short, it uses simple ruby trick:</p>
<pre class="highlight ruby"><code><span class="n">env</span> <span class="o">=</span> <span class="p">{</span><span class="ss">var1: </span><span class="s1">'some value 1'</span><span class="p">,</span> <span class="ss">var2: </span><span class="s1">'some value 2'</span><span class="p">}</span>
<span class="n">template</span> <span class="o">=</span> <span class="s2">"We have var1: %{var1} and var2: %{var2}."</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">template</span> <span class="o">%</span> <span class="n">env</span>
<span class="nb">puts</span> <span class="n">result</span> <span class="c1"># We have var1: some value 1 and var2: some value 2.</span>
</code></pre>
<p>With this technick you can use multi-level properties, e.g.:</p>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"tomcat"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/local/Cellar/tomcat7/7.0.56/libexec"</span><span class="p">,</span><span class="w">
</span><span class="s2">"deploy_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{tomcat.home}/webapps"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"jboss"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/local/Cellar/jboss-as5/5.1.0GA/libexec"</span><span class="p">,</span><span class="w">
</span><span class="s2">"deploy_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{jboss.home}/server/default/deploy"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<h2>Using ruby language for describing configuration</h2>
<p>Another idea is to use Ruby fragment (piece of code) to represent the configuration. In such a way you don’t have to <strong>a)</strong> invent yet another language to represent configuration and <strong>b)</strong> you can use language’s expressiveness to have text interpolation at the moment when you really need it. For example:</p>
<pre class="highlight ruby"><code><span class="c1"># .test_config</span>
<span class="n">rails_env</span> <span class="o">=</span> <span class="s2">"production"</span>
<span class="n">ant_home</span> <span class="o">=</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'ANT_HOME'</span><span class="p">]</span> <span class="o">||</span> <span class="s2">"</span><span class="si">#{</span><span class="no">ENV</span><span class="p">[</span><span class="s1">'HOME'</span><span class="p">]</span><span class="si">}</span><span class="s2">/apache-ant-1.8.3"</span>
<span class="n">project_name</span> <span class="o">=</span> <span class="s2">"web_app_builder_test"</span>
<span class="n">gems_to_reject</span> <span class="o">=</span> <span class="sx">%w(bundler)</span>
<span class="n">groups_to_reject</span> <span class="o">=</span> <span class="sx">%w(test)</span>
<span class="n">groups_to_reject</span> <span class="o"><<</span> <span class="s1">'development'</span> <span class="k">unless</span> <span class="sx">%w(development staging)</span><span class="p">.</span><span class="nf">include?</span> <span class="n">rails_env</span><span class="p">.</span><span class="nf">to_sym</span>
<span class="n">author</span> <span class="o">=</span> <span class="s2">"Alexander Shvets"</span>
<span class="n">templates_dir</span> <span class="o">=</span> <span class="s2">"config/templates"</span>
</code></pre>
<p>You can read this file and then convert it to a hash with the help of ruby <strong>eval</strong> method. Complete
code is implemented as part of <a href="https://github.com/shvets/meta_methods">meta_methods</a> gem:</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'meta_methods'</span>
<span class="n">content</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="s2">".test_config"</span><span class="p">)</span>
<span class="nb">hash</span> <span class="o">=</span> <span class="no">MetaMethods</span><span class="o">::</span><span class="no">Core</span><span class="p">.</span><span class="nf">instance</span><span class="p">.</span><span class="nf">block_to_hash</span> <span class="n">content</span>
</code></pre>
<h2>Conclusion</h2>
<p>All technics described above are implemented as separate <a href="https://github.com/shvets/config-file">config-file</a> gem. You can use it to read from 3 most popular formats.</p>
<ul>
<li>from yaml:</li>
</ul>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'config_file'</span>
<span class="n">config_file</span> <span class="o">=</span> <span class="no">ConfigFile</span><span class="p">.</span><span class="nf">new</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">config_file</span><span class="p">.</span><span class="nf">read</span> <span class="s2">"spec/config/test_config.yaml"</span>
<span class="nb">puts</span> <span class="n">config</span>
</code></pre>
<ul>
<li>from json:</li>
</ul>
<pre class="highlight ruby"><code><span class="n">config</span> <span class="o">=</span> <span class="n">config_file</span><span class="p">.</span><span class="nf">read</span> <span class="s2">"spec/config/test_config.json"</span>
<span class="nb">puts</span> <span class="n">config</span>
</code></pre>
<ul>
<li>from ruby:</li>
</ul>
<pre class="highlight ruby"><code><span class="n">config</span> <span class="o">=</span> <span class="n">config_file</span><span class="p">.</span><span class="nf">read</span> <span class="s2">"spec/config/.test_config"</span><span class="p">,</span> <span class="s2">".rb"</span>
<span class="nb">puts</span> <span class="n">config</span>
</code></pre>
<p>or register your own configuration format. Below is support for xml format:</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'nokogiri'</span>
<span class="nb">require</span> <span class="s1">'active_support/core_ext/hash'</span>
<span class="nb">require</span> <span class="s1">'config_file/config_file'</span>
<span class="k">class</span> <span class="nc">ConfigType</span>
<span class="k">class</span> <span class="nc">Xml</span>
<span class="nc">ConfigFile</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">extensions</span>
<span class="p">[</span><span class="s2">".xml"</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">read</span> <span class="n">file_name</span>
<span class="n">doc</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">file_name</span><span class="p">))</span>
<span class="no">HashWithIndifferentAccess</span><span class="p">.</span><span class="nf">new</span> <span class="no">Hash</span><span class="p">.</span><span class="nf">from_xml</span><span class="p">(</span><span class="n">doc</span><span class="p">.</span><span class="nf">to_s</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>and then use it:</p>
<pre class="highlight ruby"><code><span class="n">config_file</span> <span class="o">=</span> <span class="no">ConfigFile</span><span class="p">.</span><span class="nf">new</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">config_file</span><span class="p">.</span><span class="nf">read</span> <span class="s2">"spec/config/test_config.xml"</span>
<span class="nb">puts</span> <span class="n">config</span>
</code></pre>
Configuring Linux Box for Ruby/Rails Development with scripts/blog/2014/11/01/configuring_linux_box_with_scripts.html2014-10-31T20:00:00-04:002014-10-31T20:00:00-04:00Article Author<h1>Configuring Linux Box for Ruby/Rails Development with scripts</h1>
<h2>Introduction</h2>
<p>Why do we need virtualization in development?</p>
<ul>
<li><p>We want to have <strong>same environment for all developers</strong>, no matter on what platform they are working now.</p></li>
<li><p>We are <strong>working on multiple projects</strong> on same <strong>computer unit</strong>. As a result, suddenly your computer has “hidden”,
hard-to-discover inter-project dependencies or different versions of the same library.</p></li>
<li><p>We want to run Continuous Integration Server jobs that start services on <strong>same ports</strong> for different set
of acceptance tests (isolated jobs).</p></li>
<li><p>To overcome <strong>“It works on my machine!”</strong> syndrome - when development environment is different from production
environment.</p></li>
<li><p>Sometimes required software is <strong>not available</strong> on developer’s platform. Example: 64-bit instant client for oracle
was broken for almost two years on OSX >= 10.7.</p></li>
<li><p><strong>Development for PAAS</strong>, such as Heroku, Engine Yard etc. You can find and build virtualization that is pretty
close to your platform.</p></li>
</ul>
<p>We will take a look at how we can do provisioning for <strong>Vagrant</strong> and <strong>Docker</strong>. Both tools are built on top
of <strong>VirtualBox</strong>.</p>
<h2>Installing and configuring Vagrant</h2>
<p><strong>Vagrant</strong> is the wrapper around VirtualBox. It is a tool for managing virtual machines via simple to use
<strong>command line</strong> interface. With this tool you can work in a clean environment based on a standard
template - <strong>base box</strong>.</p>
<p>In order to use Vagrant you have to install these programs:</p>
<ul>
<li><p><a href="https://www.virtualbox.org/wiki/Downloads">VirtualBox</a>. Download it from dedicated web site and install as native program. You can use it
in UI mode, but it’s not required.</p></li>
<li><p><a href="http://www.vagrantup.com">Vagrant</a>. Before it was distributed as ruby gem, now it’s packaged as <strong>native application</strong>.
Once installed, it will be accessible from command line as <strong>vagrant</strong> command.</p></li>
</ul>
<p>or</p>
<pre class="highlight shell"><code>brew install caskroom/cask/brew-cask
brew cask install virtualbox
brew cask install vagrant
</code></pre>
<p>You have to decide what linux image fits your needs. In our case we use <strong>Ubuntu 14.04 LTS 64-bit</strong> image -
it is identified with <strong>“ubuntu/trusty64”</strong> key. You can see other images <a href="https://vagrantcloud.com/discover">here</a>.</p>
<p>Download and install it:</p>
<pre class="highlight shell"><code> vagrant box add ubuntu/trusty64 https://vagrantcloud.com/ubuntu/boxes/trusty64
</code></pre>
<p>Initialize it:</p>
<pre class="highlight shell"><code>vagrant init ubuntu/trusty64
</code></pre>
<p>This command creates <strong>Vagrantfile</strong> file in the root of your project. Below is an example of such a file:</p>
<pre class="highlight ruby"><code><span class="c1"># -*- mode: ruby -*-</span>
<span class="c1"># vi: set ft=ruby :</span>
<span class="no">VAGRANTFILE_API_VERSION</span> <span class="o">=</span> <span class="s2">"2"</span>
<span class="no">Vagrant</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span><span class="no">VAGRANTFILE_API_VERSION</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">box</span> <span class="o">=</span> <span class="s2">"ubuntu/trusty64"</span>
<span class="k">end</span>
</code></pre>
<p>You can do various commands with <strong>vagrant</strong> tool. For example:</p>
<pre class="highlight shell"><code>vagrant up <span class="c"># starts up: creates and configures guest machine</span>
vagrant <span class="nb">suspend</span> <span class="c"># suspends the guest machine</span>
vagrant halt <span class="c"># shuts down the running machine</span>
vagrant reload <span class="c"># vagrant halt; vagrant up</span>
vagrant destroy <span class="c"># stops machine and destroys all related resources</span>
vagrant provision <span class="c"># perform provisioning for machine</span>
vagrant box remove ubuntu/trusty64 <span class="c"># removes a box from vagrant</span>
</code></pre>
<p>You also can package currently running VirtualBox environment into reusable box:</p>
<pre class="highlight shell"><code>vagrant package --vagrantfile Vagrantfile --output linux_provision.box
</code></pre>
<p>After <strong>Vagrantfile</strong> is generated, you can start your base box:</p>
<pre class="highlight shell"><code>vagrant up
</code></pre>
<p>Now you have a <strong>fully running virtual machine</strong> in VirtualBox. You can access it through <strong>vagrant ssh</strong> command:</p>
<pre class="highlight shell"><code>vagrant ssh
</code></pre>
<p>or <strong>directly via ssh</strong> (use <strong>vagrant</strong> password for <strong>vagrant</strong> user and port <strong>2222</strong>, this port is used as default
by vagrant for ssh connections):</p>
<pre class="highlight shell"><code>ssh vagrant@127.0.0.1 -p 2222
</code></pre>
<p>You can assign IP address for your linux box, e.g.:</p>
<pre class="highlight ruby"><code><span class="no">Vagrant</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span><span class="no">VAGRANTFILE_API_VERSION</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">network</span> <span class="s2">"private_network"</span><span class="p">,</span> <span class="ss">ip: </span><span class="s2">"22.22.22.22"</span>
<span class="k">end</span>
</code></pre>
<p>With this configuration you can access ssh on default port:</p>
<pre class="highlight shell"><code>ssh vagrant@22.22.22.22
</code></pre>
<p>Your initial setup of linux box is completed now and ready to use.</p>
<h2>Installing and configuring Docker</h2>
<p>Docker helps you create and manage <strong>Linux containers</strong> - extremely lightweight VMs. Containers
allow code to run in isolation from other containers. They safely share the machine’s resources,
all without the overhead of a hypervisor.</p>
<p>In order to use Docker you have to install these programs:</p>
<ul>
<li><p><a href="https://www.virtualbox.org/wiki/Downloads">VirtualBox</a>.</p></li>
<li><p><a href="http://boot2docker.io">boot2docker</a>. You need to install it only for non-Linux environment.</p></li>
</ul>
<p>or </p>
<pre class="highlight shell"><code> brew install caskroom/cask/brew-cask
brew cask install virtualbox
brew cask install docker
</code></pre>
<p>boot2docker is a lightweight Linux image made specifically to run Docker containers. It runs completely from
RAM, weighs approximately 27 MB and boots in about 5 seconds.</p>
<p>We’ll run the Docker client natively on OSX, but the Docker server will run inside our boot2docker VM. This also
means that boot2docker, not OSX, is the Docker host.</p>
<p>This command will create <strong>boot2docker-vm</strong> virtual machine:</p>
<pre class="highlight shell"><code>boot2docker init
</code></pre>
<p>Go to VirtualBox UI - new VM will be added.</p>
<p>Start it up:</p>
<pre class="highlight shell"><code>boot2docker up
</code></pre>
<p>or shut it down:</p>
<pre class="highlight shell"><code>boot2docker down
</code></pre>
<p>Upgrade Boot2docker VM image:</p>
<pre class="highlight shell"><code>boot2docker stop
boot2docker download
boot2docker up
</code></pre>
<p>When docker daemon first started, it gives you recommendation about how to run docker client.
It needs to know where docker is running, e.g.:</p>
<pre class="highlight shell"><code><span class="nb">export </span><span class="nv">DOCKER_HOST</span><span class="o">=</span>tcp://192.168.59.103:2375
</code></pre>
<p>You have to setup it globally in <strong>.bash_profile</strong> file or specify it each time when docker client gets started.
Or, you can run this command each time:</p>
<pre class="highlight shell"><code><span class="k">$(</span>boot2docker shellinit<span class="k">)</span>
</code></pre>
<p>This will set the required environment variables. </p>
<p>You can access boot2docker over ssh (user: <strong>docker</strong>, password: <strong>tcuser</strong>):</p>
<pre class="highlight shell"><code>boot2docker ssh
</code></pre>
<p>Download the small base image named <strong>busybox</strong>:</p>
<pre class="highlight shell"><code>docker pull busybox
</code></pre>
<p>Run and test docker as separate command:</p>
<pre class="highlight shell"><code>docker run busybox <span class="nb">echo</span> <span class="s2">"hello, linus!"</span>
</code></pre>
<p>or interactively:</p>
<pre class="highlight shell"><code>docker run -t -i busybox /bin/sh
</code></pre>
<h2>Install and confige linux_provision gem</h2>
<p>Both programs - Vagrant and Docker - have their own ways to serve provisioning. Vagrant is doing it with the help of
<strong>provision</strong> attribute. Example with simple shell script:</p>
<pre class="highlight ruby"><code><span class="no">Vagrant</span><span class="o">::</span><span class="no">Config</span><span class="p">.</span><span class="nf">run</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">provision</span> <span class="ss">:shell</span><span class="p">,</span> <span class="ss">:path</span> <span class="o">=></span> <span class="s2">"bootstrap.sh"</span>
<span class="k">end</span>
</code></pre>
<p>or with chef solo:</p>
<pre class="highlight ruby"><code><span class="no">Vagrant</span><span class="o">::</span><span class="no">Config</span><span class="p">.</span><span class="nf">run</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">provision</span> <span class="ss">:chef_solo</span> <span class="k">do</span> <span class="o">|</span><span class="n">chef</span><span class="o">|</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
<span class="k">end</span>
</code></pre>
<p>Docker also lets you do provisioning in form of <strong>RUN</strong> command:</p>
<pre class="highlight plaintext"><code># Dockerfile
RUN apt-get -y -q install postgresql-9.3
</code></pre>
<p>After multiple experiments with provisions both from Vagrant and Docker it was discovered that it is not convenient
to use. It does not let you to easy install or uninstall separate packages. It’s better to do it as
<strong>set of independent scripts</strong>, separated completely from Docker or Vagrant.</p>
<p><strong>linux_provision</strong> gem is the set of such shell scripts - they install various components like postgres server, rvm,
ruby etc. with the help of thor or rake script. You can see other gems that use similar approach :
for <a href="https://github.com/shvets/oracle_client_provision">Oracle Instant Client</a> installation and for <a href="https://github.com/shvets/osx_provision">OSX</a> provision.</p>
<p>In order to use this gem add this line to your application’s Gemfile:</p>
<pre class="highlight shell"><code>gem <span class="s1">'linux_provision'</span>
</code></pre>
<p>And then execute:</p>
<pre class="highlight shell"><code>bundle
</code></pre>
<p>Before you can start using <strong>linux_provision</strong> gem within your project, you need to configure it. Do the following:</p>
<ul>
<li>Create configuration file (e.g. <strong>.linux_provision.json</strong>) in json format at the root of your project. It will
define your environment:</li>
</ul>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"node"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">...</span><span class="w">
</span><span class="err">}</span><span class="p">,</span><span class="w">
</span><span class="s2">"project"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{node.home}/demo"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ruby_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.9.3"</span><span class="p">,</span><span class="w">
</span><span class="s2">"gemset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"linux_provision_demo"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"postgres"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"hostname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"localhost"</span><span class="p">,</span><span class="w"> </span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"postgres"</span><span class="p">,</span><span class="w"> </span><span class="s2">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"postgres"</span><span class="p">,</span><span class="w">
</span><span class="s2">"app_user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pg_user"</span><span class="p">,</span><span class="w"> </span><span class="s2">"app_password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pg_password"</span><span class="p">,</span><span class="w">
</span><span class="s2">"app_schemas"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"my_project_test"</span><span class="p">,</span><span class="w"> </span><span class="s2">"my_project_dev"</span><span class="p">,</span><span class="w"> </span><span class="s2">"my_project_prod"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<p>Variables defined in this file are used by underlying shell scripts provided by the gem.</p>
<p>In <strong>node</strong> section you describe destination computer where you want to install this provision.</p>
<p>In <strong>project</strong> section you keep project-related info, like project <strong>home</strong>, project <strong>gemset name</strong> and <strong>ruby version</strong>.</p>
<p>Last <strong>postgres</strong> section contains information about your postgres server.</p>
<ul>
<li>Provide execution script</li>
</ul>
<p>Library itself if written in ruby, but for launching its code it’s more convenient to use <strong>rake</strong> or <strong>thor</strong> tool.
Here I provide thor script as an example:</p>
<pre class="highlight ruby"><code><span class="c1"># thor/linux_install.thor</span>
<span class="vg">$:</span> <span class="o"><<</span> <span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="kp">__FILE__</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'/../lib'</span><span class="p">)</span>
<span class="nb">require</span> <span class="s1">'linux_provision'</span>
<span class="k">class</span> <span class="nc">LinuxInstall</span> <span class="o"><</span> <span class="no">Thor</span>
<span class="vi">@installer</span> <span class="o">=</span> <span class="no">LinuxProvision</span><span class="p">.</span><span class="nf">new</span> <span class="nb">self</span><span class="p">,</span> <span class="s2">".linux_provision.json"</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="kp">attr_reader</span> <span class="ss">:installer</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"general"</span><span class="p">,</span> <span class="s2">"Installs general packages"</span>
<span class="k">def</span> <span class="nf">general</span>
<span class="n">invoke</span> <span class="ss">:prepare</span>
<span class="n">invoke</span> <span class="ss">:rvm</span>
<span class="n">invoke</span> <span class="ss">:ruby</span>
<span class="n">invoke</span> <span class="ss">:postgres</span>
<span class="n">invoke</span> <span class="ss">:mysql</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>You can execute separate commands from script directly with <strong>invoke</strong> thor command. Below is fragment of such script:</p>
<pre class="highlight shell"><code><span class="c">#!/bin/sh</span>
<span class="c">#######################################</span>
<span class="o">[</span>prepare]
<span class="c"># Updates linux core packages</span>
sudo apt-get update
sudo apt-get install -y curl
sudo apt-get install -y g++
sudo apt-get install -y subversion
sudo apt-get install -y git
<span class="c">#######################################</span>
<span class="o">[</span>rvm]
<span class="c"># Installs rvm</span>
curl -L https://get.rvm.io | bash
<span class="c">#sudo chown -R vagrant /opt/vagrant_ruby</span>
<span class="c">#######################################</span>
<span class="o">[</span>ruby]
<span class="c"># Installs ruby</span>
<span class="nv">USER_HOME</span><span class="o">=</span><span class="s2">"#{node.home}"</span>
<span class="nb">source</span> <span class="nv">$USER_HOME</span>/.rvm/scripts/rvm
rvm install ruby-1.9.3
</code></pre>
<p>You can add your own scripts (e.g. demo_scripts.sh):</p>
<pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">LinuxInstall</span> <span class="o"><</span> <span class="no">Thor</span>
<span class="vi">@installer</span> <span class="o">=</span> <span class="no">LinuxProvision</span><span class="p">.</span><span class="nf">new</span> <span class="nb">self</span><span class="p">,</span>
<span class="s2">".linux_provision.json"</span><span class="p">,</span>
<span class="p">[</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"demo_scripts.sh"</span><span class="p">,</span> <span class="no">File</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="kp">__FILE__</span><span class="p">))]</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
</code></pre>
<p>We defined 2 new commands in demo_script.sh:</p>
<pre class="highlight shell"><code><span class="c">#!/bin/sh</span>
<span class="c">##############################</span>
<span class="o">[</span>project]
<span class="c"># Installs demo sinatra project</span>
<span class="nv">USER_HOME</span><span class="o">=</span><span class="s2">"#{node.home}"</span>
<span class="nv">APP_HOME</span><span class="o">=</span><span class="s2">"#{project.home}"</span>
<span class="nb">cd</span> <span class="nv">$APP_HOME</span>
<span class="nb">source</span> <span class="nv">$USER_HOME</span>/.rvm/scripts/rvm
rvm use <span class="c">#{project.ruby_version}@#{project.gemset} --create</span>
bundle
rake db:migrate
<span class="c">##############################</span>
<span class="o">[</span>rackup]
<span class="c"># Starts sinatra demo application</span>
<span class="nv">USER_HOME</span><span class="o">=</span><span class="s2">"#{node.home}"</span>
<span class="nv">APP_HOME</span><span class="o">=</span><span class="s2">"#{project.home}"</span>
<span class="nb">cd</span> <span class="nv">$APP_HOME</span>
<span class="nb">source</span> <span class="nv">$USER_HOME</span>/.rvm/scripts/rvm
rvm use <span class="c">#{project.ruby_version}@#{project.gemset}</span>
rackup
</code></pre>
<h2>Demo application with Vagrant</h2>
<p>For testing purposes we have created demo web application (in <strong>demo</strong> folder) based on <a href="http://www.sinatrarb.com">sinatra</a> framework.</p>
<p>First, we need to inform Vagrant about the location of this application within virtual machine:</p>
<pre class="highlight ruby"><code><span class="no">Vagrant</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span><span class="no">VAGRANTFILE_API_VERSION</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">vm</span><span class="p">.</span><span class="nf">synced_folder</span> <span class="s2">"./demo"</span><span class="p">,</span> <span class="s2">"/home/vagrant/demo"</span>
<span class="k">end</span>
</code></pre>
<p>Second, we need to configure linux_provision gem to point to right domain/port and use correct user name/password:</p>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"node"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"22.22.22.22"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">remote</span><span class="w"> </span><span class="err">host,</span><span class="w"> </span><span class="err">see</span><span class="w"> </span><span class="s2">"config.vm.synced_folder"</span><span class="w">
</span><span class="s2">"port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"22"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">default</span><span class="w"> </span><span class="err">ssh</span><span class="w"> </span><span class="err">port</span><span class="w">
</span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"vagrant"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">vagrant</span><span class="w"> </span><span class="err">user</span><span class="w"> </span><span class="err">name</span><span class="w">
</span><span class="s2">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"vagrant"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">vagrant</span><span class="w"> </span><span class="err">user</span><span class="w"> </span><span class="err">password</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/vagrant"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w">
</span><span class="s2">"remote"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<p>Start your base box:</p>
<pre class="highlight shell"><code>vagrant up
</code></pre>
<p>Access linux box and find out this demo application’s home:</p>
<pre class="highlight shell"><code>ssh vagrant@22.22.22.22
<span class="nb">pwd</span> <span class="c"># /home/vagrant</span>
ls <span class="c"># demo</span>
<span class="nb">cd </span>demo
ls <span class="c"># content of demo folder</span>
</code></pre>
<p>These commands from <strong>linux_provision</strong> gem will build your environment for the demo project (install rvm, ruby,
postgres, postgres user and posters tables):</p>
<pre class="highlight shell"><code>thor linux_install:prepare
thor linux_install:rvm
thor linux_install:ruby
thor linux_install:postgres
thor linux_install:postgres_create_user
thor linux_install:postgres_create_schemas
</code></pre>
<p>Initialize demo project and run sinatra application:</p>
<pre class="highlight shell"><code>thor linux_install:project
thor linux_install:rackup
</code></pre>
<p>Now you can access application from your favorite browser:</p>
<pre class="highlight shell"><code>open http://22.22.22.22:9292
</code></pre>
<h2>Demo application with Docker</h2>
<p>You need to do very similar steps as with Vagrant. The only difference is in <strong>linux_provision.json</strong> file
you have to point to different host, port and user:</p>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"node"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.59.103"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">remote</span><span class="w"> </span><span class="err">host,</span><span class="w"> </span><span class="err">see</span><span class="w"> </span><span class="err">boot2docker</span><span class="w"> </span><span class="err">ip</span><span class="w">
</span><span class="s2">"port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42222"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">ssh</span><span class="w"> </span><span class="err">port</span><span class="w"> </span><span class="err">in</span><span class="w"> </span><span class="err">docker</span><span class="w">
</span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"vagrant"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">vagrant</span><span class="w"> </span><span class="err">user</span><span class="w"> </span><span class="err">name</span><span class="w">
</span><span class="s2">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"vagrant"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">vagrant</span><span class="w"> </span><span class="err">user</span><span class="w"> </span><span class="err">password</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/vagrant"</span><span class="p">,</span><span class="w">
</span><span class="s2">"remote"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<p>Our Dockerfile is responsible for the following base steps:</p>
<ul>
<li><p>Install Ubuntu 14.4.</p></li>
<li><p>Install sshd (for enabling ssh).</p></li>
<li><p>Create vagrant user (just to be in-synch with Vagrant example).</p></li>
<li><p>Reveal project home as /home/vagrant/demo.</p></li>
<li><p>Expose port 9292 (our sinatra application).</p></li>
</ul>
<p>Here is example:</p>
<pre class="highlight plaintext"><code>FROM ubuntu:14.04
MAINTAINER Alexander Shvets "alexander.shvets@gmail.com"
# 1. Update system
RUN sudo apt-get update
RUN sudo locale-gen en_US.UTF-8
# 2. Install sshd
RUN sudo apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo 'root:root' |chpasswd
RUN sed --in-place=.bak 's/without-password/yes/' /etc/ssh/sshd_config
EXPOSE 22
CMD /usr/sbin/sshd -D
# 3. Create vagrant user
RUN groupadd vagrant
RUN useradd -d /home/vagrant -g vagrant -m -s /bin/bash vagrant
RUN sudo sed -i '$a vagrant ALL=(ALL) NOPASSWD: ALL' /etc/sudoers
RUN echo vagrant:vagrant | chpasswd
RUN sudo chown -R vagrant /home/vagrant
# 4. Prepare directories for the project
# Add project dir to docker
ADD . /home/vagrant/demo
WORKDIR /home/vagrant/demo
EXPOSE 9292
</code></pre>
<p>Build docker image and run it:</p>
<pre class="highlight shell"><code>docker build -t demo demo
docker run -d -p 42222:22 -p 9292:9292 --name demo demo
</code></pre>
<p>As you can see, we map port 22 inside docker to port 42222 outside. It means that when we hit port 42222 with
regular telnet or ssh tool, we’ll hit service inside the docker.</p>
<p>You can access virtual machine via ssh:</p>
<pre class="highlight shell"><code>ssh vagrant@192.168.59.103 -p 42222
</code></pre>
<p>Now you can do your provision - it’s exactly the same as with Vagrant example:</p>
<pre class="highlight shell"><code>thor linux_install:prepare
thor linux_install:rvm
thor linux_install:ruby
thor linux_install:postgres
thor linux_install:postgres_create_user
thor linux_install:postgres_create_schemas
thor linux_install:project
thor linux_install:rackup
</code></pre>
<p>After provisioning and starting server try to access your application from the browser:</p>
<pre class="highlight shell"><code>open http://192.168.59.103:9292
</code></pre>
Reading from external source in gherkin scripts/blog/2014/10/19/adding_source_to_gherkin_script.html2014-10-18T20:00:00-04:002014-10-18T20:00:00-04:00Article Author<h1>Reading from external source in gherkin (cucumber) scripts</h1>
<h2>Introduction</h2>
<p>Gherkin is simple English-like language for representing <strong>Given-When-Then</strong> sequences to
support Behavior Driven Development (BDD). It is also a parser for the language itself.</p>
<p>Based on this simple language you can build convenient tools that make communications
between Developers, Testers, Business Analysts, Product Owners and other stakeholders <strong>easy</strong>.</p>
<p>For example, these tools use gherkin as a language for representing behavior:</p>
<ul>
<li><p><a href="https://github.com/cucumber/cucumber/tree/master">Cucumber</a> is a tool for running automated tests written in plain language.</p></li>
<li><p><a href="http://robots.thoughtbot.com/turnip-a-tasty-cucumber-alternative">Turnip</a> is a Gherkin extension for RSpec.
It allows you to write tests in Gherkin and run them through your RSpec environment so
you can write cucumber features in RSpec.</p></li>
<li><p><a href="https://github.com/codegram/spinach">Spinach</a> is a high-level BDD framework that leverages the expressive
Gherkin language to help you define executable specifications of your application or
library’s acceptance criteria.</p></li>
</ul>
<h2>Problem</h2>
<p>Gherkin language lets you build simple Given-When-Then sequences and when you need
to repeat same set of sequences, you can use <a href="https://github.com/cucumber/cucumber/wiki/Scenario-Outlines">Scenario Outline</a>.
Unfortunately, Scenario Outline lets you use data defined only inside script only. It does
not have the ability to get data from external source like file or database connection.</p>
<h2>Solution</h2>
<p>Below is an example of Gherkin (Cucumber) feature script with Scenario Outline:</p>
<pre class="highlight gherkin"><code><span class="kd">Feature</span><span class="p">:</span> Using Wikipedia
<span class="kn">Background</span><span class="p">:</span> within wikipedia.com context
<span class="nf">Given</span> I am within wikipedia.com
<span class="nt">@selenium</span>
<span class="kn">Scenario Outline</span><span class="p">:</span> Searching with selenium for a term with submit
<span class="nf">Given</span> I am on wikipedia.com
<span class="nf">When</span> I enter word <span class="nv"><keyword></span>
<span class="nf">And</span> I click submit button
<span class="err">Then I should see keyword results</span><span class="p">:</span> <span class="err"><keyword></span>
<span class="nn">Examples</span><span class="p">:</span>
<span class="p">|</span> <span class="nv">keyword</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">Capybara</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">Wombat</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">Echidna</span> <span class="p">|</span>
</code></pre>
<p>It reads from <strong>Examples</strong> section and execute script 3 times for different <strong><keyword></strong>.</p>
<p>We want to be able to externalize keywords, say in <strong>data.csv</strong> file:</p>
<pre class="highlight plaintext"><code>Capybara
Wombat
Echidna
</code></pre>
<p>and then replace section with data with the path to external file.</p>
<pre class="highlight gherkin"><code><span class="kd">Feature</span><span class="p">:</span> Using Wikipedia
<span class="kn">Background</span><span class="p">:</span> within wikipedia.com context
<span class="nf">Given</span> I am within wikipedia.com
<span class="nt">@selenium</span>
<span class="kn">Scenario Outline</span><span class="p">:</span> Searching with selenium for a term with submit
<span class="nf">Given</span> I am on wikipedia.com
<span class="nf">When</span> I enter word <span class="nv"><keyword></span>
<span class="nf">And</span> I click submit button
<span class="err">Then I should see keyword results</span><span class="p">:</span> <span class="err"><keyword></span>
<span class="nn">Examples</span><span class="p">:</span>
<span class="p">|</span> <span class="nv">keyword</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">file:spec/features/data.csv</span> <span class="p">|</span>
</code></pre>
<p>It is possible to do with a little ruby metaprogramming and code is available <a href="https://github.com/shvets/acceptance_test/blob/master/lib/acceptance_test/gherkin_ext.rb">here</a>.</p>
<p>The idea is to locate the code where gherkin reads that data and replace it with your code
dynamically in memory.</p>
<p>The code introduces hook on gherkin level, so it will work for all tools.</p>
<p>Below it an example how to use it:</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'acceptance_test/gherkin_ext'</span>
<span class="n">data_reader</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="k">do</span> <span class="o">|</span><span class="n">source_path</span><span class="o">|</span>
<span class="no">CSV</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="n">source_path</span><span class="p">))</span>
<span class="k">end</span>
<span class="no">GherkinExt</span><span class="p">.</span><span class="nf">enable_external_source</span> <span class="n">data_reader</span>
</code></pre>
Configuring Macbook for Ruby/Rails Development with automated scripts/blog/2014/09/13/configuring_macbook_with_scripts.html2014-09-12T20:00:00-04:002014-09-12T20:00:00-04:00Article Author<h1>Configuring Macbook for Ruby/Rails Development with automated scripts</h1>
<h2>Introduction</h2>
<p>When you plan to do web development, you need to do few steps, like:</p>
<ul>
<li>installing installers (homebrew, rvm)</li>
<li>installing support for various languages (ruby, java, nodejs, python, c, c++);</li>
<li>installing databases (mysql, postgres, oracle etc);</li>
<li>configuring databases (creating users, schemas, populating initial data);</li>
<li>installing additional servers (apache, passenger, jenkins, selenium);</li>
<li>starting/stopping services.</li>
</ul>
<p>Usually all those steps are made manually. If it needs to be done only once, it’s OK.
But next time when you try to reproduce all steps on new computer or explain
new developer in the team, you can forget some details of it or forget completely
what needs to be done.</p>
<p>As a common rule, it’s nice to have this information documented, or even better -
automated in form of scripts.</p>
<p>There are numerous instructions around the Internet about how to configure your MacBook to work
with Ruby and Rails. I have my own <a href="http://shvets.github.io/blog/2014/04/19/configure_macbook.html">blog entry</a> on the same topic.
The problem with all of them is that they ought to be executed <strong>manually</strong>. But we want to do
it <strong>automatically</strong> with the help of scripts.</p>
<p>When you try to solve this type of problem, you have <strong>“the chicken and the egg” problem</strong> - in order to
do automatic provision, you have to have pre-installed languages/libraries. You don’t want
to do everything in form of <strong>low-level shell script</strong>, but rather in <strong>high-level language</strong>, like ruby,
python or node. Unfortunately such high-level scripts use external libraries that need to be downloaded
and installed first before you can run scripts.</p>
<p>To make it simple and straightforward, we are going to do it <strong>remotely</strong>, e.g. you have one computer
<strong>with pre-installed language and libraries</strong> and we will install all required programs over <strong>ssh</strong>
on clean computer. It means that <strong>you have to enable ssh access</strong> on this computer. This idea is somewhat
similar to what <a href="http://capistranorb.com">capistrano</a> or <a href="https://github.com/opscode/chef">chef</a> does. Why don’t we use them here? Look at
this <a href="http://shvets.github.io/blog/2013/12/21/script_executor.html">article</a> for the explanation.</p>
<p>I have built new ruby gem called <a href="https://github.com/shvets/osx_provision">osx_provision</a>, that provides automated scripts
for configuring Macbook. This article is about how to install, configure and use it with your project.
Look at my previous article if you need to understand how to do it manually.</p>
<h2>Install</h2>
<p>Add this line to your application’s Gemfile:</p>
<pre class="highlight shell"><code>gem <span class="s1">'osx_provision'</span>
</code></pre>
<p>And then execute it:</p>
<pre class="highlight shell"><code>bundle
</code></pre>
<p>Or install it yourself as:</p>
<pre class="highlight shell"><code>gem install osx_provision
</code></pre>
<h2>Configure</h2>
<p>Before you can start using <strong>osx_provision</strong> gem within your project, you need to do the following:</p>
<ul>
<li>Create configuration file (e.g. .osx_provision.json) in json format at the root of your project.
It will define your environment:</li>
</ul>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"node"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"127.0.0.1"</span><span class="p">,</span><span class="w">
</span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ENV['USER']"</span><span class="p">,</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ENV['HOME']"</span><span class="p">,</span><span class="w">
</span><span class="s2">"port"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="s2">"remote"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"project"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{node.home}/work/my_project"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ruby_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.9.3"</span><span class="p">,</span><span class="w">
</span><span class="s2">"gemset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"new_gem"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"postgres"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"hostname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"localhost"</span><span class="p">,</span><span class="w"> </span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"postgres"</span><span class="p">,</span><span class="w"> </span><span class="s2">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"postgres"</span><span class="p">,</span><span class="w">
</span><span class="s2">"app_user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pg_user"</span><span class="p">,</span><span class="w"> </span><span class="s2">"app_password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pg_password"</span><span class="p">,</span><span class="w">
</span><span class="s2">"app_schemas"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"my_project_test"</span><span class="p">,</span><span class="w"> </span><span class="s2">"my_project_dev"</span><span class="p">,</span><span class="w"> </span><span class="s2">"my_project_prod"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<p>Variables defined in this file are used by underlying shell scripts provided by this gem.
You can also add your own shell script (see details later) - in this case .osx_provision.json
will have your variables as well.</p>
<p>Next <strong>node</strong> section describes your destination computer where you want to install this provision.
In this example we do provisioning locally, but it’s possible to do it on remote machine, e.g.:</p>
<pre class="highlight json"><code><span class="w"> </span><span class="err">...</span><span class="w">
</span><span class="s2">"node"</span><span class="err">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.1.2"</span><span class="p">,</span><span class="w">
</span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some_remote_user"</span><span class="p">,</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some_remote_password"</span><span class="p">,</span><span class="w">
</span><span class="s2">"port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"22"</span><span class="p">,</span><span class="w">
</span><span class="s2">"remote"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="err">,</span><span class="w">
</span><span class="err">...</span><span class="w">
</span></code></pre>
<p>In <strong>project</strong> section you keep project-related info, like project <strong>home</strong>, project <strong>gemset name</strong>
and <strong>ruby version</strong>.</p>
<p>If you need to refer variable form another section, use “dot” notation, like <strong>#{node.home}</strong>. It is
possible thanks to another gem called <a href="https://github.com/shvets/text-interpolator">text-interpolator</a>.</p>
<p>Last <strong>postgres</strong> section contains information about your postgres server and what database user and
schemas need to be created. In our example we describe that we want to create <strong>pg_user</strong> with <strong>password</strong>
and 3 schemas: <strong>my_project_test</strong>, <strong>my_project_dev</strong>, <strong>my_project_prod</strong>. Postgres server
itself is located at <strong>localhost</strong> address.</p>
<p>You can also add <strong>mysql</strong> section - it’s also supported.</p>
<ul>
<li>Provide execution script</li>
</ul>
<p>Library itself if written in ruby, but for launching its code you have to use <strong>rake</strong> or <strong>thor</strong> tool.
Here I provide thor script as an example:</p>
<pre class="highlight ruby"><code><span class="c1"># thor/osx_install.thor</span>
<span class="vg">$:</span> <span class="o"><<</span> <span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="kp">__FILE__</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'/../lib'</span><span class="p">)</span>
<span class="nb">require</span> <span class="s1">'osx_provision'</span>
<span class="k">class</span> <span class="nc">OsxInstall</span> <span class="o"><</span> <span class="no">Thor</span>
<span class="vi">@installer</span> <span class="o">=</span> <span class="no">OsxProvision</span><span class="p">.</span><span class="nf">new</span> <span class="nb">self</span><span class="p">,</span> <span class="s2">".osx_provision.json"</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="kp">attr_reader</span> <span class="ss">:installer</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"all"</span><span class="p">,</span> <span class="s2">"Installs all required packages"</span>
<span class="k">def</span> <span class="nf">all</span>
<span class="n">invoke</span> <span class="ss">:brew</span> <span class="c1"># execute command defined in shell script</span>
<span class="n">invoke</span> <span class="ss">:rvm</span>
<span class="n">invoke</span> <span class="ss">:qt</span>
<span class="n">invoke</span> <span class="ss">:init_launch_agent</span>
<span class="n">invoke</span> <span class="ss">:postgres</span>
<span class="n">invoke</span> <span class="ss">:postgres_restart</span>
<span class="n">invoke</span> <span class="ss">:jenkins</span>
<span class="n">invoke</span> <span class="ss">:jenkins_restart</span>
<span class="n">invoke</span> <span class="ss">:selenium</span>
<span class="n">invoke</span> <span class="ss">:ruby</span>
<span class="n">invoke</span> <span class="ss">:postgres_create_user</span>
<span class="n">invoke</span> <span class="ss">:postgres_create_schemas</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"postgres_create_schemas"</span><span class="p">,</span> <span class="s2">"Initializes postgres schemas"</span>
<span class="k">def</span> <span class="nf">postgres_create_schemas</span>
<span class="c1"># execute method from OsxProvision class - we need to read</span>
<span class="c1"># schema names from configuration file and create them in the loop</span>
<span class="no">OsxInstall</span><span class="p">.</span><span class="nf">installer</span><span class="p">.</span><span class="nf">postgres_create_schemas</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"postgres_drop_schemas"</span><span class="p">,</span> <span class="s2">"Drops postgres schemas"</span>
<span class="k">def</span> <span class="nf">postgres_drop_schemas</span>
<span class="no">OsxInstall</span><span class="p">.</span><span class="nf">installer</span><span class="p">.</span><span class="nf">postgres_drop_schemas</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>You can execute separate commands from script directly with <strong>invoke</strong> thor command. Below is fragment
of such script:</p>
<pre class="highlight shell"><code><span class="c">#!/bin/sh</span>
<span class="o">[</span>prepare]
mkdir <span class="c">#{home}/Library/LaunchAgents/</span>
<span class="o">[</span>brew]
ruby -e <span class="s2">"</span><span class="k">$(</span>curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install<span class="k">)</span><span class="s2">"</span>
brew update
brew doctor
brew tap homebrew/dupes
brew tap homebrew/versions
<span class="o">[</span>rvm]
<span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/usr/local/bin
<span class="nv">USER_HOME</span><span class="o">=</span><span class="s2">"#{node.home}"</span>
curl -L https://get.rvm.io | bash
<span class="nb">source</span> <span class="nv">$USER_HOME</span>/.rvm/scripts/rvm
</code></pre>
<p>All available scripts are located <a href="https://github.com/shvets/osx_provision/blob/master/lib/osx_provision/osx_provision_scripts.sh">here</a>. If you need more scripts, you can
create them locally (e.g. in (thor/osx_provision_scripts_my_project.sh)) and then initialize
thor script accordingly:</p>
<pre class="highlight ruby"><code><span class="c1"># thor/my_project_osx_provision_scripts.thor</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">class</span> <span class="no">OsxInstall</span> <span class="o"><</span> <span class="no">Thor</span>
<span class="vi">@installer</span> <span class="o">=</span> <span class="no">OsxProvision</span><span class="p">.</span><span class="nf">new</span> <span class="nb">self</span><span class="p">,</span> <span class="s2">".osx_provision.json"</span><span class="p">,</span>
<span class="p">[</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"my_project_osx_provision_scripts.sh"</span><span class="p">,</span> <span class="no">File</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="kp">__FILE__</span><span class="p">))]</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">end</span>
</code></pre>
<p>Below is the example of such script for running rails server in standard and debug mode:</p>
<pre class="highlight shell"><code><span class="c">#!/bin/sh</span>
<span class="c">##############################</span>
<span class="o">[</span>rails]
<span class="nv">USER_HOME</span><span class="o">=</span><span class="s2">"#{node.home}"</span>
<span class="nv">APP_HOME</span><span class="o">=</span><span class="s2">"#{project.home}"</span>
<span class="nb">cd</span> <span class="nv">$APP_HOME</span>
<span class="nb">source</span> <span class="nv">$USER_HOME</span>/.rvm/scripts/rvm
rvm use <span class="c">#{project.ruby_version}@#{project.gemset} --create</span>
bundle <span class="nb">exec </span>rails s
<span class="c">##############################</span>
<span class="o">[</span>rails_debug]
<span class="nv">USER_HOME</span><span class="o">=</span><span class="s2">"#{node.home}"</span>
<span class="nv">APP_HOME</span><span class="o">=</span><span class="s2">"#{project.home}"</span>
<span class="nb">cd</span> <span class="nv">$APP_HOME</span>
<span class="nb">source</span> <span class="nv">$USER_HOME</span>/.rvm/scripts/rvm
rvm use <span class="c">#{project.ruby_version}@#{project.gemset} --create</span>
<span class="nv">RAILS</span><span class="o">=</span><span class="sb">`</span>which rails<span class="sb">`</span>
bundle <span class="nb">exec </span>rdebug-ide --port 1234 --dispatcher-port 26162 -- <span class="nv">$RAILS</span> s
</code></pre>
<h2>Executing automated scripts</h2>
<p>First, take a look at the list of available thor command:</p>
<pre class="highlight shell"><code>thor -T
</code></pre>
<p>You can see something like this:</p>
<pre class="highlight shell"><code>osx_install
-----------
thor osx_install:all <span class="c"># Installs all required packages</span>
thor osx_install:app <span class="c"># Installs app</span>
thor osx_install:brew <span class="c"># brew</span>
thor osx_install:general <span class="c"># Installs general packages</span>
thor osx_install:git <span class="c"># git</span>
thor osx_install:jenkins <span class="c"># jenkins</span>
thor osx_install:jenkins_restart <span class="c"># jenkins_restart</span>
thor osx_install:mysql <span class="c"># mysql</span>
thor osx_install:mysql_create_schema <span class="c"># mysql_create_schema</span>
thor osx_install:mysql_create_schemas <span class="c"># Initializes mysql schemas</span>
thor osx_install:mysql_create_user <span class="c"># mysql_create_user</span>
thor osx_install:mysql_drop_schema <span class="c"># mysql_drop_schema</span>
thor osx_install:mysql_drop_schemas <span class="c"># Drops mysql schemas</span>
thor osx_install:mysql_drop_user <span class="c"># mysql_drop_user</span>
thor osx_install:mysql_restart <span class="c"># mysql_restart</span>
thor osx_install:npm <span class="c"># npm</span>
thor osx_install:package_installed <span class="c"># package_installed</span>
thor osx_install:postgres <span class="c"># postgres</span>
thor osx_install:postgres_create_schema <span class="c"># postgres_create_schema</span>
thor osx_install:postgres_create_schemas <span class="c"># Initializes postgres schemas</span>
thor osx_install:postgres_create_user <span class="c"># postgres_create_user</span>
thor osx_install:postgres_drop_schema <span class="c"># postgres_drop_schema</span>
thor osx_install:postgres_drop_schemas <span class="c"># Drops postgres schemas</span>
thor osx_install:postgres_drop_user <span class="c"># postgres_drop_user</span>
thor osx_install:postgres_restart <span class="c"># postgres_restart</span>
thor osx_install:postgres_start <span class="c"># postgres_start</span>
thor osx_install:postgres_stop <span class="c"># postgres_stop</span>
thor osx_install:prepare <span class="c"># prepare</span>
thor osx_install:qt <span class="c"># qt</span>
thor osx_install:ruby <span class="c"># ruby</span>
thor osx_install:rvm <span class="c"># rvm</span>
thor osx_install:selenium <span class="c"># selenium</span>
thor osx_install:selenium_restart <span class="c"># selenium_restart</span>
thor osx_install:service_started <span class="c"># service_started</span>
thor osx_install:svn <span class="c"># svn</span>
</code></pre>
<p>With this scripts you can execute them separately and all together:</p>
<pre class="highlight shell"><code>thor osx_install:brew
thor osx_install:rvm
thor osx_install:postgres
thor osx_install:brew
thor osx_install:all
</code></pre>
<p>To configure postgres only run this sequence:</p>
<pre class="highlight shell"><code>thor osx_install:postgres
thor osx_install:postgres_create_user
thor osx_install:postgres_create_schemas
thor osx_install:postgres_start
</code></pre>
<p>Enjoy the gem and let me know if other useful command can be added to it.</p>
Oracle Instant Client Provision/blog/2014/05/31/install_oracle_client.html2014-05-30T20:00:00-04:002014-05-30T20:00:00-04:00Article Author<h1>Oracle Instant Client Provision - thor-based tasks for facilitating Oracle Instant Client installation</h1>
<h2>Introduction</h2>
<p>Installing database driver for Oracle (activerecord-oracle_enhanced-adapter) is not a simple process.</p>
<ul>
<li>First, you need to install Oracle Instant Client.</li>
<li>Second, you have to install ruby wrapper around Instant Client (ruby-oci8 gem). On some platforms it
requires compiling source code.</li>
</ul>
<p>Only after that you can install oracle database driver.</p>
<p>Here we discuss how to do it for OSX 10 operating system.</p>
<h2>Create new gem group</h2>
<p>Create separate group (e.g. “oracle”) in Gemfile for oracle ruby-oci8 client wrapper. You have to keep
this gem in separate group because it requires special steps to be done before database driver
can be installed.</p>
<pre class="highlight ruby"><code><span class="c1"># Gemfile</span>
<span class="n">group</span> <span class="ss">:oracle</span> <span class="k">do</span>
<span class="n">gem</span> <span class="s2">"ruby-oci8"</span><span class="p">,</span> <span class="s2">"2.1.7"</span>
<span class="k">end</span>
</code></pre>
<h2>Install gems for the project (except ruby-oci8 gem)</h2>
<p>Install ruby gems for your project with bundler tool. You have to bypass oracle client wrapper installation:</p>
<pre class="highlight shell"><code>bundle install --without<span class="o">=</span>oracle
</code></pre>
<p><strong>without</strong> parameter will exclude <strong>oracle</strong> bundler group from the execution.</p>
<p>Check that you don’t have ruby-oci8 installed yet:</p>
<pre class="highlight shell"><code>gem list
</code></pre>
<h2>Download Oracle Instant Client</h2>
<p>Download Oracle Instant Client packages and save them locally (e.g. in “downloads” folder). You can find
installation packages on <strong><a href="http://www.oracle.com">www.oracle.com</a></strong> web site (you have to be registered user though).</p>
<h2>Configuration file</h2>
<p>Create configuration file for the installation (.oracle_client_provision.json) in the root of your project.
It will define where you have downloaded files and some other parameters:</p>
<pre class="highlight json"><code><span class="p">{</span><span class="w">
</span><span class="s2">"node"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"127.0.0.1"</span><span class="p">,</span><span class="w">
</span><span class="s2">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ENV['USER']"</span><span class="p">,</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ENV['HOME']"</span><span class="p">,</span><span class="w">
</span><span class="s2">"port"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="s2">"remote"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="s2">"project"</span><span class="err">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{node.home}/my_project_with_oracle_driver"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ruby_home"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ENV['MY_RUBY_HOME']"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"instant_client"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"src_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{project.home}/downloads"</span><span class="p">,</span><span class="w">
</span><span class="s2">"dest_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{oracle.oracle_base}/instantclient_11_2"</span><span class="p">,</span><span class="w">
</span><span class="s2">"basic_zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{instant_client.src_dir}/instantclient-basic-macos.x64-#{oracle.oracle_version}.zip"</span><span class="p">,</span><span class="w">
</span><span class="s2">"sdk_zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{instant_client.src_dir}/instantclient-sdk-macos.x64-#{oracle.oracle_version}.zip"</span><span class="p">,</span><span class="w">
</span><span class="s2">"sqlplus_zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{instant_client.src_dir}/instantclient-sqlplus-macos.x64-#{oracle.oracle_version}.zip"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"oracle"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"oracle_base"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/local/oracle"</span><span class="p">,</span><span class="w">
</span><span class="s2">"oracle_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"11.2.0.4.0"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ruby_oci_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.1.7"</span><span class="p">,</span><span class="w">
</span><span class="s2">"tns_admin_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#{oracle.oracle_base}/network/admin"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<p>We are going to run ruby from <strong>project.ruby_home</strong> on behalf of the <strong>node.user</strong> and we will use specific versions
of <strong>oracle</strong> client and <strong>ruby-oci8</strong> gem. We also specify where to look for installation packages (instant<em>client.src_dir)
and where to install Instant Client (instant</em>client.dest_dir). Also, as you can see, we are using <strong>macos</strong> as a platform
and <strong>x64</strong> as an architecture.</p>
<h2>Provide execution script</h2>
<p>Library itself if written in ruby, but for launching its code you have to use <strong>rake</strong> or <strong>thor</strong>. Here I provide
thor script as an example:</p>
<pre class="highlight ruby"><code><span class="nb">require</span> <span class="s1">'thor'</span>
<span class="nb">require</span> <span class="s1">'oracle_client_provision'</span>
<span class="k">class</span> <span class="nc">OracleClient</span> <span class="o"><</span> <span class="no">Thor</span>
<span class="vi">@installer</span> <span class="o">=</span> <span class="no">OracleClientProvision</span><span class="p">.</span><span class="nf">new</span> <span class="nb">self</span><span class="p">,</span> <span class="s2">".oracle_client_provision.json"</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="kp">attr_reader</span> <span class="ss">:installer</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"install"</span><span class="p">,</span> <span class="s2">"Installs Oracle Instant Client"</span>
<span class="k">def</span> <span class="nf">install</span>
<span class="no">OracleClient</span><span class="p">.</span><span class="nf">installer</span><span class="p">.</span><span class="nf">install</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"uninstall"</span><span class="p">,</span> <span class="s2">"Uninstalls Oracle Instant Client"</span>
<span class="k">def</span> <span class="nf">uninstall</span>
<span class="no">OracleClient</span><span class="p">.</span><span class="nf">installer</span><span class="p">.</span><span class="nf">uninstall</span>
<span class="k">end</span>
<span class="n">desc</span> <span class="s2">"verify"</span><span class="p">,</span> <span class="s2">"Verifies Oracle Instant Client connection"</span>
<span class="k">def</span> <span class="nf">verify</span>
<span class="n">username</span> <span class="o">=</span> <span class="s2">"scott"</span>
<span class="n">password</span> <span class="o">=</span> <span class="s2">"tiger"</span>
<span class="n">schema</span> <span class="o">=</span> <span class="s2">"ORCL"</span>
<span class="n">sql</span> <span class="o">=</span> <span class="s2">"SELECT * FROM emp where rownum <= 10"</span>
<span class="no">OracleClient</span><span class="p">.</span><span class="nf">installer</span><span class="p">.</span><span class="nf">verify</span> <span class="k">do</span>
<span class="s2">"require 'oci8'; OCI8.new('</span><span class="si">#{</span><span class="n">username</span><span class="si">}</span><span class="s2">','</span><span class="si">#{</span><span class="n">password</span><span class="si">}</span><span class="s2">','</span><span class="si">#{</span><span class="n">schema</span><span class="si">}</span><span class="s2">').exec('</span><span class="si">#{</span><span class="n">sql</span><span class="si">}</span><span class="s2">') do |r| puts r.join(','); end"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<h2>Install Oracle Instant Client</h2>
<p>Run this <strong>thor</strong> command:</p>
<pre class="highlight shell"><code>thor oracle_client:install
</code></pre>
<p>After execution all Instant Client packages (basic, sdk and sqlplus) will be installed at the right location
(instant_client.dest_dir) on your computer.</p>
<h2>Again: install gems for the project, now with ruby-oci8 gem</h2>
<p>Remove .bundle folder in order to include “oracle” group into bundle execution and
then run bundler with “oracle” group:</p>
<pre class="highlight shell"><code>rm -rf .bundle
bundle
</code></pre>
<h2>Verify the installation</h2>
<p>If you have Oracle installed locally with <strong>scott/tiger/ORCL</strong>, you can test it now:</p>
<pre class="highlight shell"><code>thor oracle_client:verify
</code></pre>
<p>If you have oracle installed somewhere on the network, you can add TNS names inside your
<strong>tnsnames.ora</strong> file located inside <strong>/usr/local/oracle/network/admin</strong> folder. You can set up
this location via <strong>tns_admin_dir</strong> property inside your configuration file.</p>
<p>tnsnames.ora file will have this section:</p>
<pre class="highlight plaintext"><code>MY_ORCL=
(DESCRIPTION=
(ADDRESS=
(PROTOCOL=TCP)
(HOST=db.your_host.com)
(PORT=1521)
)
(CONNECT_DATA=
(SID=MY_ORCL)
)
)
</code></pre>
<p>where you define <strong>MY_ORCL</strong> schema on <strong>db.your_host.com</strong> running on port <strong>1521</strong>.</p>
<p>In order to use oracle driver inside rails application, you have to include it into your Gemfile:</p>
<pre class="highlight ruby"><code><span class="c1"># Gemfile</span>
<span class="n">group</span> <span class="ss">:oracle</span> <span class="k">do</span>
<span class="p">.</span><span class="nf">.</span><span class="p">.</span>
<span class="nf">gem</span> <span class="s1">'activerecord-oracle_enhanced-adapter'</span><span class="p">,</span> <span class="s1">'1.5.5'</span>
<span class="k">end</span>
</code></pre>
<h1>Summary: all steps all together</h1>
<pre class="highlight shell"><code>bundle install --without<span class="o">=</span>oracle
thor oracle_client:install
rm -rf .bundle
bundle
thor oracle_client:verify
</code></pre>