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.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -2
- data/CHANGELOG.md +46 -1
- data/Gemfile +4 -2
- data/README.md +5 -1
- data/exe/rad +41 -0
- data/lib/radical/app.rb +61 -27
- data/lib/radical/asset.rb +24 -0
- data/lib/radical/asset_compiler.rb +40 -0
- data/lib/radical/assets.rb +45 -0
- data/lib/radical/controller.rb +54 -11
- data/lib/radical/database.rb +43 -44
- data/lib/radical/env.rb +1 -0
- data/lib/radical/flash.rb +61 -0
- data/lib/radical/form.rb +75 -15
- data/lib/radical/generator/app/.env +5 -0
- data/lib/radical/generator/app/Gemfile +7 -0
- data/lib/radical/generator/app/app.rb +37 -0
- data/lib/radical/generator/app/config.ru +5 -0
- data/lib/radical/generator/app/controllers/controller.rb +4 -0
- data/lib/radical/generator/app/models/model.rb +4 -0
- data/lib/radical/generator/app/routes.rb +5 -0
- data/lib/radical/generator/blank_migration.rb +11 -0
- data/lib/radical/generator/controller.rb +59 -0
- data/lib/radical/generator/migration.rb +13 -0
- data/lib/radical/generator/model.rb +9 -0
- data/lib/radical/generator/views/_form.rb +6 -0
- data/lib/radical/generator/views/edit.rb +3 -0
- data/lib/radical/generator/views/index.rb +24 -0
- data/lib/radical/generator/views/new.rb +4 -0
- data/lib/radical/generator/views/show.rb +5 -0
- data/lib/radical/generator.rb +155 -0
- data/lib/radical/migration.rb +45 -0
- data/lib/radical/model.rb +3 -12
- data/lib/radical/router.rb +143 -42
- data/lib/radical/routes.rb +59 -0
- data/lib/radical/security_headers.rb +27 -0
- data/lib/radical/strings.rb +17 -0
- data/lib/radical/table.rb +2 -0
- data/lib/radical/view.rb +19 -9
- data/lib/radical.rb +11 -0
- data/radical.gemspec +4 -3
- metadata +44 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d14c9a576701105d60a08c6339a19aeebc080b6a4fd6f7235d8c5dec82ea287
|
4
|
+
data.tar.gz: b03f1d945e0f441401d99375062423dff70fa9c50cc394e470444d253faa46c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbdbdd7c8a1a56755ef772bbd7ce24a197111d97d44be0d1c1f6a56be24361d1f80469583f4969c0d8b8d9500ce92a66333e71539e8a79b845f249659b72a6ec
|
7
|
+
data.tar.gz: d3425aef0d2a3d87788b7169ba81b67b8c71faed045227aaff586a699ad46482d0770cfaacf0a738d787bb28b20be42c47b7d88ef79eda5632af5abe91da1e1b
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,50 @@
|
|
1
1
|
# Changelog
|
2
|
+
|
2
3
|
All notable changes to this project will be documented in this file
|
3
4
|
|
4
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
5
6
|
|
6
|
-
|
7
|
+
# [Unreleased]
|
8
|
+
|
9
|
+
# 1.2.0 (2021-12-17)
|
10
|
+
|
11
|
+
- *Breaking* Support only one level of nested resources, make them shallow only
|
12
|
+
- *Breaking* Move `db/migrations` to `migrations`
|
13
|
+
- *Breaking* Change `<%== f.button 'Save' %>` to `<%== f.submit 'Save' %>`
|
14
|
+
- *Breaking* Change the form helpers from `model[name]` to just `name`, it's cleaner.
|
15
|
+
- Add `Radical.env.[development?|production?|test?]`
|
16
|
+
- Add `_path` support for nested resource routes
|
17
|
+
- Add a random secret to the session cookie on startup
|
18
|
+
- Add asset concatention + compression (no minification yet)
|
19
|
+
- Remove dependence on rack-flash3
|
20
|
+
- Add security headers
|
21
|
+
- Add session configuration with `session` method in `App`
|
22
|
+
- Move all `_path` method definitions to `Radical::Controller`
|
23
|
+
- Add `exe/rad`
|
24
|
+
- Add `rad g mvc Todo(s) name:text done_at:integer` generators
|
25
|
+
- Add attrs to form helpers
|
26
|
+
- Add `rad g app` generator
|
27
|
+
- Add migrate/rollback `rad` commands
|
28
|
+
|
29
|
+
# 1.1.0 (2021-12-06)
|
30
|
+
|
31
|
+
- *Breaking* `root`, `resource` and `resources` methods no longer take class constants
|
32
|
+
- *Breaking* Move route class methods to `Routes` class instead of `App`
|
33
|
+
- *Breaking* Create migration class, use migration classes in migrate!
|
34
|
+
- *Breaking* Make routes take symbols or strings, not classes to better line up with models
|
35
|
+
- *Breaking* Move connection string handling to database
|
36
|
+
- Make everything use `frozen_string_literal`
|
37
|
+
- Purposefully never add callbacks, `before_action` or autoloading
|
38
|
+
|
39
|
+
# 1.0.2 (2021-12-02)
|
40
|
+
|
41
|
+
- Set default views / migrations paths
|
42
|
+
|
43
|
+
# 1.0.1 (2021-12-02)
|
44
|
+
|
45
|
+
- Fix changelog link in gemspec
|
46
|
+
|
47
|
+
# 1.0.0 (2021-12-01)
|
7
48
|
|
8
49
|
- Very basic understanding of how much memory it takes for a basic ruby app
|
9
50
|
- Start to integrate controllers and routing
|
@@ -25,3 +66,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
25
66
|
- Use Rack::Test
|
26
67
|
- Rename routes to resources; add resource method
|
27
68
|
- Add nested resources, multi-argument resources
|
69
|
+
- Use rack sessions, rack-csrf and rack-flash3
|
70
|
+
- Add naive migrations
|
71
|
+
- Add naive models
|
72
|
+
- Add naive form helper
|
data/Gemfile
CHANGED
data/README.md
CHANGED
data/exe/rad
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/radical/generator'
|
4
|
+
require_relative '../lib/radical/database'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
@options = {}
|
8
|
+
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.on('-v', '--verbose', 'Show extra information') do
|
11
|
+
@options[:verbose] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on('-h', '--help', 'Show help information') do
|
15
|
+
@options[:help] = true
|
16
|
+
end
|
17
|
+
end.parse!
|
18
|
+
|
19
|
+
generate = Radical::Generator.new(ARGV[2], ARGV.drop(3)) if %w[g generate].include?(ARGV.first)
|
20
|
+
|
21
|
+
case ARGV[0..1]
|
22
|
+
when %w[g mvc], %w[generate mvc]
|
23
|
+
generate.mvc
|
24
|
+
when %w[g model], %w[generate model]
|
25
|
+
generate.migration
|
26
|
+
generate.model
|
27
|
+
when %w[g controller], %w[generate controller]
|
28
|
+
generate.controller
|
29
|
+
when %w[g views], %w[generate views]
|
30
|
+
generate.views
|
31
|
+
when %w[g migration], %w[generate migration]
|
32
|
+
generate.migration(model: false)
|
33
|
+
when %w[g app]
|
34
|
+
generate.app
|
35
|
+
when %w[migrate]
|
36
|
+
Radical::Database.migrate!
|
37
|
+
when %w[rollback]
|
38
|
+
Radical::Database.rollback!
|
39
|
+
else
|
40
|
+
puts 'Command not supported'
|
41
|
+
end
|
data/lib/radical/app.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
1
4
|
require 'rack'
|
2
|
-
require 'rack/flash'
|
3
5
|
require 'rack/csrf'
|
4
6
|
|
5
|
-
require_relative '
|
7
|
+
require_relative 'asset'
|
8
|
+
require_relative 'assets'
|
9
|
+
require_relative 'asset_compiler'
|
6
10
|
require_relative 'env'
|
11
|
+
require_relative 'flash'
|
12
|
+
require_relative 'routes'
|
13
|
+
require_relative 'security_headers'
|
7
14
|
|
8
15
|
# The main entry point for a Radical application
|
9
16
|
#
|
@@ -31,24 +38,39 @@ require_relative 'env'
|
|
31
38
|
module Radical
|
32
39
|
class App
|
33
40
|
class << self
|
34
|
-
def
|
35
|
-
|
41
|
+
def routes(route_class)
|
42
|
+
@routes = route_class
|
36
43
|
end
|
37
44
|
|
38
|
-
def
|
39
|
-
|
45
|
+
def assets(&block)
|
46
|
+
@assets = Assets.new
|
47
|
+
|
48
|
+
block.call(@assets)
|
40
49
|
end
|
41
50
|
|
42
|
-
def
|
43
|
-
|
51
|
+
def compile_assets
|
52
|
+
@assets.compile
|
53
|
+
end
|
44
54
|
|
45
|
-
|
55
|
+
def serve_assets
|
56
|
+
@serve_assets = true
|
57
|
+
end
|
46
58
|
|
47
|
-
|
59
|
+
def security_headers(headers = {})
|
60
|
+
@security_headers = headers
|
61
|
+
end
|
62
|
+
|
63
|
+
def session(options = {})
|
64
|
+
defaults = {
|
65
|
+
path: '/',
|
66
|
+
secret: session_secret,
|
67
|
+
http_only: true,
|
68
|
+
same_site: :lax,
|
69
|
+
secure: env.production?,
|
70
|
+
expire_after: 2_592_000 # 30 days
|
71
|
+
}
|
48
72
|
|
49
|
-
@
|
50
|
-
@parents << classes.last
|
51
|
-
block.call
|
73
|
+
@session = defaults.merge(options)
|
52
74
|
end
|
53
75
|
|
54
76
|
def env
|
@@ -56,8 +78,12 @@ module Radical
|
|
56
78
|
end
|
57
79
|
|
58
80
|
def app
|
59
|
-
router =
|
81
|
+
router = @routes.router
|
60
82
|
env = self.env
|
83
|
+
assets = @assets
|
84
|
+
serve_assets = @serve_assets
|
85
|
+
security_headers = @security_headers || {}
|
86
|
+
session = @session || self.session
|
61
87
|
|
62
88
|
@app ||= Rack::Builder.app do
|
63
89
|
use Rack::CommonLogger
|
@@ -65,23 +91,29 @@ module Radical
|
|
65
91
|
use Rack::Runtime
|
66
92
|
use Rack::MethodOverride
|
67
93
|
use Rack::ContentLength
|
68
|
-
use Rack::Deflater
|
69
94
|
use Rack::ETag
|
95
|
+
use Rack::Deflater
|
70
96
|
use Rack::Head
|
71
97
|
use Rack::ConditionalGet
|
72
98
|
use Rack::ContentType
|
73
|
-
use Rack::Session::Cookie,
|
74
|
-
secret: ENV['SESSION_SECRET'],
|
75
|
-
http_only: true,
|
76
|
-
same_site: :lax,
|
77
|
-
secure: env.production?,
|
78
|
-
expire_after: 2_592_000 # 30 days
|
99
|
+
use Rack::Session::Cookie, session
|
79
100
|
use Rack::Csrf, raise: env.development?, skip: router.routes.values.flatten.select { |a| a.is_a?(Class) }.uniq.map(&:skip_csrf_actions).flatten(1)
|
80
|
-
use
|
101
|
+
use Flash
|
102
|
+
use SecurityHeaders, security_headers
|
103
|
+
|
104
|
+
if serve_assets || env.development?
|
105
|
+
use Rack::Static, urls: ['/assets', '/public'],
|
106
|
+
header_rules: [
|
107
|
+
[/\.(?:css\.gz)$/, { 'Content-Type' => 'text/css', 'Content-Encoding' => 'gzip' }],
|
108
|
+
[/\.(?:js\.gz)$/, { 'Content-Type' => 'application/javascript', 'Content-Encoding' => 'gzip' }],
|
109
|
+
[/\.(?:css\.br)$/, { 'Content-Type' => 'text/css', 'Content-Encoding' => 'br' }],
|
110
|
+
[/\.(?:js\.br)$/, { 'Content-Type' => 'application/javascript', 'Content-Encoding' => 'br' }]
|
111
|
+
]
|
112
|
+
end
|
81
113
|
|
82
114
|
run lambda { |rack_env|
|
83
115
|
begin
|
84
|
-
router.route(Rack::Request.new(rack_env)).finish
|
116
|
+
router.route(Rack::Request.new(rack_env), options: { assets: assets }).finish
|
85
117
|
rescue ModelNotFound
|
86
118
|
raise unless env.production?
|
87
119
|
|
@@ -91,13 +123,15 @@ module Radical
|
|
91
123
|
end
|
92
124
|
end
|
93
125
|
|
94
|
-
def router
|
95
|
-
@router ||= Router.new
|
96
|
-
end
|
97
|
-
|
98
126
|
def call(env)
|
99
127
|
app.call(env)
|
100
128
|
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def session_secret
|
133
|
+
@session_secret ||= (ENV['SESSION_SECRET'] || SecureRandom.hex(32))
|
134
|
+
end
|
101
135
|
end
|
102
136
|
end
|
103
137
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Radical
|
4
|
+
class Asset
|
5
|
+
attr_reader :path
|
6
|
+
|
7
|
+
def initialize(filename, path:)
|
8
|
+
@filename = filename
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def full_path
|
13
|
+
File.join(path, @filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
def content
|
17
|
+
File.read(full_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ext
|
21
|
+
File.extname(@filename)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'brotli'
|
4
|
+
require 'digest'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
module Radical
|
8
|
+
class AssetCompiler
|
9
|
+
def self.gzip(filename, content)
|
10
|
+
# nil, 31 == support for gunzip
|
11
|
+
File.write(filename, Zlib::Deflate.new(nil, 31).deflate(content, Zlib::FINISH))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.compile(assets, path:, compressor: :none)
|
15
|
+
s = assets.map(&:content).join("\n")
|
16
|
+
ext = assets.first.ext
|
17
|
+
|
18
|
+
# hash the contents of each concatenated asset
|
19
|
+
hash = Digest::SHA1.hexdigest s
|
20
|
+
|
21
|
+
# use hash to bust the cache
|
22
|
+
name = "#{hash}#{ext}"
|
23
|
+
filename = File.join(path, name)
|
24
|
+
|
25
|
+
case compressor
|
26
|
+
when :gzip
|
27
|
+
name = "#{name}.gz"
|
28
|
+
gzip("#{filename}.gz", s)
|
29
|
+
when :brotli
|
30
|
+
name = "#{name}.br"
|
31
|
+
File.write("#{filename}.br", Brotli.deflate(s, mode: :text, quality: 11))
|
32
|
+
else
|
33
|
+
File.write(filename, s)
|
34
|
+
end
|
35
|
+
|
36
|
+
# output asset path for browser
|
37
|
+
"/assets/#{name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Radical
|
4
|
+
class Assets
|
5
|
+
attr_accessor :assets_path, :compiled, :assets
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@assets = {
|
9
|
+
css: [],
|
10
|
+
js: []
|
11
|
+
}
|
12
|
+
@compressor = :none
|
13
|
+
@assets_path = File.join(__dir__, 'assets')
|
14
|
+
@compiled = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def css(filenames)
|
18
|
+
@assets[:css] = filenames
|
19
|
+
end
|
20
|
+
|
21
|
+
def js(filenames)
|
22
|
+
@assets[:js] = filenames
|
23
|
+
end
|
24
|
+
|
25
|
+
def prepend_assets_path(path)
|
26
|
+
@assets_path = File.join(path, 'assets')
|
27
|
+
end
|
28
|
+
|
29
|
+
def brotli
|
30
|
+
@compressor = :brotli
|
31
|
+
end
|
32
|
+
|
33
|
+
def gzip
|
34
|
+
@compressor = :gzip
|
35
|
+
end
|
36
|
+
|
37
|
+
def compile
|
38
|
+
css = @assets[:css].map { |f| Asset.new(f, path: File.join(@assets_path, 'css')) }
|
39
|
+
js = @assets[:js].map { |f| Asset.new(f, path: File.join(@assets_path, 'js')) }
|
40
|
+
|
41
|
+
@compiled[:css] = AssetCompiler.compile(css, path: @assets_path, compressor: @compressor)
|
42
|
+
@compiled[:js] = AssetCompiler.compile(js, path: @assets_path, compressor: @compressor)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/radical/controller.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'rack/utils'
|
3
5
|
require 'rack/request'
|
4
6
|
require 'rack/response'
|
@@ -29,7 +31,7 @@ module Radical
|
|
29
31
|
|
30
32
|
sig { returns(String) }
|
31
33
|
def route_name
|
32
|
-
to_s.split('::').last.gsub(/Controller$/, '')
|
34
|
+
Strings.snake_case to_s.split('::').last.gsub(/Controller$/, '')
|
33
35
|
end
|
34
36
|
|
35
37
|
sig { params(actions: Symbol).void }
|
@@ -70,9 +72,12 @@ module Radical
|
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
+
attr_reader :options
|
76
|
+
|
77
|
+
sig { params(request: Rack::Request, options: T.nilable(Hash)).void }
|
78
|
+
def initialize(request, options: {})
|
75
79
|
@request = request
|
80
|
+
@options = options
|
76
81
|
end
|
77
82
|
|
78
83
|
sig { params(status: T.any(Symbol, Integer)).returns(Rack::Response) }
|
@@ -90,14 +95,14 @@ module Radical
|
|
90
95
|
@request.params
|
91
96
|
end
|
92
97
|
|
93
|
-
sig { params(name: T.any(String, Symbol)).returns(String) }
|
94
|
-
def view(name)
|
95
|
-
View.render(self.class.route_name, name, self)
|
98
|
+
sig { params(name: T.any(String, Symbol), locals: T.nilable(Hash)).returns(String) }
|
99
|
+
def view(name, locals = {})
|
100
|
+
View.render(self.class.route_name, name, self, { locals: locals })
|
96
101
|
end
|
97
102
|
|
98
|
-
sig { params(name: T.any(String, Symbol)).returns(String) }
|
99
|
-
def partial(name)
|
100
|
-
View.render(self.class.route_name, "_#{name}", self, layout: false)
|
103
|
+
sig { params(name: T.any(String, Symbol), locals: T.nilable(Hash)).returns(String) }
|
104
|
+
def partial(name, locals = {})
|
105
|
+
View.render(self.class.route_name, "_#{name}", self, { locals: locals, layout: false })
|
101
106
|
end
|
102
107
|
|
103
108
|
sig { params(options: Hash, block: T.proc.void).returns(String) }
|
@@ -128,17 +133,55 @@ module Radical
|
|
128
133
|
@request.env['rack.session']
|
129
134
|
end
|
130
135
|
|
136
|
+
def assets_path(type)
|
137
|
+
assets = options[:assets]
|
138
|
+
|
139
|
+
if Env.production?
|
140
|
+
compiled_assets_path(assets, type)
|
141
|
+
else
|
142
|
+
not_compiled_assets_path(assets, type)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def compiled_assets_path(assets, type)
|
147
|
+
if type == :css
|
148
|
+
link_tag(assets.compiled[:css])
|
149
|
+
else
|
150
|
+
script_tag(assets.compiled[:js])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def not_compiled_assets_path(assets, type)
|
155
|
+
if type == :css
|
156
|
+
assets.assets[:css].map do |asset|
|
157
|
+
link_tag("/assets/#{type}/#{asset}")
|
158
|
+
end.join("\n")
|
159
|
+
else
|
160
|
+
assets.assets[:js].map do |asset|
|
161
|
+
script_tag("/assets/#{type}/#{asset}")
|
162
|
+
end.join("\n")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
131
166
|
private
|
132
167
|
|
133
168
|
def emit(tag)
|
134
|
-
@output =
|
169
|
+
@output = String.new if @output.nil?
|
135
170
|
@output << tag.to_s
|
136
171
|
end
|
137
172
|
|
138
173
|
def capture(block)
|
139
|
-
@output = eval
|
174
|
+
@output = eval '_buf', block.binding
|
140
175
|
yield
|
141
176
|
@output
|
142
177
|
end
|
178
|
+
|
179
|
+
def script_tag(src)
|
180
|
+
"<script type=\"application/javascript\" src=\"#{src}\"></script>"
|
181
|
+
end
|
182
|
+
|
183
|
+
def link_tag(href)
|
184
|
+
"<link rel=\"stylesheet\" type=\"text/css\" href=\"#{href}\" />"
|
185
|
+
end
|
143
186
|
end
|
144
187
|
end
|
data/lib/radical/database.rb
CHANGED
@@ -1,40 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'sqlite3'
|
2
4
|
require_relative 'table'
|
5
|
+
require_relative 'migration'
|
3
6
|
|
4
7
|
module Radical
|
5
8
|
class Database
|
6
9
|
class << self
|
7
|
-
|
10
|
+
attr_writer :connection_string
|
11
|
+
attr_accessor :migrations_path
|
12
|
+
|
13
|
+
def connection_string
|
14
|
+
@connection_string || ENV['DATABASE_URL']
|
15
|
+
end
|
16
|
+
|
17
|
+
def connection
|
18
|
+
conn = SQLite3::Database.new(connection_string)
|
19
|
+
conn.results_as_hash = true
|
20
|
+
conn.type_translation = true
|
21
|
+
|
22
|
+
@connection ||= conn
|
23
|
+
end
|
24
|
+
|
25
|
+
def prepend_migrations_path(path)
|
26
|
+
self.migrations_path = path
|
27
|
+
end
|
8
28
|
|
9
29
|
def db
|
10
30
|
connection
|
11
31
|
end
|
12
32
|
|
13
|
-
def
|
14
|
-
|
33
|
+
def migration(file)
|
34
|
+
context = Module.new
|
35
|
+
context.class_eval(File.read(file), file)
|
36
|
+
const = context.constants.find do |constant|
|
37
|
+
context.const_get(constant).ancestors.include?(Radical::Migration)
|
38
|
+
end
|
39
|
+
|
40
|
+
context.const_get(const)
|
41
|
+
end
|
15
42
|
|
43
|
+
def migrate!
|
16
44
|
db.execute 'create table if not exists radical_migrations ( version integer primary key )'
|
17
45
|
|
18
|
-
pending_migrations.each do |
|
19
|
-
puts "Executing migration #{
|
20
|
-
|
21
|
-
|
22
|
-
|
46
|
+
pending_migrations.each do |file|
|
47
|
+
puts "Executing migration #{file}"
|
48
|
+
|
49
|
+
v = version(file)
|
50
|
+
|
51
|
+
migration(file).migrate!(db: db, version: v)
|
23
52
|
end
|
24
53
|
end
|
25
54
|
|
26
55
|
def rollback!
|
27
|
-
@rollback = true
|
28
|
-
|
29
56
|
db.execute 'create table if not exists radical_migrations ( version integer primary key )'
|
30
57
|
|
31
|
-
|
58
|
+
file = applied_migrations.last
|
32
59
|
|
33
|
-
puts "Rolling back migration #{
|
60
|
+
puts "Rolling back migration #{file}"
|
34
61
|
|
35
|
-
|
36
|
-
|
37
|
-
db
|
62
|
+
v = version(file)
|
63
|
+
|
64
|
+
migration(file).rollback!(db: db, version: v)
|
38
65
|
end
|
39
66
|
|
40
67
|
def applied_versions
|
@@ -53,40 +80,12 @@ module Radical
|
|
53
80
|
end
|
54
81
|
|
55
82
|
def migrations
|
56
|
-
Dir[File.join(migrations_path
|
83
|
+
Dir[File.join(migrations_path || '.', 'migrations', '*.rb')].sort
|
57
84
|
end
|
58
85
|
|
59
86
|
def version(filename)
|
60
87
|
filename.split(File::SEPARATOR).last.split('_').first.to_i
|
61
88
|
end
|
62
|
-
|
63
|
-
def migration(&block)
|
64
|
-
block.call
|
65
|
-
end
|
66
|
-
|
67
|
-
def change(&block)
|
68
|
-
@change = true
|
69
|
-
|
70
|
-
block.call
|
71
|
-
end
|
72
|
-
|
73
|
-
def up(&block)
|
74
|
-
block.call
|
75
|
-
end
|
76
|
-
|
77
|
-
def down(&block)
|
78
|
-
block.call
|
79
|
-
end
|
80
|
-
|
81
|
-
def create_table(name, &block)
|
82
|
-
return "drop table #{name}" if @change && @rollback
|
83
|
-
|
84
|
-
table = Table.new(name)
|
85
|
-
|
86
|
-
block.call(table)
|
87
|
-
|
88
|
-
"create table #{name} ( id integer primary key, #{table.columns.join(',')} )"
|
89
|
-
end
|
90
89
|
end
|
91
90
|
end
|
92
91
|
end
|
data/lib/radical/env.rb
CHANGED