radical 1.0.0 → 1.2.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -2
  3. data/CHANGELOG.md +46 -1
  4. data/Gemfile +4 -2
  5. data/README.md +5 -1
  6. data/exe/rad +41 -0
  7. data/lib/radical/app.rb +61 -27
  8. data/lib/radical/asset.rb +24 -0
  9. data/lib/radical/asset_compiler.rb +40 -0
  10. data/lib/radical/assets.rb +45 -0
  11. data/lib/radical/controller.rb +54 -11
  12. data/lib/radical/database.rb +43 -44
  13. data/lib/radical/env.rb +1 -0
  14. data/lib/radical/flash.rb +61 -0
  15. data/lib/radical/form.rb +75 -15
  16. data/lib/radical/generator/app/.env +5 -0
  17. data/lib/radical/generator/app/Gemfile +7 -0
  18. data/lib/radical/generator/app/app.rb +37 -0
  19. data/lib/radical/generator/app/config.ru +5 -0
  20. data/lib/radical/generator/app/controllers/controller.rb +4 -0
  21. data/lib/radical/generator/app/models/model.rb +4 -0
  22. data/lib/radical/generator/app/routes.rb +5 -0
  23. data/lib/radical/generator/blank_migration.rb +11 -0
  24. data/lib/radical/generator/controller.rb +59 -0
  25. data/lib/radical/generator/migration.rb +13 -0
  26. data/lib/radical/generator/model.rb +9 -0
  27. data/lib/radical/generator/views/_form.rb +6 -0
  28. data/lib/radical/generator/views/edit.rb +3 -0
  29. data/lib/radical/generator/views/index.rb +24 -0
  30. data/lib/radical/generator/views/new.rb +4 -0
  31. data/lib/radical/generator/views/show.rb +5 -0
  32. data/lib/radical/generator.rb +155 -0
  33. data/lib/radical/migration.rb +45 -0
  34. data/lib/radical/model.rb +3 -12
  35. data/lib/radical/router.rb +143 -42
  36. data/lib/radical/routes.rb +59 -0
  37. data/lib/radical/security_headers.rb +27 -0
  38. data/lib/radical/strings.rb +17 -0
  39. data/lib/radical/table.rb +2 -0
  40. data/lib/radical/view.rb +19 -9
  41. data/lib/radical.rb +11 -0
  42. data/radical.gemspec +4 -3
  43. metadata +44 -17
@@ -1,4 +1,6 @@
1
1
  # typed: true
2
+ # frozen_string_literal: true
3
+
2
4
  require 'rack'
3
5
  require 'sorbet-runtime'
4
6
 
@@ -45,6 +47,8 @@ module Radical
45
47
  ].freeze
46
48
 
