<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harish Babu</title>
    <description>The latest articles on DEV Community by Harish Babu (@chekkan).</description>
    <link>https://dev.to/chekkan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F211011%2F60a92951-2c02-455e-8187-0f65627ba15b.jpeg</url>
      <title>DEV Community: Harish Babu</title>
      <link>https://dev.to/chekkan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chekkan"/>
    <language>en</language>
    <item>
      <title>Using Domain Types with NHibernate</title>
      <dc:creator>Harish Babu</dc:creator>
      <pubDate>Sun, 21 Nov 2021 22:06:21 +0000</pubDate>
      <link>https://dev.to/chekkan/using-domain-types-with-nhibernate-38ik</link>
      <guid>https://dev.to/chekkan/using-domain-types-with-nhibernate-38ik</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NTcOAyJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/NHibernate-logo.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NTcOAyJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/NHibernate-logo.svg" alt="Using Domain Types with NHibernate" width="347" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primitive obsession is a code smell. The proposal is to replace strings, and other primitive data types with domain types. For example, user's id in system can be represented by an &lt;code&gt;integer&lt;/code&gt; or &lt;code&gt;Guid&lt;/code&gt; (&lt;code&gt;UUID&lt;/code&gt;), or &lt;code&gt;string&lt;/code&gt; (username). However, there are benefits that come along with creating a type called &lt;code&gt;UserId&lt;/code&gt; using that in place of primitive types.&lt;/p&gt;

