seiten 1.0.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ca1b6ad9f5f4c830d23928eccc0ca333069d8f88412022725c495e9315b6ea4
4
- data.tar.gz: 7ad22ccd05fa7571b4c6e858cfdf03eb9e9ecfdd8da2440e0ce4d1413e32c7cc
3
+ metadata.gz: '09cdd9e8323ed21cafe0eb5f92544ee05a8e78bc86b421a0fb2bfc6c865ecd8b'
4
+ data.tar.gz: f9860ca8c46a06a36136a992e078be502c81fba45373c5feae6682a5fb431e8e
5
5
  SHA512:
6
- metadata.gz: cc3ea9c6fed75f3887f5d6827aeca2ca06359d12c389dcecc2dcdea78dba87840a328c399e02a27430a46bde3ba96b320e1717712fac0169e8a46ee45adf9138
7
- data.tar.gz: da5ae1af8ed70e7fa3b955cb2868324345c5f2aec0245de4b484135d0dcb6886002ffbb486f2281bba185e20619e31bd5375b5b5d2ccf3dde18bd821b0af8f96
6
+ metadata.gz: 4ab559c9fb74b3373ddb9b31485dd4d78b56bcab9dbd1c1e863bafcf0a60c1a4cfb502728ea763db6e7b81ca9b1ff9d3c17d529fcf5b7b8c0bf226cca5316e6d
7
+ data.tar.gz: 4525be586db9bba6d0c8b1589d7297680b411523874c4fae5ed73dbb3ebff0445a65c7a27c22c65612c29c2dd525a30de5c845482c2cafef044824c8e7acc426
data/lib/seiten/page.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Seiten
4
4
  class Page
5
- attr_accessor :navigation_id, :id, :parent_id, :title, :slug, :refer, :data, :html_options
5
+ attr_accessor :navigation_id, :id, :parent_id, :title, :slug, :refer, :key, :data, :html_options
6
6
  attr_writer :layout
7
7
 
8
8
  # initialize Page object with attributes
@@ -13,6 +13,7 @@ module Seiten
13
13
  @title = options[:title]
14
14
  @slug = options[:slug]
15
15
  @refer = options[:refer]
16
+ @key = options[:key]&.to_s
16
17
  @layout = options[:layout]
17
18
  @data = options[:data].each_with_object({}){|(k,v), h| h[k.to_sym] = v} if options[:data]
18
19
  @data ||= {}
@@ -71,6 +72,37 @@ module Seiten
71
72
  end
72
73
  end
73
74
 
75
+ # The page's place in its navigation tree: the index among its siblings, from the root down
76
+ # (e.g. [2, 0] for the first child of the third top-level page).
77
+ def position
78
+ @position ||= (parent ? parent.position : []) + [navigation.pages.where(parent_id: parent_id).index(self)]
79
+ end
80
+
81
+ # The same page in the navigation of the same name in another locale: the page at the same
82
+ # position, or, for a page with a `key`, the page with the same key. Nil when there is none.
83
+ def translation(locale)
84
+ locale = locale.to_s
85
+ return self if locale == navigation.locale
86
+
87
+ other = Seiten::Navigation.find_by(name: navigation.name, locale: locale)
88
+ return unless other
89
+
90
+ if key
91
+ other.pages.find_by(key: key)
92
+ else
93
+ page = other.pages.all.find { |candidate| candidate.position == position }
94
+ page unless page&.key
95
+ end
96
+ end
97
+
98
+ # This page in every locale that has it, by locale (including its own).
99
+ def translations
100
+ Seiten::Navigation.where(name: navigation.name).each_with_object({}) do |other, translations|
101
+ page = translation(other.locale)
102
+ translations[other.locale] = page if page
103
+ end
104
+ end
105
+
74
106
  # get children of page
75
107
  def children
76
108
  navigation.pages.where(parent_id: id)
@@ -3,5 +3,8 @@ module Seiten
3
3
  config.action_dispatch.rescue_responses.merge!(
4
4
  'Seiten::Errors::RoutingError' => :not_found
5
5
  )
6
+
7
+ # Navigations built from I18n follow reloaded translations in development.
8
+ config.to_prepare { Seiten.reset_i18n_navigations! }
6
9
  end
7
10
  end
@@ -15,7 +15,7 @@ class ActionDispatch::Routing::Mapper
15
15
  # NOTE: See https://github.com/rails/rails/issues/31228
16
16
  resource_options[:constraints] ||= ->(req) { req.path.exclude? 'rails/active_storage' }
17
17
 
