<?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: Christopher Pezza</title>
    <description>The latest articles on DEV Community by Christopher Pezza (@chiefpansancolt).</description>
    <link>https://dev.to/chiefpansancolt</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%2F198458%2Fc25b23cb-d6e9-49b3-84a0-904a2545f044.jpeg</url>
      <title>DEV Community: Christopher Pezza</title>
      <link>https://dev.to/chiefpansancolt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chiefpansancolt"/>
    <language>en</language>
    <item>
      <title>Salesforce Automation Architecture Approach</title>
      <dc:creator>Christopher Pezza</dc:creator>
      <pubDate>Wed, 27 Nov 2024 14:55:36 +0000</pubDate>
      <link>https://dev.to/chiefpansancolt/salesforce-automation-architecture-approach-4dp4</link>
      <guid>https://dev.to/chiefpansancolt/salesforce-automation-architecture-approach-4dp4</guid>
      <description>&lt;p&gt;Salesforce automation provides a powerful set of tools for streamlining business processes and reducing manual work. Among the most significant tools in this suite are Flows, Triggers, Asynchronous (Async) processing, and Platform Events. Flows enable visual, declarative automation, allowing users to create complex logic and guide users through processes without needing to code. Triggers operate at the backend, providing fine-grained control over data manipulation in response to DML operations on objects. Async processing supports time-intensive tasks, such as data imports or callouts to external systems, by running these actions in the background to improve system efficiency. Finally, Platform Events facilitate real-time event-driven architecture by allowing Salesforce to publish and subscribe to events across different systems, making it possible to synchronize data or trigger actions based on specific events. Together, these tools enable comprehensive automation across a wide range of use cases, supporting scalable, responsive solutions in Salesforce.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into each automation type and how to leverage them effectively, it’s helpful to cover some overarching best practices. First, consider creating ways to bypass specific automations when necessary. One of the most effective methods for this is using Custom Permissions, such as Exclude Triggers or Exclude Flows. These permissions can be assigned to specific users, often an Integration User, so that automations don’t execute during data loads or API calls. Second, it’s important to determine when to use a Flow versus a Trigger. Triggers are preferable when you need to manage complex data structures, such as storing and referencing data in a map format—something that Flow currently doesn’t support. On the other hand, if you’re handling straightforward operations, like analyzing data or updating fields, Flows are generally easier to maintain and modify. Additionally, choosing Flow over Trigger is often beneficial if development resources are limited, as Flows allow for powerful automation without the need for custom code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record Trigger Flows
&lt;/h2&gt;

&lt;p&gt;Salesforce Record Triggered Flows are highly versatile and can automate both user-driven and background processes through various types of record triggered flows: before-save, after-save, before-delete, and asynchronous. When designing a Flow, understanding these distinctions is crucial to optimize performance and meet specific business requirements. Before-save Flows are best used when you need to update fields on the record being saved, as they run before the record is committed to the database. These flows are efficient because they avoid additional database writes, making them ideal for quick, straightforward updates like setting default values or populating lookup fields. After-save Flows, on the other hand, execute after the record is saved and are typically used when actions are dependent on the record's final state, such as creating related records or sending notifications. They allow more complex logic but come with additional performance considerations. Async Flows run in the background and are suitable for time-consuming operations or those that can be deferred, such as making API calls to external systems or sending bulk emails. By leveraging the right type of Flow for each scenario, you can ensure efficient, maintainable automation that aligns with business needs.&lt;/p&gt;

&lt;p&gt;Structuring your flows carefully is critical to ensuring they are maintainable. For each object, aim to have a single flow per type—before-save, after-save, before-delete, and async—to keep things organized. However, there are valid scenarios where breaking this rule is beneficial. These include cases such as no-bypass flows, email notifications, large use cases, and async processes. For no-bypass flows, you might set up flows that bypass Custom Permissions to enforce actions that should always run, like setting Record Types or default values. For email notifications, if you have multiple email alerts, it can help to isolate these into a dedicated after-save flow for ease of maintenance. In async flows, it’s essential to set entry criteria on the flow to trigger them only when needed; these flows should be dedicated to specific tasks and clearly named by their function. Lastly, for large use cases, consider creating a standalone flow when managing numerous nodes. If this flow involves shared data used across other flows, it’s often better to centralize these operations in an Autolaunched flow. This Autolaunched flow can handle query results efficiently, returning output data or performing updates directly, which helps minimize SOQL queries and optimize resource usage.&lt;/p&gt;