&lt;p&gt;One advantage is that you cannot accidentally call the following method with another &lt;code&gt;integer&lt;/code&gt; or &lt;code&gt;Guid&lt;/code&gt;. You must do the right thing otherwise code will not compile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void AcceptedBy(UserId id)
{ ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the hurdles you will have when replacing this code smell is that now, there might be places in the website where you need to display the &lt;code&gt;integer&lt;/code&gt; value or include the &lt;code&gt;Guid&lt;/code&gt; in the link to a user's profile. On the other end, you might have to store the user's Id in to some database or make an API call with it.&lt;/p&gt;

&lt;p&gt;The first instinct might be to translate the &lt;code&gt;UserId&lt;/code&gt; object to the type that's required. Instead, stop the urge to map / translate the type and use the same type through out.&lt;/p&gt;

&lt;p&gt;Most of the Entity Relational Object mappers allows you to specify how to translate from an domain type to a database type. For NHibernate, there are a couple of things that needs to be setup.&lt;/p&gt;

&lt;p&gt;First, the mapping needs to specify the database type and where to store the value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class UserMapping : ClassMap&amp;lt;User&amp;gt;
{
    public UserMapping()
    {
        Id(x =&amp;gt; x.Id)
          .CustomType&amp;lt;string&amp;gt;.Access.CamelCaseField(Prefix.None);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Access configuration tells NHibernate to look for a private field on the class &lt;code&gt;User&lt;/code&gt; that has the camel cased name of the property and is not prefixed. Therefore, NHibernate will then look for a field with name &lt;code&gt;id&lt;/code&gt; in class &lt;code&gt;User&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class User
{
    string id;
    public virtual UserId Id
    {
        get
        {
            UserId.TryParse(id, out var result);
            return result;
        }
        set =&amp;gt; id = value?.Value;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Id&lt;/code&gt; property on &lt;code&gt;User&lt;/code&gt; class will return the parse value of the string &lt;code&gt;id&lt;/code&gt; and set the field appropriately from &lt;code&gt;UserId&lt;/code&gt; object. In the example, I expose a get only property for the &lt;code&gt;string&lt;/code&gt;. With the mapping and the private field usage, NHibernate will be able to store domain type into and retrieve from database.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I would have preferred if I didn't have the need to expose &lt;code&gt;Value&lt;/code&gt; property. However, the creation is the &lt;code&gt;UserId&lt;/code&gt; still needs to happen through the constructor. So, its OK for now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For using the &lt;code&gt;Id&lt;/code&gt; property on &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/"&gt;LINQ&lt;/a&gt; queries however with NHibernate, you will need to do one more change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var userById = session.Query&amp;lt;User&amp;gt;(u =&amp;gt; u.Id == new UserId("chekkan"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above query will fail with an exception when trying to convert &lt;code&gt;UserId&lt;/code&gt; type to string. What I gathered is happening behind the scene is that NHibernate was trying to covert the right hand side of the expression to string because &lt;code&gt;Id&lt;/code&gt; column in &lt;code&gt;User&lt;/code&gt; table is a &lt;code&gt;nvarchar&lt;/code&gt; type. But, &lt;code&gt;UserId&lt;/code&gt; cannot be converted to &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we try to compare &lt;code&gt;Id&lt;/code&gt; to &lt;code&gt;UserId.Value&lt;/code&gt;, that's not going to compile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var userById = session.Query&amp;lt;User&amp;gt;(u =&amp;gt; u.Id == new UserId("chekkan").Value);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we were to rewrite the query in &lt;a href="https://nhibernate.info/doc/nhibernate-reference/queryhql.html"&gt;hql&lt;/a&gt; however, it will work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var userToFind = new UserId("chekkan");
var query = session.CreateQuery("from User u where u.Id = :Id");
query.SetParameter("Id", userToFind.Value);
var userById = query.List&amp;lt;User&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, its not type safe. We want the &lt;a href="https://nhibernate.info/doc/nhibernate-reference/querylinq.html"&gt;LINQ&lt;/a&gt;queries to work. For that, we will create an implicit coverter from &lt;code&gt;UserId&lt;/code&gt; to &lt;code&gt;string&lt;/code&gt; in our &lt;code&gt;UserId&lt;/code&gt; type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static implicit operator string(UserId id) =&amp;gt; id.Value;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this change, the above LINQ query that was not compiling before will start to compile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// with implicit string converter
var userById = session.Query&amp;lt;User&amp;gt;(u =&amp;gt; u.Id == new UserId("chekkan").Value);

// with explicit string converter
var userById = session.Query&amp;lt;User&amp;gt;(u =&amp;gt; 
    (string) u.Id == new UserId("chekkan").Value
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nhibernate</category>
      <category>softwareengineering</category>
      <category>aspnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>MiniProfiler for dotnet with NHibernate MSSQL Driver</title>
      <dc:creator>Harish Babu</dc:creator>
      <pubDate>Fri, 05 Nov 2021 22:50:08 +0000</pubDate>
      <link>https://dev.to/chekkan/miniprofiler-for-dotnet-with-nhibernate-mssql-driver-2fle</link>
      <guid>https://dev.to/chekkan/miniprofiler-for-dotnet-with-nhibernate-mssql-driver-2fle</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lYUxooAH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/Cover-Image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lYUxooAH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/Cover-Image.png" alt="MiniProfiler for dotnet with NHibernate MSSQL Driver" width="880" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I first came across &lt;a href="https://miniprofiler.com/"&gt;MiniProfiler&lt;/a&gt; when I started a new ruby on rails project. It came built in and I found it very useful to see the time requests spend and a breakdown of it. I think it was only visible on development environment.&lt;/p&gt;

&lt;p&gt;I found myself asking the question of whether it was available for ASP.NET web application. Turns out, MiniProfiler was written by the folks at stack overflow. And their primary technology stack is .NET. The library supports both full .NET Framework and the new .NET core. Their existing documentation for &lt;a href="https://miniprofiler.com/dotnet/"&gt;ASP.NET integration&lt;/a&gt; is easy to follow and they even have a &lt;a href="https://github.com/MiniProfiler/dotnet"&gt;samples&lt;/a&gt;repository.&lt;/p&gt;

&lt;p&gt;One thing that was missing was NuGet package for NHibernate integration. I wanted to log SQL queries that were getting executed by NHibernate ORM library. I saw that there was couple of NuGet packages published by community members. But, they were not actively worked on by the maintainers.&lt;/p&gt;

&lt;p&gt;The latest version for MiniProfiler.Mvc5 NuGet package at the time of writing is &lt;a href="https://www.nuget.org/packages/MiniProfiler/4.2.22"&gt;4.2.22&lt;/a&gt;, which I've installed to my Website project. Following the instructions for &lt;a href="https://miniprofiler.com/dotnet/AspDotNet"&gt;ASP.NET MVC 5&lt;/a&gt;, my Global.asax.cs file looks like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MvcApplication : System.Web.HttpApplication
{
  protected void Application_Start()
  {
    ...
    InitProfilerSettings();
  }

  protected void Application_BeginRequest()
  {
    if (Request.IsLocal) MiniProfiler.StartNew();
  }

  protected void Application_EndRequest() =&amp;gt; MiniProfiler.Current?.Stop();

  private static void InitProfilerSettings()
  {
    MiniProfiler.Configure(new MiniProfilerOptions
      {
        RouteBasePath = "~/profiler",
      }
      .AddViewProfiling() // Add MVC view profiling
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've went ahead and did the changes suggested for both the _Layout.cshtml file and Web.config file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vrng_aFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-AspNetMvc5-HomePage.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vrng_aFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-AspNetMvc5-HomePage.png" alt="MiniProfiler for dotnet with NHibernate MSSQL Driver" width="880" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've installed FluentNHibernate(3.1.0) and NHibernate(5.3.10) packages to the project. I have an MSSQL Server and have a table called employees and will query for all employees and render them in my view. I would like to view the SQL that NHibernate produced and the time it took in my mini profiler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class HomeController : Controller
{
  public async Task&amp;lt;ActionResult&amp;gt; Index()
  {
    using (var session = NHibernateHelper.OpenSession())
    {
      var employees = await session.Query&amp;lt;Employee&amp;gt;().ToListAsync();
      return View(employees);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And rendering the list in my index view&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="row"&amp;gt;
    &amp;lt;ul&amp;gt;
        @foreach (var employee in @Model)
        {
            &amp;lt;li&amp;gt;@employee.Name&amp;lt;/li&amp;gt;
        }
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows me MiniProfiler without any SQL information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LxktJGBz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-without-sql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LxktJGBz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-without-sql.png" alt="MiniProfiler for dotnet with NHibernate MSSQL Driver" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To report SQL statements to MiniProfiler, we can use one of the &lt;a href="https://miniprofiler.com/dotnet/HowTo/ProfileSql"&gt;SQL wrapper classes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MiniProfiledSqlClientDriver : SqlClientDriver
{
  public override DbCommand CreateCommand()
  {
    var dbCommand = base.CreateCommand();
    if (MiniProfiler.Current != null)
    {
      dbCommand = new ProfiledDbCommand(
                        dbCommand, null, MiniProfiler.Current
                      );
    }
    return dbCommand;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have opted to use the &lt;code&gt;ProfiledDbCommand&lt;/code&gt; wrapper as is the approach by &lt;a href="https://github.com/MRCollective"&gt;MRCollective&lt;/a&gt;/&lt;a href="https://github.com/MRCollective/MiniProfiler.NHibernate"&gt;MiniProfiler.NHibernate&lt;/a&gt; repo. Notice that I am inheriting from &lt;code&gt;SqlClientDriver&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have then registered the &lt;code&gt;MiniProfiledSqlClientDriver&lt;/code&gt; with &lt;code&gt;FluentNHibernate&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class NHibernateHelper
{
  private static ISessionFactory _sessionFactory;

  private static ISessionFactory SessionFactory
  {
    get
    {
      if (_sessionFactory != null) return _sessionFactory;
        _sessionFactory = Fluently.Configure()
          .Database(MsSqlConfiguration
            .MsSql2012.ConnectionString(c =&amp;gt;
              c.FromConnectionStringWithKey("Default"))
            .Driver&amp;lt;MiniProfiledSqlClientDriver&amp;gt;())
            .Mappings(m =&amp;gt; m.AutoMappings
              .Add(AutoMap.AssemblyOf&amp;lt;Employee&amp;gt;(new StoreConfiguration())))
            .BuildSessionFactory();
          return _sessionFactory;
    }
  }

  public static ISession OpenSession() =&amp;gt; SessionFactory.OpenSession();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these changes, I've now SQL statements available in MiniProfiler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yC5Sd13a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-with-sql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yC5Sd13a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-with-sql.png" alt="MiniProfiler for dotnet with NHibernate MSSQL Driver" width="880" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nWbzCj-A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-with-sql-statements.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nWbzCj-A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-with-sql-statements.png" alt="MiniProfiler for dotnet with NHibernate MSSQL Driver" width="880" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also works for inserts&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mT2JrcXY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-with-sql-insert-statements.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mT2JrcXY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/chekkan/image/upload/q_auto:good/MiniProfiler-with-sql-insert-statements.png" alt="MiniProfiler for dotnet with NHibernate MSSQL Driver" width="880" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I wasn't expecting the insert to work with the code I've added. When working with an older version of NHibernate library, I had to also implement &lt;a href="https://github.com/MRCollective/MiniProfiler.NHibernate/blob/master/MiniProfiler.NHibernate/Infrastructure/ProfiledSqlClientBatchingBatcherFactory.cs"&gt;ProfiledSqlClientBatchingBatcherFactory&lt;/a&gt;, inherit &lt;code&gt;MiniProfiledSqlClientDriver&lt;/code&gt; from &lt;code&gt;IEmbeddedBatcherFactoryProvider&lt;/code&gt;, and implement &lt;code&gt;IEmbeddedBatcherFactoryProvider.BatcherFactoryClass&lt;/code&gt; as done in &lt;a href="https://github.com/MRCollective"&gt;MRCollective&lt;/a&gt;/&lt;a href="https://github.com/MRCollective/MiniProfiler.NHibernate"&gt;MiniProfiler.NHibernate&lt;/a&gt;'s implementation.&lt;/p&gt;

&lt;p&gt;Repo accompanying this blog post can be found at &lt;a href="https://github.com/chekkan/MiniProfilerNHibernateAspNetMvc5"&gt;my github repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnet</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Ruby on Rails GitHub Action Workflow</title>
      <dc:creator>Harish Babu</dc:creator>
      <pubDate>Fri, 23 Apr 2021 22:36:43 +0000</pubDate>
      <link>https://dev.to/chekkan/ruby-on-rails-github-action-workflow-eeo</link>
      <guid>https://dev.to/chekkan/ruby-on-rails-github-action-workflow-eeo</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1522776851755-3914469f0ca2%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DMnwxMTc3M3wwfDF8c2VhcmNofDQxfHxydWJ5JTIwb24lMjByYWlsc3xlbnwwfHx8fDE2MTkyMTcyMjA%26ixlib%3Drb-1.2.1%26q%3D80%26w%3D2000" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1522776851755-3914469f0ca2%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DMnwxMTc3M3wwfDF8c2VhcmNofDQxfHxydWJ5JTIwb24lMjByYWlsc3xlbnwwfHx8fDE2MTkyMTcyMjA%26ixlib%3Drb-1.2.1%26q%3D80%26w%3D2000" alt="Ruby on Rails GitHub Action Workflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal is to start running tests that is part of my code base with each push to &lt;code&gt;main&lt;/code&gt; and also any pull requests to &lt;code&gt;main&lt;/code&gt; branch. This blog post will not cover any linting or deployment of the ruby on rails web application. The final yaml is tested with ruby version &lt;code&gt;3.0.0&lt;/code&gt; and ruby on rails version &lt;code&gt;6.1.3.1&lt;/code&gt; at the time of writing. Skip to end of the post to view the finished yaml.&lt;/p&gt;

&lt;p&gt;We will start with &lt;a href="https://github.com/actions/starter-workflows/blob/ffb4bccd2d52e308ec66fa63f218d93db6dd94a1/ci/ruby.yml" rel="noopener noreferrer"&gt;recommended ruby starter workflow yaml file&lt;/a&gt; from GitHub actions page. The starter file provides you with a matrix with various ruby versions. Because, the workflow I am setting up is for a web application that will be deployed to an environment with a specific ruby version install, I will delete the &lt;code&gt;strategy&lt;/code&gt; section completely and also specify my ruby version to &lt;code&gt;3.0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Ruby&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;
        &lt;span class="na"&gt;bundler-cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# runs 'bundle install' and caches installed gems automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am planning on deploying the application in a docker container running on linux. Therefore, I've set my build environment to be an &lt;code&gt;ubuntu&lt;/code&gt; base image. If you are developing on something other than linux (which is my case), you will also need to add support for linux with your bundle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle lock &lt;span class="nt"&gt;--add-platform&lt;/span&gt; x86_64-linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the ruby setup step, the next step thats already provided is the test step with &lt;code&gt;bundle rake&lt;/code&gt; command. I have my project setup to run with mysql database. Therefore, we need to define a service for mysql for the tests to pass. Read more about service containers at &lt;a href="https://docs.github.com/en/actions/guides/about-service-containers#about-service-containers" rel="noopener noreferrer"&gt;G&lt;/a&gt;&lt;a href="https://docs.github.com/en/actions/guides/about-service-containers#about-service-containers" rel="noopener noreferrer"&gt;itHub documentation site&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Label used to access the service container&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Docker Hub image and tag&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-secret-password&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;db_name&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# replace with actual name&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_user&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
        &lt;span class="c1"&gt;# Set health checks to wait until mysql has started&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a container will be spun up my GitHub action and made available on the port you specified. The &lt;code&gt;options&lt;/code&gt; section specifies that the service is only ready when &lt;code&gt;mysqladmin ping&lt;/code&gt; command succeeds. The next step is to setup the database required for your tests. To do that, we will make use of the &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable and the &lt;code&gt;bin/rails db:setup&lt;/code&gt; &lt;a href="https://guides.rubyonrails.org/active_record_migrations.html#setup-the-database" rel="noopener noreferrer"&gt;command&lt;/a&gt;. Make sure to use the prefix &lt;code&gt;bin&lt;/code&gt;. You can read more about configuration and order of preference at &lt;a href="https://guides.rubyonrails.org/configuring.html#configuring-a-database" rel="noopener noreferrer"&gt;ruby on rails documentation site&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup test database&lt;/span&gt;
      &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bin/rails db:setup&lt;/span&gt;
      &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql2://test_user:password@127.0.0.1:3306/&amp;lt;db_name&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable starts with &lt;code&gt;mysql2&lt;/code&gt;. For some reason, I can't seem to use &lt;code&gt;localhost&lt;/code&gt; for the hostname part. If someone could point me to the reason, that will be much appreciated 🙏.&lt;/p&gt;

&lt;p&gt;After the database is setup, you might also have to make sure that &lt;code&gt;webpacker&lt;/code&gt; is installed. Refer to the &lt;a href="https://edgeguides.rubyonrails.org/webpacker.html#installing-webpacker" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for up to date information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Webpacker&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bin/rails webpacker:install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final yaml file...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ruby&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Label used to access the service container&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Docker Hub image and tag&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3306:3306&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-secret-password&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;db_name&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#replace with name&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_user&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
        &lt;span class="c1"&gt;# Set health checks to wait until mysql has started&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Ruby&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;
        &lt;span class="na"&gt;bundler-cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# runs 'bundle install' and caches installed gems automatically&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup test database&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bin/rails db:setup&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql2://test_user:password@127.0.0.1:3306/&amp;lt;db_name&amp;gt;&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Webpacker&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bin/rails webpacker:install&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec rake&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>rails</category>
      <category>ruby</category>
      <category>github</category>
      <category>ci</category>
    </item>
    <item>
      <title>Update blog to ghost version 4 running in Kubernetes</title>
      <dc:creator>Harish Babu</dc:creator>
      <pubDate>Sat, 10 Apr 2021 21:11:25 +0000</pubDate>
      <link>https://dev.to/chekkan/update-blog-to-ghost-version-4-running-in-kubernetes-2b43</link>
      <guid>https://dev.to/chekkan/update-blog-to-ghost-version-4-running-in-kubernetes-2b43</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fchekkan%2Fimage%2Fupload%2Fq_auto%3Agood%2Fghostv3-kubernetes-ghostv4--2-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fchekkan%2Fimage%2Fupload%2Fq_auto%3Agood%2Fghostv3-kubernetes-ghostv4--2-.png" alt="Update blog to ghost version 4 running in Kubernetes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My personal blog site is running on the ghost blogging platform at the time of this writing. Its current running version 3.42.x and there was a new major version released recently and its currently on version 4.2.0. There are guides available on &lt;a href="https://ghost.org/docs/update/" rel="noopener noreferrer"&gt;ghost documentation site&lt;/a&gt; to help make the upgrade when you have installed ghost on a server using the ghost-CLI tooling. However, none exists for ghost running on Kubernetes, or docker containers. &lt;code&gt;ghost-cli&lt;/code&gt; update path seemed desirable compared to the clean install option especially because some database migration might be involved I assumed. Also, I didnt want to re-configure my site with google analytics etc.   &lt;/p&gt;

&lt;p&gt;Follow the initial steps of backing up all the important content as mentioned in &lt;a href="https://ghost.org/docs/update/" rel="noopener noreferrer"&gt;the documentation site&lt;/a&gt;. Then come back here...  &lt;/p&gt;

&lt;p&gt;You will have ssh into the docker container in order to copy the content folder across.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl cp &amp;lt;namespace&amp;gt;/&amp;lt;pod_name&amp;gt;:/var/lib/ghost/content ghost-migration-to-4.x/content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't have much luck copying the theme folder in content. But, I haven't made any changes to the theme using the default Casper theme at present. Make sure run the upgrade check for your theme before progressing any further. Download your existing theme from the Admin site and upload the zipped file into the &lt;a href="https://gscan.ghost.org/" rel="noopener noreferrer"&gt;GScan&lt;/a&gt; website.  &lt;/p&gt;

&lt;p&gt;The way I have the blog setup in Kubernetes is by using the base image &lt;a href="https://hub.docker.com/r/chekkan/ghost-cloudinary" rel="noopener noreferrer"&gt;chekkan/ghost-cloudinary&lt;/a&gt; which builds off the &lt;code&gt;ghost-alpine&lt;/code&gt; image. I have already gone ahead and published version &lt;code&gt;4.2.0&lt;/code&gt; of the docker image.   &lt;/p&gt;

&lt;p&gt;You will need to ssh into the docker container pod and install &lt;code&gt;ghost-cli&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl exec --stdin --tty -n "&amp;lt;namespace&amp;gt;" "&amp;lt;pod_name&amp;gt;" -- /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
ssh into the pod thats running ghost container







&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g ghost-cli@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
install ghost-cli npm package





&lt;p&gt;Make sure your current working directory is where you've installed ghost. For me, its at &lt;code&gt;/var/lib/ghost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I used environment variables in deployment spec file together with Kubernetes secrets to configure my database credentials. For some reason, these were not picked up when I ran &lt;code&gt;ghost config get database.connection.host&lt;/code&gt; command. So, I decided to configure them again manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ghost config --db mysql --dbhost &amp;lt;dbhost&amp;gt; --dbuser &amp;lt;dbuser&amp;gt; \
  --dbpass &amp;lt;dbpass&amp;gt; --dbport &amp;lt;dbport&amp;gt; --dbname &amp;lt;dbname&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Update the ghost config values for database





&lt;p&gt;Running the above command will update the &lt;em&gt;config.production.json&lt;/em&gt; file. Review the file to make sure its got the expected values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;su node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
switch user to node





&lt;p&gt;&lt;code&gt;ghost-cli&lt;/code&gt; stops you from updating as a &lt;code&gt;root&lt;/code&gt; user. If you wanted to get back to being a root user again, &lt;code&gt;exit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before you can update to version 4, ghost wants you to be in the latest version of the currently installed ghost version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ghost update v3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
updates ghost to the latest version of major version 3







&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ghost update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
updates ghost to the latest version; version 4.2.0 at the time of writing





&lt;p&gt;Once ghost is updated, go ahead and &lt;code&gt;kubectl apply&lt;/code&gt; with your deployment spec file thats updated to the same version of ghost.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>ghost</category>
    </item>
    <item>
      <title>Faking .NET Framework ConfigurationSection for Unit Tests</title>
      <dc:creator>Harish Babu</dc:creator>
      <pubDate>Sat, 30 Jan 2021 21:44:19 +0000</pubDate>
      <link>https://dev.to/chekkan/faking-net-framework-configurationsection-for-unit-tests-e3o</link>
      <guid>https://dev.to/chekkan/faking-net-framework-configurationsection-for-unit-tests-e3o</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a7W1GHSX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1546900703-cf06143d1239%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DMXwxMTc3M3wwfDF8c2VhcmNofDZ8fGNvZGV8ZW58MHx8fA%26ixlib%3Drb-1.2.1%26q%3D80%26w%3D2000" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a7W1GHSX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1546900703-cf06143d1239%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DMXwxMTc3M3wwfDF8c2VhcmNofDZ8fGNvZGV8ZW58MHx8fA%26ixlib%3Drb-1.2.1%26q%3D80%26w%3D2000" alt="Faking .NET Framework ConfigurationSection for Unit Tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many are now familiar with using typed configuration that's available in .net core and .net 5 with the help from &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/extensions/options"&gt;Options pattern&lt;/a&gt;. However, if you are working on a projects targeting .NET Framework, you will know &lt;code&gt;ConfigurationSection&lt;/code&gt; from &lt;code&gt;System.Configuration&lt;/code&gt; assembly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.configuration.configurationsection?view=netframework-4.8"&gt;ConfigurationSection&lt;/a&gt; allows you to group related configurations together in xml. Lets take an example of providing some simple configuring settings to a retry client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;configSections&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"retrySettings"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"ConfigSectionTest.RetrySettings, ConfigSectionTest"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/configSections&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;retrySettings&lt;/span&gt; &lt;span class="na"&gt;count=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;delay=&lt;/span&gt;&lt;span class="s"&gt;"00:00:30"&lt;/span&gt; &lt;span class="na"&gt;failureThreshold=&lt;/span&gt;&lt;span class="s"&gt;"0.5"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;configSections.section&lt;/code&gt; node where we are naming the config section and also pointing to the type where its implemented. &lt;code&gt;retrySettings&lt;/code&gt; xml node has various attributes and values assigned for each.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;RetrySettings&lt;/code&gt; class is implemented as...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RetrySettings&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ConfigurationSection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ConfigurationProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;RetryCount&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ConfigurationProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"delay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsRequired&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;RetryDelay&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"delay"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ConfigurationProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failureThreshold"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;FailureThreshold&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"failureThreshold"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With expression bodied property syntax, the class is fairly small. I've specified &lt;code&gt;RetryDelay&lt;/code&gt; to be required using the &lt;code&gt;ConfigurationProperty&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;In order to use the configuration section &lt;code&gt;RetrySettings&lt;/code&gt;, you'd do so by calling &lt;code&gt;ConfigurationManager&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"retrySettings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;RetrySettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though you can create an instance of &lt;code&gt;RetrySettings&lt;/code&gt;, it is not possible set the values of the properties without modifying the class (providing a setter). You will be able to mock the class by introducing an interface or using an actual appsettings.config file.&lt;/p&gt;

&lt;p&gt;However, it maybe easier to create a fake retry settings config section instead of mocking. In order to do this, you can inherit the &lt;code&gt;ConfigurationSection&lt;/code&gt; class which gives you access to the &lt;code&gt;DeserializeElement&lt;/code&gt; method. This method takes in an xml string which is your section node. The second argument indicates whether to serialize only the collection key property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeRetrySettings&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RetrySettings&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PopulateConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;xmlString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;XmlReader&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;XmlTextReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xmlString&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MoveToContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;DeserializeElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializeCollectionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now configure the retry settings as needed to orchestrate your system under test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;retrySettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FakeRetrySettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;xmlString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;retrySettings count=\"3\" delay=\"00:05:00\" /&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;retrySettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PopulateConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xmlString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can make it much more easy to work with the &lt;code&gt;RetrySettings&lt;/code&gt; class in test project by having a builder class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RetrySettingsBuilder&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"delay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"00:30:00"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"failureThreshold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"0.5"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;RetrySettingsBuilder&lt;/span&gt; &lt;span class="nf"&gt;WithProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;RetrySettings&lt;/span&gt; &lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;xmlString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;retrySettings "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;=\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;xmlString&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" /&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FakeRetrySettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PopulateConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xmlString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Builder has some default values already configured. Therefore, if you wanted the test to work and you don't worry about the values, you can create a new instance of the configuration section by calling &lt;code&gt;new RetrySettingsBuilder().Build();&lt;/code&gt;. You can override the default value by calling &lt;code&gt;WithProperty&lt;/code&gt; method e.g. &lt;code&gt;new RetrySettingsBuilder().WithProperty("count", "2").Build();&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