18
- get '(*slug)', resource_options
18
+ get '(*slug)', **resource_options
19
19
  end
20
20
  end
21
21
  end
@@ -1,3 +1,3 @@
1
1
  module Seiten
2
- VERSION = "1.0.2"
2
+ VERSION = "1.2.0"
3
3
  end
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,67 @@ 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 = File.basename(file, '.yml')
65
- name = id.gsub(/\..*/, '')
66
- locale = id.gsub(/.*\./, '')
67
- navigation = Seiten::Navigation.new(name: name, locale: locale, dir: File.join(Rails.root, Seiten.config[:pages_dir], name, locale))
68
- navigation.page_collection.build(pages: YAML.load_file(navigation.config))
69
- Seiten.navigations << navigation
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
+ # Drops the navigations built from I18n, so they're built again with the current translations
84
+ # (on every code or translation reload in development).
85
+ def reset_i18n_navigations!
86
+ @i18n_lock.synchronize do
87
+ @navigations -= @i18n_navigations if @i18n_navigations
88
+ @i18n_navigations = nil
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def build_i18n_navigations
95
+ @i18n_lock.synchronize do
96
+ return if @i18n_navigations
97
+
98
+ @i18n_navigations = @i18n_files.flat_map do |name, file|
99
+ structure = YAML.load_file(file)
100
+ I18n.available_locales.map do |locale|
101
+ navigation = Seiten::Navigation.new(name: name, locale: locale, config: file, dir: File.join(Rails.root, Seiten.config[:pages_dir], name, locale.to_s))
102
+ navigation.page_collection.build(pages: i18n_pages(structure, locale, [Seiten.config[:i18n_scope], name]))
103
+ navigation
104
+ end
105
+ end
106
+ @navigations.concat(@i18n_navigations)
107
+ end
108
+ end
109
+
110
+ # The pages of a locale: every page names its `key`; its title and slug are the translations
111
+ # <i18n_scope>.<navigation>.<parent keys…>.<key>.title and .slug (the slug defaults to the
112
+ # parameterized title). The page's key becomes its full key, e.g. "account.manage", which is the
113
+ # same page in every locale.
114
+ def i18n_pages(nodes, locale, scope)
115
+ nodes.map do |node|
116
+ node = node.dup
117
+ key = node['key'] or raise Errors::PageError, "Every page of #{scope[1]}.yml needs a key"
118
+ page_scope = [*scope, key.to_s]
119
+ title = I18n.t('title', scope: page_scope, locale: locale, default: nil)
120
+ slug = I18n.t('slug', scope: page_scope, locale: locale, default: nil)
121
+ raise Errors::PageError, "#{page_scope.join('.')} needs a title or a slug in #{locale}" unless title || slug || node.key?('url')
122
+
123
+ node['title'] = title
124
+ node['url'] = slug if slug && !node.key?('url')
125
+ node['key'] = page_scope.drop(2).join('.')
126
+ node['nodes'] = i18n_pages(node['nodes'], locale, page_scope) if node['nodes']
127
+ node
70
128
  end
71
129
  end
72
130
  end
@@ -12,9 +12,5 @@ Rails.application.config.action_controller.per_form_csrf_tokens = false
12
12
  # Enable origin-checking CSRF mitigation. Previous versions had false.
13
13
  Rails.application.config.action_controller.forgery_protection_origin_check = false
14
14
 
15
- # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
16
- # Previous versions had false.
17
- ActiveSupport.to_time_preserves_timezone = false
18
-
19
15
  # Require `belongs_to` associations by default. Previous versions had false.
20
16
  # Rails.application.config.active_record.belongs_to_required_by_default = false
@@ -0,0 +1,13 @@
1
+ de:
2
+ navigations:
3
+ guide:
4
+ start:
5
+ title: Start
6
+ account:
7
+ title: Dein Account
8
+ manage:
9
+ title: Account verwalten
10
+ security:
11
+ title: Sicherheit
12
+ billing:
13
+ title: Rechnungen
@@ -0,0 +1,14 @@
1
+ en:
2
+ navigations:
3
+ guide:
4
+ start:
5
+ title: Start
6
+ account:
7
+ title: Your account
8
+ manage:
9
+ title: Manage your account
10
+ security:
11
+ title: Security
12
+ slug: account-security
13
+ billing:
14
+ title: Billing
@@ -0,0 +1,7 @@
1
+ - key: start
2
+ url: '/'
3
+ - key: account
4
+ nodes:
5
+ - key: manage
6
+ - key: security
7
+ - key: billing