&lt;p&gt;Establishing clear naming conventions for Flows is essential to keeping them organized and maintainable. Flow names should generally follow a structured format: start with the object name, followed by the flow type (e.g., Before Save, After Save, Before Delete), and, if applicable, include a brief description of the specific use case. This approach ensures that Flow names are intuitive and consistent, making it easier for team members to understand each Flow's purpose at a glance. Here are some examples following this convention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record Trigger: Object Name Before Save&lt;/li&gt;
&lt;li&gt;Record Trigger: Object Name Before Save – No Bypass&lt;/li&gt;
&lt;li&gt;Record Trigger: Object Name After Save&lt;/li&gt;
&lt;li&gt;Record Trigger: Object Name After Save (Email Alerts)&lt;/li&gt;
&lt;li&gt;Record Trigger: Object Name Before Delete&lt;/li&gt;
&lt;li&gt;Record Trigger: Object Name After Save (1 min Delay)&lt;/li&gt;
&lt;li&gt;Record Trigger: Object Name After Save Job (Function Name) &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Salesforce Triggers
&lt;/h2&gt;

&lt;p&gt;Salesforce Triggers are powerful tools, but without proper structure, they can cause significant issues, especially when running in bulk or being expanded upon over time. To mitigate these risks, implementing a Trigger Framework is essential, along with clear guidelines for the team. A Trigger Framework offers a structured approach to managing triggers and their handlers, defining what should execute and when. It also includes mechanisms to prevent recursion, create reusable functions, and enable improved error logging.&lt;/p&gt;

&lt;p&gt;The core of a Trigger Framework is the pipeline, which controls the order and classification of handlers for each trigger action on an object. Through this pipeline, you can organize handlers based on related object updates or groups of data retrieved by queries. Structuring handlers in this way helps reduce the number of DML operations and queries, avoiding platform governor limits. Additionally, the framework can specify when a handler should execute, based on conditions like bypass permissions or data on the firing record, thus reducing unnecessary runs and conserving resources. To maintain readability and consistency, handlers should follow a standardized naming convention. This typically includes the object name (in full or abbreviated form), a brief descriptor of the handler's function, and the word "Handler" at the end, all within an 80-character limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous and Platform Events
&lt;/h2&gt;

&lt;p&gt;Next, we have async action usage within trigger automation. There are many variables to consider when determining when and how to use async actions properly to avoid platform limits.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Nested Async Tasks&lt;/strong&gt;: If the trigger that initiates an async task is possible to be inside another async update, consider using Platform Events. Platform Events can run in a new transaction outside the current context, allowing tasks to be initiated synchronously or asynchronously. This helps avoid the platform limit that prevents initiating one async task within another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger vs. Scheduled Job&lt;/strong&gt;: Determine if the task needs to run on record value changes or if it can be part of a scheduled job using a queue system. A queue system involves the trigger creating a record that adds it to a queue of records needing specific actions. A scheduled job runs periodically (e.g., every 1-5 minutes) to process the queue, updating records and removing them from the queue. This job reschedules itself to avoid duplicate runs. The main issue to watch for is row lock conflicts, which occur if a user updates the same record at the same time as the job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large Transactions&lt;/strong&gt;: If the async task will not have conflicts but involves a large transaction, using future methods, batch Apex, or Queueable Apex from a trigger is a viable solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Platform Limitation Awareness
&lt;/h2&gt;