47
49
  RESOURCE_ACTIONS = [
50
+ [:new, 'GET', '/new'],
51
+ [:create, 'POST', ''],
48
52
  [:show, 'GET', ''],
49
53
  [:edit, 'GET', '/edit'],
50
54
  [:update, 'PUT', ''],
@@ -66,54 +70,31 @@ module Radical
66
70
 
67
71
  sig { params(klass: Class).void }
68
72
  def add_root(klass)
69
- add_actions(klass, name: '')
73
+ add_routes(klass, name: '', actions: ACTIONS)
74
+ add_root_paths(klass)
70
75
  end
71
76
 
72
- sig { params(klass: Class, name: T.nilable(String), prefix: T.nilable(String), actions: Array).void }
73
- def add_actions(klass, name: nil, prefix: nil, actions: ACTIONS)
74
- name ||= klass.route_name
75
-
76
- actions.each do |method, http_method, suffix|
77
- next unless klass.method_defined?(method)
78
-
79
- path = "/#{prefix}#{name}#{suffix}"
80
- path = Regexp.new("^#{path.gsub(/:(\w+)/, '(?<\1>[a-zA-Z0-9_]+)')}$")
81
-
82
- if %i[index create show update destroy].include?(method) && !klass.method_defined?(:"#{klass.route_name}_path")
83
- klass.define_method :"#{klass.route_name}_path" do |obj = nil|
84
- if obj.is_a?(Model)
85
- "/#{klass.route_name}/#{obj.id}"
86
- else
87
- "/#{klass.route_name}"
88
- end
89
- end
90
- end
91
-
92
- if method == :new
93
- klass.define_method :"new_#{klass.route_name}_path" do
94
- "/#{klass.route_name}/new"
95
- end
96
- end
97
-
98
- if method == :edit
99
- klass.define_method :"edit_#{klass.route_name}_path" do |obj|
100
- "/#{klass.route_name}/#{obj.id}/edit"
101
- end
102
- end
103
-
104
- @routes[http_method] << [path, [klass, method]]
105
- end
77
+ sig { params(klass: Class).void }
78
+ def add_resource(klass)
79
+ add_routes(klass, actions: RESOURCE_ACTIONS)
80
+ add_resource_paths(klass)
106
81
  end
107
82
 
108
- sig { params(classes: T::Array[Class], prefix: T.nilable(String), actions: Array).void }
109
- def add_routes(classes, prefix: nil, actions: ACTIONS)
110
- classes.each do |klass|
111
- add_actions(klass, prefix: prefix, actions: actions)
83
+ sig { params(klass: Class, parents: T.nilable(T::Array[Class])).void }
84
+ def add_resources(klass, parents: nil)
85
+ if parents
86
+ parents.each do |scope|
87
+ add_routes(klass, actions: ACTIONS, scope: scope)
88
+ add_resources_paths(klass, scope: scope)
89
+ end
90
+ else
91
+ add_routes(klass, actions: ACTIONS)
92
+ add_resources_paths(klass)
112
93
  end
113
94
  end
114
95
 
115
- sig { params(request: Rack::Request).returns(Rack::Response) }
116
- def route(request)
96
+ sig { params(request: Rack::Request, options: T.nilable(Hash)).returns(Rack::Response) }
97
+ def route(request, options: {})
117
98
  params = T.let({}, T.nilable(Hash))
118
99
 
119
100
  route = @routes[request.request_method].find do |r|
@@ -128,7 +109,7 @@ module Radical
128
109
  request.update_param(k, v)
129
110
  end
130
111
 
131
- instance = klass.new(request)
112
+ instance = klass.new(request, options: options)
132
113
 
133
114
  response = instance.public_send(method)
134
115
 
@@ -140,5 +121,125 @@ module Radical
140
121
 
141
122
  Rack::Response.new(body, 200, { 'Content-Type' => 'text/html' })
142
123
  end
124
+
125
+ private
126
+
127
+ sig { params(klass: Class, actions: Array, name: T.nilable(String), scope: T.nilable(Class)).void }
128
+ def add_routes(klass, actions:, name: nil, scope: nil)
129
+ name ||= klass.route_name
130
+
131
+ actions.each do |method, http_method, suffix|
132
+ next unless klass.method_defined?(method)
133
+
134
+ path = if scope
135
+ if %i[index new create].include?(method)
136
+ "/#{scope.route_name}/:#{scope.route_name}_id/#{name}#{suffix}"
137
+ else
138
+ "/#{name}#{suffix}"
139
+ end
140
+ else
141
+ "/#{name}#{suffix}"
142
+ end
143
+
144
+ path = Regexp.new("^#{path.gsub(/:(\w+)/, '(?<\1>[a-zA-Z0-9_]+)')}$").freeze
145
+
146
+ @routes[http_method] << [path, [klass, method]]
147
+ end
148
+ end
149
+
150
+ sig { params(klass: Class).void }
151
+ def add_root_paths(klass)
152
+ route_name = klass.route_name
153
+
154
+ if %i[index create show update destroy].any? { |method| klass.method_defined?(method) }
155
+ Controller.define_method :"#{route_name}_path" do |obj = nil|
156
+ if obj
157
+ "/#{obj.id}"
158
+ else
159
+ '/'
160
+ end
161
+ end
162
+ end
163
+
164
+ if klass.method_defined?(:new)
165
+ Controller.define_method :"new_#{route_name}_path" do
166
+ '/new'
167
+ end
168
+ end
169
+
170
+ return unless klass.method_defined?(:edit)
171
+
172
+ Controller.define_method :"edit_#{route_name}_path" do |obj|
173
+ "/#{obj.id}/edit"
174
+ end
175
+ end
176
+
177
+ sig { params(klass: Class).void }
178
+ def add_resource_paths(klass)
179
+ name = klass.route_name
180
+
181
+ if %i[create show update destroy].any? { |method| klass.method_defined?(method) }
182
+ Controller.define_method :"#{name}_path" do
183
+ "/#{name}"
184
+ end
185
+ end
186
+
187
+ if klass.method_defined?(:new)
188
+ Controller.define_method :"new_#{name}_path" do
189
+ "/#{name}/new"
190
+ end
191
+ end
192
+
193
+ return unless klass.method_defined?(:edit)
194
+
195
+ Controller.define_method :"edit_#{name}_path" do
196
+ "/#{name}/edit"
197
+ end
198
+ end
199
+
200
+ sig { params(klass: Class, scope: T.nilable(Class)).void }
201
+ def add_resources_paths(klass, scope: nil)
202
+ path_name = klass.route_name
203
+ scope_path_name = [scope&.route_name, klass.route_name].compact.join('_')
204
+ name = klass.route_name
205
+
206
+ if %i[index create show update destroy].any? { |method| klass.method_defined?(method) }
207
+ if scope
208
+ Controller.define_method :"#{scope_path_name}_path" do |parent|
209
+ "/#{scope.route_name}/#{parent.id}/#{name}"
210
+ end
211
+
212
+ Controller.define_method :"#{path_name}_path" do |obj|
213
+ "/#{name}/#{obj.id}"
214
+ end
215
+ else
216
+ Controller.define_method :"#{path_name}_path" do |obj = nil|
217
+ if obj
218
+ "/#{name}/#{obj.id}"
219
+ else
220
+ "/#{name}"
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ if klass.method_defined?(:new)
227
+ if scope
228
+ Controller.define_method :"new_#{scope_path_name}_path" do |parent|
229
+ "/#{scope.route_name}/#{parent.id}/#{name}/new"
230
+ end
231
+ else
232
+ Controller.define_method :"new_#{path_name}_path" do
233
+ "/#{name}/new"
234
+ end
235
+ end
236
+ end
237
+
238
+ return unless klass.method_defined?(:edit)
239
+
240
+ Controller.define_method :"edit_#{path_name}_path" do |obj|
241
+ "/#{name}/#{obj.id}/edit"
242
+ end
243
+ end
143
244
  end
144
245
  end
@@ -0,0 +1,59 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'router'
5
+
6
+ module Radical
7
+ class Routes
8
+ class << self
9
+ extend T::Sig
10
+
11
+ sig { returns(Router) }
12
+ def router
13
+ @router ||= Router.new
14
+ end
15
+
16
+ def parents
17
+ @parents ||= []
18
+ end
19
+
20
+ sig { params(name: T.any(String, Symbol)).void }
21
+ def root(name)
22
+ klass = Object.const_get(name)
23
+
24
+ router.add_root(klass)
25
+ end
26
+
27
+ sig { params(names: T.any(String, Symbol)).void }
28
+ def resource(*names)
29
+ classes = names.map { |c| Object.const_get(c) }
30
+
31
+ classes.each do |klass|
32
+ router.add_resource(klass)
33
+ end
34
+ end
35
+
36
+ sig { params(names: T.any(String, Symbol), block: T.nilable(T.proc.void)).void }
37
+ def resources(*names, &block)
38
+ classes = names.map { |c| Object.const_get(c) }
39
+
40
+ classes.each do |klass|
41
+ if parents.any?
42
+ router.add_resources(klass, parents: @parents)
43
+
44
+ # only one level of nesting
45
+ @parents = []
46
+ else
47
+ router.add_resources(klass)
48
+ end
49
+ end
50
+
51
+ return unless block
52
+
53
+ @parents = classes
54
+
55
+ block.call
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Radical
4
+ class SecurityHeaders
5
+ DEFAULT_HEADERS = {
6
+ 'X-Content-Type-Options' => 'nosniff',
7
+ 'X-Frame-Options' => 'deny',
8
+ 'X-XSS-Protection' => '1; mode=block',
9
+ 'X-Permitted-Cross-Domain-Policies' => 'none',
10
+ 'Strict-Transport-Security' => 'max-age=31536000;, max-age=31536000; includeSubdomains',
11
+ 'Content-Security-Policy' => "default-src 'none'; style-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self'; font-src 'self'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'; block-all-mixed-content;"
12
+ }.freeze
13
+
14
+ def initialize(app, headers)
15
+ @app = app
16
+ @headers = DEFAULT_HEADERS.merge(headers)
17
+ end
18
+
19
+ def call(env)
20
+ @app.call(env).tap do |_, headers|
21
+ @headers.each do |k, v|
22
+ headers[k] ||= v
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Radical
4
+ class Strings
5
+ SNAKE_CASE_REGEX = /\B([A-Z])/.freeze
6
+
7
+ class << self
8
+ def snake_case(str)
9
+ str.gsub(SNAKE_CASE_REGEX, '_\1').downcase
10
+ end
11
+
12
+ def camel_case(str)
13
+ str.split('_').map(&:capitalize).join
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/radical/table.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Radical
2
4
  class Table
3
5
  attr_accessor :columns
data/lib/radical/view.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erubi'
2
4
  require 'tilt'
3
5
 
@@ -20,20 +22,28 @@ module Radical
20
22
  class << self
21
23
  attr_accessor :_views_path, :_layout
22
24
 
23
- def view_path(dir, name)
24
- filename = File.join(@_views_path, 'views', dir, "#{name}.erb")
25
+ def view_path!(dir, name)
26
+ filename = view_path(dir, name)
25
27
 
26
28
  raise "Could not find view file: #{filename}. You need to create it." unless File.exist?(filename)
27
29
 
28
30
  filename
29
31
  end
30
32
 
33
+ def view_path(dir, name)
34
+ File.join(@_views_path || '.', 'views', dir, "#{name}.erb")
35
+ end
36
+
31
37
  def template(dir, name)
32
38
  Tilt.new(view_path(dir, name), engine_class: CaptureEngine, escape_html: true)
33
39
  end
34
40
 
35
- def path(path = nil, test = Env.test?)
36
- @_views_path = path || ((test ? 'test' : '') + __dir__)
41
+ def template!(dir, name)
42
+ Tilt.new(view_path!(dir, name), engine_class: CaptureEngine, escape_html: true)
43
+ end
44
+
45
+ def path(path = nil)
46
+ @_views_path = path
37
47
  end
38
48
 
39
49
  def layout(name)
@@ -41,13 +51,13 @@ module Radical
41
51
  end
42
52
 
43
53
  def render(dir, name, scope, options = {})
44
- t = template(dir, name)
54
+ t = template!(dir, name)
45
55
 
46
- if @_layout && options[:layout] != false
47
- layout = template('', @_layout)
56
+ layout = template('', @_layout || 'layout') unless options[:layout] != false
48
57
 
49
- layout.render(scope, {}) do
50
- t.render scope
58
+ if layout
59
+ layout.render scope, {} do
60
+ t.render scope, options[:locals] || {}
51
61
  end
52
62
  else
53
63
  t.render scope
data/lib/radical.rb CHANGED
@@ -1,4 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'radical/app'
2
4
  require_relative 'radical/controller'
3
5
  require_relative 'radical/view'
4
6
  require_relative 'radical/model'
7
+ require_relative 'radical/migration'
8
+ require_relative 'radical/routes'
9
+ require_relative 'radical/env'
10
+
11
+ module Radical
12
+ def self.env
13
+ Radical::Env
14
+ end
15
+ end
data/radical.gemspec CHANGED
@@ -3,7 +3,7 @@
3
3
  Gem::Specification.new do |spec|
4
4
  spec.platform = Gem::Platform::RUBY
5
5
  spec.name = 'radical'
6
- spec.version = '1.0.0'
6
+ spec.version = '1.2.0'
7
7
  spec.author = 'Sean Walker'
8
8
  spec.email = 'sean@swlkr.com'
9
9
 
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  if spec.respond_to?(:metadata)
16
16
  spec.metadata['homepage_uri'] = spec.homepage
17
17
  spec.metadata['source_code_uri'] = 'https://github.com/swlkr/radical'
18
- spec.metadata['changelog_uri'] = 'https://github.com/swlkr/radical/CHANGELOG.md'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/swlkr/radical/blob/main/CHANGELOG.md'
19
19
  else
20
20
  raise 'RubyGems 2.0 or newer is required to protect against ' \
21
21
  'public gem pushes.'
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.bindir = 'exe'
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ['lib']
32
+ spec.required_ruby_version = '>= 2.7.0'
32
33
 
33
34
  spec.add_development_dependency 'minitest', '~> 5.14'
34
35
  spec.add_development_dependency 'puma', '~> 5.5'
@@ -36,10 +37,10 @@ Gem::Specification.new do |spec|
36
37
  spec.add_development_dependency 'rake', '~> 13.0'
37
38
  spec.add_development_dependency 'sorbet', '~> 0.5'
38
39
 
40
+ spec.add_dependency 'brotli', '~> 0.4'
39
41
  spec.add_dependency 'erubi', '~> 1.10'
40
42
  spec.add_dependency 'rack', '~> 2.2'
41
43
  spec.add_dependency 'rack_csrf', '~> 2.6'
42
- spec.add_dependency 'rack-flash3', '~> 1.0'
43
44
  spec.add_dependency 'sorbet-runtime', '~> 0.5'
44
45
  spec.add_dependency 'sqlite3', '~> 1.4'
45
46
  spec.add_dependency 'tilt', '~> 2.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radical
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Walker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-03 00:00:00.000000000 Z
11
+ date: 2021-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -81,61 +81,61 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.5'
83
83
  - !ruby/object:Gem::Dependency
84
- name: erubi
84
+ name: brotli
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.10'
89
+ version: '0.4'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1.10'
96
+ version: '0.4'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rack
98
+ name: erubi
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '2.2'
103
+ version: '1.10'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '2.2'
110
+ version: '1.10'
111
111
  - !ruby/object:Gem::Dependency
112
- name: rack_csrf
112
+ name: rack
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '2.6'
117
+ version: '2.2'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '2.6'
124
+ version: '2.2'
125
125
  - !ruby/object:Gem::Dependency
126
- name: rack-flash3
126
+ name: rack_csrf
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '1.0'
131
+ version: '2.6'
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '1.0'
138
+ version: '2.6'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: sorbet-runtime
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -180,7 +180,8 @@ dependencies:
180
180
  version: '2.0'
181
181
  description: This gem helps you write rails-like web applications
182
182
  email: sean@swlkr.com
183
- executables: []
183
+ executables:
184
+ - rad
184
185
  extensions: []
185
186
  extra_rdoc_files: []
186
187
  files:
@@ -192,14 +193,40 @@ files:
192
193
  - LICENSE
193
194
  - README.md
194
195
  - Rakefile
196
+ - exe/rad
195
197
  - lib/radical.rb
196
198
  - lib/radical/app.rb
199
+ - lib/radical/asset.rb
200
+ - lib/radical/asset_compiler.rb
201
+ - lib/radical/assets.rb
197
202
  - lib/radical/controller.rb
198
203
  - lib/radical/database.rb
199
204
  - lib/radical/env.rb
205
+ - lib/radical/flash.rb
200
206
  - lib/radical/form.rb
207
+ - lib/radical/generator.rb
208
+ - lib/radical/generator/app/.env
209
+ - lib/radical/generator/app/Gemfile
210
+ - lib/radical/generator/app/app.rb
211
+ - lib/radical/generator/app/config.ru
212
+ - lib/radical/generator/app/controllers/controller.rb
213
+ - lib/radical/generator/app/models/model.rb
214
+ - lib/radical/generator/app/routes.rb
215
+ - lib/radical/generator/blank_migration.rb
216
+ - lib/radical/generator/controller.rb
217
+ - lib/radical/generator/migration.rb
218
+ - lib/radical/generator/model.rb
219
+ - lib/radical/generator/views/_form.rb
220
+ - lib/radical/generator/views/edit.rb
221
+ - lib/radical/generator/views/index.rb
222
+ - lib/radical/generator/views/new.rb
223
+ - lib/radical/generator/views/show.rb
224
+ - lib/radical/migration.rb
201
225
  - lib/radical/model.rb
202
226
  - lib/radical/router.rb
227
+ - lib/radical/routes.rb
228
+ - lib/radical/security_headers.rb
229
+ - lib/radical/strings.rb
203
230
  - lib/radical/table.rb
204
231
  - lib/radical/view.rb
205
232
  - radical.gemspec
@@ -212,7 +239,7 @@ licenses:
212
239
  metadata:
213
240
  homepage_uri: https://github.com/swlkr/radical
214
241
  source_code_uri: https://github.com/swlkr/radical
215
- changelog_uri: https://github.com/swlkr/radical/CHANGELOG.md
242
+ changelog_uri: https://github.com/swlkr/radical/blob/main/CHANGELOG.md
216
243
  post_install_message:
217
244
  rdoc_options: []
218
245
  require_paths:
@@ -221,7 +248,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
221
248
  requirements:
222
249
  - - ">="
223
250
  - !ruby/object:Gem::Version
224
- version: '0'
251
+ version: 2.7.0
225
252
  required_rubygems_version: !ruby/object:Gem::Requirement
226
253
  requirements:
227
254
  - - ">="