moirai 0.1.0 → 0.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/.github/workflows/test.yml +15 -6
- data/CHANGELOG.md +21 -0
- data/Gemfile +12 -1
- data/README.md +63 -27
- data/app/assets/config/moirai_manifest.js +0 -1
- data/app/assets/javascripts/moirai_translation_controller.js +41 -0
- data/app/assets/stylesheets/moirai/application.css +2 -0
- data/app/assets/stylesheets/translation_files.css +22 -0
- data/app/controllers/moirai/translation_files_controller.rb +66 -18
- data/app/controllers/moirai/translations_controller.rb +7 -0
- data/app/models/moirai/change.rb +15 -0
- data/app/models/moirai/key_finder.rb +55 -0
- data/app/models/moirai/translation.rb +9 -5
- data/app/models/moirai/translation_dumper.rb +40 -13
- data/app/views/layouts/moirai/application.html.erb +28 -1
- data/app/views/moirai/translation_files/_form.html.erb +3 -3
- data/app/views/moirai/translation_files/index.html.erb +1 -1
- data/app/views/moirai/translation_files/show.html.erb +11 -5
- data/app/views/moirai/translations/index.html.erb +7 -0
- data/bin/check +3 -1
- data/config/routes.rb +2 -1
- data/lib/generators/moirai/install_generator.rb +27 -2
- data/lib/generators/moirai/migration_generator.rb +5 -1
- data/lib/generators/moirai/templates/make_moirai_translations_file_path_not_required.rb.erb +5 -0
- data/lib/i18n/backend/moirai.rb +1 -53
- data/lib/i18n/extensions/i18n.rb +13 -0
- data/lib/moirai/engine.rb +8 -6
- data/lib/moirai/pull_request_creator.rb +42 -33
- data/lib/moirai/version.rb +1 -1
- data/lib/moirai.rb +2 -1
- data/moirai.gemspec +3 -6
- metadata +12 -77
- data/app/assets/javascripts/moirai/application.js +0 -2
- data/app/assets/javascripts/moirai/controllers/moirai_translation_controller.js +0 -27
- data/app/assets/javascripts/moirai/stimulus/init.js +0 -5
- /data/lib/generators/moirai/templates/{migration.rb.erb → create_moirai_translations.rb.erb} +0 -0
@@ -1,24 +1,51 @@
|
|
1
1
|
module Moirai
|
2
2
|
class TranslationDumper
|
3
|
+
def initialize
|
4
|
+
@key_finder = KeyFinder.new
|
5
|
+
end
|
6
|
+
|
7
|
+
# @return Array[Moirai::Change]
|
3
8
|
def call
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
translations_by_file_path = group_translations_by_file_path
|
10
|
+
changes = []
|
11
|
+
translations_by_file_path.each do |file_path, translations|
|
12
|
+
relative_file_path = Pathname.new(file_path).relative_path_from(Rails.root)
|
13
|
+
changes << Change.new(relative_file_path, get_updated_file_contents(file_path, translations))
|
14
|
+
end
|
15
|
+
changes
|
16
|
+
end
|
17
|
+
|
18
|
+
def best_file_path_for(key, locale)
|
19
|
+
file_paths = @key_finder.file_paths_for(key, locale: locale)
|
20
|
+
file_paths.filter! { |p| p.start_with?(Rails.root.to_s) }
|
21
|
+
if file_paths.any?
|
22
|
+
file_paths.first
|
23
|
+
elsif key.split(".").size > 1
|
24
|
+
parent_key = key.split(".")[0..-2].join(".")
|
25
|
+
best_file_path_for(parent_key, locale)
|
26
|
+
else
|
27
|
+
"./config/locales/#{locale}.yml"
|
28
|
+
end
|
15
29
|
end
|
16
30
|
|
17
31
|
private
|
18
32
|
|
19
|
-
def
|
20
|
-
|
33
|
+
def group_translations_by_file_path
|
34
|
+
translations_grouped_by_file_path = {}
|
35
|
+
Moirai::Translation.order(created_at: :asc).each do |translation|
|
36
|
+
file_path = best_file_path_for(translation.key, translation.locale)
|
37
|
+
absolute_file_path = File.expand_path(file_path, Rails.root)
|
38
|
+
|
39
|
+
# skip file paths that don't belong to the project
|
40
|
+
next unless absolute_file_path.to_s.start_with?(Rails.root.to_s)
|
41
|
+
|
42
|
+
translations_grouped_by_file_path[absolute_file_path] ||= []
|
43
|
+
translations_grouped_by_file_path[absolute_file_path] << translation
|
44
|
+
end
|
45
|
+
translations_grouped_by_file_path
|
46
|
+
end
|
21
47
|
|
48
|
+
def get_updated_file_contents(file_path, translations)
|
22
49
|
yaml = YAML.load_file(file_path)
|
23
50
|
|
24
51
|
translations.each do |translation|
|
@@ -6,9 +6,36 @@
|
|
6
6
|
<%= csp_meta_tag %>
|
7
7
|
|
8
8
|
<%= stylesheet_link_tag "moirai/application", media: "all" %>
|
9
|
-
<%= javascript_include_tag "moirai/application", "data-turoblinks-track": "reload", type: "module" %>
|
10
9
|
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
11
10
|
<link rel="icon" href="data:image/png;base64,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">
|
11
|
+
|
12
|
+
<style>
|
13
|
+
td {
|
14
|
+
height: 100px; /* Ensure the cell has a consistent height */
|
15
|
+
width: 200px; /* Set a reasonable width */
|
16
|
+
vertical-align: top;
|
17
|
+
}
|
18
|
+
|
19
|
+
form {
|
20
|
+
height: 100%;
|
21
|
+
display: flex;
|
22
|
+
align-items: stretch;
|
23
|
+
}
|
24
|
+
|
25
|
+
textarea {
|
26
|
+
flex: 1; /* Allow the textarea to expand */
|
27
|
+
width: 100%; /* Ensure it fills the width */
|
28
|
+
resize: none; /* Disable manual resizing */
|
29
|
+
box-sizing: border-box; /* Account for padding and borders */
|
30
|
+
border: 1px solid #ccc; /* Standard input border style */
|
31
|
+
border-radius: 4px; /* Match the design */
|
32
|
+
padding: 5px; /* Inner padding for text */
|
33
|
+
font-family: inherit; /* Use consistent font */
|
34
|
+
font-size: inherit; /* Match the font size */
|
35
|
+
overflow: hidden; /* Hide overflow before it resizes */
|
36
|
+
}
|
37
|
+
</style>
|
38
|
+
|
12
39
|
</head>
|
13
40
|
<body>
|
14
41
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<span
|
2
2
|
contenteditable
|
3
|
-
data-action="blur->moirai-translation#submit click->moirai-translation#click"
|
3
|
+
data-action="blur->moirai-translation#submit click->moirai-translation#click"
|
4
4
|
style="border: 1px dashed #1d9f74; min-width: 30px; display: inline-block;"
|
5
|
-
data-key="<%= key %>"
|
6
|
-
data-
|
5
|
+
data-moirai-translation-key-value="<%= key %>"
|
6
|
+
data-moirai-translation-locale-value="<%= locale %>"
|
7
7
|
data-controller="moirai-translation">
|
8
8
|
<%= value %>
|
9
9
|
</span>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<h1>Translation files</h1>
|
3
3
|
|
4
4
|
<% if Moirai::PullRequestCreator.available? %>
|
5
|
-
<%= button_to "Create or update
|
5
|
+
<%= button_to "Create or update Pull Request", moirai_open_pr_path %>
|
6
6
|
<% else %>
|
7
7
|
<p>PR creation is not available. Add the gem octokit to your gemfile to enable this feature</p>
|
8
8
|
<% end %>
|
@@ -9,11 +9,12 @@
|
|
9
9
|
<tr>
|
10
10
|
<th>Key</th>
|
11
11
|
<th>Value</th>
|
12
|
+
<th>Original Translation</th>
|
12
13
|
</tr>
|
13
14
|
</thead>
|
14
15
|
<tbody>
|
15
16
|
<% @translation_keys.each do |key, value| %>
|
16
|
-
<% translation =
|
17
|
+
<% translation = @translations.find { |t| t.key == key } %>
|
17
18
|
|
18
19
|
<tr>
|
19
20
|
<td>
|
@@ -24,13 +25,18 @@
|
|
24
25
|
<% end %>
|
25
26
|
</td>
|
26
27
|
<td>
|
27
|
-
<%= form_for Moirai::Translation.new,
|
28
|
-
|
29
|
-
|
30
|
-
<%= f.
|
28
|
+
<%= form_for translation&.presence || Moirai::Translation.new(key: key, locale: @locale, value: value),
|
29
|
+
url: moirai_create_or_update_translation_path,
|
30
|
+
method: :post do |f| %>
|
31
|
+
<%= f.hidden_field :key %>
|
32
|
+
<%= f.hidden_field :locale %>
|
33
|
+
<%= f.text_area :value, class: 'translation-textarea' %>
|
31
34
|
<%= f.submit 'Update', style: 'display: none;' %>
|
32
35
|
<% end %>
|
33
36
|
</td>
|
37
|
+
<td>
|
38
|
+
<%= I18n.translate_without_moirai(key, @locale) %>
|
39
|
+
</td>
|
34
40
|
</tr>
|
35
41
|
<% end %>
|
36
42
|
</tbody>
|
data/bin/check
CHANGED
data/config/routes.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
Moirai::Engine.routes.draw do
|
4
4
|
root to: "translation_files#index"
|
5
5
|
|
6
|
-
resources :
|
6
|
+
resources :translations, only: %i[index]
|
7
|
+
resources :translation_files, only: %i[index show], as: "moirai_translation_files", param: :hashed_file_path
|
7
8
|
post "/translation_files/open_pr", to: "translation_files#open_pr", as: "moirai_open_pr"
|
8
9
|
post "/translation_files", to: "translation_files#create_or_update", as: "moirai_create_or_update_translation"
|
9
10
|
end
|
@@ -10,9 +10,34 @@ module Moirai
|
|
10
10
|
invoke "moirai:migration"
|
11
11
|
end
|
12
12
|
|
13
|
+
def setup_javascript
|
14
|
+
if using_importmap?
|
15
|
+
say "Pin moirai"
|
16
|
+
string_to_be_added = "pin \"controllers/moirai_translation_controller\", to: \"moirai_translation_controller.js\""
|
17
|
+
say %(Appending: #{string_to_be_added})
|
18
|
+
append_to_file "config/importmap.rb", %(#{string_to_be_added}\n)
|
19
|
+
elsif using_js_bundling?
|
20
|
+
append_path = "app/javascript/controllers/moirai_translation_controller.js"
|
21
|
+
say "Copying Moirai Stimulus controller in #{append_path}"
|
22
|
+
copy_file "../../../../app/assets/javascripts/moirai_translation_controller.js", append_path
|
23
|
+
rails_command "stimulus:manifest:update"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def using_js_bundling?
|
30
|
+
Rails.root.join("app/javascript/controllers").exist?
|
31
|
+
end
|
32
|
+
|
13
33
|
def mount_engine
|
14
|
-
route 'mount Moirai::Engine => "/moirai", as: "moirai"
|
15
|
-
|
34
|
+
route 'mount Moirai::Engine => "/moirai", as: "moirai"'
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def using_importmap?
|
40
|
+
Rails.root.join("config/importmap.rb").exist?
|
16
41
|
end
|
17
42
|
end
|
18
43
|
end
|
@@ -11,7 +11,11 @@ module Moirai
|
|
11
11
|
source_root File.expand_path("templates", __dir__)
|
12
12
|
|
13
13
|
def create_migration_file
|
14
|
-
migration_template "
|
14
|
+
migration_template "create_moirai_translations.rb.erb", "db/migrate/create_moirai_translations.rb", migration_version: migration_version
|
15
|
+
end
|
16
|
+
|
17
|
+
def file_path_migration_file
|
18
|
+
migration_template "make_moirai_translations_file_path_not_required.rb.erb", "db/migrate/make_moirai_translations_file_path_not_required.rb", migration_version: migration_version
|
15
19
|
end
|
16
20
|
|
17
21
|
private
|
data/lib/i18n/backend/moirai.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module I18n
|
2
2
|
module Backend
|
3
|
-
class Moirai < I18n::Backend::Simple
|
3
|
+
class Moirai < I18n::Backend::Simple
|
4
4
|
# TODO: mega inefficient. we don't want to perform a SQL query for each key!
|
5
5
|
def translate(locale, key, options = EMPTY_HASH)
|
6
6
|
overridden_translation = ::Moirai::Translation.find_by(locale: locale, key: key)
|
@@ -8,58 +8,6 @@ module I18n
|
|
8
8
|
overridden_translation.value
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
12
|
-
def store_moirai_translations(filename, locale, data, options)
|
13
|
-
moirai_translations[locale] ||= Concurrent::Hash.new
|
14
|
-
flatten_data = flatten_hash(filename, data)
|
15
|
-
flatten_data = Utils.deep_symbolize_keys(flatten_data) unless options.fetch(:skip_symbolize_keys, false)
|
16
|
-
Utils.deep_merge!(moirai_translations[locale], flatten_data)
|
17
|
-
end
|
18
|
-
|
19
|
-
def moirai_translations(do_init: false)
|
20
|
-
@moirai_translations ||= Concurrent::Hash.new do |h, k|
|
21
|
-
MUTEX.synchronize do
|
22
|
-
h[k] = Concurrent::Hash.new
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def flatten_hash(filename, hash, parent_key = "", result = {})
|
28
|
-
hash.each do |key, value|
|
29
|
-
new_key = parent_key.empty? ? key.to_s : "#{parent_key}.#{key}"
|
30
|
-
case value
|
31
|
-
when Hash
|
32
|
-
flatten_hash(filename, value, new_key, result)
|
33
|
-
when Array
|
34
|
-
value.each_with_index do |item, index|
|
35
|
-
array_key = "#{new_key}.#{index}"
|
36
|
-
if item.is_a?(Hash)
|
37
|
-
flatten_hash(filename, item, array_key, result)
|
38
|
-
else
|
39
|
-
result[array_key] = filename
|
40
|
-
end
|
41
|
-
end
|
42
|
-
else
|
43
|
-
result[new_key] = filename
|
44
|
-
end
|
45
|
-
end
|
46
|
-
result
|
47
|
-
end
|
48
|
-
|
49
|
-
def load_file(filename)
|
50
|
-
type = File.extname(filename).tr(".", "").downcase
|
51
|
-
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
|
52
|
-
data, keys_symbolized = send(:"load_#{type}", filename)
|
53
|
-
unless data.is_a?(Hash)
|
54
|
-
raise InvalidLocaleData.new(filename, "expects it to return a hash, but does not")
|
55
|
-
end
|
56
|
-
data.each do |locale, d|
|
57
|
-
store_translations(locale, d || {}, skip_symbolize_keys: keys_symbolized)
|
58
|
-
store_moirai_translations(filename, locale, d || {}, skip_symbolize_keys: keys_symbolized)
|
59
|
-
end
|
60
|
-
|
61
|
-
data
|
62
|
-
end
|
63
11
|
end
|
64
12
|
end
|
65
13
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
class << self
|
5
|
+
attr_accessor :original_backend
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.translate_without_moirai(key, locale, **)
|
9
|
+
raise "Original backend is not set" unless original_backend
|
10
|
+
|
11
|
+
original_backend.translate(locale, key, **)
|
12
|
+
end
|
13
|
+
end
|
data/lib/moirai/engine.rb
CHANGED
@@ -9,9 +9,12 @@ module Moirai
|
|
9
9
|
end
|
10
10
|
|
11
11
|
config.after_initialize do
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
I18n.original_backend = I18n.backend
|
13
|
+
if ActiveRecord::Base.connection.data_source_exists?("moirai_translations") || ENV["RAILS_ENV"] == "test"
|
14
|
+
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Moirai.new, I18n.backend)
|
15
|
+
else
|
16
|
+
Rails.logger.warn("moirai disabled: tables have not been generated yet.")
|
17
|
+
end
|
15
18
|
end
|
16
19
|
|
17
20
|
# TODO: how to do this without rewriting the entire method?
|
@@ -25,12 +28,11 @@ module Moirai
|
|
25
28
|
value = original_translate(key, **options)
|
26
29
|
|
27
30
|
if moirai_edit_enabled?
|
28
|
-
|
29
|
-
file_path = moirai_translations[I18n.locale][scope_key_by_partial(key)]
|
31
|
+
@key_finder ||= Moirai::KeyFinder.new
|
30
32
|
|
31
33
|
render(partial: "moirai/translation_files/form",
|
32
34
|
locals: {key: scope_key_by_partial(key),
|
33
|
-
|
35
|
+
locale: I18n.locale,
|
34
36
|
value: value})
|
35
37
|
else
|
36
38
|
value
|
@@ -1,41 +1,43 @@
|
|
1
1
|
class Moirai::PullRequestCreator
|
2
|
-
BRANCH_NAME = "moirai-translations"
|
3
|
-
|
4
2
|
def self.available?
|
5
|
-
defined?(Octokit)
|
3
|
+
!!defined?(Octokit)
|
6
4
|
end
|
7
5
|
|
6
|
+
BRANCH_PREFIX = "moirai-translations-"
|
7
|
+
|
8
|
+
attr_reader :github_repo_name, :github_access_token, :github_client, :github_repository, :branch_name
|
9
|
+
|
8
10
|
def initialize
|
9
11
|
@github_repo_name = ENV["MOIRAI_GITHUB_REPO_NAME"] || Rails.application.credentials.dig(:moirai, :github_repo_name)
|
10
12
|
@github_access_token = ENV["MOIRAI_GITHUB_ACCESS_TOKEN"] || Rails.application.credentials.dig(:moirai, :github_access_token)
|
11
|
-
@
|
12
|
-
|
13
|
-
)
|
13
|
+
@github_client = Octokit::Client.new(access_token: github_access_token)
|
14
|
+
@github_repository = github_client.repo(github_repo_name)
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
# @param changes Array[Moirai::Change]
|
18
|
+
def create_pull_request(changes)
|
19
|
+
@branch_name = "#{BRANCH_PREFIX}#{Time.current.strftime("%F-%H-%M-%S")}-#{rand(1000..9999)}"
|
20
|
+
default_branch = github_repository.default_branch
|
19
21
|
|
20
|
-
if
|
21
|
-
|
22
|
+
if moirai_branch_exists?
|
23
|
+
Rails.logger.debug { "Branch #{branch_name} already exists - the branch will be updated with the new changes" }
|
22
24
|
else
|
23
|
-
|
24
|
-
default_branch_ref = @
|
25
|
+
Rails.logger.debug { "Branch #{branch_name} does not exist - creating branch" }
|
26
|
+
default_branch_ref = @github_client.ref(@github_repo_name, "heads/#{default_branch}")
|
25
27
|
latest_commit_sha = default_branch_ref.object.sha
|
26
28
|
|
27
|
-
@
|
29
|
+
@github_client.create_ref(@github_repo_name, "heads/#{branch_name}", latest_commit_sha)
|
28
30
|
end
|
29
31
|
|
30
|
-
|
31
|
-
update_file(
|
32
|
+
changes.each do |change|
|
33
|
+
update_file(change.file_path, change.content)
|
32
34
|
end
|
33
35
|
|
34
|
-
unless
|
35
|
-
pull_request = @
|
36
|
+
unless existing_open_pull_request.present?
|
37
|
+
pull_request = @github_client.create_pull_request(
|
36
38
|
@github_repo_name,
|
37
39
|
default_branch,
|
38
|
-
|
40
|
+
branch_name,
|
39
41
|
"Adding new content by Moirai",
|
40
42
|
"BODY - This is a pull request created by Moirai"
|
41
43
|
)
|
@@ -44,40 +46,47 @@ class Moirai::PullRequestCreator
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
def
|
48
|
-
@
|
49
|
+
def moirai_branch_exists?
|
50
|
+
@github_client.ref(@github_repo_name, "heads/#{branch_name}")
|
49
51
|
true
|
50
52
|
rescue Octokit::NotFound
|
51
53
|
false
|
52
54
|
end
|
53
55
|
|
56
|
+
def existing_open_pull_request
|
57
|
+
@github_client.pull_requests(@github_repo_name).find do |pull_request|
|
58
|
+
pull_request.head.ref.start_with?(BRANCH_PREFIX) && (pull_request.state == "open")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def cleanup
|
63
|
+
pr = existing_open_pull_request
|
64
|
+
@github_client.close_pull_request(@github_repo_name, pr.number)
|
65
|
+
@github_client.delete_branch(@github_repo_name, pr.head.ref)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
54
70
|
def update_file(path, content)
|
55
71
|
# TODO: check what happens if branch exists
|
56
|
-
|
57
|
-
file = @client.contents(@github_repo_name, path: path, ref: BRANCH_NAME)
|
72
|
+
file = @github_client.contents(@github_repo_name, path: path, ref: branch_name)
|
58
73
|
file_sha = file.sha
|
59
74
|
|
60
|
-
@
|
75
|
+
@github_client.update_contents(
|
61
76
|
@github_repo_name,
|
62
77
|
path,
|
63
78
|
"Updating translations for #{path} by Moirai",
|
64
79
|
file_sha,
|
65
80
|
content,
|
66
|
-
branch:
|
81
|
+
branch: branch_name
|
67
82
|
)
|
68
83
|
rescue Octokit::NotFound
|
69
|
-
@
|
84
|
+
@github_client.create_contents(
|
70
85
|
@github_repo_name,
|
71
86
|
path,
|
72
87
|
"Creating translations for #{path} by Moirai",
|
73
88
|
content,
|
74
|
-
branch:
|
89
|
+
branch: branch_name
|
75
90
|
)
|
76
91
|
end
|
77
|
-
|
78
|
-
def pull_request_exists?
|
79
|
-
@client.pull_requests(@github_repo_name).any? do |pull_request|
|
80
|
-
pull_request.head.ref == BRANCH_NAME
|
81
|
-
end
|
82
|
-
end
|
83
92
|
end
|
data/lib/moirai/version.rb
CHANGED
data/lib/moirai.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "moirai/version"
|
4
|
+
require "i18n/extensions/i18n"
|
5
|
+
require "i18n/backend/moirai"
|
4
6
|
require "moirai/engine"
|
5
7
|
require "moirai/pull_request_creator"
|
6
|
-
require "i18n/backend/moirai"
|
7
8
|
|
8
9
|
module Moirai
|
9
10
|
# Your code goes here...
|
data/moirai.gemspec
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "lib/moirai/version"
|
4
|
+
|
3
5
|
Gem::Specification.new do |spec|
|
4
6
|
spec.name = "moirai"
|
5
|
-
spec.version =
|
7
|
+
spec.version = Moirai::VERSION
|
6
8
|
spec.authors = ["Alessandro Rodi", "Oliver Anthony", "Daniel Bengl"]
|
7
9
|
spec.email = %w[alessandro.rodi@renuo.ch oliver.anthony@renuo.ch daniel.bengl@renuo.ch]
|
8
10
|
|
@@ -25,11 +27,6 @@ Gem::Specification.new do |spec|
|
|
25
27
|
|
26
28
|
spec.required_ruby_version = ">= 2.7.0"
|
27
29
|
spec.add_dependency "rails", ">= 6.1"
|
28
|
-
spec.add_development_dependency "octokit", ">= 4.0"
|
29
|
-
spec.add_development_dependency "capybara"
|
30
|
-
spec.add_development_dependency "selenium-webdriver"
|
31
|
-
spec.add_development_dependency "rails", "~> 7.2.0"
|
32
|
-
spec.add_development_dependency "dotenv"
|
33
30
|
|
34
31
|
spec.license = "MIT"
|
35
32
|
spec.metadata["rubygems_mfa_required"] = "true"
|