&lt;p&gt;When designing Salesforce automation, it's crucial to be aware of platform-imposed limits to avoid performance issues and errors. Both Flows and Triggers are subject to governor limits, including a maximum of 100 SOQL queries and 150 DML operations per transaction. However, Flows have unique limitations compared to Apex, particularly around data handling, recursion control, and permissions enforcement. For example, Flows cannot create or manipulate data structures like maps, which are useful in Apex Triggers for managing complex data relationships. This limitation can make certain use cases more challenging or impractical to implement in Flow alone. Additionally, Flows lack native recursion prevention, so care must be taken to avoid unintentional loops that could hit limits. Flows also fully enforce user permissions, which may require additional design considerations when bypassing certain actions for specific user roles. For asynchronous (Async) versus synchronous (Sync) operations, limits vary significantly. Async transactions allow up to 200 SOQL queries, double the heap size at 12 MB, and offer a CPU limit of 60,000 milliseconds—six times that of Sync operations. This increased capacity often makes Async operations more suitable for tasks with high data volumes or complex processing needs, though Sync may still be preferable for real-time requirements. Other advantages to Async is the ability to use the finish method which is run once all batches complete to then clean up data and/or kick off another batch class which we call daisy chaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring
&lt;/h2&gt;

&lt;p&gt;Monitoring is an essential aspect of Salesforce Automation Architecture, allowing administrators to track system events, especially errors. In the current landscape, errors can often be obscured or difficult to replicate without a debug log, which can be challenging to manage. There are two primary approaches to address this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Salesforce Shield&lt;/strong&gt;: This tool provides robust monitoring capabilities to track and report errors effectively. However, it comes with additional licensing costs. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Monitoring Solution&lt;/strong&gt;: An alternative is to build an internal monitoring tool. This could involve creating a custom object in your org to log error records via a service that triggers when an error is caught in try-catch blocks. These DML operations would only execute during error states, so careful placement of these error-catching mechanisms is crucial. While not a comprehensive solution, it can effectively cover common scenarios.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When choosing between these options, consider your environment: if you have managed packages or significant custom code, Salesforce Shield might be the more comprehensive choice. On the other hand, a custom-built tool is more suited to fully custom orgs where you control all the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, Salesforce's suite of automation tools—Flows, Triggers, async processing, and Platform Events—provides a powerful framework for creating efficient, responsive solutions that reduce manual work and streamline business processes. By following best practices for each tool, carefully structuring automations, and implementing a well-organized Trigger Framework, you can build scalable and maintainable automation that aligns with business needs. Thoughtful design, clear naming conventions, and performance considerations are essential for long-term success, ensuring your Salesforce automation architecture remains efficient, flexible, and resilient as your organization grows. With these tools and practices in place, Salesforce can become a powerful engine for operational excellence and innovation in your organization.&lt;/p&gt;




&lt;p&gt;Now that you have read the high-level discussion of Automation Architecture usage let's talk about implementing it into your organization in a few simple steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Before implementing a Trigger Framework, establish clear rules and strategies for your team to follow. Skipping this step risks inconsistent implementations, conflicts with the framework, or issues like governor limit violations. Key elements to include in your rule's documentation are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Naming Conventions&lt;/strong&gt;: Standardize names for Flows, Classes, Triggers, methods, and variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flow Groupings&lt;/strong&gt;: Define what should be included in each Record-Triggered Flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger Handler Groupings&lt;/strong&gt;: Specify how handlers should be organized for each object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async and Platform Event Usage&lt;/strong&gt;: Outline when to use each for optimal performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These foundational guidelines ensure consistency and scalability. To enforce these rules, leverage tools like &lt;a href="https://semgrep.dev/resources/semgrep-vs-snyk/" rel="noopener noreferrer"&gt;Semgrep&lt;/a&gt; within your version control system, helping maintain compliance and prevent deviations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trigger Framework &amp;amp; Error Monitoring
&lt;/h2&gt;

&lt;p&gt;Choosing the right Trigger Framework is crucial for effective automation. I’ve developed a simple yet powerful Framework that allows admins to maintain a pipeline of handlers, control their execution order, and break them into smaller, more manageable components for easier maintenance. The Framework enables you to toggle functionality on or off without constantly updating code and includes an ErrorLogger for tracking issues. It comes with Record Trigger Templates to kickstart creating Record-Triggered Flows, along with prebuilt Custom Permissions, Permission Sets, and a Permission Set Group to manage bypassing Flows and Triggers. A baseline Permission Set for the Framework ensures all users can access it without extra effort. The Framework also features a Double Fire-safe mechanism, providing control over when processes should or shouldn’t fire multiple times within a Salesforce Trigger’s context. Additionally, it allows precise control over handler execution using Custom Permissions or criteria of your choice.&lt;/p&gt;

