Skip to content

raindeer-rb/raindeer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

282 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Raindeer logo

Raindeer

Gem version GitHub repo Codeberg repo Discord

Raindeer is a new web framework using the dynamic features and latest async improvements in Ruby + some weird ideas, to build a new breed of web application. Each Raindeer component can be used individually in your exisiting application, or all together as a cohesive framework. Deer to be different.

Components

See: 🎬 Video Overview of Raindeer

LowType

LowType introduces the concept of "type expressions", allowing you to add inline types in your code, only when you need them. LowType is an elegant type checking system with the most minimal DSL possible. It looks like if Ruby had native types; def method(var: String).

LowLoop

LowLoop is an asynchronous event-driven server that ties into LowEvent to create and send events from the request layer right through to the application and data layers. Finally you can see and track events through every step of your application.

LowEvent

LowEvent represents events of all kinds; Raindeer uses RequestEvent, RouteEvent, RenderEvent and ResponseEvent. Plus you can extend with your own event types. Events can be observed with Observers.

RainRouter

The RainRouter accepts RequestEvents and directs the request to the appropriate observers. Simply add observe 'path/:id' to a LowNode and now it will be called every time a request is made to this route.

LowNode

LowNodes are the flexible building blocks of your application. They can respond to a route request, or they can be called by another node. They can render a return value, or they can create an event. They are designed to be specific enough to observe events and return values, but generic enough to be split up to represent a complex application with its own patterns and structure. Nodes can render HTML/JSON directly from the Ruby class (via RBX, similar to JSX) and render other nodes into the output using Raindeer's special Antlers syntax; <html><{ ChildNode }></html>.

LowData

LowData follows the repository pattern with a twist; Data Expressions. A data expression like Users[:username] + Posts[:title, :body] builds a SQL query to OUTER JOIN the Users table into the Posts table and results in a list of posts with the user's username in each row.

Architecture

Raindeer glues Low components together with a router, observers, pipelines and client-side integrations. It's decoupled and event-driven in a way that's deceptively simple whilst enabling scalable architectures.

Raindeer architecture diagram

Philosophy

πŸ™ I don't want to be a priest. If you see any opinions here that are too preachy then please let me know. I want my opinions to be opinionless

πŸ₯š Less is more

Anything that just "is how it is" can be made simpler. It may take lots of time to find a way how but it's worth it. We should really care about people learning a new framework; they shouldn't have to learn much. One way to do this is by removing things:

  • Namespaces - Namespaces are confusing to new developers and the :: syntax just doesn't look right. They are optional (and still used internally by Raindeer)
  • Heredoc - If you want to write multi-line HTML then you can just be able to write it directly into a LowNode via RBX. Raindeer lets you do this via LowLoad
  • MVC - You shouldn't have to learn the 5 exact locations to put files in and know the order in which they are called by the framework. Just observe an event in a node and render output, or call more code

🧩 The Pattern Anti-Pattern

Developers will use patterns applicable to their application's domain that are different to the framework's patterns. This results in a tension between highly rigid framework patterns (MVC) and an application's. We shouldn't fight that but provide designated entities and events to glue the framework and the application together. Raindeer uses LowNode to intercept the "request and response" layer then gets out of the way and let's the application structure itself from there.

πŸ‘£ No build steps

Your files should just work out of the box. No one ever asked for a build step and it takes you out of your flow.

No build steps can internally create issues:

Problem Solution
Extra runtime processing Process once on "Class Load" and clean up at the end of the application's boot stage
Less isolation between concerns With extra effort we can still isolate these "mixed" concerns internally

🌲 Living framework

An application is a living organism and so is the framework below it. Raindeer does dynamic processing of previously static elements; from type checking and expressions to parallelization of nodes. This is okay, the framework should do more and feel alive. That being said, dynamic doesn't mean "magic"...

πŸͺ† Composition over convention

Methods and classes should be compositional, so that you can understand their hidden complexity by drilling down into them as they go, rather than calling one magic method that does a bunch of side quests. APIs should be less magical and more compositional.

A perfect example is the has_many helper method provided by various ORMs. This method adds "association" methods to a model, then hides the fact that databases do joins on tables. You will have to do a JOIN on a table eventually, so it's better to represent tables as being merged together from the start. There has to be a more compositional way that exposes the database structure while letting you query that structure easily. Raindeer uses LowData to represent table structure compositionally:

class PostsData < LowData
  def all
    Users[:username] + Posts[:title, :body]
  end
end

ℹ️ This data expression generates SQL to OUTER JOIN the user table with the posts table and results in a list of posts with the user's username included in each row.

Getting Started

Currently the best way to get a feel for Raindeer is to clone this repo and run bin/server. Look inside lib/system to see Raindeer's internal application, which you can use as a template for your own application.

In the future the Raindeer Template will be the better way to begin developing your own Raindeer application.

Then in the far future we'll probably have an app generator command... could that future be you?

Contributing

Raindeer needs developer contributions around:

  • Database layer - SQL generation, performance, migrations
  • Deployment layer - Credential management, authentication
  • Tooling
  • Performance testing and fixing

If you have a love for code first then AI second, please consider making a contribution.

About

An event-driven and compositional web framework that's easy to use

Resources

License

Contributing

Stars

20 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors