Merge pull request #3989 from evazion/fix-3987

Wiki pages: convert other_names column to array (#3987)
This commit is contained in:
Albert Yi
2018-11-19 16:23:32 -08:00
committed by GitHub
27 changed files with 238 additions and 181 deletions

View File

@@ -105,7 +105,7 @@ private
end end
def artist_params def artist_params
permitted_params = %i[name other_names other_names_comma group_name url_string notes] permitted_params = %i[name other_names other_names_string group_name url_string notes]
permitted_params << :is_active if CurrentUser.is_builder? permitted_params << :is_active if CurrentUser.is_builder?
params.fetch(:artist, {}).permit(permitted_params) params.fetch(:artist, {}).permit(permitted_params)

View File

@@ -96,7 +96,7 @@ class PoolsController < ApplicationController
private private
def pool_params def pool_params
permitted_params = %i[name description category is_active post_ids] permitted_params = %i[name description category is_active post_ids post_ids_string]
params.require(:pool).permit(*permitted_params, post_ids: []) params.require(:pool).permit(*permitted_params, post_ids: [])
end end
end end

View File

@@ -5,7 +5,7 @@ class WikiPagesController < ApplicationController
before_action :normalize_search_params, :only => [:index] before_action :normalize_search_params, :only => [:index]
def new def new
@wiki_page = WikiPage.new(wiki_page_create_params) @wiki_page = WikiPage.new(wiki_page_params(:create))
respond_with(@wiki_page) respond_with(@wiki_page)
end end
@@ -54,13 +54,13 @@ class WikiPagesController < ApplicationController
end end
def create def create
@wiki_page = WikiPage.create(wiki_page_create_params) @wiki_page = WikiPage.create(wiki_page_params(:create))
respond_with(@wiki_page) respond_with(@wiki_page)
end end
def update def update
@wiki_page = WikiPage.find(params[:id]) @wiki_page = WikiPage.find(params[:id])
@wiki_page.update(wiki_page_update_params) @wiki_page.update(wiki_page_params(:update))
respond_with(@wiki_page) respond_with(@wiki_page)
end end
@@ -98,18 +98,11 @@ class WikiPagesController < ApplicationController
end end
end end
def wiki_page_create_params def wiki_page_params(context)
permitted_params = %i[title body other_names skip_secondary_validations] permitted_params = %i[body other_names other_names_string skip_secondary_validations]
permitted_params += %i[is_locked is_deleted] if CurrentUser.is_builder? permitted_params += %i[is_locked is_deleted] if CurrentUser.is_builder?
permitted_params += %i[title] if context == :create || CurrentUser.is_builder?
params.fetch(:wiki_page, {}).permit(permitted_params) params.fetch(:wiki_page, {}).permit(permitted_params)
end end
def wiki_page_update_params
permitted_params = %i[body other_names skip_secondary_validations]
permitted_params += %i[title is_locked is_deleted] if CurrentUser.is_builder?
params.fetch(:wiki_page, {}).permit(permitted_params)
end
end end

View File

@@ -18,7 +18,7 @@ module WikiPagesHelper
end end
def wiki_page_other_names_list(wiki_page) def wiki_page_other_names_list(wiki_page)
names_html = wiki_page.other_names_array.map{|name| link_to(name, "http://www.pixiv.net/search.php?s_mode=s_tag_full&word=#{u(name)}", :class => "wiki-other-name")} names_html = wiki_page.other_names.map{|name| link_to(name, "http://www.pixiv.net/search.php?s_mode=s_tag_full&word=#{u(name)}", :class => "wiki-other-name")}
names_html.join(" ").html_safe names_html.join(" ").html_safe
end end
end end

View File

@@ -14,7 +14,7 @@ div#c-artists, div#excerpt {
textarea { textarea {
height: 10em; height: 10em;
&#artist_other_names_comma { &#artist_other_names_string {
height: 3em; height: 3em;
} }
} }

View File

@@ -187,7 +187,7 @@ module Sources
def translate_tag(untranslated_tag) def translate_tag(untranslated_tag)
return [] if untranslated_tag.blank? return [] if untranslated_tag.blank?
translated_tag_names = WikiPage.active.other_names_equal(untranslated_tag).uniq.pluck(:title) translated_tag_names = WikiPage.active.other_names_include(untranslated_tag).uniq.pluck(:title)
translated_tag_names = TagAlias.to_aliased(translated_tag_names) translated_tag_names = TagAlias.to_aliased(translated_tag_names)
translated_tags = Tag.where(name: translated_tag_names) translated_tags = Tag.where(name: translated_tag_names)

View File

@@ -269,6 +269,37 @@ class ApplicationRecord < ActiveRecord::Base
end end
end end
concerning :AttributeMethods do
class_methods do
# Defines `<attribute>_string`, `<attribute>_string=`, and `<attribute>=`
# methods for converting an array attribute to or from a string.
#
# The `<attribute>=` setter parses strings into an array using the
# `parse` regex. The resulting strings can be converted to another type
# with the `cast` option.
def array_attribute(name, parse: /[^[:space:]]+/, cast: :itself)
define_method "#{name}_string" do
send(name).join(" ")
end
define_method "#{name}_string=" do |value|
raise ArgumentError, "#{name} must be a String" unless value.respond_to?(:to_str)
send("#{name}=", value)
end
define_method "#{name}=" do |value|
if value.respond_to?(:to_str)
super value.to_str.scan(parse).map(&cast)
elsif value.respond_to?(:to_a)
super value.to_a
else
raise ArgumentError, "#{name} must be a String or an Array"
end
end
end
end
end
def warnings def warnings
@warnings ||= ActiveModel::Errors.new(self) @warnings ||= ActiveModel::Errors.new(self)
end end

View File

