sail 3.2.3 → 3.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +48 -7
- data/Rakefile +3 -3
- data/app/assets/images/sail/angle-left.svg +1 -1
- data/app/assets/images/sail/angle-right.svg +1 -1
- data/app/assets/images/sail/checkmark.svg +1 -0
- data/app/assets/images/sail/cog.svg +1 -1
- data/app/assets/images/sail/error.svg +1 -0
- data/app/assets/images/sail/reset.svg +1 -0
- data/app/assets/images/sail/sort.svg +1 -1
- data/app/assets/javascripts/sail/{application.js.erb → application.js} +1 -10
- data/app/assets/javascripts/{settings.js.erb → sail/settings.js} +16 -3
- data/app/assets/stylesheets/sail/application.css.erb +419 -0
- data/app/assets/stylesheets/sail/settings.css +555 -0
- data/app/controllers/sail/settings_controller.rb +2 -13
- data/app/helpers/sail/application_helper.rb +4 -0
- data/app/models/sail/setting.rb +4 -8
- data/app/views/layouts/sail/application.html.erb +2 -6
- data/app/views/sail/profiles/_profile.html.erb +29 -17
- data/app/views/sail/settings/_guide_modal.html.erb +2 -2
- data/app/views/sail/settings/_setting.html.erb +80 -50
- data/app/views/sail/settings/index.html.erb +21 -17
- data/app/views/sail/settings/update.js.erb +6 -2
- data/config/locales/en.yml +1 -4
- data/lib/generators/sail/install/install_generator.rb +5 -1
- data/lib/generators/sail/install/templates/sail.yml.tt +5 -0
- data/lib/generators/sail/update/update_generator.rb +1 -1
- data/lib/sail.rb +3 -3
- data/lib/sail/constant_collection.rb +2 -2
- data/lib/sail/engine.rb +3 -3
- data/lib/sail/graphql.rb +43 -0
- data/lib/sail/instrumenter.rb +1 -1
- data/lib/sail/mutations.rb +49 -0
- data/lib/sail/types.rb +1 -0
- data/lib/sail/types/set.rb +20 -0
- data/lib/sail/version.rb +1 -1
- metadata +26 -102
- data/app/assets/images/sail/refresh.svg +0 -1
- data/app/assets/stylesheets/sail/_colors.scss +0 -9
- data/app/assets/stylesheets/sail/_shared.scss +0 -52
- data/app/assets/stylesheets/sail/application.scss +0 -351
- data/app/assets/stylesheets/sail/settings.scss +0 -422
- data/app/views/sail/settings/_setting_minimal.html.erb +0 -6
@@ -10,15 +10,12 @@ module Sail
|
|
10
10
|
class SettingsController < ApplicationController
|
11
11
|
before_action :set_locale, only: :index
|
12
12
|
after_action :log_update, only: %i[update reset], if: -> { Sail.configuration.enable_logging && @successful_update }
|
13
|
-
|
14
|
-
# rubocop:disable AbcSize
|
15
13
|
def index
|
16
14
|
@settings = Setting.by_query(s_params[:query]).ordered_by(s_params[:order_field])
|
17
|
-
@number_of_pages = (@settings.count.to_f /
|
18
|
-
@settings = @settings.paginated(s_params[:page],
|
15
|
+
@number_of_pages = (@settings.count.to_f / Sail::ConstantCollection::SETTINGS_PER_PAGE).ceil
|
16
|
+
@settings = @settings.paginated(s_params[:page], Sail::ConstantCollection::SETTINGS_PER_PAGE)
|
19
17
|
fresh_when(@settings)
|
20
18
|
end
|
21
|
-
# rubocop:enable AbcSize
|
22
19
|
|
23
20
|
def update
|
24
21
|
respond_to do |format|
|
@@ -65,14 +62,6 @@ module Sail
|
|
65
62
|
|
66
63
|
private
|
67
64
|
|
68
|
-
def settings_per_page
|
69
|
-
if params[:monitor_mode] == Sail::ConstantCollection::TRUE
|
70
|
-
Sail::ConstantCollection::MINIMAL_SETTINGS_PER_PAGE
|
71
|
-
else
|
72
|
-
Sail::ConstantCollection::SETTINGS_PER_PAGE
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
65
|
def s_params
|
77
66
|
params.permit(:page, :query, :name,
|
78
67
|
:value, :positive, :negative,
|
data/app/models/sail/setting.rb
CHANGED
@@ -11,7 +11,7 @@ module Sail
|
|
11
11
|
class UnexpectedCastType < StandardError; end
|
12
12
|
|
13
13
|
FULL_RANGE = (0...100).freeze
|
14
|
-
AVAILABLE_MODELS = Dir[Rails.root.join("app
|
14
|
+
AVAILABLE_MODELS = Dir[Rails.root.join("app/models/*.rb")]
|
15
15
|
.map { |dir| dir.split("/").last.camelize.gsub(".rb", "") }.freeze
|
16
16
|
|
17
17
|
has_many :entries, dependent: :destroy
|
@@ -21,7 +21,7 @@ module Sail
|
|
21
21
|
validates :name, presence: true, uniqueness: { case_sensitive: false }
|
22
22
|
enum cast_type: %i[integer string boolean range array float
|
23
23
|
ab_test cron obj_model date uri throttle
|
24
|
-
locales].freeze
|
24
|
+
locales set].freeze
|
25
25
|
|
26
26
|
validate :value_is_within_range, if: -> { range? }
|
27
27
|
validate :value_is_true_or_false, if: -> { boolean? || ab_test? }
|
@@ -32,11 +32,7 @@ module Sail
|
|
32
32
|
|
33
33
|
after_initialize :instantiate_caster
|
34
34
|
|
35
|
-
scope :paginated,
|
36
|
-
select(:name, :description, :group, :value, :cast_type, :updated_at)
|
37
|
-
.offset(page.to_i * per_page)
|
38
|
-
.limit(per_page)
|
39
|
-
}
|
35
|
+
scope :paginated, ->(page, per_page) { offset(page.to_i * per_page).limit(per_page) }
|
40
36
|
|
41
37
|
scope :by_query, lambda { |query|
|
42
38
|
if cast_types.key?(query) || query == Sail::ConstantCollection::STALE
|
@@ -44,7 +40,7 @@ module Sail
|
|
44
40
|
elsif select(:id).by_group(query).exists?
|
45
41
|
by_group(query)
|
46
42
|
elsif query.to_s.include?(Sail::ConstantCollection::RECENT)
|
47
|
-
recently_updated(query.delete("recent ").strip)
|
43
|
+
recently_updated(query.delete("recent ").strip.to_i)
|
48
44
|
else
|
49
45
|
by_name(query)
|
50
46
|
end
|
@@ -21,16 +21,12 @@
|
|
21
21
|
<button id="btn-guide" type="button" class="nav-button">
|
22
22
|
<%= I18n.t("sail.guide") %>
|
23
23
|
</button>
|
24
|
-
|
25
|
-
<% if params[:monitor_mode] == Sail::ConstantCollection::TRUE %>
|
26
|
-
<%= link_to(I18n.t("sail.regular_mode"), settings_path, method: :get, class: "nav-button", id: "btn-regular-mode") %>
|
27
|
-
<% else %>
|
28
|
-
<%= link_to(I18n.t("sail.monitor_mode"), settings_path(monitor_mode: "true"), method: :get, class: "nav-button", id: "btn-monitor-mode") %>
|
29
|
-
<% end %>
|
30
24
|
</nav>
|
31
25
|
|
32
26
|
<div class="clearfix"></div>
|
33
27
|
<%= yield %>
|
28
|
+
|
29
|
+
<%= hidden_field_tag(:auto_search_enabled, Sail.configuration.enable_search_auto_submit) %>
|
34
30
|
</div>
|
35
31
|
</body>
|
36
32
|
</html>
|
@@ -1,31 +1,43 @@
|
|
1
|
-
<div id="<%= profile.name %>" class="profile-entry">
|
2
|
-
|
1
|
+
<div id="<%= profile.name %>" class="profile-entry flex-container space-between">
|
2
|
+
<div>
|
3
|
+
<% if profile.active? %>
|
3
4
|
<span class="active-indicator <%= profile.dirty? ? "yellow" : "green" %>"
|
4
5
|
title="<%= profile.dirty? ? I18n.t("sail.dirty_profile_tooltip") : I18n.t("sail.clean_profile_tooltip") %>">
|
5
6
|
⬤
|
6
7
|
</span>
|
7
8
|
|
8
|
-
|
9
|
+
<% unless Sail.instrumenter.profile(profile.name).zero? %>
|
9
10
|
<span class="errors-indicator" title="<%= I18n.t("sail.profile_error_tooltip") %>">
|
10
11
|
<%= I18n.t("sail.profile_errors", count: Sail.instrumenter.profile(profile.name)) %>
|
11
12
|
</span>
|
13
|
+
<% end %>
|
12
14
|
<% end %>
|
13
|
-
|
15
|
+
</div>
|
14
16
|
|
15
|
-
<
|
17
|
+
<div>
|
18
|
+
<span class="entry-name"><%= profile.name.titleize %></span>
|
19
|
+
</div>
|
16
20
|
|
17
|
-
|
18
|
-
<
|
19
|
-
|
21
|
+
<div>
|
22
|
+
<div class="flex-container space-between buttons">
|
23
|
+
<div>
|
24
|
+
<%= form_tag(profile_path(name: profile.name), method: :delete, remote: true) do %>
|
25
|
+
<button class="btn-sail"><%= I18n.t("sail.delete") %></button>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
20
28
|
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
<div>
|
30
|
+
<%= form_tag(switch_profile_path(name: profile.name), method: :put, remote: true) do %>
|
31
|
+
<button class="btn-sail" type="submit"><%= I18n.t("sail.activate") %></button>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
24
34
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
<div>
|
36
|
+
<%= form_tag(profiles_path, method: :post, remote: true) do %>
|
37
|
+
<input type="hidden" name="name" value="<%= profile.name %>">
|
38
|
+
<button class="btn-sail" type="submit"><%= I18n.t("sail.save") %></button>
|
39
|
+
<% end %>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
31
43
|
</div>
|
@@ -12,7 +12,6 @@
|
|
12
12
|
</div>
|
13
13
|
</summary>
|
14
14
|
|
15
|
-
<p>
|
16
15
|
<div class="items-container">
|
17
16
|
<ul>
|
18
17
|
<li><%= I18n.t("sail.by_setting_name_html").html_safe %></li>
|
@@ -21,8 +20,9 @@
|
|
21
20
|
<li><%= I18n.t("sail.by_stale_html").html_safe %></li>
|
22
21
|
<li><%= I18n.t("sail.by_recent_html").html_safe %></li>
|
23
22
|
</ul>
|
23
|
+
|
24
|
+
<p><%= I18n.translate("sail.click_title") %></p>
|
24
25
|
</div>
|
25
|
-
</p>
|
26
26
|
</details>
|
27
27
|
<div class="clearfix"></div>
|
28
28
|
|
@@ -1,60 +1,90 @@
|
|
1
1
|
<% cache setting, expires_in: Sail.configuration.cache_life_span do %>
|
2
2
|
<div class="card">
|
3
|
-
<
|
4
|
-
|
5
|
-
|
6
|
-
<span class="relevancy-score" title="<%= I18n.t("sail.relevancy_tooltip") %>"><%= setting.relevancy %></span>
|
7
|
-
<div class="clearfix"></div>
|
8
|
-
</h3>
|
9
|
-
<hr/>
|
10
|
-
|
11
|
-
<%= form_tag(reset_setting_path(name: setting.name), method: :put, remote: true) do %>
|
12
|
-
<button class="refresh-button" title="<%= I18n.t("sail.refresh_tooltip") %>">
|
13
|
-
<%= image_tag("sail/refresh.svg", alt: I18n.t("sail.refresh_tooltip")) %>
|
14
|
-
</button>
|
15
|
-
<% end %>
|
16
|
-
|
17
|
-
<div class="label-container">
|
18
|
-
<%= link_to(setting.cast_type, settings_path(query: setting.cast_type), method: :get, class: "tag type-label") %>
|
19
|
-
</div>
|
3
|
+
<div class="front">
|
4
|
+
<h3 class="title card-title"><%= setting.display_name %></h3>
|
5
|
+
<hr/>
|
20
6
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
7
|
+
<div class="flex-container-vertical">
|
8
|
+
<div>
|
9
|
+
<div class="flex-container space-between">
|
10
|
+
<div>
|
11
|
+
<%= form_tag(reset_setting_path(name: setting.name), method: :put, remote: true) do %>
|
12
|
+
<button class="refresh-button" title="<%= I18n.t("sail.refresh_tooltip") %>">
|
13
|
+
<%= image_tag("sail/reset.svg", alt: I18n.t("sail.refresh_tooltip")) %>
|
14
|
+
</button>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
26
17
|
|
27
|
-
|
18
|
+
<div>
|
19
|
+
<span class="relevancy-score" title="<%= I18n.t("sail.relevancy_tooltip") %>"><%= setting.relevancy %></span>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</div>
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
<input id="<%= "input_for_#{setting.name}" %>" type="range" min="0" max="99" value="<%= setting.value %>" name="value" class="value-slider">
|
49
|
-
<% elsif setting.date? %>
|
50
|
-
<input id="<%= "input_for_#{setting.name}" %>" type="datetime-local" value="<%= formatted_date(setting) %>" name="value" class="date-picker">
|
51
|
-
<% else %>
|
52
|
-
<input id="<%= "input_for_#{setting.name}" %>" type="text" name="value" class="value-input" value="<%= setting.value %>"/>
|
53
|
-
<% end %>
|
24
|
+
<div>
|
25
|
+
<div class="flex-container justify-end padded">
|
26
|
+
<div>
|
27
|
+
<%= link_to(setting.cast_type, settings_path(query: setting.cast_type), method: :get, class: "tag type-label") %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<% if setting.group.present? %>
|
31
|
+
<div>
|
32
|
+
<%= link_to(setting.group, settings_path(query: setting.group), method: :get, class: "tag group-label") %>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
<% if setting.stale? %>
|
37
|
+
<div>
|
38
|
+
<%= link_to(I18n.t("sail.stale"), settings_path(query: Sail::ConstantCollection::STALE), method: :get, class: "tag stale-label", title: I18n.t("sail.stale_tooltip", days: Sail.configuration.days_until_stale)) %>
|
39
|
+
</div>
|
40
|
+
<% end %>
|
41
|
+
</div>
|
42
|
+
</div>
|
54
43
|
|
55
|
-
|
44
|
+
<div>
|
45
|
+
<%= form_tag(setting_path(name: setting.name), method: :put, remote: true) do %>
|
46
|
+
<div class="flex-container space-between align-bottom">
|
47
|
+
<% if setting.boolean? || setting.ab_test? %>
|
48
|
+
<div>
|
49
|
+
<label class="switch">
|
50
|
+
<input id="<%= "input_for_#{setting.name}" %>" type="checkbox" name="value" <%= setting.value == "true" ? "checked" : "" %>>
|
51
|
+
<span class="slider round"></span>
|
52
|
+
</label>
|
53
|
+
</div>
|
54
|
+
<% elsif setting.range? %>
|
55
|
+
<div class="flex-grow">
|
56
|
+
<input id="<%= "input_for_#{setting.name}" %>" type="range" min="0" max="99" value="<%= setting.value %>" name="value" class="value-slider">
|
57
|
+
</div>
|
58
|
+
<% else %>
|
59
|
+
<div>
|
60
|
+
<input id="<%= "input_for_#{setting.name}" %>" type="text" name="value" class="value-input" value="<%= setting.value %>"/>
|
61
|
+
</div>
|
62
|
+
<% end %>
|
63
|
+
|
64
|
+
<div class="submit-container">
|
65
|
+
<button id="btn-submit-<%= setting.name %>" type="submit" class="btn-value-submit"><%= I18n.t("sail.save") %></button>
|
66
|
+
|
67
|
+
<span id="success-<%= setting.name %>" class="notice success">
|
68
|
+
<%= image_tag("sail/checkmark.svg") %>
|
69
|
+
</span>
|
70
|
+
|
71
|
+
<span id="alert-<%= setting.name %>" class="notice alert">
|
72
|
+
<%= image_tag("sail/error.svg") %>
|
73
|
+
</span>
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
<% end %>
|
56
77
|
</div>
|
57
78
|
</div>
|
58
|
-
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<div class="back">
|
82
|
+
<h3 class="title card-title"><%= setting.display_name %></h3>
|
83
|
+
<hr/>
|
84
|
+
|
85
|
+
<p class="setting-description" title="<%= setting.description %>">
|
86
|
+
<label for="<%= "input_for_#{setting.name}" %>"><%= setting.description %></label>
|
87
|
+
</p>
|
88
|
+
</div>
|
59
89
|
</div>
|
60
90
|
<% end %>
|
@@ -1,19 +1,10 @@
|
|
1
1
|
<div id="settings-dashboard">
|
2
|
-
<% cache
|
3
|
-
|
4
|
-
<span id="notice-alert" class="notice alert"><%= I18n.t("sail.failed") %></span>
|
2
|
+
<% cache @settings do %>
|
3
|
+
<%= render(partial: "search") %>
|
5
4
|
|
6
|
-
|
7
|
-
<%= render(partial: "search") %>
|
8
|
-
<% end %>
|
9
|
-
|
10
|
-
<div id="settings-container">
|
5
|
+
<div id="settings-container" class="<%= settings_container_class(@number_of_pages) %>">
|
11
6
|
<% if @number_of_pages > 0 %>
|
12
|
-
|
13
|
-
<%= render(partial: "setting_minimal", collection: @settings) %>
|
14
|
-
<% else %>
|
15
|
-
<%= render(partial: "setting", collection: @settings) %>
|
16
|
-
<% end%>
|
7
|
+
<%= render(partial: "setting", collection: @settings) %>
|
17
8
|
<% else %>
|
18
9
|
<h1><%= I18n.t("sail.no_settings") %></h1>
|
19
10
|
<% end %>
|
@@ -25,13 +16,26 @@
|
|
25
16
|
<div class="clearfix"></div>
|
26
17
|
|
27
18
|
<div class="page-links">
|
28
|
-
<%= link_to("", settings_path(page: [params[:page].to_i - 1, 0].max,
|
19
|
+
<%= link_to("", settings_path(page: [params[:page].to_i - 1, 0].max, query: params[:query]), method: :get, id: "angle-left-link", title: I18n.t("sail.previous_page")) %>
|
20
|
+
<%= link_to(1, settings_path(page: 0, query: params[:query]), method: :get, class: params[:page].to_i.zero? || params[:page].blank? ? "active" : "") %>
|
21
|
+
|
22
|
+
<% if params[:page].to_i - Sail::ConstantCollection::MAX_PAGES > 1 %>
|
23
|
+
●●●
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<% ([params[:page].to_i - Sail::ConstantCollection::MAX_PAGES, 1].max...[@number_of_pages - 1, params[:page].to_i + Sail::ConstantCollection::MAX_PAGES].min).each do |page| %>
|
27
|
+
<%= link_to(page + 1, settings_path(page: page, query: params[:query]), method: :get, class: params[:page].to_i == page || params[:page].blank? && page.zero? ? "active" : "") %>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<% if params[:page].to_i + Sail::ConstantCollection::MAX_PAGES < @number_of_pages - 1 %>
|
31
|
+
●●●
|
32
|
+
<% end %>
|
29
33
|
|
30
|
-
<%
|
31
|
-
<%= link_to(
|
34
|
+
<% if @number_of_pages > 1 %>
|
35
|
+
<%= link_to(@number_of_pages, settings_path(page: @number_of_pages - 1, query: params[:query]), method: :get, class: params[:page].to_i == @number_of_pages - 1 ? "active" : "") %>
|
32
36
|
<% end %>
|
33
37
|
|
34
|
-
<%= link_to("", settings_path(page: [params[:page].to_i + 1, @number_of_pages - 1].min,
|
38
|
+
<%= link_to("", settings_path(page: [params[:page].to_i + 1, @number_of_pages - 1].min, query: params[:query]), method: :get, id: "angle-right-link", title: I18n.t("sail.next_page")) %>
|
35
39
|
</div>
|
36
40
|
</div>
|
37
41
|
<% end %>
|
@@ -1,8 +1,12 @@
|
|
1
|
-
var notice = document.getElementById("<%= @successful_update ? "
|
2
|
-
|
1
|
+
var notice = document.getElementById("<%= @successful_update ? "success-#{@setting.name}" : "alert-#{@setting.name}" %>");
|
2
|
+
var submit = document.getElementById("btn-submit-<%= @setting.name %>");
|
3
|
+
|
4
|
+
submit.style.display = "none";
|
5
|
+
notice.style.display = "inline-block";
|
3
6
|
|
4
7
|
setTimeout(function () {
|
5
8
|
notice.style.display = "none";
|
9
|
+
submit.style.display = "inline-block";
|
6
10
|
}, 1500);
|
7
11
|
|
8
12
|
if ("<%= @successful_update %>" === "true") {
|
data/config/locales/en.yml
CHANGED
@@ -4,8 +4,6 @@ en:
|
|
4
4
|
save: SAVE
|
5
5
|
activate: ACTIVATE
|
6
6
|
delete: DELETE
|
7
|
-
updated: Updated!
|
8
|
-
failed: Failed!
|
9
7
|
search_placeholder: Setting name, group, cast type, stale or recent x
|
10
8
|
search_tooltip: "When searching for recently updated settings, x is the number of hours since the update (e.g.: recent 2)"
|
11
9
|
main_app: Main app
|
@@ -33,6 +31,7 @@ en:
|
|
33
31
|
by_cast_type_html: "<b>By cast type:</b> will find all settings with the same type (must be exact match)"
|
34
32
|
by_stale_html: "<b>By stale:</b> will find settings that are stale (haven't been updated recently)"
|
35
33
|
by_recent_html: "<b>By recent:</b> will find settings updated in the last X hours (e.g.: recent 50)"
|
34
|
+
click_title: Click a setting's title to view its description.
|
36
35
|
profiles_can_be_used: Profiles can be used to configure many states of settings. They save the values of all settings in a given moment.
|
37
36
|
profile_configuring: Configure settings as desired and create a new profile. Activate profiles to change the value of all settings at once.
|
38
37
|
relevancy_score: Relevancy Score
|
@@ -40,8 +39,6 @@ en:
|
|
40
39
|
available_groups_and_types: Available groups and types
|
41
40
|
groups_are: "The groups currently used are:"
|
42
41
|
types_are: "The cast types currently used are:"
|
43
|
-
regular_mode: Regular mode
|
44
|
-
monitor_mode: Monitor mode
|
45
42
|
how_to_find_settings: How to find settings you are looking for
|
46
43
|
how_to_profiles: How to organize your settings in profiles
|
47
44
|
how_to_relevancy_score: What is the relevancy score and how to use it
|
@@ -33,8 +33,12 @@ module Sail
|
|
33
33
|
migration_version: migration_version
|
34
34
|
end
|
35
35
|
|
36
|
+
def create_config_file
|
37
|
+
template "sail.yml", "config/sail.yml"
|
38
|
+
end
|
39
|
+
|
36
40
|
def migration_version
|
37
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
41
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|