tarsier 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +175 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +984 -0
  5. data/exe/tarsier +7 -0
  6. data/lib/tarsier/application.rb +336 -0
  7. data/lib/tarsier/cli/commands/console.rb +87 -0
  8. data/lib/tarsier/cli/commands/generate.rb +85 -0
  9. data/lib/tarsier/cli/commands/help.rb +50 -0
  10. data/lib/tarsier/cli/commands/new.rb +59 -0
  11. data/lib/tarsier/cli/commands/routes.rb +139 -0
  12. data/lib/tarsier/cli/commands/server.rb +123 -0
  13. data/lib/tarsier/cli/commands/version.rb +14 -0
  14. data/lib/tarsier/cli/generators/app.rb +528 -0
  15. data/lib/tarsier/cli/generators/base.rb +93 -0
  16. data/lib/tarsier/cli/generators/controller.rb +91 -0
  17. data/lib/tarsier/cli/generators/middleware.rb +81 -0
  18. data/lib/tarsier/cli/generators/migration.rb +109 -0
  19. data/lib/tarsier/cli/generators/model.rb +109 -0
  20. data/lib/tarsier/cli/generators/resource.rb +27 -0
  21. data/lib/tarsier/cli/loader.rb +18 -0
  22. data/lib/tarsier/cli.rb +46 -0
  23. data/lib/tarsier/controller.rb +282 -0
  24. data/lib/tarsier/database.rb +588 -0
  25. data/lib/tarsier/errors.rb +77 -0
  26. data/lib/tarsier/middleware/base.rb +47 -0
  27. data/lib/tarsier/middleware/compression.rb +113 -0
  28. data/lib/tarsier/middleware/cors.rb +101 -0
  29. data/lib/tarsier/middleware/csrf.rb +88 -0
  30. data/lib/tarsier/middleware/logger.rb +74 -0
  31. data/lib/tarsier/middleware/rate_limit.rb +110 -0
  32. data/lib/tarsier/middleware/stack.rb +143 -0
  33. data/lib/tarsier/middleware/static.rb +124 -0
  34. data/lib/tarsier/model.rb +590 -0
  35. data/lib/tarsier/params.rb +269 -0
  36. data/lib/tarsier/query.rb +495 -0
  37. data/lib/tarsier/request.rb +274 -0
  38. data/lib/tarsier/response.rb +282 -0
  39. data/lib/tarsier/router/compiler.rb +173 -0
  40. data/lib/tarsier/router/node.rb +97 -0
  41. data/lib/tarsier/router/route.rb +119 -0
  42. data/lib/tarsier/router.rb +272 -0
  43. data/lib/tarsier/version.rb +5 -0
  44. data/lib/tarsier/websocket.rb +275 -0
  45. data/lib/tarsier.rb +167 -0
  46. data/sig/tarsier.rbs +485 -0
  47. metadata +230 -0
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tarsier
4
+ class CLI
5
+ module Commands
6
+ class Routes
7
+ HELP = <<~HELP
8
+ Usage: tarsier routes [options]
9
+
10
+ Display all routes defined in your application.
11
+
12
+ Options:
13
+ -g, --grep PATTERN Filter routes by pattern
14
+ --json Output routes as JSON
15
+
16
+ Examples:
17
+ tarsier routes
18
+ tarsier routes -g users
19
+ tarsier routes --json
20
+ HELP
21
+
22
+ def self.show_help
23
+ puts HELP
24
+ end
25
+
26
+ def self.run(args)
27
+ options = parse_options(args)
28
+
29
+ app = load_application
30
+ unless app
31
+ puts "Error: Could not load application"
32
+ exit 1
33
+ end
34
+
35
+ routes = app.routes_list
36
+ routes = filter_routes(routes, options[:grep]) if options[:grep]
37
+
38
+ if options[:json]
39
+ output_json(routes)
40
+ else
41
+ output_table(routes)
42
+ end
43
+ end
44
+
45
+ def self.parse_options(args)
46
+ options = { grep: nil, json: false }
47
+
48
+ i = 0
49
+ while i < args.length
50
+ case args[i]
51
+ when "-g", "--grep"
52
+ options[:grep] = args[i + 1]
53
+ i += 2
54
+ when "--json"
55
+ options[:json] = true
56
+ i += 1
57
+ else
58
+ i += 1
59
+ end
60
+ end
61
+
62
+ options
63
+ end
64
+
65
+ def self.load_application
66
+ if File.exist?("config/application.rb")
67
+ require "./config/application"
68
+ elsif File.exist?("app.rb")
69
+ require "./app"
70
+ end
71
+
72
+ Tarsier.app
73
+ rescue LoadError, StandardError => e
74
+ puts "Warning: #{e.message}"
75
+ nil
76
+ end
77
+
78
+ def self.filter_routes(routes, pattern)
79
+ regex = Regexp.new(pattern, Regexp::IGNORECASE)
80
+ routes.select { |r| r.path.match?(regex) || r.name&.to_s&.match?(regex) }
81
+ end
82
+
83
+ def self.output_table(routes)
84
+ puts
85
+ puts "Application Routes"
86
+ puts "=" * 70
87
+
88
+ if routes.empty?
89
+ puts "No routes defined"
90
+ return
91
+ end
92
+
93
+ # Calculate column widths
94
+ method_width = routes.map { |r| r.method.to_s.length }.max
95
+ path_width = routes.map { |r| r.path.length }.max
96
+ name_width = routes.map { |r| r.name.to_s.length }.max
97
+
98
+ # Header
99
+ puts format("%-#{method_width}s %-#{path_width}s %-#{name_width}s %s",
100
+ "Method", "Path", "Name", "Handler")
101
+ puts "-" * 70
102
+
103
+ # Routes
104
+ routes.each do |route|
105
+ handler = format_handler(route)
106
+ puts format("%-#{method_width}s %-#{path_width}s %-#{name_width}s %s",
107
+ route.method, route.path, route.name || "", handler)
108
+ end
109
+
110
+ puts
111
+ puts "Total: #{routes.size} routes"
112
+ end
113
+
114
+ def self.output_json(routes)
115
+ require "json"
116
+ data = routes.map do |route|
117
+ {
118
+ method: route.method,
119
+ path: route.path,
120
+ name: route.name,
121
+ handler: format_handler(route)
122
+ }
123
+ end
124
+ puts JSON.pretty_generate(data)
125
+ end
126
+
127
+ def self.format_handler(route)
128
+ if route.proc_handler?
129
+ "Proc"
130
+ elsif route.controller_class
131
+ "#{route.controller_class}##{route.action_name}"
132
+ else
133
+ route.handler.to_s
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tarsier
4
+ class CLI
5
+ module Commands
6
+ class Server
7
+ HELP = <<~HELP
8
+ Usage: tarsier server [options]
9
+
10
+ Start the Tarsier development server.
11
+
12
+ Options:
13
+ -p, --port PORT Port to listen on (default: 7827)
14
+ -b, --binding HOST Host to bind to (default: 0.0.0.0)
15
+ -e, --env ENV Environment (default: development)
16
+ --no-reload Disable auto-reload
17
+
18
+ Examples:
19
+ tarsier server
20
+ tarsier server -p 4000
21
+ tarsier server -e production
22
+ HELP
23
+
24
+ def self.show_help
25
+ puts HELP
26
+ end
27
+
28
+ def self.run(args)
29
+ options = parse_options(args)
30
+
31
+ unless File.exist?("config.ru") || File.exist?("app.rb")
32
+ puts "Error: No Tarsier application found in current directory"
33
+ puts "Make sure you're in a Tarsier project directory"
34
+ exit 1
35
+ end
36
+
37
+ start_server(options)
38
+ end
39
+
40
+ def self.parse_options(args)
41
+ options = { port: 7827, host: "0.0.0.0", env: "development", reload: true }
42
+
43
+ i = 0
44
+ while i < args.length
45
+ case args[i]
46
+ when "-p", "--port"
47
+ options[:port] = args[i + 1].to_i
48
+ i += 2
49
+ when "-b", "--binding"
50
+ options[:host] = args[i + 1]
51
+ i += 2
52
+ when "-e", "--env"
53
+ options[:env] = args[i + 1]
54
+ i += 2
55
+ when "--no-reload"
56
+ options[:reload] = false
57
+ i += 1
58
+ else
59
+ i += 1
60
+ end
61
+ end
62
+
63
+ options
64
+ end
65
+
66
+ def self.start_server(options)
67
+ ENV["TARSIER_ENV"] = options[:env]
68
+
69
+ puts "Tarsier Development Server"
70
+ puts "=" * 40
71
+ puts "Environment: #{options[:env]}"
72
+ puts "Listening on: http://#{options[:host]}:#{options[:port]}"
73
+ puts "Press Ctrl+C to stop"
74
+ puts "=" * 40
75
+ puts
76
+
77
+ # Try to use Puma if available, otherwise fall back to WEBrick
78
+ if puma_available?
79
+ start_puma(options)
80
+ elsif webrick_available?
81
+ start_webrick(options)
82
+ else
83
+ start_builtin(options)
84
+ end
85
+ end
86
+
87
+ def self.puma_available?
88
+ require "puma"
89
+ true
90
+ rescue LoadError
91
+ false
92
+ end
93
+
94
+ def self.webrick_available?
95
+ require "webrick"
96
+ true
97
+ rescue LoadError
98
+ false
99
+ end
100
+
101
+ def self.start_puma(options)
102
+ require "puma/cli"
103
+ # Use only -b for binding, not both -p and -b
104
+ puma_args = ["-b", "tcp://#{options[:host]}:#{options[:port]}"]
105
+ Puma::CLI.new(puma_args).run
106
+ end
107
+
108
+ def self.start_webrick(options)
109
+ require "rack"
110
+ require "rack/handler/webrick"
111
+ app = Rack::Builder.parse_file("config.ru")
112
+ Rack::Handler::WEBrick.run(app, Host: options[:host], Port: options[:port])
113
+ end
114
+
115
+ def self.start_builtin(options)
116
+ puts "Note: Install 'puma' gem for better performance"
117
+ require_relative "../server"
118
+ Tarsier::BuiltinServer.run(options)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tarsier
4
+ class CLI
5
+ module Commands
6
+ class Version
7
+ def self.run(_args)
8
+ puts "Tarsier #{Tarsier::VERSION}"
9
+ puts "Ruby #{RUBY_VERSION} (#{RUBY_PLATFORM})"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end