@@ -3,8 +3,10 @@ class Artist < ApplicationRecord
class RevertError < Exception ; end class RevertError < Exception ; end
attr_accessor :url_string_changed attr_accessor :url_string_changed
array_attribute :other_names
before_validation :normalize_name before_validation :normalize_name
before_validation :normalize_other_names
after_save :create_version after_save :create_version
after_save :categorize_tag after_save :categorize_tag
after_save :update_wiki after_save :update_wiki
@@ -174,11 +176,11 @@ class Artist < ApplicationRecord
end end
def url_array def url_array
urls.map(&:to_s) urls.map(&:to_s).sort
end end
def url_string def url_string
url_array.sort.join("\n") url_array.join("\n")
end end
def url_string=(string) def url_string=(string)
@@ -239,16 +241,8 @@ class Artist < ApplicationRecord
name.tr("_", " ") name.tr("_", " ")
end end
def other_names_array def normalize_other_names
other_names.try(:split, /[[:space:]]+/) self.other_names = other_names.map { |x| Artist.normalize_name(x) }
end
def other_names_comma
other_names_array.try(:join, ", ")
end
def other_names_comma=(string)
self.other_names = string.split(/,/).map {|x| Artist.normalize_name(x)}.join(" ")
end end
end end
@@ -275,7 +269,7 @@ class Artist < ApplicationRecord
:name => name, :name => name,
:updater_id => CurrentUser.id, :updater_id => CurrentUser.id,
:updater_ip_addr => CurrentUser.ip_addr, :updater_ip_addr => CurrentUser.ip_addr,
:url_string => url_string, :urls => url_array,
:is_active => is_active, :is_active => is_active,
:is_banned => is_banned, :is_banned => is_banned,
:other_names => other_names, :other_names => other_names,
@@ -287,7 +281,7 @@ class Artist < ApplicationRecord
prev = versions.last prev = versions.last
prev.update_attributes( prev.update_attributes(
:name => name, :name => name,
:url_string => url_string, :urls => url_array,
:is_active => is_active, :is_active => is_active,
:is_banned => is_banned, :is_banned => is_banned,
:other_names => other_names, :other_names => other_names,
@@ -306,7 +300,7 @@ class Artist < ApplicationRecord
end end
self.name = version.name self.name = version.name
self.url_string = version.url_string self.url_string = version.urls.join("\n")
self.is_active = version.is_active self.is_active = version.is_active
self.other_names = version.other_names self.other_names = version.other_names
self.group_name = version.group_name self.group_name = version.group_name
@@ -466,13 +460,21 @@ class Artist < ApplicationRecord
where(name: normalize_name(name)) where(name: normalize_name(name))
end end
def any_other_name_matches(regex)
where(id: Artist.from("unnest(other_names) AS other_name").where("other_name ~ ?", regex))
end
def any_other_name_like(name)
where(id: Artist.from("unnest(other_names) AS other_name").where("other_name LIKE ?", name.to_escaped_for_sql_like))
end
def any_name_matches(query) def any_name_matches(query)
if query =~ %r!\A/(.*)/\z! if query =~ %r!\A/(.*)/\z!
where_regex(:name, $1).or(where_regex(:other_names, $1)).or(where_regex(:group_name, $1)) where_regex(:name, $1).or(any_other_name_matches($1)).or(where_regex(:group_name, $1))
else else
normalized_name = normalize_name(query) normalized_name = normalize_name(query)
normalized_name = "*#{normalized_name}*" unless normalized_name.include?("*") normalized_name = "*#{normalized_name}*" unless normalized_name.include?("*")
where_like(:name, normalized_name).or(where_like(:other_names, normalized_name)).or(where_like(:group_name, normalized_name)) where_like(:name, normalized_name).or(any_other_name_like(normalized_name)).or(where_like(:group_name, normalized_name))
end end
end end
@@ -492,9 +494,12 @@ class Artist < ApplicationRecord
q = super q = super
q = q.search_text_attribute(:name, params) q = q.search_text_attribute(:name, params)
q = q.search_text_attribute(:other_names, params)
q = q.search_text_attribute(:group_name, params) q = q.search_text_attribute(:group_name, params)
if params[:any_other_name_like]
q = q.any_other_name_like(params[:any_other_name_like])
end
if params[:any_name_matches].present? if params[:any_name_matches].present?
q = q.any_name_matches(params[:any_name_matches]) q = q.any_name_matches(params[:any_name_matches])
end end
@@ -536,12 +541,6 @@ class Artist < ApplicationRecord
end end
end end
module ApiMethods
def hidden_attributes
super + [:other_names_index]
end
end
include UrlMethods include UrlMethods
include NameMethods include NameMethods
include GroupMethods include GroupMethods
@@ -551,7 +550,6 @@ class Artist < ApplicationRecord
include TagMethods include TagMethods
include BanMethods include BanMethods
extend SearchMethods extend SearchMethods
include ApiMethods
def status def status
if is_banned? && is_active? if is_banned? && is_active?

View File

@@ -1,4 +1,7 @@
class ArtistVersion < ApplicationRecord class ArtistVersion < ApplicationRecord
array_attribute :urls
array_attribute :other_names
belongs_to_updater belongs_to_updater
belongs_to :artist belongs_to :artist
delegate :visible?, :to => :artist delegate :visible?, :to => :artist
@@ -47,18 +50,10 @@ class ArtistVersion < ApplicationRecord
extend SearchMethods extend SearchMethods
def url_array
url_string.to_s.split(/[[:space:]]+/)
end
def other_names_array
other_names.to_s.split(/[[:space:]]+/)
end
def urls_diff(version) def urls_diff(version)
latest_urls = artist.url_array || [] latest_urls = artist.url_array || []
new_urls = url_array new_urls = urls
old_urls = version.present? ? version.url_array : [] old_urls = version.present? ? version.urls : []
added_urls = new_urls - old_urls added_urls = new_urls - old_urls
removed_urls = old_urls - new_urls removed_urls = old_urls - new_urls
@@ -73,9 +68,9 @@ class ArtistVersion < ApplicationRecord
end end
def other_names_diff(version) def other_names_diff(version)
latest_names = artist.other_names_array || [] latest_names = artist.other_names || []
new_names = other_names_array new_names = other_names
old_names = version.present? ? version.other_names_array : [] old_names = version.present? ? version.other_names : []
added_names = new_names - old_names added_names = new_names - old_names
removed_names = old_names - new_names removed_names = old_names - new_names

View File

@@ -1,13 +1,15 @@
class Pool < ApplicationRecord class Pool < ApplicationRecord
class RevertError < Exception ; end class RevertError < Exception ; end
array_attribute :post_ids, parse: /\d+/, cast: :to_i
belongs_to_creator
validates_uniqueness_of :name, case_sensitive: false, if: :name_changed? validates_uniqueness_of :name, case_sensitive: false, if: :name_changed?
validate :validate_name, if: :name_changed? validate :validate_name, if: :name_changed?
validates_inclusion_of :category, :in => %w(series collection) validates_inclusion_of :category, :in => %w(series collection)
validate :updater_can_change_category validate :updater_can_change_category
validate :updater_can_remove_posts validate :updater_can_remove_posts
validate :updater_can_edit_deleted validate :updater_can_edit_deleted
belongs_to_creator
before_validation :normalize_post_ids before_validation :normalize_post_ids
before_validation :normalize_name before_validation :normalize_name
after_save :update_category_pseudo_tags_for_posts_async after_save :update_category_pseudo_tags_for_posts_async
@@ -153,18 +155,6 @@ class Pool < ApplicationRecord
self.post_ids = post_ids.uniq if is_collection? self.post_ids = post_ids.uniq if is_collection?
end end
# allow assigning a string to post_ids so it can be assigned from the text
# field in the pool edit form (PUT /pools/1?post_ids=1+2+3).
def post_ids=(value)
if value.respond_to?(:to_str)
super value.to_str.scan(/\d+/).map(&:to_i)
elsif value.respond_to?(:to_a)
super value.to_a
else
raise ArgumentError, "post_ids must be a String or an Array"
end
end
def revert_to!(version) def revert_to!(version)
if id != version.pool_id if id != version.pool_id
raise RevertError.new("You cannot revert to a previous version of another pool.") raise RevertError.new("You cannot revert to a previous version of another pool.")

View File

@@ -1,6 +1,8 @@
class TagImplication < TagRelationship class TagImplication < TagRelationship
extend Memoist extend Memoist
array_attribute :descendant_names
before_save :update_descendant_names before_save :update_descendant_names
after_save :update_descendant_names_for_parents after_save :update_descendant_names_for_parents
after_destroy :update_descendant_names_for_parents after_destroy :update_descendant_names_for_parents
@@ -22,7 +24,7 @@ class TagImplication < TagRelationship
module ClassMethods module ClassMethods
# assumes names are normalized # assumes names are normalized
def with_descendants(names) def with_descendants(names)
(names + active.where(antecedent_name: names).flat_map(&:descendant_names_array)).uniq (names + active.where(antecedent_name: names).flat_map(&:descendant_names)).uniq
end end
def automatic_tags_for(names) def automatic_tags_for(names)
@@ -44,12 +46,8 @@ class TagImplication < TagRelationship
end end
memoize :descendants memoize :descendants
def descendant_names_array
descendant_names.split(/ /)
end
def update_descendant_names def update_descendant_names
self.descendant_names = descendants.join(" ") self.descendant_names = descendants
end end
def update_descendant_names! def update_descendant_names!
@@ -88,7 +86,7 @@ class TagImplication < TagRelationship
def absence_of_transitive_relation def absence_of_transitive_relation
# Find everything else the antecedent implies, not including the current implication. # Find everything else the antecedent implies, not including the current implication.
implications = TagImplication.active.where("antecedent_name = ? and consequent_name != ?", antecedent_name, consequent_name) implications = TagImplication.active.where("antecedent_name = ? and consequent_name != ?", antecedent_name, consequent_name)
implied_tags = implications.flat_map(&:descendant_names_array) implied_tags = implications.flat_map(&:descendant_names)
if implied_tags.include?(consequent_name) if implied_tags.include?(consequent_name)
self.errors[:base] << "#{antecedent_name} already implies #{consequent_name} through another implication" self.errors[:base] << "#{antecedent_name} already implies #{consequent_name} through another implication"
end end
@@ -170,7 +168,7 @@ class TagImplication < TagRelationship
def update_posts def update_posts
Post.without_timeout do Post.without_timeout do
Post.raw_tag_match(antecedent_name).where("true /* TagImplication#update_posts */").find_each do |post| Post.raw_tag_match(antecedent_name).where("true /* TagImplication#update_posts */").find_each do |post|
fixed_tags = "#{post.tag_string} #{descendant_names}".strip fixed_tags = "#{post.tag_string} #{descendant_names_string}".strip
CurrentUser.scoped(creator, creator_ip_addr) do CurrentUser.scoped(creator, creator_ip_addr) do
post.update_attributes( post.update_attributes(
:tag_string => fixed_tags :tag_string => fixed_tags

View File

@@ -4,14 +4,16 @@ class WikiPage < ApplicationRecord
before_save :normalize_title before_save :normalize_title
before_save :normalize_other_names before_save :normalize_other_names
after_save :create_version after_save :create_version
belongs_to_creator
belongs_to_updater
validates_uniqueness_of :title, :case_sensitive => false validates_uniqueness_of :title, :case_sensitive => false
validates_presence_of :title validates_presence_of :title
validates_presence_of :body, :unless => -> { is_deleted? || other_names.present? } validates_presence_of :body, :unless => -> { is_deleted? || other_names.present? }
validate :validate_rename validate :validate_rename
validate :validate_not_locked validate :validate_not_locked
attr_accessor :skip_secondary_validations attr_accessor :skip_secondary_validations
array_attribute :other_names
belongs_to_creator
belongs_to_updater
has_one :tag, :foreign_key => "name", :primary_key => "title" has_one :tag, :foreign_key => "name", :primary_key => "title"
has_one :artist, -> {where(:is_active => true)}, :foreign_key => "name", :primary_key => "title" has_one :artist, -> {where(:is_active => true)}, :foreign_key => "name", :primary_key => "title"
has_many :versions, -> {order("wiki_page_versions.id ASC")}, :class_name => "WikiPageVersion", :dependent => :destroy has_many :versions, -> {order("wiki_page_versions.id ASC")}, :class_name => "WikiPageVersion", :dependent => :destroy
@@ -29,17 +31,16 @@ class WikiPage < ApplicationRecord
order("updated_at DESC").limit(25) order("updated_at DESC").limit(25)
end end
def other_names_equal(name) def other_names_include(name)
query_sql = name.unicode_normalize(:nfkc).to_escaped_for_tsquery where("wiki_pages.other_names @> ARRAY[?]", name.unicode_normalize(:nfkc))
where("other_names_index @@ to_tsquery('danbooru', E?)", query_sql)
end end
def other_names_match(name) def other_names_match(name)
if name =~ /\*/ if name =~ /\*/
subquery = WikiPage.from("unnest(string_to_array(other_names, ' ')) AS other_name").where("other_name ILIKE ?", name.to_escaped_for_sql_like) subquery = WikiPage.from("unnest(other_names) AS other_name").where("other_name ILIKE ?", name.to_escaped_for_sql_like)
where(id: subquery) where(id: subquery)
else else
other_names_equal(name) other_names_include(name)
end end
end end
@@ -73,9 +74,9 @@ class WikiPage < ApplicationRecord
end end
if params[:other_names_present].to_s.truthy? if params[:other_names_present].to_s.truthy?
q = q.where("other_names is not null and other_names != ''") q = q.where("other_names is not null and other_names != '{}'")
elsif params[:other_names_present].to_s.falsy? elsif params[:other_names_present].to_s.falsy?
q = q.where("other_names is null or other_names = ''") q = q.where("other_names is null or other_names = '{}'")
end end
q = q.attribute_matches(:is_locked, params[:is_locked]) q = q.attribute_matches(:is_locked, params[:is_locked])
@@ -97,7 +98,7 @@ class WikiPage < ApplicationRecord
module ApiMethods module ApiMethods
def hidden_attributes def hidden_attributes
super + [:body_index, :other_names_index] super + [:body_index]
end end
def method_attributes def method_attributes
@@ -145,8 +146,7 @@ class WikiPage < ApplicationRecord
end end
def normalize_other_names def normalize_other_names
normalized_other_names = other_names.to_s.unicode_normalize(:nfkc).scan(/[^[:space:]]+/) self.other_names = other_names.map { |name| name.unicode_normalize(:nfkc) }.uniq
self.other_names = normalized_other_names.uniq.join(" ")
end end
def skip_secondary_validations=(value) def skip_secondary_validations=(value)
@@ -224,8 +224,4 @@ class WikiPage < ApplicationRecord
def visible? def visible?
artist.blank? || !artist.is_banned? || CurrentUser.is_builder? artist.blank? || !artist.is_banned? || CurrentUser.is_builder?
end end
def other_names_array
other_names.to_s.split(/[[:space:]]+/)
end
end end

View File

@@ -1,4 +1,5 @@
class WikiPageVersion < ApplicationRecord class WikiPageVersion < ApplicationRecord
array_attribute :other_names
belongs_to :wiki_page belongs_to :wiki_page
belongs_to_updater belongs_to_updater
belongs_to :artist, optional: true belongs_to :artist, optional: true
@@ -45,8 +46,4 @@ class WikiPageVersion < ApplicationRecord
def category_name def category_name
Tag.category_for(title) Tag.category_for(title)
end end
def other_names_array
other_names.to_s.split(/[[:space:]]+/)
end
end end

View File

@@ -14,7 +14,7 @@
<p><%= @artist.name %></p> <p><%= @artist.name %></p>
<% end %> <% end %>
</div> </div>
<%= f.input :other_names_comma, :hint => "Separate with commas", :as => :text, :label => "Other names" %> <%= f.input :other_names_string, :hint => "Separate with spaces", :as => :text, :label => "Other names" %>
<%= f.input :group_name %> <%= f.input :group_name %>
<%= f.input :url_string, :label => "URLs", :as => :text, :input_html => {:size => "50x5", :value => params.dig(:artist, :url_string) || @artist.urls.join("\n")}, :hint => "You can prefix a URL with - to mark it as dead." %> <%= f.input :url_string, :label => "URLs", :as => :text, :input_html => {:size => "50x5", :value => params.dig(:artist, :url_string) || @artist.urls.join("\n")}, :hint => "You can prefix a URL with - to mark it as dead." %>

View File

@@ -7,7 +7,7 @@
<li><strong>Tag Alias</strong> <%= artist.tag_alias_name %></li> <li><strong>Tag Alias</strong> <%= artist.tag_alias_name %></li>
<% end %> <% end %>
<% if artist.other_names.present? %> <% if artist.other_names.present? %>
<li><strong>Other Names</strong> <%= link_to_artists(artist.other_names.split(/ /)) %></li> <li><strong>Other Names</strong> <%= link_to_artists(artist.other_names) %></li>
<% end %> <% end %>
<% if artist.group_name.present? %> <% if artist.group_name.present? %>
<li><strong>Group</strong> <%= link_to_artist(artist.group_name) %></li> <li><strong>Group</strong> <%= link_to_artist(artist.group_name) %></li>

View File

@@ -19,7 +19,7 @@
(group: <%= link_to(artist.group_name, artist_path(artist)) %>) (group: <%= link_to(artist.group_name, artist_path(artist)) %>)
<% end %> <% end %>
</td> </td>
<td><%= artist.other_names %></td> <td><%= artist.other_names_string %></td>
<td><%= artist.status %></td> <td><%= artist.status %></td>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -8,7 +8,7 @@
<%= f.input :name, :as => :string, :input_html => { :value => @pool.pretty_name } %> <%= f.input :name, :as => :string, :input_html => { :value => @pool.pretty_name } %>
<%= dtext_field "pool", "description" %> <%= dtext_field "pool", "description" %>
<%= dtext_preview_button "pool", "description" %> <%= dtext_preview_button "pool", "description" %>
<%= f.input :post_ids, as: :text, label: "Posts", input_html: { value: @pool.post_ids.join(" ") } %> <%= f.input :post_ids_string, as: :text, label: "Posts" %>
<%= f.input :category, :collection => ["series", "collection"], :include_blank => false %> <%= f.input :category, :collection => ["series", "collection"], :include_blank => false %>
<%= f.input :is_active %> <%= f.input :is_active %>
<%= f.button :submit %> <%= f.button :submit %>

View File

@@ -7,7 +7,7 @@
<%= simple_form_for(@pool) do |f| %> <%= simple_form_for(@pool) do |f| %>
<%= f.input :name, :as => :string, :required => true %> <%= f.input :name, :as => :string, :required => true %>
<%= dtext_field "pool", "description" %> <%= dtext_field "pool", "description" %>
<%= f.input :post_ids, as: :text, label: "Posts", input_html: { value: @pool.post_ids.join(" ") } %> <%= f.input :post_ids_string, as: :text, label: "Posts" %>
<%= f.input :category, :collection => ["series", "collection"], :include_blank => true, :selected => "", :required => true %> <%= f.input :category, :collection => ["series", "collection"], :include_blank => true, :selected => "", :required => true %>
<%= f.input :is_active %> <%= f.input :is_active %>
<%= f.button :submit, "Submit" %> <%= f.button :submit, "Submit" %>

View File

@@ -10,7 +10,7 @@
<h1 id="wiki-page-title"><%= @wiki_page.pretty_title %></h1> <h1 id="wiki-page-title"><%= @wiki_page.pretty_title %></h1>
<% end %> <% end %>
<%= f.input :other_names, :as => :text, :label => "Other names (<a href='/wiki_pages/help:translated_tags'>view help</a>)".html_safe, :hint => "Names used for this tag on other sites such as Pixiv. Separate with spaces." %> <%= f.input :other_names_string, as: :text, label: "Other names (#{link_to "help", wiki_pages_path(title: "help:translated_tags")})".html_safe, hint: "Names used for this tag on other sites such as Pixiv. Separate with spaces." %>
<%= dtext_field "wiki_page", "body" %> <%= dtext_field "wiki_page", "body" %>

View File

@@ -0,0 +1,26 @@
class ChangeStringsToArraysOnWikiPages < ActiveRecord::Migration[5.2]
def up
WikiPage.without_timeout do
change_column :wiki_pages, :other_names, "text[]", using: "string_to_array(other_names, ' ')::text[]", default: "{}"
change_column :wiki_page_versions, :other_names, "text[]", using: "string_to_array(other_names, ' ')::text[]", default: "{}"
remove_column :wiki_pages, :other_names_index
execute "DROP TRIGGER trigger_wiki_pages_on_update_for_other_names ON wiki_pages"
add_index :wiki_pages, :other_names, using: :gin
end
end
def down
WikiPage.without_timeout do
remove_index :wiki_pages, :other_names
add_column :wiki_pages, :other_names_index, :tsvector
add_index :wiki_pages, :other_names_index, using: :gin
execute "CREATE TRIGGER trigger_wiki_pages_on_update_for_other_names BEFORE INSERT OR UPDATE ON wiki_pages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('other_names_index', 'public.danbooru', 'other_names')"
change_column :wiki_pages, :other_names, "text", using: "array_to_string(other_names, ' ')", default: nil
change_column :wiki_page_versions, :other_names, "text", using: "array_to_string(other_names, ' ')", default: nil
end
end
end

View File

@@ -0,0 +1,13 @@
class ChangeDescendantNamesToArrayOnTagImplications < ActiveRecord::Migration[5.2]
def up
TagImplication.without_timeout do
change_column :tag_implications, :descendant_names, "text[]", using: "string_to_array(descendant_names, ' ')::text[]", default: "{}"
end
end
def down
TagImplication.without_timeout do
change_column :tag_implications, :descendant_names, "text", using: "array_to_string(descendant_names, ' ')", default: nil
end
end
end

View File

@@ -0,0 +1,19 @@
class ChangeStringsToArraysOnArtistVersions < ActiveRecord::Migration[5.2]
def up
ArtistVersion.without_timeout do
change_column :artist_versions, :other_names, "text[]", using: "array_remove(regexp_split_to_array(other_names, '\\s+'), '')", default: "{}"
change_column :artist_versions, :url_string, "text[]", using: "array_remove(regexp_split_to_array(url_string, '\\s+'), '')", default: "{}"
rename_column :artist_versions, :url_string, :urls
end
end
def down
ArtistVersion.without_timeout do
rename_column :artist_versions, :urls, :url_string
change_column :artist_versions, :url_string, "text", using: "array_to_string(url_string, '\\n')", default: nil
change_column :artist_versions, :other_names, "text", using: "array_to_string(other_names, ' ')", default: nil
end
end
end

View File

@@ -0,0 +1,23 @@
class ChangeOtherNamesToArrayOnArtists < ActiveRecord::Migration[5.2]
def up
Artist.without_timeout do
remove_index :artists, name: "index_artists_on_other_names_trgm"
change_column :artists, :other_names, "text[]", using: "array_remove(regexp_split_to_array(other_names, '\\s+'), '')", default: "{}"
add_index :artists, :other_names, using: :gin
remove_column :artists, :other_names_index
execute "DROP TRIGGER trigger_artists_on_update ON artists"
end
end
def down
Artist.without_timeout do
remove_index :artists, :other_names
change_column :artists, :other_names, "text", using: "array_to_string(other_names, ' ')", default: nil
add_index :artists, :other_names, name: "index_artists_on_other_names_trgm", using: :gin, opclass: :gin_trgm_ops
add_column :artists, :other_names_index, :tsvector
execute "CREATE TRIGGER trigger_artists_on_update BEFORE INSERT OR UPDATE ON artists FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('other_names_index', 'public.danbooru', 'other_names')"
end
end
end

View File

@@ -718,9 +718,9 @@ CREATE TABLE public.artist_versions (
updater_id integer NOT NULL, updater_id integer NOT NULL,
updater_ip_addr inet NOT NULL, updater_ip_addr inet NOT NULL,
is_active boolean DEFAULT true NOT NULL, is_active boolean DEFAULT true NOT NULL,
other_names text, other_names text[] DEFAULT '{}'::text[],
group_name character varying, group_name character varying,
url_string text, urls text[] DEFAULT '{}'::text[],
is_banned boolean DEFAULT false NOT NULL, is_banned boolean DEFAULT false NOT NULL,
created_at timestamp without time zone, created_at timestamp without time zone,
updated_at timestamp without time zone updated_at timestamp without time zone
@@ -756,8 +756,7 @@ CREATE TABLE public.artists (
creator_id integer NOT NULL, creator_id integer NOT NULL,
is_active boolean DEFAULT true NOT NULL, is_active boolean DEFAULT true NOT NULL,
is_banned boolean DEFAULT false NOT NULL, is_banned boolean DEFAULT false NOT NULL,
other_names text, other_names text[] DEFAULT '{}'::text[],
other_names_index tsvector,
group_name character varying, group_name character varying,
created_at timestamp without time zone, created_at timestamp without time zone,
updated_at timestamp without time zone updated_at timestamp without time zone
@@ -2971,7 +2970,7 @@ CREATE TABLE public.tag_implications (
id integer NOT NULL, id integer NOT NULL,
antecedent_name character varying NOT NULL, antecedent_name character varying NOT NULL,
consequent_name character varying NOT NULL, consequent_name character varying NOT NULL,
descendant_names text NOT NULL, descendant_names text[] DEFAULT '{}'::text[] NOT NULL,
creator_id integer NOT NULL, creator_id integer NOT NULL,
creator_ip_addr inet NOT NULL, creator_ip_addr inet NOT NULL,
forum_topic_id integer, forum_topic_id integer,
@@ -3314,7 +3313,7 @@ CREATE TABLE public.wiki_page_versions (
is_locked boolean NOT NULL, is_locked boolean NOT NULL,
created_at timestamp without time zone, created_at timestamp without time zone,
updated_at timestamp without time zone, updated_at timestamp without time zone,
other_names text, other_names text[] DEFAULT '{}'::text[],
is_deleted boolean DEFAULT false NOT NULL is_deleted boolean DEFAULT false NOT NULL
); );
@@ -3352,8 +3351,7 @@ CREATE TABLE public.wiki_pages (
created_at timestamp without time zone, created_at timestamp without time zone,
updated_at timestamp without time zone, updated_at timestamp without time zone,
updater_id integer, updater_id integer,
other_names text, other_names text[] DEFAULT '{}'::text[],
other_names_index tsvector,
is_deleted boolean DEFAULT false NOT NULL is_deleted boolean DEFAULT false NOT NULL
); );
@@ -5028,17 +5026,10 @@ CREATE INDEX index_artists_on_name_trgm ON public.artists USING gin (name public
-- --
-- Name: index_artists_on_other_names_index; Type: INDEX; Schema: public; Owner: - -- Name: index_artists_on_other_names; Type: INDEX; Schema: public; Owner: -
-- --
CREATE INDEX index_artists_on_other_names_index ON public.artists USING gin (other_names_index); CREATE INDEX index_artists_on_other_names ON public.artists USING gin (other_names);
--
-- Name: index_artists_on_other_names_trgm; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_artists_on_other_names_trgm ON public.artists USING gin (other_names public.gin_trgm_ops);
-- --
@@ -7261,10 +7252,10 @@ CREATE INDEX index_wiki_pages_on_body_index_index ON public.wiki_pages USING gin
-- --
-- Name: index_wiki_pages_on_other_names_index; Type: INDEX; Schema: public; Owner: - -- Name: index_wiki_pages_on_other_names; Type: INDEX; Schema: public; Owner: -
-- --
CREATE INDEX index_wiki_pages_on_other_names_index ON public.wiki_pages USING gin (other_names_index); CREATE INDEX index_wiki_pages_on_other_names ON public.wiki_pages USING gin (other_names);
-- --
@@ -7295,13 +7286,6 @@ CREATE INDEX index_wiki_pages_on_updated_at ON public.wiki_pages USING btree (up
CREATE TRIGGER insert_favorites_trigger BEFORE INSERT ON public.favorites FOR EACH ROW EXECUTE PROCEDURE public.favorites_insert_trigger(); CREATE TRIGGER insert_favorites_trigger BEFORE INSERT ON public.favorites FOR EACH ROW EXECUTE PROCEDURE public.favorites_insert_trigger();
--
-- Name: artists trigger_artists_on_update; Type: TRIGGER; Schema: public; Owner: -
--
CREATE TRIGGER trigger_artists_on_update BEFORE INSERT OR UPDATE ON public.artists FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('other_names_index', 'public.danbooru', 'other_names');
-- --
-- Name: comments trigger_comments_on_update; Type: TRIGGER; Schema: public; Owner: - -- Name: comments trigger_comments_on_update; Type: TRIGGER; Schema: public; Owner: -
-- --
@@ -7351,13 +7335,6 @@ CREATE TRIGGER trigger_posts_on_tag_index_update BEFORE INSERT OR UPDATE ON publ
CREATE TRIGGER trigger_wiki_pages_on_update BEFORE INSERT OR UPDATE ON public.wiki_pages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('body_index', 'public.danbooru', 'body', 'title'); CREATE TRIGGER trigger_wiki_pages_on_update BEFORE INSERT OR UPDATE ON public.wiki_pages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('body_index', 'public.danbooru', 'body', 'title');
--
-- Name: wiki_pages trigger_wiki_pages_on_update_for_other_names; Type: TRIGGER; Schema: public; Owner: -
--
CREATE TRIGGER trigger_wiki_pages_on_update_for_other_names BEFORE INSERT OR UPDATE ON public.wiki_pages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('other_names_index', 'public.danbooru', 'other_names');
-- --
-- PostgreSQL database dump complete -- PostgreSQL database dump complete
-- --
@@ -7528,6 +7505,10 @@ INSERT INTO "schema_migrations" (version) VALUES
('20180913184128'), ('20180913184128'),
('20180916002448'), ('20180916002448'),
('20181108162204'), ('20181108162204'),
('20181108205842'); ('20181108205842'),
('20181113174914'),
('20181114180205'),
('20181114185032'),
('20181114202744');

View File

@@ -407,8 +407,8 @@ class ArtistTest < ActiveSupport::TestCase
end end
should "normalize its other names" do should "normalize its other names" do
artist = FactoryBot.create(:artist, :name => "a1", :other_names_comma => "aaa, bbb, ccc ddd") artist = FactoryBot.create(:artist, :name => "a1", :other_names => "aaa bbb ccc_ddd")
assert_equal("aaa, bbb, ccc_ddd", artist.other_names_comma) assert_equal("aaa bbb ccc_ddd", artist.other_names_string)
end end
should "search on its name should return results" do should "search on its name should return results" do
@@ -421,11 +421,11 @@ class ArtistTest < ActiveSupport::TestCase
end end
should "search on other names should return matches" do should "search on other names should return matches" do
artist = FactoryBot.create(:artist, :name => "artist", :other_names_comma => "aaa, ccc ddd") artist = FactoryBot.create(:artist, :name => "artist", :other_names_string => "aaa ccc_ddd")
assert_nil(Artist.search(other_names_like: "*artist*").first) assert_nil(Artist.search(any_other_name_like: "*artist*").first)
assert_not_nil(Artist.search(other_names_like: "*aaa*").first) assert_not_nil(Artist.search(any_other_name_like: "*aaa*").first)
assert_not_nil(Artist.search(other_names_like: "*ccc_ddd*").first) assert_not_nil(Artist.search(any_other_name_like: "*ccc_ddd*").first)
assert_not_nil(Artist.search(name: "artist").first) assert_not_nil(Artist.search(name: "artist").first)
assert_not_nil(Artist.search(:any_name_matches => "aaa").first) assert_not_nil(Artist.search(:any_name_matches => "aaa").first)
assert_not_nil(Artist.search(:any_name_matches => "/a/").first) assert_not_nil(Artist.search(:any_name_matches => "/a/").first)
@@ -475,10 +475,10 @@ class ArtistTest < ActiveSupport::TestCase
end end
first_version = ArtistVersion.first first_version = ArtistVersion.first
assert_equal("yyy", first_version.other_names) assert_equal(%w[yyy], first_version.other_names)
artist.revert_to!(first_version) artist.revert_to!(first_version)
artist.reload artist.reload
assert_equal("yyy", artist.other_names) assert_equal(%w[yyy], artist.other_names)
end end
should "update the category of the tag when created" do should "update the category of the tag when created" do
@@ -506,35 +506,35 @@ class ArtistTest < ActiveSupport::TestCase
should "create a new version when an url is added" do should "create a new version when an url is added" do
assert_difference("ArtistVersion.count") do assert_difference("ArtistVersion.count") do
@artist.update(:url_string => "http://foo.com http://bar.com") @artist.update(:url_string => "http://foo.com http://bar.com")
assert_equal(%w[http://bar.com http://foo.com], @artist.versions.last.url_array) assert_equal(%w[http://bar.com http://foo.com], @artist.versions.last.urls)
end end
end end
should "create a new version when an url is removed" do should "create a new version when an url is removed" do
assert_difference("ArtistVersion.count") do assert_difference("ArtistVersion.count") do
@artist.update(:url_string => "") @artist.update(:url_string => "")
assert_equal(%w[], @artist.versions.last.url_array) assert_equal(%w[], @artist.versions.last.urls)
end end
end end
should "create a new version when an url is marked inactive" do should "create a new version when an url is marked inactive" do
assert_difference("ArtistVersion.count") do assert_difference("ArtistVersion.count") do
@artist.update(:url_string => "-http://foo.com") @artist.update(:url_string => "-http://foo.com")
assert_equal(%w[-http://foo.com], @artist.versions.last.url_array) assert_equal(%w[-http://foo.com], @artist.versions.last.urls)
end end
end end
should "not create a new version when nothing has changed" do should "not create a new version when nothing has changed" do
assert_no_difference("ArtistVersion.count") do assert_no_difference("ArtistVersion.count") do
@artist.save @artist.save
assert_equal(%w[http://foo.com], @artist.versions.last.url_array) assert_equal(%w[http://foo.com], @artist.versions.last.urls)
end end
end end
should "not save invalid urls" do should "not save invalid urls" do
assert_no_difference("ArtistVersion.count") do assert_no_difference("ArtistVersion.count") do
@artist.update(:url_string => "http://foo.com www.example.com") @artist.update(:url_string => "http://foo.com www.example.com")
assert_equal(%w[http://foo.com], @artist.versions.last.url_array) assert_equal(%w[http://foo.com], @artist.versions.last.urls)
end end
end end
end end

View File

@@ -46,7 +46,7 @@ class TagImplicationTest < ActiveSupport::TestCase
ti2 = FactoryBot.build(:tag_implication, :antecedent_name => "b", :consequent_name => "c", :status => "pending") ti2 = FactoryBot.build(:tag_implication, :antecedent_name => "b", :consequent_name => "c", :status => "pending")
ti2.save ti2.save
ti1 = FactoryBot.create(:tag_implication, :antecedent_name => "a", :consequent_name => "b") ti1 = FactoryBot.create(:tag_implication, :antecedent_name => "a", :consequent_name => "b")
assert_equal("b", ti1.descendant_names) assert_equal(%w[b], ti1.descendant_names)
end end
should "populate the creator information" do should "populate the creator information" do
@@ -89,14 +89,11 @@ class TagImplicationTest < ActiveSupport::TestCase
should "calculate all its descendants" do should "calculate all its descendants" do
ti1 = FactoryBot.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc") ti1 = FactoryBot.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
assert_equal("ccc", ti1.descendant_names) assert_equal(%w[ccc], ti1.descendant_names)
assert_equal(["ccc"], ti1.descendant_names_array)
ti2 = FactoryBot.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb") ti2 = FactoryBot.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
assert_equal("bbb ccc", ti2.descendant_names) assert_equal(%w[bbb ccc], ti2.descendant_names)
assert_equal(["bbb", "ccc"], ti2.descendant_names_array)
ti1.reload ti1.reload
assert_equal("ccc", ti1.descendant_names) assert_equal(%w[ccc], ti1.descendant_names)
assert_equal(["ccc"], ti1.descendant_names_array)
end end
should "update its descendants on save" do should "update its descendants on save" do
@@ -109,8 +106,8 @@ class TagImplicationTest < ActiveSupport::TestCase
) )
ti1.reload ti1.reload
ti2.reload ti2.reload
assert_equal("bbb ddd", ti1.descendant_names) assert_equal(%w[bbb ddd], ti1.descendant_names)
assert_equal("ddd", ti2.descendant_names) assert_equal(%w[ddd], ti2.descendant_names)
end end
should "update the descendants for all of its parents on destroy" do should "update the descendants for all of its parents on destroy" do
@@ -122,49 +119,49 @@ class TagImplicationTest < ActiveSupport::TestCase
ti2.reload ti2.reload
ti3.reload ti3.reload
ti4.reload ti4.reload
assert_equal("bbb ccc ddd", ti1.descendant_names) assert_equal(%w[bbb ccc ddd], ti1.descendant_names)
assert_equal("bbb ccc ddd", ti2.descendant_names) assert_equal(%w[bbb ccc ddd], ti2.descendant_names)
assert_equal("ccc ddd", ti3.descendant_names) assert_equal(%w[ccc ddd], ti3.descendant_names)
assert_equal("ddd", ti4.descendant_names) assert_equal(%w[ddd], ti4.descendant_names)
ti3.destroy ti3.destroy
ti1.reload ti1.reload
ti2.reload ti2.reload
ti4.reload ti4.reload
assert_equal("bbb", ti1.descendant_names) assert_equal(%w[bbb], ti1.descendant_names)
assert_equal("bbb", ti2.descendant_names) assert_equal(%w[bbb], ti2.descendant_names)
assert_equal("ddd", ti4.descendant_names) assert_equal(%w[ddd], ti4.descendant_names)
end end
should "update the descendants for all of its parents on create" do should "update the descendants for all of its parents on create" do
ti1 = FactoryBot.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb") ti1 = FactoryBot.create(:tag_implication, :antecedent_name => "aaa", :consequent_name => "bbb")
ti1.reload ti1.reload
assert_equal("active", ti1.status) assert_equal("active", ti1.status)
assert_equal("bbb", ti1.descendant_names) assert_equal(%w[bbb], ti1.descendant_names)
ti2 = FactoryBot.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc") ti2 = FactoryBot.create(:tag_implication, :antecedent_name => "bbb", :consequent_name => "ccc")
ti1.reload ti1.reload
ti2.reload ti2.reload
assert_equal("active", ti1.status) assert_equal("active", ti1.status)
assert_equal("active", ti2.status) assert_equal("active", ti2.status)
assert_equal("bbb ccc", ti1.descendant_names) assert_equal(%w[bbb ccc], ti1.descendant_names)
assert_equal("ccc", ti2.descendant_names) assert_equal(%w[ccc], ti2.descendant_names)
ti3 = FactoryBot.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "ddd") ti3 = FactoryBot.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "ddd")
ti1.reload ti1.reload
ti2.reload ti2.reload
ti3.reload ti3.reload
assert_equal("bbb ccc ddd", ti1.descendant_names) assert_equal(%w[bbb ccc ddd], ti1.descendant_names)
assert_equal("ccc ddd", ti2.descendant_names) assert_equal(%w[ccc ddd], ti2.descendant_names)
ti4 = FactoryBot.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "eee") ti4 = FactoryBot.create(:tag_implication, :antecedent_name => "ccc", :consequent_name => "eee")
ti1.reload ti1.reload
ti2.reload ti2.reload
ti3.reload ti3.reload
ti4.reload ti4.reload
assert_equal("bbb ccc ddd eee", ti1.descendant_names) assert_equal(%w[bbb ccc ddd eee], ti1.descendant_names)
assert_equal("ccc ddd eee", ti2.descendant_names) assert_equal(%w[ccc ddd eee], ti2.descendant_names)
assert_equal("ddd", ti3.descendant_names) assert_equal(%w[ddd], ti3.descendant_names)
assert_equal("eee", ti4.descendant_names) assert_equal(%w[eee], ti4.descendant_names)
ti5 = FactoryBot.create(:tag_implication, :antecedent_name => "xxx", :consequent_name => "bbb") ti5 = FactoryBot.create(:tag_implication, :antecedent_name => "xxx", :consequent_name => "bbb")
ti1.reload ti1.reload
@@ -172,11 +169,11 @@ class TagImplicationTest < ActiveSupport::TestCase
ti3.reload ti3.reload
ti4.reload ti4.reload
ti5.reload ti5.reload
assert_equal("bbb ccc ddd eee", ti1.descendant_names) assert_equal(%w[bbb ccc ddd eee], ti1.descendant_names)
assert_equal("ccc ddd eee", ti2.descendant_names) assert_equal(%w[ccc ddd eee], ti2.descendant_names)
assert_equal("ddd", ti3.descendant_names) assert_equal(%w[ddd], ti3.descendant_names)
assert_equal("eee", ti4.descendant_names) assert_equal(%w[eee], ti4.descendant_names)
assert_equal("bbb ccc ddd eee", ti5.descendant_names) assert_equal(%w[bbb ccc ddd eee], ti5.descendant_names)
end end
should "update any affected post upon save" do should "update any affected post upon save" do

View File

@@ -63,7 +63,7 @@ class WikiPageTest < ActiveSupport::TestCase
should "normalize its other names" do should "normalize its other names" do
@wiki_page.update(:other_names => "foo*bar baz baz 加賀(艦これ)") @wiki_page.update(:other_names => "foo*bar baz baz 加賀(艦これ)")
assert(%w[foo*bar baz 加賀(艦これ)], @wiki_page.other_names_array) assert_equal(%w[foo*bar baz 加賀(艦これ)], @wiki_page.other_names)
end end
should "search by title" do should "search by title" do