flamerb 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +25 -0
  4. data/README.md +59 -0
  5. data/bin/flame +26 -0
  6. data/lib/flame/app_builder.rb +26 -0
  7. data/lib/flame/generators/app.rb +51 -0
  8. data/lib/flame/generators/base.rb +28 -0
  9. data/lib/flame/generators/cors_generator.rb +23 -0
  10. data/lib/flame/generators/devise_generator.rb +88 -0
  11. data/lib/flame/generators/haml_generator.rb +9 -0
  12. data/lib/flame/generators/testing_generator.rb +12 -0
  13. data/lib/flame/generators/vite_generator.rb +76 -0
  14. data/lib/flame/version.rb +5 -0
  15. data/lib/flame.rb +8 -0
  16. data/templates/Gemfile.erb +39 -0
  17. data/templates/app/views/layouts/application.html.haml +14 -0
  18. data/templates/db/seeds.rb +5 -0
  19. data/templates/eslintrc.js +3 -0
  20. data/templates/frontend/assets/react.svg +1 -0
  21. data/templates/frontend/assets/stylesheets/application.sass +8 -0
  22. data/templates/frontend/common/components/sidebar/index.jsx +43 -0
  23. data/templates/frontend/common/components/sidebar/sidebar-link.jsx +32 -0
  24. data/templates/frontend/common/components/sidebar/sidebar.styled.jsx +0 -0
  25. data/templates/frontend/entrypoints/application.js +28 -0
  26. data/templates/frontend/entrypoints/application.jsx +27 -0
  27. data/templates/frontend/layouts/content.jsx +11 -0
  28. data/templates/frontend/layouts/main-layout.jsx +16 -0
  29. data/templates/frontend/layouts/page-layout.jsx +28 -0
  30. data/templates/frontend/pages/home/index.jsx +25 -0
  31. data/templates/frontend/pages/sign-in/components/login-form/index.jsx +66 -0
  32. data/templates/frontend/pages/sign-in/components/login-form/login-form.styled.js +14 -0
  33. data/templates/frontend/pages/sign-in/components/login-form/utils/index.js +11 -0
  34. data/templates/frontend/pages/sign-in/components/login-form/validation-schema.js +6 -0
  35. data/templates/frontend/pages/sign-in/index.jsx +22 -0
  36. data/templates/frontend/router/index.jsx +43 -0
  37. data/templates/frontend/services/base.js +6 -0
  38. data/templates/frontend/services/users-service/index.js +21 -0
  39. data/templates/frontend/store/index.js +8 -0
  40. data/templates/frontend/store/slices/userSlice.js +42 -0
  41. data/templates/frontend/theme/index.js +13 -0
  42. data/templates/jsconfig.json +8 -0
  43. data/templates/vite.config.js +16 -0
  44. metadata +129 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 27b827963415a5a78744f0ee9188375a86f7fb12ea2af70b7de692e69d005879
