flame 5.0.0.rc5 → 5.0.0.rc6

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 (64) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +921 -0
  3. data/LICENSE.txt +19 -0
  4. data/README.md +135 -0
  5. data/lib/flame.rb +12 -5
  6. data/lib/flame/application.rb +47 -46
  7. data/lib/flame/config.rb +73 -0
  8. data/lib/flame/controller.rb +45 -78
  9. data/lib/flame/controller/actions.rb +122 -0
  10. data/lib/flame/{dispatcher → controller}/cookies.rb +8 -3
  11. data/lib/flame/controller/path_to.rb +34 -10
  12. data/lib/flame/dispatcher.rb +14 -17
  13. data/lib/flame/dispatcher/request.rb +25 -6
  14. data/lib/flame/dispatcher/routes.rb +22 -14
  15. data/lib/flame/dispatcher/static.rb +13 -9
  16. data/lib/flame/errors/argument_not_assigned_error.rb +3 -8
  17. data/lib/flame/errors/config_file_not_found_error.rb +17 -0
  18. data/lib/flame/errors/controller_not_found_error.rb +19 -0
  19. data/lib/flame/errors/route_arguments_order_error.rb +5 -10
  20. data/lib/flame/errors/route_extra_arguments_error.rb +10 -20
  21. data/lib/flame/errors/route_not_found_error.rb +3 -8
  22. data/lib/flame/errors/template_not_found_error.rb +2 -8
  23. data/lib/flame/path.rb +36 -18
  24. data/lib/flame/render.rb +13 -5
  25. data/lib/flame/router.rb +7 -157
  26. data/lib/flame/router/controller_finder.rb +56 -0
  27. data/lib/flame/router/route.rb +9 -0
  28. data/lib/flame/router/routes.rb +58 -8
  29. data/lib/flame/router/routes_refine.rb +144 -0
  30. data/lib/flame/router/routes_refine/mounting.rb +57 -0
  31. data/lib/flame/validators.rb +14 -10
  32. data/lib/flame/version.rb +1 -1
  33. metadata +91 -99
  34. data/bin/flame +0 -16
  35. data/lib/flame/application/config.rb +0 -49
  36. data/template/.editorconfig +0 -15
  37. data/template/.gitignore +0 -28
  38. data/template/.rubocop.yml +0 -14
  39. data/template/Gemfile +0 -55
  40. data/template/Rakefile +0 -824
  41. data/template/application.rb.erb +0 -10
  42. data/template/config.ru.erb +0 -72
  43. data/template/config/config.rb.erb +0 -56
  44. data/template/config/database.example.yml +0 -5
  45. data/template/config/deploy.example.yml +0 -2
  46. data/template/config/puma.rb +0 -56
  47. data/template/config/sequel.rb.erb +0 -22
  48. data/template/config/server.example.yml +0 -32
  49. data/template/config/session.example.yml +0 -7
  50. data/template/controllers/_controller.rb.erb +0 -14
  51. data/template/controllers/site/_controller.rb.erb +0 -18
  52. data/template/controllers/site/index_controller.rb.erb +0 -12
  53. data/template/db/.keep +0 -0
  54. data/template/filewatchers.yml +0 -12
  55. data/template/helpers/.keep +0 -0
  56. data/template/lib/.keep +0 -0
  57. data/template/locales/en.yml +0 -0
  58. data/template/models/.keep +0 -0
  59. data/template/public/.keep +0 -0
  60. data/template/server +0 -200
  61. data/template/services/.keep +0 -0
  62. data/template/views/.keep +0 -0
  63. data/template/views/site/index.html.erb.erb +0 -1
  64. data/template/views/site/layout.html.erb.erb +0 -10
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Flame
4
- class Application
5
- ## Class for Flame::Application.config
6
- class Config < Hash
7
- ## Create an instance of application config
8
- ## @param app [Flame::Application] application
9
- ## @param hash [Hash] config content
10
- def initialize(app, hash = {})
11
- @app = app
12
- replace(hash)
13
- end
14
-
15
- ## Get config value by key
16
- ## @param key [Symbol] config key
17
- ## @return [Object] config value
18
- def [](key)
19
- result = super(key)
20
- if result.class <= Proc && result.parameters.empty?
21
- result = @app.class_exec(&result)
22
- end
23
- result
24
- end
25
-
26
- ## Method for loading YAML-files from config directory
27
- ## @param file [String, Symbol] file name (typecast to String with '.yml')
28
- ## @param key [Symbol, String, nil]
29
- ## key for allocating YAML in config Hash (typecast to Symbol)
30
- ## @param set [Boolean] allocating YAML in Config Hash
31
- ## @example Load SMTP file from `config/smtp.yml' to config[]
32
- ## config.load_yaml('smtp.yml')
33
- ## @example Load SMTP file without extension, by Symbol
34
- ## config.load_yaml(:smtp)
35
- ## @example Load SMTP file with other key to config[:mail]
36
- ## config.load_yaml('smtp.yml', key: :mail)
37
- ## @example Load SMTP file without allocating in config[]
38
- ## config.load_yaml('smtp.yml', set: false)
39
- def load_yaml(file, key: nil, set: true)
40
- file = "#{file}.yml" if file.is_a? Symbol
41
- file_path = File.join(self[:config_dir], file)
42
- yaml = YAML.load_file(file_path)
43
- key ||= File.basename(file, '.*')
44
- self[key.to_sym] = yaml if set
45
- yaml
46
- end
47
- end
48
- end
49
- end
@@ -1,15 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
- end_of_line = lf
7
- charset = utf-8
8
- trim_trailing_whitespace = true
9
- insert_final_newline = true
10
-
11
- [*.yml]
12
- indent_style = space
13
-
14
- [*.md]
15
- indent_style = space
data/template/.gitignore DELETED
@@ -1,28 +0,0 @@
1
- # configuration
2
- config/**/*
3
- !config/**/*.example*
4
- !config/**/*.rb
5
- !config/**/*.erb
6
- !config/nginx
7
- !config/nginx/snippets
8
- !config/nginx/snippets/*.example*
9
- !config/nginx/snippets/ssl-params.conf
10
- !config/nginx/sites
11
- !config/nginx/sites/*.example*
12
-
13
- # temp
14
- .sass-cache/
15
- log/
16
- tmp/
17
- *.bak
18
- *~
19
-
20
- # uploaded files
21
- #public/files
22
-
23
- # dumps
24
- *.sql
25
-
26
- # seo files
27
- #views/site/sitemap.html
28
- #public/robots.txt
@@ -1,14 +0,0 @@
1
- Layout/Tab:
2
- Enabled: false
3
- Layout/IndentationWidth:
4
- Width: 1
5
- Layout/MultilineMethodCallIndentation:
6
- EnforcedStyle: indented
7
-
8
- AllCops:
9
- TargetRubyVersion: 2.3
10
-
11
- Metrics/BlockLength:
12
- Exclude:
13
- - Rakefile
14
- - db/migrations/*
data/template/Gemfile DELETED
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- ## https://github.com/bundler/bundler/issues/4978
6
- git_source(:github) { |name| "https://github.com/#{name}.git" }
7
-
8
- ## system
9
- # gem 'gorilla_patch'
10
-
11
- ## server
12
- gem 'flame', github: 'AlexWayfer/flame'
13
- # gem 'flame-flash', github: 'AlexWayfer/flame-flash'
14
- gem 'puma'
15
- # gem 'rack-slashenforce'
16
- gem 'rack-utf8_sanitizer'
17
- # gem 'rack_csrf', require: 'rack/csrf'
18
-
19
- group :development do
20
- gem 'filewatcher'
21
- gem 'pry-byebug'
22
- end
23
-
24
- group :linter do
25
- # gem 'rubocop'
26
- end
27
-
28
- ## database
29
- # gem 'pg'
30
- # gem 'sequel'
31
- # gem 'sequel_pg', require: 'sequel'
32
-
33
- ## translations
34
- # gem 'flame-r18n', github: 'AlexWayfer/flame-r18n'
35
- ## for named_variables filter
36
- # gem 'r18n-rails-api'
37
-
38
- ## views
39
- gem 'erubi', require: 'tilt/erubi'
40
-
41
- ## assets
42
- # gem 'sass'
43
-
44
- ## others
45
- # gem 'faker'
46
- # gem 'google_currency'
47
- # gem 'kramdown'
48
- # gem 'mail'
49
- # gem 'money'
50
- # gem 'sentry-raven'
51
-
52
- ## tools
53
- gem 'pry'
54
- gem 'rack-console'
55
- gem 'rake'
data/template/Rakefile DELETED
@@ -1,824 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
-
5
- require 'pry-byebug' ## for `binding.pry` debugging
6
-
7
- def alias_task(name, old_name)
8
- t = Rake::Task[old_name]
9
- desc t.full_comment if t.full_comment
10
- task name, *t.arg_names do |_, args|
11
- # values_at is broken on Rake::TaskArguments
12
- args = t.arg_names.map { |a| args[a] }
13
- t.invoke(*args)
14
- end
15
- end
16
-
17
- def edit_file(filename)
18
- sh "eval $EDITOR #{filename}"
19
- end
20
-
21
- def show_diff(filename, other_filename)
22
- sh "diff -u --color=always #{filename} #{other_filename} || true"
23
- puts
24
- end
25
-
26
- def env_true?(key)
27
- %(true yes 1 y).include?(ENV[key.to_s].to_s.downcase)
28
- end
29
-
30
- ## Class for questions
31
- class Question
32
- def initialize(text, possible_answers)
33
- @text = text
34
- @possible_answers = Set.new(possible_answers) << 'quit' << 'help'
35
- end
36
-
37
- def answer
38
- while @answer.nil?
39
- ask
40
- @answer = @possible_answers.find do |possible_answer|
41
- possible_answer.start_with? @real_answer
42
- end
43
- print_help if @answer.nil?
44
- end
45
- @answer
46
- end
47
-
48
- private
49
-
50
- def print_question
51
- print "#{@text} [#{@possible_answers.map(&:chr).join(',')}] : "
52
- end
53
-
54
- def print_help
55
- @possible_answers.each do |possible_answer|
56
- puts "#{possible_answer.chr} - #{possible_answer}"
57
- end
58
- end
59
-
60
- def ask
61
- print_question
62
- @real_answer = STDIN.gets.chomp.downcase
63
- case @real_answer
64
- when 'h'
65
- print_help
66
- return ask
67
- when 'q'
68
- exit
69
- end
70
- end
71
- end
72
-
73
- DB_CONFIG_FILE = File.join(__dir__, 'config/database.yml').freeze
74
-
75
- if File.exist? DB_CONFIG_FILE
76
- namespace :db do
77
- ## Require libs and config
78
- require 'logger'
79
- require 'sequel'
80
-
81
- ## Constants for DB directories
82
-
83
- DB_DIR = File.join(__dir__, 'db')
84
- DB_MIGRATIONS_DIR = File.join(DB_DIR, 'migrations')
85
- DB_DUMPS_DIR = File.join(DB_DIR, 'dumps')
86
-
87
- DB_CONFIG = YAML.load_file DB_CONFIG_FILE
88
-
89
- env_db_name = ENV['DB_NAME']
90
- DB_CONFIG[:database] = env_db_name if env_db_name
91
-
92
- def db_connection
93
- @db_connection ||= Sequel.connect DB_CONFIG
94
- end
95
-
96
- DB_ACCESS = "-U #{DB_CONFIG[:user]} -h #{DB_CONFIG[:host]}"
97
-
98
- DB_EXTENSIONS = %w[citext pgcrypto].freeze
99
-
100
- PGPASS_FILE = File.expand_path('~/.pgpass').freeze
101
-
102
- PGPASS_LINE =
103
- DB_CONFIG
104
- .fetch_values(:host, :port, :database, :user, :password) { |_key| '*' }
105
- .join(':')
106
-
107
- def update_pgpass
108
- pgpass_lines =
109
- File.exist?(PGPASS_FILE) ? File.read(PGPASS_FILE).split($RS) : []
110
- return if pgpass_lines&.include? PGPASS_LINE
111
- File.write PGPASS_FILE, pgpass_lines.push(PGPASS_LINE, nil).join($RS)
112
- File.chmod(0o600, PGPASS_FILE)
113
- end
114
-
115
- # db_connection.loggers << Logger.new($stdout)
116
-
117
- namespace :migrations do
118
- ## Migration file
119
- class MigrationFile
120
- MIGRATION_CONTENT =
121
- <<~STR
122
- # frozen_string_literal: true
123
-
124
- Sequel.migration do
125
- change do
126
- end
127
- end
128
- STR
129
-
130
- DISABLING_EXT = '.bak'
131
-
132
- def self.find(query, only_one: true, enabled: true, disabled: true)
133
- filenames = Dir[File.join(DB_MIGRATIONS_DIR, "*#{query}*")]
134
- filenames.select! { |filename| File.file? filename }
135
- files = filenames.map { |filename| new filename: filename }.sort!
136
- files.reject!(&:disabled) unless disabled
137
- files.select!(&:disabled) unless enabled
138
- return files unless only_one
139
- return files.first if files.size < 2
140
- raise 'More than one file mathes the query'
141
- end
142
-
143
- attr_accessor :version, :name, :disabled
144
-
145
- def initialize(filename: nil, name: nil)
146
- self.filename = filename
147
- self.name = name if name
148
- end
149
-
150
- ## Accessors
151
-
152
- def basename
153
- File.basename(@filename)
154
- end
155
-
156
- def filename=(value)
157
- parse_filename value if value.is_a? String
158
- @filename = value
159
- end
160
-
161
- def name=(value)
162
- @name = value.tr(' ', '_').downcase
163
- end
164
-
165
- def disabled=(value)
166
- @disabled =
167
- case value
168
- when String
169
- [DISABLING_EXT, DISABLING_EXT[1..-1]].include? value
170
- else
171
- value
172
- end
173
- end
174
-
175
- def <=>(other)
176
- version <=> other.version
177
- end
178
-
179
- ## Behavior
180
-
181
- def print
182
- datetime = Time.parse(version).strftime('%F %R')
183
- fullname = name.tr('_', ' ').capitalize
184
- fullname = "#{fullname} (disabled)" if disabled
185
- version_color, name_color =
186
- disabled ? ["\e[37m", "\e[37m- "] : ["\e[36m", '']
187
- puts "\e[37m[#{version}]\e[0m #{version_color}#{datetime}\e[0m" \
188
- " #{name_color}#{fullname}\e[0m"
189
- end
190
-
191
- def generate
192
- self.version = new_version
193
- FileUtils.mkdir_p File.dirname new_filename
194
- File.write new_filename, MIGRATION_CONTENT
195
- end
196
-
197
- def reversion
198
- rename version: new_version
199
- end
200
-
201
- def disable
202
- abort 'Migration already disabled' if disabled
203
-
204
- rename disabled: true
205
- end
206
-
207
- def enable
208
- abort 'Migration already enabled' unless disabled
209
-
210
- rename disabled: false
211
- end
212
-
213
- private
214
-
215
- def parse_filename(value = @filename)
216
- basename = File.basename value
217
- self.version, parts = basename.split('_', 2)
218
- self.name, _ext, self.disabled = parts.split('.')
219
- end
220
-
221
- def new_version
222
- Time.now.strftime('%Y%m%d%H%M')
223
- end
224
-
225
- def rename(vars = {})
226
- vars.each { |key, value| send :"#{key}=", value }
227
- return unless @filename.is_a? String
228
- File.rename @filename, new_filename
229
- self.filename = new_filename
230
- end
231
-
232
- def new_filename
233
- new_basename = "#{version}_#{name}.rb#{DISABLING_EXT if disabled}"
234
- File.join DB_MIGRATIONS_DIR, new_basename
235
- end
236
- end
237
-
238
- desc 'Run migrations'
239
- task :run, %i[target current] do |_t, args|
240
- Rake::Task['db:dump'].invoke
241
-
242
- Sequel.extension :migration
243
- Sequel.extension :inflector
244
- # db_connection.extension :pg_enum
245
-
246
- options = {
247
- allow_missing_migration_files: env_true?(:ignore)
248
- }
249
- if (target = args[:target])
250
- if target == '0'
251
- puts 'Migrating all the way down'
252
- else
253
- file = MigrationFile.find target, disabled: false
254
-
255
- abort 'Migration with this version not found' if file.nil?
256
-
257
- current = args[:current] || 'current'
258
- puts "Migrating from #{current} to #{file.basename}"
259
- target = file.version
260
- end
261
- options[:current] = args[:current].to_i
262
- options[:target] = target.to_i
263
- else
264
- puts 'Migrating to latest'
265
- end
266
-
267
- db_connection.loggers << Logger.new($stdout)
268
-
269
- Sequel::Migrator.run(
270
- db_connection,
271
- DB_MIGRATIONS_DIR,
272
- options
273
- )
274
- end
275
-
276
- desc 'Rollback the database N steps'
277
- task :rollback, :step do |_task, args|
278
- Rake::Task['db:dump'].invoke
279
-
280
- step = args[:step] ? Integer(args[:step]).abs : 1
281
-
282
- file = MigrationFile.find('*', only_one: false)[-1 - step]
283
-
284
- Rake::Task['db:migrations:run'].invoke(file.version)
285
-
286
- puts "Rolled back to #{file.basename}"
287
- end
288
-
289
- desc 'Create migration'
290
- task :new, :name do |_t, args|
291
- abort 'You must specify a migration name' if args[:name].nil?
292
-
293
- file = MigrationFile.new name: args[:name]
294
- file.generate
295
- end
296
-
297
- desc 'Change version of migration to latest'
298
- task :reversion, :filename do |_t, args|
299
- # rubocop:disable Style/IfUnlessModifier
300
- if args[:filename].nil?
301
- abort 'You must specify a migration name or version'
302
- end
303
-
304
- file = MigrationFile.find args[:filename]
305
- file.reversion
306
- end
307
-
308
- desc 'Disable migration'
309
- task :disable, :filename do |_t, args|
310
- if args[:filename].nil?
311
- abort 'You must specify a migration name or version'
312
- end
313
-
314
- file = MigrationFile.find args[:filename]
315
- file.disable
316
- end
317
-
318
- desc 'Enable migration'
319
- task :enable, :filename do |_t, args|
320
- if args[:filename].nil?
321
- abort 'You must specify a migration name or version'
322
- end
323
-
324
- file = MigrationFile.find args[:filename]
325
- file.enable
326
- end
327
-
328
- desc 'Show all migrations'
329
- task :list do |_t, _args|
330
- files = MigrationFile.find '*', only_one: false
331
- files.each(&:print)
332
- end
333
-
334
- desc 'Check applied migrations'
335
- task :check do
336
- applied_names = db_connection[:schema_migrations].select_map(:filename)
337
- applied = applied_names.map { |one| MigrationFile.new filename: one }
338
- existing = MigrationFile.find '*', only_one: false, disabled: false
339
- existing_names = existing.map(&:basename)
340
- a_not_e = applied.reject { |one| existing_names.include? one.basename }
341
- e_not_a = existing.reject { |one| applied_names.include? one.basename }
342
- if a_not_e.any?
343
- puts 'Applied, but not existing'
344
- a_not_e.each(&:print)
345
- puts "\n" if e_not_a.any?
346
- end
347
- if e_not_a.any?
348
- puts 'Existing, but not applied'
349
- e_not_a.each(&:print)
350
- end
351
- end
352
- end
353
-
354
- alias_task :migrate, 'migrations:run'
355
-
356
- desc 'Run seeds'
357
- task :seed do
358
- require 'sequel/extensions/seed'
359
- seeds_dir = File.join(DB_DIR, 'seeds')
360
-
361
- ## Doesn't support version yet
362
- puts 'Seeding latest'
363
- Sequel::Seeder.apply(db_connection, seeds_dir)
364
- end
365
-
366
- namespace :dumps do
367
- ## Class for single DB dump file
368
- class DumpFile
369
- DB_DUMP_TIMESTAMP = '%Y-%m-%d_%H-%M'
370
-
371
- DB_DUMP_TIMESTAMP_REGEXP_MAP = {
372
- 'Y' => '\d{4}',
373
- 'm' => '\d{2}',
374
- 'd' => '\d{2}',
375
- 'H' => '\d{2}',
376
- 'M' => '\d{2}'
377
- }.freeze
378
-
379
- missing_keys =
380
- DB_DUMP_TIMESTAMP.scan(/%(\w)/).flatten -
381
- DB_DUMP_TIMESTAMP_REGEXP_MAP.keys
382
-
383
- if missing_keys.any?
384
- raise "`DB_DUMP_TIMESTAMP_REGEXP_MAP` doesn't contain keys" \
385
- " #{missing_keys} for `DB_DUMP_TIMESTAMP`"
386
- end
387
-
388
- DB_DUMP_TIMESTAMP_REGEXP =
389
- DB_DUMP_TIMESTAMP_REGEXP_MAP
390
- .each_with_object(DB_DUMP_TIMESTAMP.dup) do |(key, value), result|
391
- result.gsub! "%#{key}", value
392
- end
393
-
394
- DB_DUMP_FORMATS = %w[custom plain].freeze
395
-
396
- DB_DUMP_EXTENSIONS = {
397
- 'plain' => '.sql',
398
- 'custom' => '.dump'
399
- }.freeze
400
-
401
- missing_formats = DB_DUMP_FORMATS.reject do |db_dump_format|
402
- DB_DUMP_EXTENSIONS[db_dump_format]
403
- end
404
-
405
- if missing_formats.any?
406
- raise "`DB_DUMP_EXTENSIONS` has no keys for #{missing_formats}" \
407
- ' from `DB_DUMP_FORMATS`'
408
- end
409
-
410
- regexp_escaped_db_dump_extensions =
411
- DB_DUMP_EXTENSIONS.values.map do |db_dump_extension|
412
- Regexp.escape(db_dump_extension)
413
- end
414
-
415
- DB_DUMP_REGEXP = /^
416
- #{DB_DUMPS_DIR}#{Regexp.escape(File::SEPARATOR)}
417
- #{DB_CONFIG[:database]}_#{DB_DUMP_TIMESTAMP_REGEXP}
418
- (#{regexp_escaped_db_dump_extensions.join('|')})
419
- $/xo
420
-
421
- def self.all
422
- Dir[File.join(DB_DUMPS_DIR, '*')]
423
- .select { |file| file.match?(DB_DUMP_REGEXP) }
424
- .map! { |file| new filename: file }
425
- .sort!
426
- end
427
-
428
- attr_reader :version, :timestamp, :format
429
-
430
- def initialize(filename: nil, format: 'custom')
431
- if filename
432
- @extension = File.extname(filename)
433
- @format = DB_DUMP_EXTENSIONS.key(@extension)
434
- self.version = filename[/#{DB_DUMP_TIMESTAMP_REGEXP}/o]
435
- else
436
- @format = format
437
- @extension = DB_DUMP_EXTENSIONS[@format]
438
- self.timestamp = Time.now
439
- end
440
- end
441
-
442
- def <=>(other)
443
- timestamp <=> other.timestamp
444
- end
445
-
446
- def to_s
447
- "#{readable_timestamp} #{format}"
448
- end
449
-
450
- def print
451
- puts to_s
452
- end
453
-
454
- def path
455
- File.join(
456
- DB_DUMPS_DIR, "#{DB_CONFIG[:database]}_#{version}#{@extension}"
457
- )
458
- end
459
-
460
- private
461
-
462
- def version=(value)
463
- @version = value
464
- @timestamp = Time.strptime(version, DB_DUMP_TIMESTAMP)
465
- end
466
-
467
- def timestamp=(value)
468
- @timestamp = value
469
- @version = timestamp.strftime(DB_DUMP_TIMESTAMP)
470
- end
471
-
472
- def readable_timestamp
473
- datetime = timestamp.strftime('%F %R')
474
- "\e[36m#{datetime}\e[0m"
475
- end
476
- end
477
-
478
- desc 'Make DB dump'
479
- task :create, :format do |_task, args|
480
- dump_format =
481
- if args[:format]
482
- DumpFile::DB_DUMP_FORMATS.find do |db_dump_format|
483
- db_dump_format.start_with? args[:format]
484
- end
485
- else
486
- DumpFile::DB_DUMP_FORMATS.first
487
- end
488
-
489
- update_pgpass
490
-
491
- filename = DumpFile.new(format: dump_format).path
492
- sh "mkdir -p #{DB_DUMPS_DIR}"
493
- sh "pg_dump #{DB_ACCESS} -F#{dump_format.chr}" \
494
- " #{DB_CONFIG[:database]} > #{filename}"
495
- end
496
-
497
- desc 'Restore DB dump'
498
- task :restore, :step do |_task, args|
499
- step = args[:step] ? Integer(args[:step]) : -1
500
-
501
- update_pgpass
502
-
503
- dump_file = DumpFile.all[step]
504
-
505
- abort 'Dump file not found' unless dump_file
506
-
507
- if Question.new("Restore #{dump_file} ?", %w[yes no]).answer == 'no'
508
- abort 'Okay'
509
- end
510
-
511
- Rake::Task['db:dump'].invoke
512
-
513
- case dump_file.format
514
- when 'custom'
515
- sh "pg_restore #{DB_ACCESS} -n public -d #{DB_CONFIG[:database]}" \
516
- " #{dump_file.path} --jobs=4 --clean --if-exists"
517
- when 'plain'
518
- Rake::Task['db:drop'].invoke
519
- Rake::Task['db:create'].invoke
520
- sh "psql #{DB_ACCESS} #{DB_CONFIG[:database]} < #{dump_file.path}"
521
- else
522
- raise 'Unknown DB dump file format'
523
- end
524
- end
525
-
526
- desc 'List DB dumps'
527
- task :list do
528
- DumpFile.all.each(&:print)
529
- end
530
- end
531
-
532
- alias_task :dumps, 'dumps:list'
533
- alias_task :dump, 'dumps:create'
534
- alias_task :restore, 'dumps:restore'
535
-
536
- desc 'Create empty DB'
537
- task :create do
538
- sh "createdb -U postgres #{DB_CONFIG[:database]} -O #{DB_CONFIG[:user]}"
539
- DB_EXTENSIONS.each do |db_extension|
540
- sh "psql -U postgres -c 'CREATE EXTENSION #{db_extension}'" \
541
- " #{DB_CONFIG[:database]}"
542
- end
543
- end
544
-
545
- desc 'Drop DB'
546
- task :drop, :force do |_task, args|
547
- case Question.new("Drop #{DB_CONFIG[:database]} ?", %w[yes no]).answer
548
- when 'no'
549
- abort 'OK'
550
- end
551
-
552
- Rake::Task['db:dump'].invoke unless args[:force]
553
- sh "dropdb #{DB_ACCESS} #{DB_CONFIG[:database]}"
554
- end
555
- end
556
- end
557
-
558
- namespace :locales do
559
- CROWDIN_CONFIG_FILE = 'config/crowdin.yml'
560
-
561
- desc 'Upload files for translation'
562
- task :upload do
563
- sh "crowdin --config #{CROWDIN_CONFIG_FILE} upload sources"
564
- end
565
-
566
- desc 'Download translated files'
567
- task :download do
568
- sh "crowdin --config #{CROWDIN_CONFIG_FILE} download translations"
569
- end
570
-
571
- desc 'Check locales'
572
- task :check do
573
- require 'yaml'
574
- require 'json'
575
-
576
- ## Class for Locale file
577
- class Locale
578
- attr_reader :code, :hash
579
-
580
- EXT = '.yml'
581
-
582
- def self.load(locales_dir = 'locales')
583
- Dir[File.join(__dir__, locales_dir, "*#{EXT}")].map do |file|
584
- new File.basename(file, EXT), YAML.load_file(file)
585
- end
586
- end
587
-
588
- def initialize(code, hash)
589
- @code = code
590
- @hash = hash
591
- end
592
-
593
- class HashCompare
594
- def initialize(hash, other_hash)
595
- @hash = hash
596
- @other_hash = other_hash
597
- @diff = {}
598
- end
599
-
600
- def different_keys
601
- @hash.each_pair do |key, value|
602
- other_value = @other_hash[key]
603
- if value.is_a?(Hash) && other_value.is_a?(Hash)
604
- add_differences_in_hash(value, other_value, key)
605
- elsif value.is_a?(Array) && other_value.is_a?(Array)
606
- add_differences_in_array(value, other_value, key)
607
- elsif other_value.nil? || value.class != other_value.class
608
- add_difference(value, key)
609
- end
610
- end
611
- @diff
612
- end
613
-
614
- private
615
-
616
- def add_difference(difference, key)
617
- @diff[key] = difference unless difference.empty?
618
- end
619
-
620
- def add_differences_in_hash(hash, other_hash, key)
621
- difference = self.class.new(hash, other_hash).different_keys
622
- add_difference(difference, key)
623
- end
624
-
625
- def add_differences_in_array(array, other_array, key)
626
- difference =
627
- if array.size != other_array.size
628
- array
629
- else
630
- differences_in_array(array, other_array)
631
- end
632
- add_difference(difference, key)
633
- end
634
-
635
- def differences_in_array(array, other_array)
636
- array.each_with_object([]).with_index do |(object, diff), i|
637
- other_object = other_array[i]
638
- if object.is_a?(Hash) && other_object.is_a?(Hash)
639
- difference = self.class.new(object, other_object).different_keys
640
- diff << difference unless difference.empty?
641
- end
642
- end.compact
643
- end
644
- end
645
-
646
- def diff(other)
647
- HashCompare.new(hash, other.hash).different_keys
648
- end
649
- end
650
-
651
- locales = Locale.load
652
-
653
- def compare_locales(locale, other_locale)
654
- puts "#{locale.code.upcase} -> #{other_locale.code.upcase}:\n\n"
655
- puts locale.diff(other_locale).to_yaml
656
- end
657
-
658
- locales.each_with_index do |locale, ind|
659
- locales[ind..-1].each do |other_locale|
660
- next if locale == other_locale
661
- compare_locales(locale, other_locale)
662
- compare_locales(other_locale, locale)
663
- end
664
- end
665
- end
666
- end
667
-
668
- namespace :static do
669
- desc 'Check static files'
670
- task :check do
671
- Dir[File.join(__dir__, 'public/**/*')].each do |file|
672
- basename = File.basename(file)
673
- grep_options = '--exclude-dir={\.git,log} --color=always'
674
- found = `grep -ir '#{basename}' ./ #{grep_options}`
675
- next unless found.empty? && File.dirname(file) != @skipping_dir
676
- filename = file.sub(__dir__, '')
677
- case Question.new("Delete #{filename} ?", %w[yes no skip]).answer
678
- when 'yes'
679
- `git rm #{file.gsub(' ', '\ ')}`
680
- when 'skip'
681
- @skipping_dir = File.dirname(file)
682
- end
683
- end
684
- end
685
- end
686
-
687
- namespace :config do
688
- desc 'Check config files'
689
- task :check do
690
- example_suffix = '.example'
691
- Dir[
692
- File.join(__dir__, "config/**/*#{example_suffix}*")
693
- ].each do |example_filename|
694
- regular_filename = example_filename.sub(example_suffix, '')
695
- if File.exist? regular_filename
696
- if File.mtime(example_filename) > File.mtime(regular_filename)
697
- example_basename = File.basename example_filename
698
- regular_basename = File.basename regular_filename
699
-
700
- ask_what_to_do = proc do
701
- case answer = Question.new(
702
- "\e[32m\e[1m#{example_basename}\e[22m\e[0m was modified after" \
703
- " \e[31m\e[1m#{regular_basename}\e[22m\e[0m." \
704
- " Do you want to edit \e[31m\e[1m#{regular_basename}\e[22m\e[0m ?",
705
- %w[yes no show quit]
706
- ).answer
707
- when 'yes'
708
- edit_file regular_filename
709
- when 'show'
710
- show_diff regular_filename, example_filename
711
- answer = ask_what_to_do.call
712
- end
713
-
714
- answer
715
- end
716
-
717
- break if ask_what_to_do.call == 'quit'
718
- end
719
- else
720
- FileUtils.cp example_filename, regular_filename
721
- edit_file regular_filename
722
- end
723
- end
724
- end
725
- end
726
-
727
- desc 'Start interactive console'
728
- task :console, :environment do |_t, args|
729
- require 'rack/console'
730
-
731
- args = args.to_hash
732
- args[:environment] ||= 'development'
733
- ARGV.clear
734
- Rack::Console.new(args).start
735
- end
736
-
737
- desc 'Start psql'
738
- task :psql do
739
- update_pgpass
740
- sh "psql #{DB_ACCESS} #{DB_CONFIG[:database]}"
741
- end
742
-
743
- ## Command for update server
744
- desc 'Update from git'
745
- task :update, :branch, :without_restart do |_t, args|
746
- args = args.to_hash
747
- args[:branch] ||= :master
748
- server = './server'
749
- sh "git checkout #{args[:branch]}"
750
- sh "git pull origin #{args[:branch]}"
751
- next if args[:without_restart]
752
- sh 'bundle check || bundle install'
753
- sh "#{server} stop"
754
- sh 'rake config:check'
755
- sh 'rake db:migrate'
756
- sh "#{server} start"
757
- end
758
-
759
- ## Command before creating new branch
760
- desc 'Fetch origin and rebase branch from master'
761
- task :rebase do
762
- sh 'git fetch origin'
763
- sh 'git rebase origin/master'
764
- end
765
-
766
- ## Command for deploy code from git to server
767
- ## @example rake deploy
768
- ## Update from git with migrations and restart (for .rb and .erb files update)
769
- ## @example rake deploy[true]
770
- ## Update from git without migrations and restart (for static files update)
771
- desc 'Deploy to production server'
772
- task :deploy, :without_restart do |_t, args|
773
- servers = YAML.load_file File.join(__dir__, 'config/deploy.yml')
774
- rake_command = "rake update[master#{',true' if args.without_restart}]"
775
- servers.each do |server|
776
- update_command = "cd #{server[:path]} && #{rake_command}"
777
- sh "ssh -t #{server[:ssh]} 'bash --login -c \"#{update_command}\"'"
778
- end
779
- end
780
-
781
- namespace :assets do
782
- assets_dir = File.join __dir__, 'assets'
783
- public_dir = File.join __dir__, 'public'
784
-
785
- styles_input_dir = File.join assets_dir, 'styles'
786
- styles_input_file = File.join styles_input_dir, 'main.scss'
787
- styles_output_dir = File.join public_dir, 'styles'
788
- styles_output_file = File.join styles_output_dir, 'main.css'
789
-
790
- scripts_input_dir = File.join assets_dir, 'scripts'
791
- scripts_input_file = File.join scripts_input_dir, 'app.js'
792
- scripts_output_dir = File.join public_dir, 'scripts', 'app', 'compiled'
793
- scripts_output_file = 'app.js'
794
-
795
- namespace :build do
796
- desc 'Build all assets'
797
- task all: %w[assets:build:styles assets:build:scripts]
798
-
799
- desc 'Build styles assets'
800
- task :styles do
801
- next unless File.exist? styles_input_file
802
- FileUtils.mkdir_p styles_output_dir
803
- sh "sass #{styles_input_file} #{styles_output_file} -t compact"
804
- end
805
-
806
- desc 'Build scripts assets'
807
- task :scripts do
808
- next unless File.exist? scripts_input_file
809
- sh 'yarn run webpack' \
810
- " --entry #{scripts_input_file}" \
811
- " --output-path #{scripts_output_dir}" \
812
- " --output-filename #{scripts_output_file}"
813
- end
814
- end
815
-
816
- alias_task :build, 'build:all'
817
-
818
- namespace :watch do
819
- desc 'Watch for styles assets'
820
- task :styles do
821
- sh "sass --watch #{styles_input_dir}:#{styles_output_dir} -t compact"
822
- end
823
- end
824
- end