Skip to content

Commit

Permalink
Add basic structure
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexAvlonitis committed Oct 15, 2022
1 parent daeb909 commit 083170a
Show file tree
Hide file tree
Showing 41 changed files with 730 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.gem
5 changes: 5 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

require 'bundler/gem_tasks'

task default: :spec
56 changes: 56 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Pico API

A tiny boilerplate JSON API template in ruby. Its minimal footprint makes it ideal for microservices or even full blown APIs.
It has been created for those who don't need the RAILS "magic" and bloat. Can easily create different architectures, like STAR, CQRS..., and MVC.

Pico API is just a collection of the fastest ruby libraries put together to form a basic API environment, with the JSON:API specification. It doesn't create any folder structures for your business logic, it only creates the initial configurations to get you started.

Example demo project can be found [here](https://github.com/alexavlonitis/pico_api_example)

## Libraries Used

- [roda](https://github.com/jeremyevans/roda) -> Routing
- [rom-rb](https://github.com/rom-rb/rom) -> ORM
- [jsonapi serializer](https://github.com/jsonapi-serializer/jsonapi-serializer) -> Fast-jsonapi fork

## Usage

### Create your app

```ruby
gem install pico_api

pico_api --new my_app

cd my_app

bundle install
```

### Configure your database

Migration info: https://rom-rb.org/5.0/learn/sql/migrations/

- Add the database config details in `config/database.yml`
- Setup the Database: `rake db:setup`
- Create a migration: `rake db:create_migration[create_users]` (include your preferred DB gem in the Gemfile)
- Run the migrations: `rake db:migrate`
- Run the server: `rackup -p 3001`


## Development
- [x] Create Database config
- [x] Hook a json-api library
- [x] Handle Errors
- [ ] Create a Logger config
- [ ] Create a testing environment
- [ ] Allow multiple db gateways in the config
- [ ] Add irb/pry console script

## Contributing

All Pull Requests are welcome, from a single typo to a new feature.

- Fork this repo
- Create a new branch, add your changes with tests when necessary
- Submit a Pull Request
15 changes: 15 additions & 0 deletions bin/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'bundler/setup'
require 'pico_api'

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start

require 'irb'
IRB.start(__FILE__)
14 changes: 14 additions & 0 deletions bin/pico_api
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

#!/usr/bin/env ruby

require 'optparse'
require 'pico_api'

options = {}
OptionParser.new do |opts|
opts.banner = "Usage: pico_api [options]"

opts.on('-n', '--new project_name') { |o| options[:project_name] = o }
end.parse!

PicoApi::Generators::Generator.call(options[:project_name])
8 changes: 8 additions & 0 deletions bin/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here
30 changes: 30 additions & 0 deletions lib/pico_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require 'roda'
require 'zeitwerk'

loader = Zeitwerk::Loader.for_gem
loader.setup

module PicoApi
def self.root_path
File.dirname(__dir__)
end

def self.lib_path
File.join(root_path, 'lib/pico_api')
end

class Application < Roda
parser = proc { |data| JSON.parse(data, symbolize_names: true) }

plugin :symbol_status
plugin :json_parser, parser: parser
plugin :json, content_type: 'application/vnd.api+json'
plugin :error_handler do |e|
PicoApi::Handlers::Errors.call(e, response)
end
end
end

loader.eager_load
30 changes: 30 additions & 0 deletions lib/pico_api/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require 'erb'
require 'yaml'

module PicoApi
class << self
def configuration
@configuration ||= Configuration.new(db_config)
end

def db_config
template = ERB.new(File.new('./config/database.yml').read)
YAML.safe_load(template.result(binding))
end

def configure
yield(configuration)
end
end

class Configuration
attr_reader :db_config
attr_accessor :namespace, :lib_path, :errors_map

def initialize(db_config)
@db_config = db_config
end
end
end
24 changes: 24 additions & 0 deletions lib/pico_api/database.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

require 'rom'
require 'rom-sql'

module PicoApi
class Database
class << self
attr_accessor :container

def setup!
database_config = PicoApi.configuration.db_config['default']
adapter = database_config['adapter'].to_sym
options = database_config['options'].symbolize_keys
connection_string = database_config['connection_string']

config = ROM::Configuration.new(adapter, connection_string, options)
yield config if block_given?

@container = ROM.container(config)
end
end
end
end
22 changes: 22 additions & 0 deletions lib/pico_api/entities/error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require 'securerandom'

module PicoApi
module Entities
class Error < ROM::Struct
attribute? :title, ROM::Types::String
attribute? :status, ROM::Types::String.optional
attribute? :code, ROM::Types::String.optional
attribute? :detail, ROM::Types::String.optional

def to_h
id.merge(super)
end

def id
{ id: SecureRandom.hex(5) }
end
end
end
end
22 changes: 22 additions & 0 deletions lib/pico_api/entities/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module PicoApi
module Entities
class Errors < ROM::Struct
class << self
def call(error)
class_name = error.class.name.demodulize
error_entities = [
Entities::Error.new(
title: class_name.underscore,
detail: error.message
)
]
new(errors: error_entities)
end
end

attribute? :errors, ROM::Types::Array(Entities::Error)
end
end
end
45 changes: 45 additions & 0 deletions lib/pico_api/generators/commands/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require 'fileutils'
require 'erb'

module PicoApi
module Generators
module Commands
class Base
def self.call(project_name)
new(project_name).call
end

def initialize(project_name)
@project_name_camelcased = project_name.camelize
@project_name_snakecased = project_name.underscore
end

def call
NotImplementError
end

def get_binding
binding
end

private

attr_reader :project_name_camelised, :project_name_snakecased

def erb
ERB.new(File.read(template_full_path))
end

def template_full_path
File.join(PicoApi.lib_path, template_relative_path)
end

def template_relative_path
''
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/pico_api/generators/commands/copy_template.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module PicoApi
module Generators
module Commands
class CopyTemplate < Base
def call
copy_file
end

private

def copy_file
FileUtils.cp(template_full_path, "#{project_name_snakecased}#{destination_path}")
end
end
end
end
end
29 changes: 29 additions & 0 deletions lib/pico_api/generators/commands/create_base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module PicoApi
module Generators
module Commands
class CreateBase < Base
def call
create_bin_folder
create_lib_folder
create_config_folder
end

private

def create_bin_folder
FileUtils.mkdir_p("#{project_name_snakecased}/bin")
end

def create_lib_folder
FileUtils.mkdir_p("#{project_name_snakecased}/lib/#{project_name_snakecased}")
end

def create_config_folder
FileUtils.mkdir_p("#{project_name_snakecased}/config")
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/pico_api/generators/commands/create_config_application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module PicoApi
module Generators
module Commands
class CreateConfigApplication < CreateTemplate
private

def destination_path
'/config/application.rb'
end

def template_relative_path
'/generators/templates/config/application.erb'
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/pico_api/generators/commands/create_config_boot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module PicoApi
module Generators
module Commands
class CreateConfigBoot < CopyTemplate
private

def destination_path
'/config/boot.rb'
end

def template_relative_path
'/generators/templates/config/boot.erb'
end
end
end
end
end
Loading

0 comments on commit 083170a

Please sign in to comment.