flame 5.0.0.rc5 → 5.0.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
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