<?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>Julia Community 🟣: Nathan Ortega</title>
    <description>The latest articles on Julia Community 🟣 by Nathan Ortega (@ndortega).</description>
    <link>https://forem.julialang.org/ndortega</link>
    <image>
      <url>https://forem.julialang.org/images/_DOYzqRkMaeH82B5sQSda8tA87rT2mThru8JVfhybiI/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L3VzZXIvcHJvZmls/ZV9pbWFnZS8zNzMv/NzNjNzc5NjgtZWNj/MS00ZjUxLTg2MzYt/M2ZiZjEyZjdlZmZh/LnBuZw</url>
      <title>Julia Community 🟣: Nathan Ortega</title>
      <link>https://forem.julialang.org/ndortega</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.julialang.org/feed/ndortega"/>
    <language>en</language>
    <item>
      <title>[ANN] Oxygen.jl v1.1.6 (Cron Scheduling)</title>
      <dc:creator>Nathan Ortega</dc:creator>
      <pubDate>Mon, 10 Apr 2023 00:17:54 +0000</pubDate>
      <link>https://forem.julialang.org/ndortega/ann-oxygenjl-v116-cron-scheduling-4ken</link>
      <guid>https://forem.julialang.org/ndortega/ann-oxygenjl-v116-cron-scheduling-4ken</guid>
      <description>&lt;p&gt;Hello, everyone!&lt;/p&gt;

&lt;p&gt;Today, I'm here to introduce the latest update to the &lt;a href="https://github.com/ndortega/Oxygen.jl"&gt;Oxygen.jl&lt;/a&gt;. It's been almost a year since my &lt;a href="https://forem.julialang.org/ndortega/oxygenjl-a-breath-of-fresh-air-for-programming-web-apps-in-julia-454d"&gt;last post&lt;/a&gt;, and I've been steadily working on incorporating your feedback and implementing new features to enhance the development experience.&lt;/p&gt;

&lt;p&gt;In this post, I'll specifically discuss the most recent addition to the framework: Cron Scheduling. This feature allows you to schedule recurring tasks effortlessly for both endpoints and standalone functions, using the same Cron expressions we're all familiar with.&lt;/p&gt;

&lt;p&gt;Additionally, I'll briefly cover the most significant updates that have been introduced since last time. The latest version offers a range of improvements designed to help you build more robust and versatile applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ndortega/Oxygen.jl#custom-response-serializers"&gt;JSON serialization &amp;amp; deserialization (customizable)&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;This package has always supported automatic JSON serialization and deserialization for request and response data. But now you can also define your own serializers and deserializers by using middleware functions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Added:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/ndortega/Oxygen.jl#cron-scheduling"&gt;Cron Scheduling&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added support for Cron scheduling, which makes it easy to schedule recurring tasks to run at specific intervals for both endpoints and functions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/ndortega/Oxygen.jl#repeat-tasks"&gt;Repeat Tasks&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added the ability to create tasks that repeat at specified intervals, making it easy to schedule and manage recurring tasks within your application.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/ndortega/Oxygen.jl#middleware"&gt;Middleware&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Middleware functions enable the creation of custom workflows to intercept incoming requests and outgoing responses in a specific order. They can be set at the application, router, and route layers using the middleware keyword argument, combining and executing the middleware defined in each layer. The execution order is always: application, router, and then route.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/ndortega/Oxygen.jl#autogenerated-docs-with-swagger"&gt;Auto-generated Swagger Documentation&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically generates Swagger documentation for each registered route, making it easier to maintain and share API documentation with others. You can view, modify, and extend the autogenerated schema.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;a href="https://github.com/ndortega/Oxygen.jl#routers"&gt;Route Tagging&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added route tagging, allowing you to categorize routes and easily manage or filter them based on assigned tags. This is used when autogenerating the Swagger documentation to group together similar endpoints.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Cron Scheduling
&lt;/h1&gt;

&lt;p&gt;Oxygen comes with a built-in cron scheduling system that allows you to call endpoints and functions automatically when the cron expression matches the current time.&lt;/p&gt;

&lt;p&gt;When a job is scheduled, a new task is created and runs in the background. Each task uses its given cron expression and the current time to determine how long it needs to sleep before it can execute.&lt;/p&gt;

&lt;p&gt;The cron parser in Oxygen is based on the same specifications as the one used in Spring. You can find more information about this on the &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/support/CronExpression.html"&gt;Spring Cron Expressions page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cron Expression Syntax
&lt;/h2&gt;

&lt;p&gt;The following is a breakdown of what each parameter in our cron expression represents. While our specification closely resembles the one defined by Spring, it's not an exact 1-to-1 match.&lt;/p&gt;

&lt;p&gt;The string has six single space-separated time and date fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ┌───────────── second (0-59)
 │ ┌───────────── minute (0 - 59)
 │ │ ┌───────────── hour (0 - 23)
 │ │ │ ┌───────────── day of the month (1 - 31)
 │ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
 │ │ │ │ │ ┌───────────── day of the week (1 - 7)
 │ │ │ │ │ │          (Monday is 1, Tue is 2... and Sunday is 7)
 │ │ │ │ │ │
 * * * * * *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partial expressions are also supported, which means that subsequent expressions can be left out (they are defaulted to '*').&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# In this example we see only the `seconds` part of the expression is defined. &lt;/span&gt;