4
+ data.tar.gz: 2d6fc4655d6fbd865e9f65024eb10c92a12a90b924c5d9617b90424f9595c6dd
5
+ SHA512:
6
+ metadata.gz: 88d0abbb6d5773d5c4c36143e290c2a83ed9d2af9c97d524306fd50a7f9362eb805f7144beb996915b4d2bbdc7c2fbefa91113c4814474480f076aaa293fe8be
7
+ data.tar.gz: 1ec41342b515ae8727d60be8146d4534c25532d86e901641957ad3baaf31faeb7a370b4319b34b7015d5388e9ebf7b28324b5677516125b4c63e8e65fcdea8c2
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ ## v0.1.1 (2023-07-10)
2
+
3
+ ### Fix
4
+
5
+ - conflicts with templates
6
+
7
+ ## v0.1.0 (2023-07-10)
8
+
9
+ ### Feat
10
+
11
+ - add frontend template with basic configuration
12
+ - some changes :p
13
+ - remove devise-jwt integration
14
+ - rename all modules
15
+ - add devise and devise-jwt at the app generator
16
+ - add pagination gems on the gemfile template
17
+ - add active_model_serializer to the gemfile.erb
18
+ - add annotate and rspec on the app generator
19
+ - add configuration for default generators in the app builder
20
+ - add gemfile template in app builder
21
+ - add commitizen configuration
22
+
23
+ ### Refactor
24
+
25
+ - refactor function on app builder on generators
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Flame
2
+
3
+ Flame is rails app generator inspired in [Suspenders](https://github.com/thoughtbot/suspenders)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+
10
+ $ gem install flame
11
+ $ flame <app_name>
12
+
13
+ ## Gemfile
14
+
15
+ * [AMS](https://github.com/rails-api/active_model_serializers) for serialize json (api)
16
+ * [Devise](https://github.com/heartcombo/devise) for user authentication
17
+ * [Haml](https://github.com/haml/haml) we prefer use haml extension instead classic .erb
18
+ * [Pager API](https://github.com/IcaliaLabs/pager-api) & [Pagy](https://github.com/ddnexus/pagy) for api pagination
19
+ * [Vite](https://github.com/ElMassimo/vite_ruby) instead webpack
20
+ * [Annotate](https://github.com/ctran/annotate_models) generate comments on the model with current schema
21
+
22
+
23
+ for test environment we use:
24
+ * [FactoryBot](https://github.com/thoughtbot/factory_bot)
25
+ * [FFaker](https://github.com/ffaker/ffaker)
26
+ * [RSpec](https://github.com/rspec/rspec-rails)
27
+ * [Shoulda Matchers](https://github.com/thoughtbot/shoulda-matchers)
28
+ * [Database Cleanner](https://github.com/DatabaseCleaner/database_cleaner)
29
+
30
+ ## Package.json
31
+ * [react](https://yarnpkg.com/package/react) & [react-dom](https://yarnpkg.com/package/react-dom)
32
+ * [formik](https://yarnpkg.com/package/formik) & [yup](https://yarnpkg.com/package/yup) for handle and validate forms.
33
+ * [axios](https://yarnpkg.com/package/axios) for http request
34
+ * [@reduxjs/toolkit](https://yarnpkg.com/package/@reduxjs/toolkit)
35
+ * [react-router-dom](https://yarnpkg.com/package/react-router-dom) for routing
36
+ * [@mui/material](https://yarnpkg.com/package/@mui/material) library for components
37
+ * [@mui/icons-material](https://yarnpkg.com/package/@mui/icons-material) library for icons
38
+ * [react-toastify](https://yarnpkg.com/package/react-toastify) simple and beauty notifcations.
39
+ * [sass](https://yarnpkg.com/package/sass)
40
+
41
+ for dev dependencies
42
+ * standard
43
+ * @vitejs/plugin-react-swc
44
+ * eslint-plugin-react
45
+
46
+
47
+ ## Style Guide
48
+
49
+ Projects created with Flame come with [Standard](https://github.com/standardrb/standard)
50
+
51
+ ## Contributing
52
+
53
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/Flame.
54
+
55
+
56
+ ## License
57
+
58
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
59
+
data/bin/flame ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+
4
+ source_path = (Pathname.new(__FILE__).dirname + '../lib').expand_path
5
+ $LOAD_PATH << source_path
6
+
7
+ require 'flame'
8
+
9
+ if ARGV.empty?
10
+ puts "Please provide a path for the new application"
11
+ puts
12
+ puts "See --help for more info"
13
+ exit 0
14
+ elsif ["-v", "--version"].include? ARGV[0]
15
+ puts Suspenders::VERSION
16
+ exit 0
17
+ end
18
+
19
+ ARGV << "--skip-javascript"
20
+ ARGV << "--skip-jbuilder"
21
+ ARGV << "--database=postgresql"
22
+ ARGV << "-T"
23
+
24
+ templates_path = File.expand_path(File.join("..", "templates"), File.dirname(__FILE__))
25
+ Flame::Generators::App.source_paths << Rails::Generators::AppGenerator.source_root << templates_path
26
+ Flame::Generators::App.start
@@ -0,0 +1,26 @@
1
+ module Flame
2
+ class AppBuilder < Rails::AppBuilder
3
+ def gemfile
4
+ template "Gemfile.erb", "Gemfile"
5
+ end
6
+
7
+ def configure_generators
8
+ config = <<-RUBY
9
+ config.generators do |generate|
10
+ generate.helper false
11
+ generate.javascripts false
12
+ generate.controller_specs false
13
+ generate.request_specs true
14
+ generate.routing_specs false
15
+ generate.stylesheets false
16
+ generate.test_framework :rspec
17
+ generate.view_specs false
18
+ generate.template_engine :haml
19
+ end
20
+
21
+ RUBY
22
+
23
+ inject_into_class "config/application.rb", "Application", config
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ require "rails/generators"
2
+ require "rails/generators/rails/app/app_generator"
3
+
4
+ module Flame
5
+ module Generators
6
+ class App < Rails::Generators::AppGenerator
7
+ hide!
8
+ def finish_template
9
+ invoke :custom_template
10
+ invoke :generators
11
+ super
12
+ end
13
+
14
+ def custom_template
15
+ build :configure_generators
16
+ end
17
+
18
+ def generators
19
+ run("spring stop > /dev/null 2>&1 || true")
20
+ run("bundle install")
21
+ invoke "flame:haml"
22
+ rails_command "db:create"
23
+
24
+ invoke "flame:testing"
25
+ invoke "flame:cors"
26
+ invoke "flame:devise"
27
+ invoke "flame:vite"
28
+
29
+ generate("annotate:install")
30
+ run("bundle exec standardrb --fix-unsafely")
31
+ run("bundle exec haml-lint app/views -A -a")
32
+ rails_command("db:migrate") if yes?("\nDo you want to run migrations? [y/n]")
33
+ rails_command("db:seed") if yes?("\nDo you want to run seed? [y/n]")
34
+ welcome_message
35
+
36
+ exit 0
37
+ end
38
+
39
+ protected
40
+
41
+ def welcome_message
42
+ say "Flame app successfully created!", :green
43
+ say "Run `foreman start -f Procfile.dev` to start the server", :green
44
+ end
45
+
46
+ def get_builder_class
47
+ Flame::AppBuilder
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,28 @@
1
+ require "rails/generators"
2
+ require "rails/generators/base"
3
+
4
+ module Flame
5
+ module Generators
6
+ class Base < Rails::Generators::Base
7
+ source_root File.expand_path("../../../templates", __dir__)
8
+
9
+ private
10
+
11
+ # @param [Array] packages
12
+ # @return [void]
13
+ # @example
14
+ # yarn_install(["vite", "react", "react-dom"])
15
+ def yarn_install(packages)
16
+ run("yarn add #{packages.join(" ")} --silent")
17
+ end
18
+
19
+ # @param [Array] packages
20
+ # @return [void]
21
+ # @example
22
+ # yarn_install_dev(["vite", "react", "react-dom"])
23
+ def yarn_install_dev(packages)
24
+ run("yarn add -D #{packages.join(" ")} --silent")
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "base"
2
+
3
+ module Flame
4
+ class CorsGenerator < Generators::Base
5
+ CONFIG = <<~RUBY
6
+ Rails.application.config.middleware.insert_before 0, Rack::Cors do
7
+ allow do
8
+ origins Rails.env.development? ? ["localhost:5100", "127.0.0.1:5100"] : ENV["FRONTEND_URL"]
9
+ resource "*",
10
+ headers: %w[Authorization],
11
+ methods: :any,
12
+ expose: %w[Authorization],
13
+ max_age: 600,
14
+ credentials: true
15
+ end
16
+ end
17
+ RUBY
18
+
19
+ def create_cors_file
20
+ create_file("config/initializers/cors.rb", CONFIG)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,88 @@
1
+ require_relative "base"
2
+
3
+ module Flame
4
+ class DeviseGenerator < Generators::Base
5
+ APP_CONTROLLER = <<~RUBY
6
+ before_action :authenticate_user!, only: [:current]
7
+ protect_from_forgery with: :exception, unless: :json_request?
8
+
9
+ def current
10
+ render json: current_user
11
+ end
12
+
13
+
14
+ private
15
+
16
+ def json_request?
17
+ request.format.json?
18
+ end
19
+ RUBY
20
+
21
+ SESSION_CONTROLLER = <<~RUBY
22
+ class SessionsController < Devise::SessionsController
23
+ respond_to :json
24
+
25
+ private
26
+
27
+ def respond_with(resource, _opts = {})
28
+ render json: resource
29
+ end
30
+
31
+ def respond_to_on_destroy
32
+ head :no_content
33
+ end
34
+ end
35
+ RUBY
36
+
37
+ def install
38
+ generate("devise:install")
39
+ generate("devise", "User")
40
+ end
41
+
42
+ def add_timeoutable
43
+ gsub_file(
44
+ "app/models/user.rb",
45
+ /^\s*devise\s*:[a-z_]+(?:,\s*:[a-z_]+)*\s*$/,
46
+ " devise :database_authenticatable, :registerable, :timeoutable, timeout_in: 1.day"
47
+ )
48
+ end
49
+
50
+ def generate_session_controller
51
+ create_file(
52
+ "app/controllers/sessions_controller.rb",
53
+ SESSION_CONTROLLER
54
+ )
55
+ end
56
+
57
+ def modify_session_route
58
+ gsub_file(
59
+ "config/routes.rb",
60
+ /devise_for\s*:\s*users/,
61
+ <<~RUBY
62
+ devise_for :users, defaults: {format: :json}, controllers: {sessions: "sessions"}
63
+ get "/users/current", to: "application#current", defaults: {format: :json}
64
+
65
+ get "*path", to: "pages#index", constraints: ->(request) do
66
+ !request.xhr? && request.format.html?
67
+ end
68
+ RUBY
69
+ )
70
+ end
71
+
72
+ def modify_app_controller
73
+ inject_into_class(
74
+ "app/controllers/application_controller.rb",
75
+ "ApplicationController",
76
+ APP_CONTROLLER
77
+ )
78
+ end
79
+
80
+ def insert_secret_key
81
+ inject_into_file(
82
+ "config/initializers/devise.rb",
83
+ " config.secret_key = Rails.application.credentials.secret_key_base\n",
84
+ after: "Devise.setup do |config|\n"
85
+ )
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "base"
2
+
3
+ module Flame
4
+ class HamlGenerator < Generators::Base
5
+ def convert_erb_to_haml
6
+ run("HAML_RAILS_DELETE_ERB=true rails haml:erb2haml")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "base"
2
+
3
+ module Flame
4
+ class TestingGenerator < Generators::Base
5
+ def install_rspec
6
+ generate("rspec:install")
7
+ end
8
+
9
+ def install_gems
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,76 @@
1
+ require_relative "base"
2
+
3
+ module Flame
4
+ class ViteGenerator < Generators::Base
5
+ source_root File.expand_path("../../../templates", __dir__)
6
+
7
+ def install
8
+ run("rm -rf ./package.lock")
9
+ run("yarn init -y")
10
+
11
+ run("bundle exec vite install")
12
+
13
+ run("rm -rf ./package-lock.json")
14
+ run("rm -rf ./yarn.lock")
15
+ run("rm -rf ./node_modules")
16
+ end
17
+
18
+ def install_packages
19
+ packages = %w[
20
+ react
21
+ react-dom
22
+ formik
23
+ yup
24
+ axios
25
+ @reduxjs/toolkit
26
+ react-redux
27
+ react-router-dom
28
+ @mui/material
29
+ @mui/icons-material
30
+ @emotion/react
31
+ @emotion/styled
32
+ react-toastify
33
+ styled-components
34
+ sass
35
+ ]
36
+ yarn_install(packages)
37
+ end
38
+
39
+ def install_dev_packages
40
+ packages = %w[
41
+ standard
42
+ @vitejs/plugin-react-swc
43
+ eslint-plugin-react
44
+ ]
45
+
46
+ yarn_install_dev(packages)
47
+ end
48
+
49
+ def copy_templates
50
+ remove_file "vite.config.js"
51
+ remove_file "vite.config.ts"
52
+ template("vite.config.js", "vite.config.js")
53
+ template("jsconfig.json", "jsconfig.json")
54
+ run("rm -rf app/frontend")
55
+ run("rm -rf db/seeds.rb")
56
+ run("rm app/views/layouts/application.html.haml")
57
+ directory("frontend", "app/frontend")
58
+ template("app/views/layouts/application.html.haml", "app/views/layouts/application.html.haml")
59
+ template("db/seeds.rb", "db/seeds.rb")
60
+ end
61
+
62
+ def install_standardjs
63
+ template("eslintrc.js", ".eslintrc.js")
64
+ end
65
+
66
+ def generate_home
67
+ generate("controller", "pages", "index", "--skip-routes", "--no-test-framework")
68
+ run("echo '' > app/views/pages/index.html.haml")
69
+ append_to_file("app/views/pages/index.html.haml", "#root\n")
70
+ end
71
+
72
+ def generate_root_route
73
+ route("root to: 'pages#index'")
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ module Flame
2
+ VERSION = "0.1.1"
3
+ RUBY_VERSION = "3.2.2"
4
+ RAILS_VERSION = "6.1.7"
5
+ end
data/lib/flame.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "flame/version"
2
+ require "flame/generators/cors_generator"
3
+ require "flame/generators/testing_generator"
4
+ require "flame/generators/devise_generator"
5
+ require "flame/generators/vite_generator"
6
+ require "flame/generators/haml_generator"
7
+ require "flame/generators/app"
8
+ require "flame/app_builder"
@@ -0,0 +1,39 @@
1
+ source "https://rubygems.org"
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ ruby "<%= Flame::RUBY_VERSION %>"
5
+
6
+ gem "active_model_serializers", "~> 0.9.9"
7
+ gem "bootsnap", require: false
8
+ gem "devise"
9
+ gem "haml-rails"
10
+ gem "pager_api"
11
+ gem "pagy"
12
+ gem "pg"
13
+ gem "puma"
14
+ gem "rails", "<%= Flame::RAILS_VERSION %>"
15
+ gem "rack-cors"
16
+ gem "vite_rails"
17
+
18
+ gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
19
+
20
+ group :development do
21
+ gem "annotate"
22
+ gem "listen"
23
+ gem "spring"
24
+ gem "standard"
25
+ gem "html2haml" # Remove this gem!
26
+ gem "haml_lint", require: false
27
+ end
28
+
29
+ group :development, :test do
30
+ gem "byebug", platforms: %i[mri mingw x64_mingw]
31
+ gem "factory_bot_rails", "~> 6.2"
32
+ gem "ffaker", "~> 2.21"
33
+ gem "rspec-rails", "~> 6.0.0"
34
+ gem "shoulda-matchers", "~> 5.0"
35
+ end
36
+
37
+ group :test do
38
+ gem "database_cleaner-active_record", "2.1"
39
+ end
@@ -0,0 +1,14 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
5
+ %title TestLast
6
+ %meta{:content => "width=device-width,initial-scale=1", :name => "viewport"}/
7
+ = csrf_meta_tags
8
+ = csp_meta_tag
9
+ = stylesheet_link_tag 'application', media: 'all'
10
+ = vite_client_tag
11
+ = vite_react_refresh_tag
12
+ = vite_javascript_tag 'application.jsx'
13
+ %body
14
+ = yield
@@ -0,0 +1,5 @@
1
+ User.create(
2
+ email: "admin@example.com",
3
+ password: "admin123",
4
+ password_confirmation: "admin123"
5
+ )
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ extends: ['./node_modules/standard/eslintrc.json']
3
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,8 @@
1
+
2
+ html, body, #root
3
+ height: 100%
4
+ width: 100%
5
+ margin: 0
6
+ padding: 0
7
+ background-color: #F7F8FA
8
+
@@ -0,0 +1,43 @@
1
+ import { List, ListSubheader } from '@mui/material/'
2
+
3
+ import { Home as HomeIcon } from '@mui/icons-material/'
4
+ import { styled } from '@mui/system'
5
+ import { SidebarLink } from './sidebar-link'
6
+
7
+ const Sidebar = () => {
8
+ const SidebarContainer = styled('nav')({
9
+ display: 'flex',
10
+ padding: 10,
11
+ pr: 0,
12
+ flex: 1
13
+ })
14
+
15
+ const SidebarSubHeader = styled(ListSubheader)(({ theme }) => ({
16
+ fontSize: 18,
17
+ fontWeight: 'bold',
18
+ pl: 0,
19
+ backgroundColor: theme.palette.background.default
20
+ }))
21
+
22
+ return (
23
+ <SidebarContainer>
24
+ <List
25
+ sx={{
26
+ width: '100%',
27
+ maxWidth: '100%',
28
+ fontSize: '1.5rem'
29
+ }}
30
+ component='nav'
31
+ subheader={
32
+ <SidebarSubHeader>
33
+ General
34
+ </SidebarSubHeader>
35
+ }
36
+ >
37
+ <SidebarLink to='/' label='Home' icon={<HomeIcon />} />
38
+
39
+ </List>
40
+ </SidebarContainer>
41
+ )
42
+ }
43
+ export default Sidebar
@@ -0,0 +1,32 @@
1
+ import { ListItemText, ListItemButton, ListItemIcon } from '@mui/material'
2
+ import PropTypes from 'prop-types'
3
+ import { Link } from 'react-router-dom'
4
+
5
+ export const SidebarLink = ({ label, icon, route }) => {
6
+ return (
7
+ <ListItemButton
8
+ component={Link}
9
+ to={route}
10
+ sx={{ py: 0, px: 1 }}
11
+ >
12
+ <ListItemIcon
13
+ sx={{
14
+ minWidth: 30
15
+ }}
16
+ >
17
+ {icon}
18
+ </ListItemIcon>
19
+ <ListItemText
20
+ primaryTypographyProps={{ fontSize: 14, fontWeight: 'medium' }}
21
+ primary={label}
22
+ />
23
+ </ListItemButton>
24
+
25
+ )
26
+ }
27
+
28
+ SidebarLink.propTypes = {
29
+ label: PropTypes.string,
30
+ icon: PropTypes.node,
31
+ to: PropTypes.string
32
+ }
@@ -0,0 +1,28 @@
1
+ // To see this message, add the following to the `<head>` section in your
2
+ // views/layouts/application.html.erb
3
+ //
4
+ // <%= vite_client_tag %>
5
+ // <%= vite_javascript_tag 'application' %>
6
+ console.log('Vite ⚡️ Rails')
7
+
8
+ // If using a TypeScript entrypoint file:
9
+ // <%= vite_typescript_tag 'application' %>
10
+ //
11
+ // If you want to use .jsx or .tsx, add the extension:
12
+ // <%= vite_javascript_tag 'application.jsx' %>
13
+
14
+ console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify.app/guide/rails')
15
+
16
+ // Example: Load Rails libraries in Vite.
17
+ //
18
+ // import * as Turbo from '@hotwired/turbo'
19
+ // Turbo.start()
20
+ //
21
+ // import ActiveStorage from '@rails/activestorage'
22
+ // ActiveStorage.start()
23
+ //
24
+ // // Import all channels.
25
+ // const channels = import.meta.globEager('./**/*_channel.js')
26
+
27
+ // Example: Import a stylesheet in app/frontend/index.css
28
+ // import '~/index.css'
@@ -0,0 +1,27 @@
1
+ import '@/assets/stylesheets/application.sass'
2
+ import 'react-toastify/dist/ReactToastify.css'
3
+
4
+ import React from 'react'
5
+ import { ToastContainer } from 'react-toastify'
6
+ import { CssBaseline, ThemeProvider } from '@mui/material'
7
+ import { createRoot } from 'react-dom/client'
8
+ import { RouterProvider } from 'react-router-dom'
9
+ import router from '@/router'
10
+ import { Provider } from 'react-redux'
11
+ import { store } from '@/store'
12
+ import { theme } from '@/theme'
13
+
14
+ const container = document.getElementById('root')
15
+ const root = createRoot(container)
16
+
17
+ root.render(
18
+ <React.StrictMode>
19
+ <CssBaseline />
20
+ <ToastContainer />
21
+ <Provider store={store}>
22
+ <ThemeProvider theme={theme}>
23
+ <RouterProvider router={router} />
24
+ </ThemeProvider>
25
+ </Provider>
26
+ </React.StrictMode>
27
+ )
@@ -0,0 +1,11 @@
1
+ import { Box, Card } from '@mui/material'
2
+
3
+ const Content = ({ children }) => {
4
+ return (
5
+ <Box sx={{ m: 1, boxShadow: 3 }} component={Card} flex={7} px={3} py={2}>
6
+ {children}
7
+ </Box>
8
+ )
9
+ }
10
+
11
+ export default Content
@@ -0,0 +1,16 @@
1
+ import { Stack } from '@mui/material'
2
+ import Content from './content'
3
+ import Sidebar from '@/common/components/sidebar'
4
+
5
+ const MainLayout = ({ children }) => {
6
+ return (
7
+ <Stack sx={{ width: '100%', height: '100%' }} direction='row' justifyContent='flex-start' gap={1}>
8
+ <Sidebar />
9
+ <Content>
10
+ {children}
11
+ </Content>
12
+ </Stack>
13
+ )
14
+ }
15
+
16
+ export default MainLayout
@@ -0,0 +1,28 @@
1
+ import { Box, Divider, Stack, Typography } from '@mui/material'
2
+ import PropTypes from 'prop-types'
3
+
4
+ const PageLayout = ({ title, children }) => {
5
+ return (
6
+ <Stack>
7
+ <Box sx={{mb:2}}>
8
+ <Typography gutterBottom variant='h5' sx={{ fontWeight: 'bold' }}>{title}</Typography>
9
+ <Divider />
10
+ </Box>
11
+
12
+ <div>
13
+ {children}
14
+ </div>
15
+ </Stack>
16
+ )
17
+ }
18
+
19
+ PageLayout.propTypes = {
20
+ title: PropTypes.string,
21
+ children: PropTypes.node
22
+ }
23
+
24
+ PageLayout.defaultProps = {
25
+ title: 'Custom'
26
+ }
27
+
28
+ export default PageLayout
@@ -0,0 +1,25 @@
1
+ import { Button, Typography } from '@mui/material'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import { usersService } from '@/services/users-service'
4
+ import PageLayout from '@/layouts/page-layout'
5
+
6
+ const Home = () => {
7
+ const navigate = useNavigate()
8
+ const handleLogout = async () => {
9
+ try {
10
+ await usersService.signOut()
11
+ navigate('/sign_in')
12
+ } catch (e) {
13
+ /* handle error */
14
+ }
15
+ }
16
+
17
+ return (
18
+ <PageLayout>
19
+ <Typography variant='h4'>Home</Typography>
20
+ <Button variant='contained' onClick={() => handleLogout()}>Log out</Button>
21
+ </PageLayout>
22
+ )
23
+ }
24
+
25
+ export default Home
@@ -0,0 +1,66 @@
1
+ import { Button, TextField, Typography } from '@mui/material'
2
+ import { CardLogin, Form } from './login-form.styled'
3
+ import { useFormik } from 'formik'
4
+ import { usersService } from '@/services/users-service'
5
+ import validationSchema from './validation-schema'
6
+ import { toast } from 'react-toastify'
7
+ import { useNavigate } from 'react-router-dom'
8
+
9
+ const LoginForm = () => {
10
+ const navigate = useNavigate()
11
+ const handleLogin = async (values) => {
12
+ try {
13
+ await usersService.signIn({ user: values })
14
+ navigate('/')
15
+ } catch ({ response }) {
16
+ toast.error(response.data.error)
17
+ }
18
+ }
19
+
20
+ const { touched, errors, handleBlur, handleSubmit, handleChange, values } = useFormik({
21
+ initialValues: { email: 'admin@example.com', password: 'admin123' },
22
+ validationSchema,
23
+ onSubmit: handleLogin
24
+ })
25
+
26
+ return (
27
+ <CardLogin>
28
+ <Form onSubmit={handleSubmit}>
29
+ <Typography variant='h5' align='center'>
30
+ Sign in to your account
31
+ </Typography>
32
+ <TextField
33
+ fullWidth
34
+ name='email'
35
+ placeholder='john.doe@example.com'
36
+ onChange={handleChange}
37
+ value={values.email}
38
+ error={touched.email && Boolean(errors.email)}
39
+ onBlur={handleBlur}
40
+ />
41
+
42
+ <TextField
43
+ fullWidth
44
+ name='password'
45
+ type='password'
46
+ placeholder='********'
47
+ onChange={handleChange}
48
+ value={values.password}
49
+ error={touched.email && Boolean(errors.email)}
50
+ onBlur={handleBlur}
51
+ />
52
+ <Button
53
+ type='submit'
54
+ variant='contained'
55
+ >
56
+ Sign in
57
+ </Button>
58
+ <Typography variant='subtitle2' align='center'>
59
+ Forgot your password?
60
+ </Typography>
61
+ </Form>
62
+ </CardLogin>
63
+ )
64
+ }
65
+
66
+ export default LoginForm
@@ -0,0 +1,14 @@
1
+ import { styled } from '@mui/system'
2
+ import { Card } from '@mui/material'
3
+
4
+ export const CardLogin = styled(Card)({
5
+ width: '100%',
6
+ maxWidth: '31.25rem',
7
+ padding: '2rem'
8
+ })
9
+
10
+ export const Form = styled('form')({
11
+ display: 'flex',
12
+ flexDirection: 'column',
13
+ gap: '1rem'
14
+ })
@@ -0,0 +1,11 @@
1
+ import usersService from '@/services/users-service'
2
+ /**
3
+ * Handle login request for login-form
4
+ * @param {object} params
5
+ * @param {string} params.email - user email
6
+ * @param {string} params.password - user password
7
+ * @returns {void}
8
+ */
9
+ export const handleLogin = async (params) => {
10
+ const response = await usersService.login(params)
11
+ }
@@ -0,0 +1,6 @@
1
+ import * as yup from 'yup'
2
+
3
+ export default yup.object({
4
+ email: yup.string().email().required(),
5
+ password: yup.string().required()
6
+ })
@@ -0,0 +1,22 @@
1
+ import { styled } from '@mui/system'
2
+ import LoginForm from './components/login-form'
3
+
4
+ const SignIn = () => {
5
+ const LayoutLogin = styled('div')({
6
+ width: '100%',
7
+ height: '100%',
8
+ maxWidth: '31.25rem',
9
+ padding: '1rem',
10
+ margin: '0 auto',
11
+ display: 'flex',
12
+ alignItems: 'center'
13
+ })
14
+
15
+ return (
16
+ <LayoutLogin>
17
+ <LoginForm />
18
+ </LayoutLogin>
19
+ )
20
+ }
21
+
22
+ export default SignIn
@@ -0,0 +1,43 @@
1
+ import Home from '@/pages/home'
2
+ import SignIn from '@/pages/sign-in'
3
+ import { Navigate, Outlet, createBrowserRouter } from 'react-router-dom'
4
+ import { useSelector } from 'react-redux'
5
+ import { validateCurrentUser } from '@/store/slices/userSlice'
6
+ import { store } from '@/store'
7
+ import MainLayout from '@/layouts/main-layout'
8
+
9
+ const PrivateRoute = () => {
10
+ const user = useSelector(state => state.user)
11
+ console.log(user)
12
+ return (
13
+ user.isLogged ? <MainLayout><Outlet /></MainLayout> : <Navigate to='/sign_in' />
14
+ )
15
+ }
16
+
17
+ const router = createBrowserRouter([
18
+ {
19
+ element: <PrivateRoute />,
20
+ loader: () => {
21
+ return store.dispatch(validateCurrentUser())
22
+ },
23
+ children: [
24
+ {
25
+ path: '/',
26
+ element: <Home />,
27
+ exact: true
28
+ },
29
+ {
30
+ path: '/home',
31
+ element: <Home />,
32
+ exact: true
33
+ }
34
+ ]
35
+ },
36
+ {
37
+ path: '/sign_in',
38
+ element: <SignIn />
39
+
40
+ }
41
+ ])
42
+
43
+ export default router
@@ -0,0 +1,6 @@
1
+ import axios from 'axios'
2
+
3
+ export default axios.create({
4
+ timeout: 1000,
5
+ withCredentials: true
6
+ })
@@ -0,0 +1,21 @@
1
+ import api from '@/services/base'
2
+
3
+ export const usersService = {
4
+ /**
5
+ * Sign in request
6
+ * @param {object} params
7
+ * @param {string} params.user.email - user email
8
+ * @param {string} params.user.password - user password
9
+ * @returns {Promise<object>} - response
10
+ * @example
11
+ * const response = await usersService.signIn({
12
+ * user: {
13
+ * email: 'admin@example.com',
14
+ * password: 'password'
15
+ * }
16
+ * })
17
+ */
18
+ signIn: (params) => api.post('/users/sign_in', params),
19
+ currentUser: () => api.get('/users/current'),
20
+ signOut: () => api.delete('/users/sign_out')
21
+ }
@@ -0,0 +1,8 @@
1
+ import { configureStore } from '@reduxjs/toolkit'
2
+ import userSlice from '@/store/slices/userSlice'
3
+
4
+ export const store = configureStore({
5
+ reducer: {
6
+ user: userSlice
7
+ }
8
+ })
@@ -0,0 +1,42 @@
1
+ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
2
+ import { usersService } from '@/services/users-service'
3
+ import { toast } from 'react-toastify'
4
+
5
+ const validateCurrentUser = createAsyncThunk(
6
+ 'user/current-user',
7
+ async (_, { rejectWithValue }) => {
8
+ try {
9
+ const response = await usersService.currentUser()
10
+ return response
11
+ } catch ({ response }) {
12
+ throw rejectWithValue(response.data)
13
+ }
14
+ }
15
+ )
16
+
17
+ const initialState = {
18
+ isLogged: false
19
+ }
20
+
21
+ export const userSlice = createSlice({
22
+ name: 'user',
23
+ initialState,
24
+ reducers: {},
25
+ extraReducers: (builder) => {
26
+ builder.addCase(validateCurrentUser.fulfilled, (state, action) => {
27
+ state.isLogged = true
28
+ })
29
+
30
+ builder.addCase(validateCurrentUser.rejected, (state, action) => {
31
+ state.isLogged = false
32
+ toast.error(action.payload.error)
33
+ })
34
+ }
35
+ })
36
+
37
+ // export const { increment, decrement, incrementByAmount } = userSlice.actions
38
+
39
+ export {
40
+ validateCurrentUser
41
+ }
42
+ export default userSlice.reducer
@@ -0,0 +1,13 @@
1
+ import { createTheme } from '@mui/material'
2
+
3
+ export const theme = createTheme({
4
+ palette: {
5
+ primary: {
6
+ main: '#09090D'
7
+ },
8
+ background: {
9
+ paper: '#fff',
10
+ default: '#F7F8FA'
11
+ }
12
+ }
13
+ })
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "es6",
5
+ "baseUrl": "./app/frontend"
6
+ },
7
+ "include": ["app/frontend/**/*"]
8
+ }
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from 'vite'
2
+ import path from 'path'
3
+ import RubyPlugin from 'vite-plugin-ruby'
4
+ import react from '@vitejs/plugin-react-swc'
5
+
6
+ export default defineConfig({
7
+ plugins: [
8
+ RubyPlugin(),
9
+ react()
10
+ ],
11
+ resolve: {
12
+ alias: [
13
+ { find: '@', replacement: path.resolve(__dirname, 'app/frontend') }
14
+ ]
15
+ }
16
+ })
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flamerb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - n0tbot
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 6.1.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 6.1.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: standard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.29'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.29'
55
+ description: Flame is a powerful Ruby gem designed to simplify and streamline application
56
+ development by providing a comprehensive set of tools and functionalities.
57
+ email:
58
+ - wilber.garcia@outlook.es
59
+ executables:
60
+ - flame
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".ruby-version"
65
+ - CHANGELOG.md
66
+ - README.md
67
+ - bin/flame
68
+ - lib/flame.rb
69
+ - lib/flame/app_builder.rb
70
+ - lib/flame/generators/app.rb
71
+ - lib/flame/generators/base.rb
72
+ - lib/flame/generators/cors_generator.rb
73
+ - lib/flame/generators/devise_generator.rb
74
+ - lib/flame/generators/haml_generator.rb
75
+ - lib/flame/generators/testing_generator.rb
76
+ - lib/flame/generators/vite_generator.rb
77
+ - lib/flame/version.rb
78
+ - templates/Gemfile.erb
79
+ - templates/app/views/layouts/application.html.haml
80
+ - templates/db/seeds.rb
81
+ - templates/eslintrc.js
82
+ - templates/frontend/assets/react.svg
83
+ - templates/frontend/assets/stylesheets/application.sass
84
+ - templates/frontend/common/components/sidebar/index.jsx
85
+ - templates/frontend/common/components/sidebar/sidebar-link.jsx
86
+ - templates/frontend/common/components/sidebar/sidebar.styled.jsx
87
+ - templates/frontend/entrypoints/application.js
88
+ - templates/frontend/entrypoints/application.jsx
89
+ - templates/frontend/layouts/content.jsx
90
+ - templates/frontend/layouts/main-layout.jsx
91
+ - templates/frontend/layouts/page-layout.jsx
92
+ - templates/frontend/pages/home/index.jsx
93
+ - templates/frontend/pages/sign-in/components/login-form/index.jsx
94
+ - templates/frontend/pages/sign-in/components/login-form/login-form.styled.js
95
+ - templates/frontend/pages/sign-in/components/login-form/utils/index.js
96
+ - templates/frontend/pages/sign-in/components/login-form/validation-schema.js
97
+ - templates/frontend/pages/sign-in/index.jsx
98
+ - templates/frontend/router/index.jsx
99
+ - templates/frontend/services/base.js
100
+ - templates/frontend/services/users-service/index.js
101
+ - templates/frontend/store/index.js
102
+ - templates/frontend/store/slices/userSlice.js
103
+ - templates/frontend/theme/index.js
104
+ - templates/jsconfig.json
105
+ - templates/vite.config.js
106
+ homepage: https://github.com/notb0t/flame
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 3.2.2
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.4.10
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Generate a Rails app with best practices
129
+ test_files: []