&lt;p&gt;Example Implementation&lt;br&gt;
Before diving into implementation snippets, let’s review the key features this framework supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Execution Control&lt;/strong&gt;: Define precise conditions for when the framework should execute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DoubleFire-Safe Technology&lt;/strong&gt;: Prevent unintended multiple executions within a single trigger context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Trigger Contexts&lt;/strong&gt;: Supports all key contexts, including Before/After, Insert, Update, Delete, and Undelete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline DML Operations&lt;/strong&gt;: Perform DML actions in bulk at the end of the pipeline, optimizing performance and reducing governor limit usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is an example code snippet for the Trigger implemented on an Object in salesforce.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="nc"&gt;TestObjectTrigger&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="nf"&gt;Test_Object__c&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="n"&gt;undelete&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TriggerPipeline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Test_Object__c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sObjectType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is the example code snippet for a Handler added to a pipeline on an object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestObjectHandler&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ATriggerHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Optional if you want to fire all the time no need to check if it should or not.&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="nf"&gt;shouldExecute&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nc"&gt;FeatureManagement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkPermission&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;Exclude_Trigger&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/** Override this method and return the Type of the implementing trigger class
     * (e.g. return Type.getName('NameOfImplementingTriggerClass'))
     * (e.g. return NameOfImplementingTriggerClass.class
     *
     * Required do not delete method.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="nc"&gt;Type&lt;/span&gt; &lt;span class="nf"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;TestObjectHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Override this method and set isDoubleFireSafe to true in any trigger that needs to set
     * special rules for determining if a record has already been trigged upon. Only applies to
     * update operations. Salesforce guarantees that triggers only fire once for insert and delete
     * operations.
     *
     * @return a Boolean indicating if a trigger is using the double-fire safe trigger framework
     *
     * Optional if you want to fire only once.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="nf"&gt;isDoubleFireSafe&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add Remove the methods below based on what is needed for your use case&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * beforeInsert method to fire in the before insert context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeInsert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * afterInsert method to fire in the after insert context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterInsert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * beforeUpdate method to fire in the before update context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeUpdate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * afterUpdate method to fire in the after update context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterUpdate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * beforeDelete method to fire in the before delete context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeDelete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * afterDelete method to fire in the after delete context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterDelete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * afterUndelete method to fire in the after undelete context of a trigger.
     *
     * @param triggerData TriggerData the trigger data found in the Trigger class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterUndelete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TriggerData&lt;/span&gt; &lt;span class="n"&gt;triggerData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Code Goes Here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check out more details on this Trigger Framework at my &lt;a href="https://github.com/chiefpansancolt/salesforce-automation-framework" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>architecture</category>
      <category>automation</category>
    </item>
    <item>
      <title>Ruby Testing with a Formatter that is modernized</title>
      <dc:creator>Christopher Pezza</dc:creator>
      <pubDate>Sun, 11 Oct 2020 15:32:37 +0000</pubDate>
      <link>https://dev.to/chiefpansancolt/ruby-testing-with-a-formatter-that-is-modernized-39ai</link>
      <guid>https://dev.to/chiefpansancolt/ruby-testing-with-a-formatter-that-is-modernized-39ai</guid>
      <description>&lt;p&gt;Testing is the pinnacle of development, when you develop in an MVC model the controllers expect unit tests to prove it works as expected. Many of these libraries for Ruby are of the likes of &lt;a href="https://github.com/seattlerb/minitest"&gt;Minitest&lt;/a&gt;, &lt;a href="https://github.com/rspec/rspec"&gt;RSpec&lt;/a&gt;, and many more.&lt;/p&gt;

&lt;p&gt;With Ruby, a common library used to see what the output code coverage is, is &lt;a href="https://github.com/colszowka/simplecov"&gt;Simplecov&lt;/a&gt;. This tool is one I love, shows you what lines are covered, what was missed, and what you have defined as skipped. You also have the ability to configured many other settings. One of those settings is the formatter. You can describe one or many, out of the box comes an HTML Formatter that produces a webpage found in the coverage/index.html. With the new support of branches in ruby testing, this adds more columns to the table and to the file view that becomes hard to see/read. This has been a format I have lived by for years but as I have used it on large projects with large file sets and long file names, it has not been the cleanest UX to work with. I have found it to be outdated and needs to be upgraded.&lt;/p&gt;

&lt;p&gt;With that, I decided to go to the drawing board to design and build a new UX for an HTML web output that leveraged the new standards. Previously I built one with the guidelines of &lt;a href="https://material.io/"&gt;Material Design&lt;/a&gt;, but have come to love the likes of &lt;a href="https://tailwindcss.com/"&gt;Tailwindcss&lt;/a&gt;, so I utilized &lt;a href="https://tailwindcss.com/"&gt;Tailwindcss&lt;/a&gt; and &lt;a href="https://tailwindui.com/"&gt;TailwindUI&lt;/a&gt;. I analyzed what worked and what didn't with the HTML Formatter offered and concluded the following.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ability to search files in a group.&lt;/li&gt;
&lt;li&gt;Ability to see groups.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Bads
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When groups get over 6 in total it starts to cause the page to look weird. Especially when your screen size is not large.&lt;/li&gt;
&lt;li&gt;When the file name gets long and the screen is small rows fall off of the table view and become unreadable.&lt;/li&gt;
&lt;li&gt;Difficult to read coverage per file and per group.&lt;/li&gt;
&lt;li&gt;Scrolling on a file view could scroll past the file if your cursor was in the wrong spot.&lt;/li&gt;
&lt;li&gt;When viewing an individual file source you really didn't know what file you clicked on or what the details were about that file without closing the modal to view the table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I knew that I had to maintain a singular page generation to ensure not too many dependency files were needed when generating. This led me to move away from a tabular approach and more for a left navigation approach. This new standardized view enables a user to see all available views without the page getting distorted when a large number of group views are generated. This would allow for scrollable user interaction. I also looked to move the sentence about the numbers, which was too much and hard to read. So I looked to change that to a tile grid at the top of each group page. There were little changes that will be noticed throughout the page such as, the footer is cleaner, the name of the project is displayed with dashes, and underscores stripped out. From a repo structure, I looked to implement and use Webpack to generate the output application JS and CSS files for usage.&lt;/p&gt;

&lt;p&gt;In this new view, I also added the ability to search for files and filter down a list using a search bar. This was accomplished with some very basic JS that looks in the actively viewed table and uses a single search bar. I found that the old search box was not the easiest to see but is one of the most powerful tools so it should be recognized and emphasized to the user in the view. This is a big tool when large projects have hundreds of files to view that can be hard to find when scrolling to the naked eye.&lt;/p&gt;

&lt;p&gt;The results are as follows with a before (&lt;a href="https://github.com/colszowka/simplecov-html"&gt;simplecov-html&lt;/a&gt;) and after (&lt;a href="https://github.com/chiefpansancolt/simplecov-taiwindcss"&gt;simplecov-tailwindcss&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Before (simplecov-html)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Full View
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--StDZIsR6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0vj2dzy2k6khtmtpfb26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--StDZIsR6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0vj2dzy2k6khtmtpfb26.png" alt="Full View HTML" width="880" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Individual File View
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_O1H_AfM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qjxsb759t8tmlf5yb9x1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_O1H_AfM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qjxsb759t8tmlf5yb9x1.png" alt="Individual View HTML" width="880" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  After (simplecov-tailwindcss)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Full View
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z2di5A_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yxz3d70s4s469vpjyvfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z2di5A_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yxz3d70s4s469vpjyvfy.png" alt="Full View Tailwind" width="880" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Individual View
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--09UQ5lMO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zzjw6qgyacbo4zercol1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--09UQ5lMO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zzjw6qgyacbo4zercol1.png" alt="Individual View Tailwind" width="880" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above the look is drastically different in styles, but still maintains the same data, and functionality prior and some. I hope to expand the functionality based on community feedback over time so please if you have any feedback checkout the repo &lt;a href="https://github.com/chiefpansancolt/simplecov-tailwindcss"&gt;here&lt;/a&gt; and leave your comments under the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/chiefpansancolt/simplecov-tailwindcss/issues/new?assignees=chiefpansancolt&amp;amp;labels=bug%2C+new&amp;amp;template=bug_report.md&amp;amp;title="&gt;&lt;strong&gt;Bug&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/chiefpansancolt/simplecov-tailwindcss/issues/new?assignees=chiefpansancolt&amp;amp;labels=enhancement%2C+new&amp;amp;template=feature-request.md&amp;amp;title=Feature+Request"&gt;&lt;strong&gt;Feature Request&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/chiefpansancolt/simplecov-tailwindcss/issues/new?assignees=&amp;amp;labels=new%2C+question&amp;amp;template=question.md&amp;amp;title=Question"&gt;&lt;strong&gt;Question&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for taking the time to read this on the approach I took to develop &lt;a href="https://github.com/chiefpansancolt/simplecov-tailwindcss"&gt;Simplecov Tailwind&lt;/a&gt; Formatter and hope you love it as much as I do! It's just so clean and easy to use!!&lt;/p&gt;

&lt;p&gt;To use this gem simply install the gem in your repo by adding to your gemfile. This gem is hosted on &lt;a href="https://rubygems.org/gems/simplecov-tailwindcss"&gt;rubygems.org&lt;/a&gt; and on the new &lt;a href="https://github.com/chiefpansancolt/simplecov-tailwindcss/packages"&gt;Github Registry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add to your gemfile see below or check out the &lt;a href="https://chiefpansancolt.live/docs/simplecov_tailwindcss"&gt;Documentation&lt;/a&gt; on the repo for reference&lt;/p&gt;

&lt;h4&gt;
  
  
  Ruby Gems Host
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ./Gemfile&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov-tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Github Rubygems Host
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ./Gemfile&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"https://rubygems.pkg.github.com/chiefpansancolt"&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov-tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>rubygems</category>
      <category>tailwindcss</category>
      <category>tailwindui</category>
    </item>
    <item>
      <title>Using a Clean Formatter for Ruby Testing</title>
      <dc:creator>Christopher Pezza</dc:creator>
      <pubDate>Sat, 20 Jul 2019 21:52:41 +0000</pubDate>
      <link>https://dev.to/chiefpansancolt/using-a-clean-formatter-for-ruby-testing-2khe</link>
      <guid>https://dev.to/chiefpansancolt/using-a-clean-formatter-for-ruby-testing-2khe</guid>
      <description>&lt;p&gt;Testing is the pinnacle of development, when you develop in a MVC model the controllers expect unit tests to prove it works as expected. Many of these libraries for Ruby are of the likes of &lt;a href="https://github.com/seattlerb/minitest" rel="noopener noreferrer"&gt;Minitest&lt;/a&gt;, &lt;a href="https://github.com/rspec/rspec" rel="noopener noreferrer"&gt;RSpec&lt;/a&gt; and many more.&lt;/p&gt;

&lt;p&gt;With Ruby, a common library used to see what the outputed code coverage is, is &lt;a href="https://github.com/colszowka/simplecov" rel="noopener noreferrer"&gt;Simplecov&lt;/a&gt;. This tool is one I love, shows you what lines are covered, what was missed, and what you have defined as skipped. You also have the ability to configured many other settings. One of those settings is the formatter. You can describe one or many, out of the box comes a HTML Formatter that produces a webpage found in the coverage/index.html. This has been a format I have lived by for years but as I have used it on large projects with large file sets and long file names, it has not been the cleanest UX to work with. I have found it to be outdated and needing to be upgraded.&lt;/p&gt;

&lt;p&gt;With that, I decided to go to the drawing board to design and build a new UX for a HTML web output that leveraged the new standards of &lt;a href="https://material.io/" rel="noopener noreferrer"&gt;Material Design&lt;/a&gt;. I analyzed what worked and what didn't with the HTML Formatter offered and concluded the following. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Goods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ability to search files in a group.&lt;/li&gt;
&lt;li&gt;Ability to see groups.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Bads
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When groups get over 6 in total it starts to cause the page to look weird.&lt;/li&gt;
&lt;li&gt;When file name get long and screen is small rows fall off of table view and become unreadable.&lt;/li&gt;
&lt;li&gt;Difficult to read coverage per file and per group.&lt;/li&gt;
&lt;li&gt;Scrolling on a file view could scroll past the file if your cursor was in the wrong spot.&lt;/li&gt;
&lt;li&gt;When viewing an individual file source you really didn't know what file you clicked on or what the details were about that file without closing the modal to view the table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I knew that I had to maintain a singular page generation to ensure not to many dependency files were needed when generating. This led me to move away from a tabular approach and more for a left navigation approach. This would allow for a scrollable user interaction. I also looked to move the sentence about the numbers, which was to much and hard to read. So I looked to change that to a tile grid at the top of each group page. There were little changes that will be noticed throughout the page such as, the footer is cleaner, the name of the project is displayed with dashes and underscores stripped out, the text for a file is cut off with ellipses in the table to ensure rows adhere to column width appropriately. From a repo structure I looked to implement and use Webpack to generate the output application js and css files for usage.&lt;/p&gt;

&lt;p&gt;The results are as follows with a before (&lt;a href="https://github.com/colszowka/simplecov-html" rel="noopener noreferrer"&gt;simplecov-html&lt;/a&gt;) and after (&lt;a href="https://github.com/chiefpansancolt/simplecov-material" rel="noopener noreferrer"&gt;simplecov-material&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Before (simplecov-html)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Full View
&lt;/h4&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vj2dzy2k6khtmtpfb26.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vj2dzy2k6khtmtpfb26.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Individual File View
&lt;/h4&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqjxsb759t8tmlf5yb9x1.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqjxsb759t8tmlf5yb9x1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  After (simplecov-material)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Full View
&lt;/h4&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F66keo0thbrmg7cse5wy6.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F66keo0thbrmg7cse5wy6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Individual View
&lt;/h4&gt;

&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ff8buhic7vca3ka3z7a22.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ff8buhic7vca3ka3z7a22.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above the look is drastically different in styles, but still maintains the same data, and functionality prior and some. I hope to expand the functionality based on community feedback overtime so please if you have any feedback checkout the repo &lt;a href="https://github.com/chiefpansancolt/simplecov-material" rel="noopener noreferrer"&gt;here&lt;/a&gt; and leave your comments under the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/chiefpansancolt/simplecov-material/issues/new?assignees=chiefpansancolt&amp;amp;labels=bug%2C+new&amp;amp;template=bug_report.md&amp;amp;title=" rel="noopener noreferrer"&gt;&lt;strong&gt;Bug&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/chiefpansancolt/simplecov-material/issues/new?assignees=chiefpansancolt&amp;amp;labels=enhancement%2C+new&amp;amp;template=feature-request.md&amp;amp;title=Feature+Request" rel="noopener noreferrer"&gt;&lt;strong&gt;Feature Request&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/chiefpansancolt/simplecov-material/issues/new?assignees=&amp;amp;labels=new%2C+question&amp;amp;template=question.md&amp;amp;title=Question" rel="noopener noreferrer"&gt;&lt;strong&gt;Question&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for taking the time to read this on the approach I took to develop &lt;a href="https://github.com/chiefpansancolt/simplecov-material" rel="noopener noreferrer"&gt;Simplecov Material&lt;/a&gt; Formatter and hope you love it as much as I do! It's just so clean and easy to use!!&lt;/p&gt;

&lt;p&gt;To use this gem simply install the gem in your repo by adding to your gemfile. This gem is hosted on &lt;a href="https://rubygems.org/gems/simplecov-material" rel="noopener noreferrer"&gt;rubygems.org&lt;/a&gt; and on the new &lt;a href="https://github.com/chiefpansancolt/simplecov-material/packages" rel="noopener noreferrer"&gt;Github Registry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add to your gemfile see below or checkout the &lt;a href="https://github.com/chiefpansancolt/simplecov-material/blob/master/README.md" rel="noopener noreferrer"&gt;Readme.md&lt;/a&gt; on the repo for reference&lt;/p&gt;

&lt;h4&gt;
  
  
  Ruby Gems Host
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ./Gemfile&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov-material"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Github Rubygems Host
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ./Gemfile&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"https://rubygems.pkg.github.com/chiefpansancolt"&lt;/span&gt; 
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"simplecov-material"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>gems</category>
      <category>simplecov</category>
      <category>material</category>
    </item>
  </channel>
</rss>
