seiten 1.1.0 → 1.3.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/lib/seiten/navigation.rb +10 -0
- data/lib/seiten/railtie.rb +3 -0
- data/lib/seiten/routes_helper.rb +79 -4
- data/lib/seiten/version.rb +1 -1
- data/lib/seiten.rb +69 -6
- data/test/dummy/app/controllers/guide/pages_controller.rb +5 -0
- data/test/dummy/config/locales/guide.de.yml +14 -0
- data/test/dummy/config/locales/guide.en.yml +15 -0
- data/test/dummy/config/navigations/guide.yml +7 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/log/development.log +0 -0
- data/test/dummy/log/test.log +19452 -0
- data/test/integration/localized_routes_test.rb +71 -0
- data/test/lib/seiten/html/helpers_test.rb +4 -3
- data/test/lib/seiten/i18n_navigation_test.rb +57 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6289826464b8f2c34c78a4d469fe26e0e772c79f67adf29f38dc3465c9d35ed6
|
|
4
|
+
data.tar.gz: fb131648fd8558fadf4e3b17f27aa1c24e4b4d4b59a13fcf0d173632c561c2a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b69aa165a103bd083dba0cb57c6c34078b139d56dba5a0e83679e4d654bd863e1e245d7dc247e429f6f3bf393818faa450f6c59fe9ad2255498e773427eb865c
|
|
7
|
+
data.tar.gz: '0643406590aa1ae9a182222163f903764807699d460cfa9652f3a56e235685bbb4c50e440701cae5a519f1f0a2afb5c5ba680ea41d74bf9098257737bd39242b'
|
data/lib/seiten/navigation.rb
CHANGED
|
@@ -32,6 +32,16 @@ module Seiten
|
|
|
32
32
|
page_collection
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# The navigation's own title and slug, from I18n like its pages' (<i18n_scope>.<name>.title and
|
|
36
|
+
# .slug; the slug defaults to the parameterized title). Its localized routes start with the slug.
|
|
37
|
+
def title
|
|
38
|
+
I18n.t('title', scope: [Seiten.config[:i18n_scope], name], locale: locale, default: nil)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def slug
|
|
42
|
+
I18n.t('slug', scope: [Seiten.config[:i18n_scope], name], locale: locale, default: nil) || title&.parameterize
|
|
43
|
+
end
|
|
44
|
+
|
|
35
45
|
def pages=(pages_array)
|
|
36
46
|
page_collection.pages = pages_array.map { |page| page.navigation_id = id; page }
|
|
37
47
|
# return page_collection
|
data/lib/seiten/railtie.rb
CHANGED
data/lib/seiten/routes_helper.rb
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
1
|
class ActionDispatch::Routing::Mapper
|
|
2
|
+
# Routes a navigation's pages. Draw it after your other routes, which it would swallow.
|
|
3
|
+
#
|
|
4
|
+
# A navigation built in every locale from one file (config/navigations/help.yml) gets a route per
|
|
5
|
+
# locale, under the locale and the navigation's slug (its I18n title or slug, see
|
|
6
|
+
# Navigation#slug), to Help::PagesController#show, named help_page_<locale>:
|
|
7
|
+
#
|
|
8
|
+
# seiten :help # /en/help(/*slug), /de/hilfe(/*slug)
|
|
9
|
+
#
|
|
10
|
+
# Every page gets a link helper named after the navigation and its key, and the navigation one that
|
|
11
|
+
# takes a key; both link to the page in I18n.locale or the `locale:` option:
|
|
12
|
+
#
|
|
13
|
+
# help_account_manage_path # => "/en/help/your-account/manage-your-account"
|
|
14
|
+
# help_account_manage_url(locale: :de) # => "http://…/de/hilfe/dein-account/account-verwalten"
|
|
15
|
+
# help_page_path(page.key) # the same, for a key in a variable; unknown keys raise
|
|
16
|
+
# help_page_path # => "/en/help", the navigation's root
|
|
17
|
+
#
|
|
18
|
+
# `to:`, `as:` (of the navigation's helper) and `path:` (the same in every locale) replace the
|
|
19
|
+
# defaults, `prefix: false` names the page helpers without the navigation (account_manage_path), and
|
|
20
|
+
# `localized: false` draws one catch-all route instead.
|
|
21
|
+
#
|
|
22
|
+
# Other navigations get one catch-all route to seiten/pages#show (or `to:`) that sets
|
|
23
|
+
# params[:navigation_id] (except for the application navigation); `localized: true` routes them per
|
|
24
|
+
# locale too.
|
|
25
|
+
#
|
|
26
|
+
# seiten # /*slug, the application navigation
|
|
27
|
+
# scope(:help) { seiten :help }
|
|
2
28
|
def seiten(*resources)
|
|
3
29
|
options = resources.extract_options!
|
|
4
|
-
options
|
|
30
|
+
localized, path, prefix = options.extract!(:localized, :path, :prefix).values_at(:localized, :path, :prefix)
|
|
5
31
|
|
|
6
32
|
resources.each do |resource|
|
|
7
|
-
|
|
33
|
+
name = resource.to_s
|
|
34
|
+
|
|
35
|
+
if localized.nil? ? Seiten.i18n_navigation?(name) : localized
|
|
36
|
+
seiten_localized_routes(name, options.dup, path: path, prefix: prefix.nil? ? name : prefix)
|
|
37
|
+
next
|
|
38
|
+
end
|
|
8
39
|
|
|
40
|
+
resource_options = options.dup
|
|
41
|
+
resource_options[:to] ||= 'seiten/pages#show'
|
|
9
42
|
resource_options[:as] ||= resource == :application ? :seiten_page : "seiten_#{resource}_page"
|
|
10
43
|
|
|
11
|
-
resource_options[:defaults]
|
|
12
|
-
resource_options[:defaults][:navigation_id] =
|
|
44
|
+
resource_options[:defaults] = resource_options[:defaults].to_h.dup
|
|
45
|
+
resource_options[:defaults][:navigation_id] = name unless resource == :application
|
|
13
46
|
resource_options[:defaults][:slug] = ''
|
|
14
47
|
|
|
15
48
|
# NOTE: See https://github.com/rails/rails/issues/31228
|
|
@@ -18,4 +51,46 @@ class ActionDispatch::Routing::Mapper
|
|
|
18
51
|
get '(*slug)', **resource_options
|
|
19
52
|
end
|
|
20
53
|
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def seiten_localized_routes(name, options, path:, prefix:)
|
|
58
|
+
navigations = Seiten::Navigation.where(name: name)
|
|
59
|
+
raise ArgumentError, "No #{name} navigation in #{Seiten.config[:config_dir]}" if navigations.empty?
|
|
60
|
+
|
|
61
|
+
route = (options.delete(:as) || "#{name}_page").to_s
|
|
62
|
+
options = { to: "#{name}/pages#show", format: false, **options }
|
|
63
|
+
defaults = { **options.delete(:defaults).to_h, navigation_id: name, slug: '' }
|
|
64
|
+
|
|
65
|
+
navigations.each do |navigation|
|
|
66
|
+
locale_path = [navigation.locale, path || navigation.slug].compact_blank.join('/')
|
|
67
|
+
get "#{locale_path}(/*slug)", **options, as: "#{route}_#{navigation.locale}", defaults: defaults.merge(locale: navigation.locale)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
keys = navigations.first.pages.all.filter_map(&:key)
|
|
71
|
+
helpers = keys.map { |key| [prefix.presence, *key.split('.')].compact.join('_') }
|
|
72
|
+
duplicate = [route, *helpers].tally.find { |_, count| count > 1 }&.first
|
|
73
|
+
raise ArgumentError, "The #{name} navigation would define #{duplicate}_path twice" if duplicate
|
|
74
|
+
|
|
75
|
+
seiten_link_helper(route, name, route)
|
|
76
|
+
keys.zip(helpers).each { |key, helper| seiten_link_helper(helper, name, route, key) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# <helper>_path and <helper>_url: the page with `page_key` (else the key given first, else the
|
|
80
|
+
# navigation's root) in the navigation of I18n.locale or the `locale:` option.
|
|
81
|
+
def seiten_link_helper(helper, navigation_name, route, page_key = nil)
|
|
82
|
+
@set.add_url_helper(helper, {}) do |*args|
|
|
83
|
+
options = args.extract_options!
|
|
84
|
+
key = page_key || args.first
|
|
85
|
+
locale = (options[:locale] || I18n.locale).to_s
|
|
86
|
+
|
|
87
|
+
navigation = Seiten::Navigation.find_by(name: navigation_name, locale: locale)
|
|
88
|
+
raise ArgumentError, "No #{navigation_name} navigation in #{locale}" unless navigation
|
|
89
|
+
|
|
90
|
+
page = navigation.pages.find_by(key: key.to_s) if key
|
|
91
|
+
raise ArgumentError, "No page #{key} in the #{navigation_name} navigation" if key && !page
|
|
92
|
+
|
|
93
|
+
{ use_route: "#{route}_#{locale}", slug: page&.slug.to_s, **options.except(:locale) }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
21
96
|
end
|
data/lib/seiten/version.rb
CHANGED
data/lib/seiten.rb
CHANGED
|
@@ -21,6 +21,7 @@ module Seiten
|
|
|
21
21
|
config_dir: File.join('config', 'navigations'),
|
|
22
22
|
pages_dir: File.join('app', 'pages'),
|
|
23
23
|
root_page: 'home',
|
|
24
|
+
i18n_scope: 'navigations',
|
|
24
25
|
default_layout: 'application',
|
|
25
26
|
html: {
|
|
26
27
|
navigation: {
|
|
@@ -45,6 +46,9 @@ module Seiten
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
@navigations = []
|
|
49
|
+
@i18n_files = {}
|
|
50
|
+
@i18n_navigations = nil
|
|
51
|
+
@i18n_lock = Mutex.new
|
|
48
52
|
|
|
49
53
|
class << self
|
|
50
54
|
def config
|
|
@@ -52,6 +56,7 @@ module Seiten
|
|
|
52
56
|
end
|
|
53
57
|
|
|
54
58
|
def navigations
|
|
59
|
+
build_i18n_navigations unless @i18n_navigations
|
|
55
60
|
@navigations
|
|
56
61
|
end
|
|
57
62
|
|
|
@@ -59,14 +64,72 @@ module Seiten
|
|
|
59
64
|
@navigations = navigations
|
|
60
65
|
end
|
|
61
66
|
|
|
67
|
+
# config/navigations/<name>.<locale>.yml holds one locale's navigation; <name>.yml holds the pages
|
|
68
|
+
# of every locale, with titles and slugs from I18n (built on first use, see #i18n_pages).
|
|
62
69
|
def initialize_navigations
|
|
63
70
|
Dir[File.join(Rails.root, Seiten.config[:config_dir], "*.yml")].each do |file|
|
|
64
|
-
id
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
id = File.basename(file, '.yml')
|
|
72
|
+
if id.include?('.')
|
|
73
|
+
name, locale = id.split('.', 2)
|
|
74
|
+
navigation = Seiten::Navigation.new(name: name, locale: locale, dir: File.join(Rails.root, Seiten.config[:pages_dir], name, locale))
|
|
75
|
+
navigation.page_collection.build(pages: YAML.load_file(navigation.config))
|
|
76
|
+
@navigations << navigation
|
|
77
|
+
else
|
|
78
|
+
@i18n_files[id] = file
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Whether the navigation is built in every locale from one file (config/navigations/<name>.yml).
|
|
84
|
+
def i18n_navigation?(name)
|
|
85
|
+
@i18n_files.key?(name.to_s)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Drops the navigations built from I18n, so they're built again with the current translations
|
|
89
|
+
# (on every code or translation reload in development).
|
|
90
|
+
def reset_i18n_navigations!
|
|
91
|
+
@i18n_lock.synchronize do
|
|
92
|
+
@navigations -= @i18n_navigations if @i18n_navigations
|
|
93
|
+
@i18n_navigations = nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def build_i18n_navigations
|
|
100
|
+
@i18n_lock.synchronize do
|
|
101
|
+
return if @i18n_navigations
|
|
102
|
+
|
|
103
|
+
@i18n_navigations = @i18n_files.flat_map do |name, file|
|
|
104
|
+
structure = YAML.load_file(file)
|
|
105
|
+
I18n.available_locales.map do |locale|
|
|
106
|
+
navigation = Seiten::Navigation.new(name: name, locale: locale, config: file, dir: File.join(Rails.root, Seiten.config[:pages_dir], name, locale.to_s))
|
|
107
|
+
navigation.page_collection.build(pages: i18n_pages(structure, locale, [Seiten.config[:i18n_scope], name]))
|
|
108
|
+
navigation
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
@navigations.concat(@i18n_navigations)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The pages of a locale: every page names its `key`; its title and slug are the translations
|
|
116
|
+
# <i18n_scope>.<navigation>.<parent keys…>.<key>.title and .slug (the slug defaults to the
|
|
117
|
+
# parameterized title). The page's key becomes its full key, e.g. "account.manage", which is the
|
|
118
|
+
# same page in every locale.
|
|
119
|
+
def i18n_pages(nodes, locale, scope)
|
|
120
|
+
nodes.map do |node|
|
|
121
|
+
node = node.dup
|
|
122
|
+
key = node['key'] or raise Errors::PageError, "Every page of #{scope[1]}.yml needs a key"
|
|
123
|
+
page_scope = [*scope, key.to_s]
|
|
124
|
+
title = I18n.t('title', scope: page_scope, locale: locale, default: nil)
|
|
125
|
+
slug = I18n.t('slug', scope: page_scope, locale: locale, default: nil)
|
|
126
|
+
raise Errors::PageError, "#{page_scope.join('.')} needs a title or a slug in #{locale}" unless title || slug || node.key?('url')
|
|
127
|
+
|
|
128
|
+
node['title'] = title
|
|
129
|
+
node['url'] = slug if slug && !node.key?('url')
|
|
130
|
+
node['key'] = page_scope.drop(2).join('.')
|
|
131
|
+
node['nodes'] = i18n_pages(node['nodes'], locale, page_scope) if node['nodes']
|
|
132
|
+
node
|
|
70
133
|
end
|
|
71
134
|
end
|
|
72
135
|
end
|
data/test/dummy/config/routes.rb
CHANGED
|
File without changes
|