Skip to content

Instantly share code, notes, and snippets.

@rossmari
Last active August 29, 2015 14:15
Show Gist options
  • Save rossmari/8bea8b06d9e224e0b580 to your computer and use it in GitHub Desktop.
Save rossmari/8bea8b06d9e224e0b580 to your computer and use it in GitHub Desktop.
Refactoring example [02]
# === BEFORE REFACTORING
# problems : include russian text, include translation, include unused code
#encoding: utf-8
class SearchOption < ActiveRecord::Base
has_no_table
column :include, :string
column :field_name, :string
column :text, :string
column :state, :string
column :mood_id, :integer
OPERATIONS = {eql: '=', neql: '!=', include: 'LIKE'}
HUMAN_OPERATIONS = {eql: 'равно', neql: 'не равно', include: 'похоже на'}
COLUMN_CONTROLS = {text: :value, state: :relation, mood_id: :relation}
RELATED_MODELS = {mood_id: Mood}
def self.get_related_models(column)
if column == :state
states = Publishing::Publication::Base.state_machine.states.map(&:name)
return states.map{|state| {name: I18n.t("PublicationStates.#{state.to_s}"), id: state} }
end
model = RELATED_MODELS[column]
model.all.map{|i| {id: i.id, name: i.name}}
end
# параметры объявления по которым разрешен параметризованный поиск
SEARCH_COLUMNS = {state: 'Статус', text: 'Текст', mood_id: 'Настроение'}
def self.search_columns
SEARCH_COLUMNS.map{|c| [c[1], c[0]]}
end
def self.operations_with_human_labels
operations = []
OPERATIONS.keys.each do |key|
operations << [HUMAN_OPERATIONS[key], key]
end
operations
end
end
# === AFTER REFACTORING
# remove all translation and russian code , move them into presenter class
# add procs to define different queries (solve problem with LIKE query)
# include generation of query logic in this class
class ParametrizeSearcher < ActiveRecord::Base
# no table model to store and process custom search
has_no_table
column :include, :string
column :field_name, :string
# параметры объявления по которым разрешен параметризованный поиск
SEARCH_COLUMNS = [ :state, :text, :mood_id ]
# поддерживаемые операции сравнения
OPERATIONS = [:equal, :not_equal, :include]
SEARCH_PROCS = { equal: ->(param, value){"#{param} = '#{value}'"},
not_equal: ->(param, value){"#{param} != #{value}"},
include: ->(param, value){"#{param} ILIKE '%#{value}%'"}
}
# construct search query from array of sub queries containing operation_type (proc), column and value
def self.construct_query(options)
query = Publishing::Publication::Base
options.each do |option|
proc = SEARCH_PROCS[option['include'].to_sym]
param = option['field_name']
value = option['value_field']
query = query.where(proc.call(param, value))
end
return query
end
end
# === AFTER REFACTORING
# this class was added to store logic of translation and data presentation
class ParametrizeSearchPresenter
# to set relation between column and form control type
COLUMN_CONTROLS = {text: :value, state: :relation, mood_id: :relation}
class << self
def search_columns
ParametrizeSearcher::SEARCH_COLUMNS.map{|c| [I18n.t("parametrize_searcher.columns.#{c}"), c]}
end
def operations_with_human_labels
ParametrizeSearcher::OPERATIONS.map{|o| [I18n.t("parametrize_searcher.operations.#{o}"), o.to_s]}
end
def available_values(column)
case column
when :state
states = Publishing::Publication::Base.state_machine.states.map(&:name)
states.map{|state| {name: I18n.t("PublicationStates.#{state.to_s}"), id: state} }
when :mood_id
Mood.all.map{|i| {id: i.id, name: i.name}}
else
nil
end
end
end
end
# === AFTER REFACTORING
# yml translation file now include all translations
ru:
parametrize_searcher:
columns:
state: Статус объявления
text: Текст объявления
mood_id: Настроение объявления
operations:
equal: Равно
not_equal: Не равно
include: Похоже на
# === BEFORE REFACTORING
# problems: to fat, include logic for query generation
# problems: cant create query "column LIKE '%value%'" (row 9)
def find
options = params[:search_query][:search_options_attributes].map{|s| s[1]}.select{|s| s['_destroy'] == 'false'}
query = Publishing::Publication::Base
options.each do |option|
operator = SearchOption::OPERATIONS[option['include'].to_sym]
query = query.where("#{option['field_name']} #{operator} '#{option['value_field']}'")
end
@users = query.map{|p| p.user}.uniq.compact
@users_count = @users.count
@notification = ArchiveEmail.new()
end
# === AFTER REFACTORING
# use ParametrizeSearcher.construct_query() to create query
def find
# remove deleted rows from nested attributes
options = params[:search_query][:parametrize_searchers_attributes].map{|s| s[1]}.select{|s| s['_destroy'] == 'false'}
query = ParametrizeSearcher.construct_query(options)
@users = query.map{|p| p.user}.uniq.compact
@users_count = @users.count
@notification = ArchiveEmail.new()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment