diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
index 60345eb..17d90ae 100644
--- a/app/controllers/messages_controller.rb
+++ b/app/controllers/messages_controller.rb
@@ -1,20 +1,23 @@
class MessagesController < ApplicationController
PER_PAGE = 50
- # GET /ruby-dev or /q=searchterm
- def index(list_name: nil, yyyymm: nil, q: nil, page: nil)
+ # GET /ruby-dev
+ def index(list_name: nil, yyyymm: nil, q: nil)
if list_name
@list = List.find_by_name list_name
- render_threads yyyymm: yyyymm
- elsif q
- search q, page
+ render_threads yyyymm: yyyymm, q: q
+ else
+ redirect_to List.find_by_name('ruby-core')
+ end
+ end
- render :search
+ # GET /messages/search_all
+ def search_all(q: nil, page: nil)
+ if q
+ search q, page
else
@messages = []
-
- render :search
end
end
@@ -59,38 +62,37 @@ def calculate_navigation_links
@next_message_in_thread = thread_messages[current_index + 1] if current_index
end
- def render_threads(yyyymm: nil)
- @yyyymms = Message.where(list_id: @list).order('yyyymm').pluck(Arel.sql "distinct to_char(published_at, 'YYYYMM') as yyyymm")
- @yyyymm = yyyymm || @yyyymms.last
+ def render_threads(yyyymm: nil, q: nil)
+ root_query = Message.where(list_id: @list, parent_id: nil).order(:id)
+
+ if q
+ root_query.where!('body %> ?', q)
+ else
+ @yyyymms = Message.where(list_id: @list, parent_id: nil).order('yyyymm').pluck(Arel.sql "distinct to_char(published_at, 'YYYYMM') as yyyymm")
+ @yyyymm = yyyymm || @yyyymms.last
+ root_query.where!("to_char(published_at, 'YYYYMM') = ?", @yyyymm)
+ end
- root_query = Message.where(list_id: @list, parent_id: nil).where("to_char(published_at, 'YYYYMM') = ?", @yyyymm).order(:id)
messages = Message.with_recursive(parent_and_children: [root_query, Message.joins('inner join parent_and_children on messages.parent_id = parent_and_children.id')])
.joins('inner join parent_and_children on parent_and_children.id = messages.id')
@messages = compose_tree(messages)
- render :index
- end
-
- def get_list_ids(params)
- list_ids = []
- ['ruby-talk', 'ruby-core', 'ruby-list', 'ruby-dev'].each do |name|
- if params[name.tr('-', '_').to_sym] != '0'
- list_ids << List.find_by_name(name).id
- end
+ if q
+ @yyyymms = @messages.map { it.published_at.strftime('%Y%m') }.uniq
+ @yyyymm = @yyyymms.last
end
- list_ids
+
+ render :index
end
def search(query, page)
- list_ids = get_list_ids(params)
- if list_ids.empty?
- raise "Need to select at least one list"
- end
+ lists = List.all.select { params[it.name] != '0' }
+ raise "Need to select at least one list" if lists.empty?
# %> and <-> are defined by pg_trgm.
# https://www.postgresql.org/docs/17/pgtrgm.html
- message_where = Message.where('body %> ? AND list_id IN (?)', query, list_ids).order(Arel.sql('body <-> ?', query))
+ message_where = Message.where('body %> ? AND list_id IN (?)', query, lists.map(&:id)).order(Arel.sql('body <-> ?', query))
@messages = message_where.offset(page.to_i * PER_PAGE).limit(PER_PAGE)
end
diff --git a/app/models/list.rb b/app/models/list.rb
index 31cbcc8..f64e263 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -1,31 +1,35 @@
class List
include ActiveModel::Model
- def initialize(name, id)
- @name = name
- @id = id
+ def initialize(id, name, label)
+ @id, @name, @label = id, name, label
+ nil
end
- attr_reader :name, :id
+ attr_reader :id, :name, :label
# Ordered by the established dates. ruby-list was started in 1995.
LISTS = [
- List.new('ruby-list', 1),
- List.new('ruby-dev', 2),
- List.new('ruby-core', 3),
- List.new('ruby-talk', 4),
+ List.new(1, 'ruby-list', 'ruby-list (For Ruby users, JA)'),
+ List.new(2, 'ruby-dev', 'ruby-dev (For Ruby developers, JA)'),
+ List.new(3, 'ruby-core', 'ruby-core (For Ruby developers, EN)'),
+ List.new(4, 'ruby-talk', 'ruby-talk (For Ruby users, EN)')
]
class << self
- def find_by_name(name)
- List::LISTS.find { |list| list.name == name }
- end
-
def find_by_id(id)
- List::LISTS.find { |list| list.id == id }
+ List.all.detect { |list| list.id == id }
end
alias find find_by_id
+
+ def find_by_name(name)
+ List.all.detect { |list| list.name == name }
+ end
+
+ def all
+ List::LISTS
+ end
end
def to_param
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 42cc57d..61459ce 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -20,11 +20,52 @@
-
-
- <%= link_to @list&.name || 'blade.ruby-lang.org', @list || root_path, class: "hover:text-red-600 dark:hover:text-red-400 transition-colors" %>
-
-
Mailing list archive
+
+
+
+
+ <%= link_to @list&.name || yield(:title) || 'blade.ruby-lang.org', @list || root_path, class: 'hover:text-red-600 dark:hover:text-red-400 transition-colors' %>
+
+
+
Mailing Lists
+
+ <% List.all.each do |list| %>
+ <%= link_to list, class: "flex items-center gap-2 px-2 py-1 rounded text-sm transition-colors #{@list&.id == list.id ? 'bg-red-600 dark:bg-red-500 text-white' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-red-600 dark:hover:text-red-400'}" do %>
+
+
+
+
<%= list.label %>
+ <% end %>
+ <% end %>
+
+
+
+
+
+ <%= form_with method: :get, class: 'mb-3' do |f| %>
+
+ <%= f.text_field :q, value: params[:q], placeholder: "Search messages #{"within #{@list.name}" if @list}...", class: 'flex-1 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400' %>
+ <%= f.submit 'Search', name: nil, class: 'px-4 py-2 text-sm font-medium rounded bg-red-600 dark:bg-red-500 text-white hover:bg-red-700 dark:hover:bg-red-600 transition-colors' %>
+
+
+ <% if @list %>
+ <%= render 'shared/yyyymm_selector' if @yyyymms.present? %>
+ <% else %>
+
+
Select mailing lists from below to search from, or choose one from the left to navigate:
+
+ <% List.all.each do |list| %>
+
+ <%= f.check_box list.name, {checked: params[list.name] != '0'} %>
+ <%= list.name %>
+
+ <% end %>
+
+
+ <% end %>
+ <% end %>
+
+
diff --git a/app/views/messages/index.html.erb b/app/views/messages/index.html.erb
index e97cd38..48e6f08 100644
--- a/app/views/messages/index.html.erb
+++ b/app/views/messages/index.html.erb
@@ -6,35 +6,6 @@
<% end %>
-
- <%
- selected_year = @yyyymm&.[](0, 4)
- selected_month = @yyyymm&.[](4, 2)
- years_with_months = @yyyymms.group_by {|yyyymm| yyyymm[0, 4] }.sort.reverse
- %>
-
-
-
- <% years_with_months.each do |year, months| %>
- <%= link_to year, [@list, yyyymm: "#{year}#{months.sort.first[4, 2]}"], class: "px-3 py-1 text-sm font-medium rounded whitespace-nowrap transition-colors #{selected_year == year ? 'bg-red-600 dark:bg-red-500 text-white' : 'bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'}" %>
- <% end %>
-
-
-
- <% if selected_year %>
-
- <% available_months = years_with_months.detect {|y, _| y == selected_year }&.last&.map {|yyyymm| yyyymm[4, 2] } || [] %>
- <% ('01'..'12').each do |month| %>
- <% if available_months.include?(month) %>
- <%= link_to month, [@list, yyyymm: "#{selected_year}#{month}"], class: "px-2 py-1 text-xs font-medium rounded transition-colors #{selected_month == month ? 'bg-red-600 dark:bg-red-500 text-white border-2 border-red-700 dark:border-red-400' : 'bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 border-2 border-transparent'}" %>
- <% else %>
- <%= month %>
- <% end %>
- <% end %>
-
- <% end %>
-
-
<%= render partial: 'thread', collection: @messages, as: :message, locals: {list: @list, depth: 0} %>
diff --git a/app/views/messages/search.html.erb b/app/views/messages/search.html.erb
deleted file mode 100644
index e9a8089..0000000
--- a/app/views/messages/search.html.erb
+++ /dev/null
@@ -1,36 +0,0 @@
-
<%= notice %>
-
-
blade.ruby-lang.org
-
-
- <%= form_with(method: :get) do |form| %>
- <%= form.text_field :q, { value: params[:q] } %>
- <%= form.submit "Search" %>
-
-
- <%
- [
- [:ruby_talk, 'ruby-talk (English)'],
- [:ruby_core, 'ruby-core (For developing Ruby iself, English)'],
- [:ruby_list, 'ruby-list (Japanese)'],
- [:ruby_dev, 'ruby-dev (For developing Ruby itself, Japanese)'],
- ].each do |name, label|
- %>
-
- <%= form.check_box name, {checked: params[name] != '0' } %>
- <%= form.label name, label %>
-
- <% end %>
-
- <% end %>
-
-
-<% @messages.each do |message| %>
-
-
- <%= message.list.name %>:<%= message.list_seq %>
- <%= link_to without_list_prefix(message.subject), [message.list, message] %>
-
-
<%= search_snippet(message.body, params[:q]) %>
-
-<% end %>
diff --git a/app/views/messages/search_all.html.erb b/app/views/messages/search_all.html.erb
new file mode 100644
index 0000000..86bcbfd
--- /dev/null
+++ b/app/views/messages/search_all.html.erb
@@ -0,0 +1,35 @@
+<% content_for :title, 'Blade Archive Search' %>
+
+<% if notice %>
+
+ <%= notice %>
+
+<% end %>
+
+<% if @messages.present? %>
+
+
Search Results
+ <% @messages.each do |message| %>
+
+
+
+ <%= message.list.name %>:<%= message.list_seq %>
+
+
+
+ <%= link_to without_list_prefix(message.subject), [message.list, message], class: "text-gray-900 dark:text-gray-100 hover:text-red-600 dark:hover:text-red-400 transition-colors" %>
+
+
+ <%= search_snippet(message.body, params[:q]) %>
+
+
+ <% end %>
+
+<% elsif params[:q].present? %>
+
+
+
+
+
No results found for "<%= params[:q] %>"
+
+<% end %>
diff --git a/app/views/shared/_yyyymm_selector.html.erb b/app/views/shared/_yyyymm_selector.html.erb
new file mode 100644
index 0000000..f98d391
--- /dev/null
+++ b/app/views/shared/_yyyymm_selector.html.erb
@@ -0,0 +1,28 @@
+<%
+ selected_year = @yyyymm&.[](0, 4)
+ selected_month = @yyyymm&.[](4, 2)
+ years_with_months = @yyyymms.group_by {|yyyymm| yyyymm[0, 4] }.sort.reverse
+%>
+
+
+
+
+ <% years_with_months.each do |year, months| %>
+ <%= link_to year, [@list, yyyymm: "#{year}#{months.sort.first[4, 2]}"], class: "px-3 py-1 text-sm font-medium rounded whitespace-nowrap transition-colors #{selected_year == year ? 'bg-red-600 dark:bg-red-500 text-white' : 'bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'}" %>
+ <% end %>
+
+
+
+ <% if selected_year %>
+
+ <% available_months = years_with_months.detect {|y, _| y == selected_year }&.last&.map {|yyyymm| yyyymm[4, 2] } || [] %>
+ <% ('01'..'12').each do |month| %>
+ <% if available_months.include?(month) %>
+ <%= link_to month, [@list, yyyymm: "#{selected_year}#{month}"], class: "px-2 py-1 text-xs font-medium rounded transition-colors #{selected_month == month ? 'bg-red-600 dark:bg-red-500 text-white border-2 border-red-700 dark:border-red-400' : 'bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 border-2 border-transparent'}" %>
+ <% else %>
+ <%= month %>
+ <% end %>
+ <% end %>
+
+ <% end %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 309f23c..62a4d95 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,6 +5,8 @@
end
get '/attachments/:encoded_key/*filename' => 'attachments#show', as: :attachment
+ get '/messages/search_all', to: 'messages#search_all', as: :search_all_messages
+
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.