&lt;span class="c"&gt;# This means that all following expressions are automatically defaulted to '*' expressions&lt;/span&gt;
&lt;span class="nd"&gt;@cron&lt;/span&gt; &lt;span class="s"&gt;"*/2"&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"runs every 2 seconds"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scheduling Endpoints
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;router()&lt;/code&gt; function has a keyword argument called cron, which accepts a cron expression that determines when an endpoint is called. Just like the other keyword arguments, it can be reused by endpoints that share routers or be overridden by inherited endpoints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# execute at 8, 9 and 10 o'clock of every day.&lt;/span&gt;
&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/cron-example"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 8-10 * * *"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"here"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# execute this endpoint every 5 seconds (whenever current_seconds % 5 == 0)&lt;/span&gt;
&lt;span class="n"&gt;every5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/cron"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"*/5"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# this endpoint inherits the cron expression&lt;/span&gt;
&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="n"&gt;every5&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/first"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"first"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Now this endpoint executes every 2 seconds ( whenever current_seconds % 2 == 0 ) instead of every 5&lt;/span&gt;
&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="n"&gt;every5&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/second"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"*/2"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"second"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scheduling Functions
&lt;/h2&gt;

&lt;p&gt;In addition to scheduling endpoints, you can also use the new &lt;code&gt;@cron&lt;/code&gt; macro to schedule standalone functions. This is useful if you want to run code at specific times without making it visible or callable in the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="nd"&gt;@cron&lt;/span&gt; &lt;span class="s"&gt;"*/2"&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"runs every 2 seconds"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@cron&lt;/span&gt; &lt;span class="s"&gt;"0 0/30 8-10 * * *"&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"runs at 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Starting &amp;amp; Stopping Cron Jobs
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;serve()&lt;/code&gt; or &lt;code&gt;serveparallel()&lt;/code&gt;, all registered cron jobs are automatically started. If the server is stopped or killed, all running jobs will also be terminated. You can stop the server and all repeat tasks and cron jobs by calling the &lt;code&gt;terminate()&lt;/code&gt; function or manually killing the server with &lt;code&gt;ctrl+C&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition, Oxygen provides utility functions to manually start and stop cron jobs: &lt;code&gt;startcronjobs()&lt;/code&gt; and &lt;code&gt;stopcronjobs()&lt;/code&gt;. These functions can be used outside of a web server as well.&lt;/p&gt;

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

&lt;p&gt;As we wrap up, I want to extend a sincere thank you to each and every one of you for your incredible support, insightful feedback, and active involvement with the project. Your input has played a crucial role in the growth and development of the package, and I look forward to our ongoing collaboration as we team up to make Oxygen.jl even better!&lt;/p&gt;

</description>
      <category>web</category>
      <category>http</category>
      <category>cron</category>
      <category>framework</category>
    </item>
    <item>
      <title>Oxygen.jl: A breath of fresh air for programming web apps in Julia</title>
      <dc:creator>Nathan Ortega</dc:creator>
      <pubDate>Mon, 06 Jun 2022 01:01:29 +0000</pubDate>
      <link>https://forem.julialang.org/ndortega/oxygenjl-a-breath-of-fresh-air-for-programming-web-apps-in-julia-454d</link>
      <guid>https://forem.julialang.org/ndortega/oxygenjl-a-breath-of-fresh-air-for-programming-web-apps-in-julia-454d</guid>
      <description>&lt;p&gt;I'm excited to announce &lt;a href="https://github.com/ndortega/Oxygen.jl"&gt;Oxygen.jl&lt;/a&gt;, a micro-framework built on top of the HTTP.jl library that's designed to simplify building web applications in Julia.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Oxygen&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;HTTP&lt;/span&gt;

&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="s"&gt;"/greet"&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"hello world!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# both path parameters are automatically converted to Float64's &lt;/span&gt;
&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="s"&gt;"/multiply/{a}/{b}"&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# all objects are automatically deserialized into JSON&lt;/span&gt;
&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="s"&gt;"/getdata"&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"hello!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# mount all nested files inside the "content" folder under "/static"&lt;/span&gt;
&lt;span class="nd"&gt;@staticfiles&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt; &lt;span class="s"&gt;"static"&lt;/span&gt;

&lt;span class="c"&gt;# start the web server&lt;/span&gt;
&lt;span class="n"&gt;serve&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Straightforward routing (@get, @post, @put, @patch, @delete and @route macros)&lt;/li&gt;
&lt;li&gt;Out-of-the-box JSON serialization &amp;amp; deserialization &lt;/li&gt;
&lt;li&gt;Optional type definition support for Path parameters&lt;/li&gt;
&lt;li&gt;Helper functions to parse &amp;amp; transform the body of requests &amp;amp; responses&lt;/li&gt;
&lt;li&gt;Hosting static files&lt;/li&gt;
&lt;li&gt;Built-in multithreading support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why build another web framework?
&lt;/h3&gt;

&lt;p&gt;That's a great question, why create another package if Julia already has full stack frameworks like Genie.jl that can do just about anything?&lt;/p&gt;

&lt;p&gt;My quick answer: most people don't need all features that come with these heavyweight packages. About a month ago, I was one of those people looking to quickly spin up a web server to prototype some ideas. &lt;/p&gt;

&lt;p&gt;I wanted something lightweight and straightforward, and I didn't want to spend a lot of time learning a new syntax. Ideally, I wanted something similar to Flask or FastApi but in Julia. &lt;/p&gt;

&lt;p&gt;So, I set out to create this package and design a no-nonsense api that would hopefully feel familiar to JS and Python programmers and drive greater adoption in these communities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature Requests
&lt;/h3&gt;

&lt;p&gt;If you have any ideas or feature requests, please feel free to open an issue on the github page! &lt;/p&gt;

</description>
      <category>package</category>
      <category>web</category>
      <category>framework</category>
      <category>http</category>
    </item>
  </channel>
</rss>
