discerner 1.1.18
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +97 -0
- data/Rakefile +29 -0
- data/app/assets/images/discerner/add.png +0 -0
- data/app/assets/images/discerner/ajax-loader.gif +0 -0
- data/app/assets/images/discerner/bar.gif +0 -0
- data/app/assets/images/discerner/bullet_arrow_down.png +0 -0
- data/app/assets/images/discerner/bullet_arrow_up.png +0 -0
- data/app/assets/images/discerner/cog.png +0 -0
- data/app/assets/images/discerner/delete.png +0 -0
- data/app/assets/images/discerner/edit.png +0 -0
- data/app/assets/images/discerner/excel.png +0 -0
- data/app/assets/images/discerner/greencheck.gif +0 -0
- data/app/assets/images/discerner/rails.png +0 -0
- data/app/assets/images/discerner/show.png +0 -0
- data/app/assets/images/discerner/switch_minus.gif +0 -0
- data/app/assets/images/discerner/switch_plus.gif +0 -0
- data/app/assets/javascripts/discerner/application.js +20 -0
- data/app/assets/javascripts/discerner/combobox.js +301 -0
- data/app/assets/javascripts/discerner/discerner.js +19 -0
- data/app/assets/javascripts/discerner/export_parameters.js +13 -0
- data/app/assets/javascripts/discerner/jquery/jquery.blockUI.js +576 -0
- data/app/assets/javascripts/discerner/jquery/jquery.form.js +1074 -0
- data/app/assets/javascripts/discerner/nested_attributes.js +69 -0
- data/app/assets/javascripts/discerner/search_combinations.js +49 -0
- data/app/assets/javascripts/discerner/search_parameter.js +131 -0
- data/app/assets/javascripts/discerner/search_parameter_value.js +172 -0
- data/app/assets/javascripts/discerner/searches.js +93 -0
- data/app/assets/javascripts/discerner/url.js +42 -0
- data/app/assets/stylesheets/discerner/application.css +16 -0
- data/app/assets/stylesheets/discerner/buttons.sass +13 -0
- data/app/assets/stylesheets/discerner/categorized_autocompleter.sass +53 -0
- data/app/assets/stylesheets/discerner/discerner.sass +51 -0
- data/app/assets/stylesheets/discerner/export_parameters.sass +43 -0
- data/app/assets/stylesheets/discerner/links.sass +29 -0
- data/app/assets/stylesheets/discerner/searches.sass +205 -0
- data/app/assets/stylesheets/discerner/tables.sass +32 -0
- data/app/controllers/discerner/application_controller.rb +2 -0
- data/app/controllers/discerner/export_parameters_controller.rb +5 -0
- data/app/controllers/discerner/parameters_controller.rb +5 -0
- data/app/controllers/discerner/searches_controller.rb +5 -0
- data/app/helpers/discerner/application_helper.rb +4 -0
- data/app/helpers/discerner/parameters_helper.rb +5 -0
- data/app/helpers/discerner/searches_helper.rb +5 -0
- data/app/models/discerner/dictionary.rb +5 -0
- data/app/models/discerner/export_parameter.rb +5 -0
- data/app/models/discerner/operator.rb +5 -0
- data/app/models/discerner/parameter.rb +5 -0
- data/app/models/discerner/parameter_category.rb +5 -0
- data/app/models/discerner/parameter_type.rb +5 -0
- data/app/models/discerner/parameter_value.rb +5 -0
- data/app/models/discerner/parameter_value_categorization.rb +5 -0
- data/app/models/discerner/parameter_value_category.rb +5 -0
- data/app/models/discerner/search.rb +5 -0
- data/app/models/discerner/search_combination.rb +5 -0
- data/app/models/discerner/search_parameter.rb +5 -0
- data/app/models/discerner/search_parameter_value.rb +5 -0
- data/app/views/discerner/export_parameters/index.html.haml +55 -0
- data/app/views/discerner/parameters/_values_autocompleter.html.haml +49 -0
- data/app/views/discerner/parameters/values.html.haml +1 -0
- data/app/views/discerner/searches/_form.html.haml +21 -0
- data/app/views/discerner/searches/_form_combined_searches.html.haml +32 -0
- data/app/views/discerner/searches/_form_controls.html.haml +8 -0
- data/app/views/discerner/searches/_form_header.html.haml +39 -0
- data/app/views/discerner/searches/_form_search_parameters.html.haml +28 -0
- data/app/views/discerner/searches/_list.html.haml +17 -0
- data/app/views/discerner/searches/_search_combination_fields.html.haml +22 -0
- data/app/views/discerner/searches/_search_parameter_fields.html.haml +48 -0
- data/app/views/discerner/searches/_search_parameter_value_fields.html.haml +62 -0
- data/app/views/discerner/searches/_summary.html.haml +25 -0
- data/app/views/discerner/searches/edit.html.haml +8 -0
- data/app/views/discerner/searches/index.html.haml +13 -0
- data/app/views/discerner/searches/index.js.haml +1 -0
- data/app/views/discerner/searches/new.html.haml +3 -0
- data/app/views/discerner/searches/rename.html.haml +6 -0
- data/app/views/discerner/searches/update.js.haml +7 -0
- data/app/views/discerner/shared/_categorized_autocompleter_items.html.haml +15 -0
- data/app/views/discerner/shared/_error_messages.html.haml +5 -0
- data/app/views/layouts/discerner/searches.html.erb +17 -0
- data/config/cucumber.yml +8 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20121004040716_create_discerner_dictionaries.rb +11 -0
- data/db/migrate/20121004153043_create_discerner_parameter_categories.rb +12 -0
- data/db/migrate/20121005194843_create_discerner_parameter_types.rb +11 -0
- data/db/migrate/20121005203223_create_discerner_parameters.rb +14 -0
- data/db/migrate/20121008154855_create_discerner_operators.rb +13 -0
- data/db/migrate/20121008160313_create_discerner_operators_parameter_types.rb +8 -0
- data/db/migrate/20121008161455_create_discerner_parameter_values.rb +13 -0
- data/db/migrate/20121008180829_create_discerner_searches.rb +11 -0
- data/db/migrate/20121008182443_create_discerner_search_parameters.rb +11 -0
- data/db/migrate/20121011205130_create_discerner_search_parameter_values.rb +15 -0
- data/db/migrate/20121211213215_add_dictionary_id_to_discerner_searches.rb +5 -0
- data/db/migrate/20130205193602_create_discerner_search_combinations.rb +12 -0
- data/db/migrate/20130211230636_create_discerner_export_parameters.rb +9 -0
- data/db/migrate/20130213185818_add_search_columns_to_discerner_parameters.rb +38 -0
- data/db/migrate/20130213205255_rename_database_name_to_search_value_in_discerner_parameter_values.rb +5 -0
- data/db/migrate/20130215165416_add_searchable_to_discerner_parameters.rb +5 -0
- data/db/migrate/20130218230257_add_exclusive_to_discerner_parameters.rb +5 -0
- data/db/migrate/20130220163015_replace_integer_type_with_numeric.rb +22 -0
- data/db/migrate/20130221172826_add_unique_identifier_to_discerner_parameters.rb +5 -0
- data/db/migrate/20130222052924_change_search_attribute_to_method.rb +29 -0
- data/db/migrate/20130222070959_add_export_columns_to_discerner_parameters.rb +6 -0
- data/db/migrate/20130227031747_add_unique_identifier_to_discerner_operators.rb +16 -0
- data/db/migrate/20130306015019_change_discerner_parameter_values_name.rb +9 -0
- data/db/migrate/20130306212430_add_deleted_at_to_discerner_search_parameters.rb +5 -0
- data/db/migrate/20130306212504_add_deleted_at_to_discerner_search_parameter_values.rb +5 -0
- data/db/migrate/20130306212527_add_deleted_at_to_discerner_search_combinations.rb +5 -0
- data/db/migrate/20130306212818_add_deleted_at_to_discerner_export_parameters.rb +5 -0
- data/db/migrate/20130311190717_add_operator_type_to_discerner_operators.rb +5 -0
- data/db/migrate/20131212175110_remove_extra_search_parameter_values.rb +10 -0
- data/db/migrate/20140204170625_create_discerner_parameter_value_categories.rb +16 -0
- data/db/migrate/20140204170646_create_discerner_parameter_value_categorizations.rb +12 -0
- data/db/migrate/20140227191827_remove_blank_parameter_values_from_export_parameters.rb +10 -0
- data/db/seeds.rb +0 -0
- data/lib/discerner/engine.rb +16 -0
- data/lib/discerner/methods/controllers/export_parameters_controller.rb +38 -0
- data/lib/discerner/methods/controllers/parameters_controller.rb +30 -0
- data/lib/discerner/methods/controllers/searches_controller.rb +204 -0
- data/lib/discerner/methods/helpers/searches_helper.rb +145 -0
- data/lib/discerner/methods/models/dictionary.rb +54 -0
- data/lib/discerner/methods/models/export_parameter.rb +35 -0
- data/lib/discerner/methods/models/operator.rb +42 -0
- data/lib/discerner/methods/models/parameter.rb +90 -0
- data/lib/discerner/methods/models/parameter_category.rb +60 -0
- data/lib/discerner/methods/models/parameter_type.rb +33 -0
- data/lib/discerner/methods/models/parameter_value.rb +87 -0
- data/lib/discerner/methods/models/parameter_value_categorization.rb +21 -0
- data/lib/discerner/methods/models/parameter_value_category.rb +45 -0
- data/lib/discerner/methods/models/search.rb +147 -0
- data/lib/discerner/methods/models/search_combination.rb +50 -0
- data/lib/discerner/methods/models/search_parameter.rb +128 -0
- data/lib/discerner/methods/models/search_parameter_value.rb +130 -0
- data/lib/discerner/methods/models/soft_delete.rb +35 -0
- data/lib/discerner/methods/models/warning.rb +15 -0
- data/lib/discerner/parser.rb +497 -0
- data/lib/discerner/version.rb +3 -0
- data/lib/discerner.rb +4 -0
- data/lib/generators/discerner/dictionary/dictionary_generator.rb +38 -0
- data/lib/generators/discerner/dictionary/templates/model.rb +15 -0
- data/lib/generators/discerner/dictionary/templates/show.xls.erb +37 -0
- data/lib/generators/discerner/dictionary/templates/view.html.haml +2 -0
- data/lib/generators/discerner/install/install_generator.rb +153 -0
- data/lib/generators/discerner/install/templates/controllers/export_parameters_controller.rb +20 -0
- data/lib/generators/discerner/install/templates/controllers/parameters_controller.rb +15 -0
- data/lib/generators/discerner/install/templates/controllers/searches_controller.rb +39 -0
- data/lib/generators/discerner/install/templates/dictionaries.yml +236 -0
- data/lib/generators/discerner/install/templates/helpers/searches_helper.rb +9 -0
- data/lib/generators/discerner/install/templates/models/book.rb +15 -0
- data/lib/generators/discerner/install/templates/models/dictionary.rb +9 -0
- data/lib/generators/discerner/install/templates/models/export_parameter.rb +9 -0
- data/lib/generators/discerner/install/templates/models/operator.rb +9 -0
- data/lib/generators/discerner/install/templates/models/parameter.rb +10 -0
- data/lib/generators/discerner/install/templates/models/parameter_category.rb +9 -0
- data/lib/generators/discerner/install/templates/models/parameter_type.rb +9 -0
- data/lib/generators/discerner/install/templates/models/parameter_value.rb +9 -0
- data/lib/generators/discerner/install/templates/models/parameter_value_categorization.rb +9 -0
- data/lib/generators/discerner/install/templates/models/parameter_value_category.rb +9 -0
- data/lib/generators/discerner/install/templates/models/search.rb +9 -0
- data/lib/generators/discerner/install/templates/models/search_combination.rb +9 -0
- data/lib/generators/discerner/install/templates/models/search_parameter.rb +9 -0
- data/lib/generators/discerner/install/templates/models/search_parameter_value.rb +9 -0
- data/lib/generators/discerner/install/templates/views/layouts/searches.html.erb +17 -0
- data/lib/setup/operators.yml +91 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/discerner_tasks.rake +30 -0
- metadata +531 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
module Discerner
|
|
2
|
+
class Parser
|
|
3
|
+
attr_accessor :options, :errors, :updated_dictionaries, :updated_categories, :updated_parameters, :updated_parameter_value_categories, :updated_parameter_values, :blank_parameter_values
|
|
4
|
+
|
|
5
|
+
def initialize(options={})
|
|
6
|
+
self.options = options
|
|
7
|
+
self.errors = []
|
|
8
|
+
reset_counts
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def reset_counts
|
|
12
|
+
self.updated_dictionaries = []
|
|
13
|
+
self.updated_categories = []
|
|
14
|
+
self.updated_parameters = []
|
|
15
|
+
self.updated_parameter_values = []
|
|
16
|
+
self.updated_parameter_value_categories = []
|
|
17
|
+
self.blank_parameter_values = []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_dictionaries(str)
|
|
21
|
+
reset_counts
|
|
22
|
+
hash_from_file = YAML.load(str)
|
|
23
|
+
|
|
24
|
+
# find or initialize dictionaries
|
|
25
|
+
dictionaries_from_file = hash_from_file[:dictionaries]
|
|
26
|
+
error_message 'No dictionaries detected.' if dictionaries_from_file.blank?
|
|
27
|
+
|
|
28
|
+
Discerner::Dictionary.transaction do
|
|
29
|
+
dictionaries_from_file.each do |dictionary_from_file|
|
|
30
|
+
dictionary = parse_dictionary(dictionary_from_file)
|
|
31
|
+
|
|
32
|
+
## find or initialize parameter categories
|
|
33
|
+
parameter_categories_from_file = dictionary_from_file[:parameter_categories]
|
|
34
|
+
error_message 'no parameter categories detected' if parameter_categories_from_file.blank?
|
|
35
|
+
|
|
36
|
+
parameter_categories_from_file.each do |parameter_category_from_file|
|
|
37
|
+
parameter_category = parse_parameter_category(dictionary, parameter_category_from_file)
|
|
38
|
+
|
|
39
|
+
## find or initialize parameters
|
|
40
|
+
parameters_from_file = parameter_category_from_file[:parameters]
|
|
41
|
+
error_message 'no parameters detected' if parameters_from_file.blank?
|
|
42
|
+
|
|
43
|
+
parameters_from_file.each do |parameter_from_file|
|
|
44
|
+
parameter = parse_parameter(parameter_category, parameter_from_file)
|
|
45
|
+
|
|
46
|
+
search_identifiers = parameter_from_file[:search]
|
|
47
|
+
unless search_identifiers.blank?
|
|
48
|
+
## find or initialize parameter value categories
|
|
49
|
+
unless search_identifiers[:parameter_value_categories].blank?
|
|
50
|
+
search_identifiers[:parameter_value_categories].each do |parameter_value_category_from_file|
|
|
51
|
+
parse_parameter_value_category(parameter, parameter_value_category_from_file)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
unless search_identifiers[:parameter_value_categories_source].blank?
|
|
56
|
+
load_parameter_value_categories_from_source(parameter, search_identifiers[:parameter_value_categories_source])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
## find or initialize parameter values
|
|
60
|
+
unless search_identifiers[:parameter_values].blank?
|
|
61
|
+
search_identifiers[:parameter_values].each do |parameter_value_from_file|
|
|
62
|
+
parse_parameter_value(parameter, parameter_value_from_file)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
unless search_identifiers[:source].blank?
|
|
67
|
+
load_parameter_value_from_source(parameter, search_identifiers[:source])
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
unless search_identifiers[:allow_empty_values] == false
|
|
71
|
+
blank_parameter_values << find_or_create_parameter_value(parameter, '', 'None', nil, true)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
notification_message "cleaing up ..."
|
|
79
|
+
cleanup unless errors.any?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def parse_dictionary(hash)
|
|
83
|
+
error_message 'dictionary definition was not provided' if hash.blank?
|
|
84
|
+
|
|
85
|
+
dictionary_name = hash[:name]
|
|
86
|
+
error_message 'dictionary name cannot be blank' if dictionary_name.blank?
|
|
87
|
+
notification_message "processing dictionary '#{dictionary_name}'"
|
|
88
|
+
|
|
89
|
+
dictionary = Discerner::Dictionary.find_or_initialize_by_name(dictionary_name)
|
|
90
|
+
dictionary.deleted_at = nil
|
|
91
|
+
|
|
92
|
+
if dictionary.new_record?
|
|
93
|
+
notification_message "creating dictionary ..."
|
|
94
|
+
dictionary.created_at = Time.now
|
|
95
|
+
else
|
|
96
|
+
notification_message "updating dictionary ..."
|
|
97
|
+
dictionary.updated_at = Time.now
|
|
98
|
+
end
|
|
99
|
+
error_message "dictionary could not be saved: #{dictionary.errors.full_messages}", dictionary_name unless dictionary.save
|
|
100
|
+
notification_message 'dictionary saved'
|
|
101
|
+
updated_dictionaries << dictionary
|
|
102
|
+
dictionary
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def parse_parameter_category(dictionary, hash)
|
|
106
|
+
error_message 'parameter category definition was not provided' if hash.blank?
|
|
107
|
+
|
|
108
|
+
parameter_category_name = hash[:name]
|
|
109
|
+
error_message 'parameter category name cannot be blank' if parameter_category_name.blank?
|
|
110
|
+
notification_message "processing parameter category '#{parameter_category_name}'"
|
|
111
|
+
|
|
112
|
+
parameter_category = Discerner::ParameterCategory.where(:name => parameter_category_name, :dictionary_id => dictionary.id).first_or_initialize
|
|
113
|
+
parameter_category.deleted_at = nil
|
|
114
|
+
|
|
115
|
+
if parameter_category.new_record?
|
|
116
|
+
notification_message "creating parameter category ..."
|
|
117
|
+
parameter_category.created_at = Time.now
|
|
118
|
+
else
|
|
119
|
+
notification_message "updating parameter category ..."
|
|
120
|
+
parameter_category.updated_at = Time.now
|
|
121
|
+
end
|
|
122
|
+
error_message "parameter category could not be saved: #{parameter_category.errors.full_messages}", parameter_category_name unless parameter_category.save
|
|
123
|
+
notification_message 'parameter category saved'
|
|
124
|
+
updated_categories << parameter_category
|
|
125
|
+
parameter_category
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def parse_parameter(parameter_category, hash)
|
|
129
|
+
error_message 'parameter definition was not provided' if hash.blank?
|
|
130
|
+
|
|
131
|
+
parameter_name = hash[:name]
|
|
132
|
+
error_message 'parameter name cannot be blank' if parameter_name.blank?
|
|
133
|
+
|
|
134
|
+
notification_message "processing parameter '#{parameter_name}'"
|
|
135
|
+
unique_identifier = hash[:unique_identifier]
|
|
136
|
+
error_message "unique_identifier cannot be blank", parameter_name if unique_identifier.blank?
|
|
137
|
+
|
|
138
|
+
existing_parameter = Discerner::Parameter.
|
|
139
|
+
includes({:parameter_category => :dictionary}).
|
|
140
|
+
where('discerner_parameters.unique_identifier = ? and discerner_dictionaries.id = ?', unique_identifier, parameter_category.dictionary.id).first
|
|
141
|
+
parameter = existing_parameter || Discerner::Parameter.new(:unique_identifier => unique_identifier, :parameter_category => parameter_category)
|
|
142
|
+
|
|
143
|
+
parameter.name = parameter_name
|
|
144
|
+
parameter.deleted_at = nil
|
|
145
|
+
parameter.exclusive = hash[:exclusive].nil? ? true : to_bool(hash[:exclusive])
|
|
146
|
+
parameter.parameter_category = parameter_category
|
|
147
|
+
|
|
148
|
+
search_identifiers = hash[:search]
|
|
149
|
+
unless search_identifiers.blank?
|
|
150
|
+
error_message "Searchable parameter should search model, search method and parameter_type defined." if
|
|
151
|
+
search_identifiers[:model].blank? || search_identifiers[:method].blank? || search_identifiers[:parameter_type].blank?
|
|
152
|
+
|
|
153
|
+
parameter.search_model = search_identifiers[:model].to_s
|
|
154
|
+
parameter.search_method = search_identifiers[:method].to_s
|
|
155
|
+
parameter.parameter_type = find_or_initialize_parameter_type(search_identifiers[:parameter_type])
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
export_identifiers = hash[:export]
|
|
159
|
+
unless export_identifiers.blank?
|
|
160
|
+
error_message "Exportable parameter should export model and export method defined." if
|
|
161
|
+
export_identifiers[:model].blank? || export_identifiers[:method].blank?
|
|
162
|
+
|
|
163
|
+
parameter.export_model = export_identifiers[:model].to_s
|
|
164
|
+
parameter.export_method = export_identifiers[:method].to_s
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
if parameter.new_record?
|
|
168
|
+
notification_message "creating parameter ..."
|
|
169
|
+
parameter.created_at = Time.now
|
|
170
|
+
else
|
|
171
|
+
notification_message "updating parameter ..."
|
|
172
|
+
parameter.updated_at = Time.now
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
error_message "parameter could not be saved: #{parameter.errors.full_messages}", parameter_name unless parameter.save
|
|
176
|
+
notification_message 'parameter saved'
|
|
177
|
+
updated_parameters << parameter
|
|
178
|
+
parameter
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def parse_parameter_value(parameter, hash)
|
|
182
|
+
error_message 'parameter value definition was not provided' if hash.blank?
|
|
183
|
+
search_value = hash[:search_value]
|
|
184
|
+
error_message 'parameter value search_value cannot be blank' if search_value.nil?
|
|
185
|
+
find_or_create_parameter_value(parameter, search_value, hash[:name], hash[:parameter_value_category], false)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def parse_parameter_value_category(parameter, hash)
|
|
189
|
+
error_message 'parameter value category definition was not provided' if hash.blank?
|
|
190
|
+
unique_identifier = hash[:unique_identifier]
|
|
191
|
+
name = hash[:name]
|
|
192
|
+
|
|
193
|
+
error_message 'parameter value category unique_identifier cannot be blank' if unique_identifier.nil?
|
|
194
|
+
error_message 'parameter value category name cannot be blank' if name.nil?
|
|
195
|
+
find_or_create_parameter_value_category(parameter, unique_identifier, name, hash[:display_order], hash[:collapse])
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def load_parameter_value_from_source(parameter, hash)
|
|
199
|
+
error_message 'parameter value definition was not provided' if hash.blank?
|
|
200
|
+
|
|
201
|
+
model_name = hash[:model]
|
|
202
|
+
method_name = hash[:method]
|
|
203
|
+
error_message "model and method must be defined for parameter source" if model_name.blank? || method_name.blank?
|
|
204
|
+
|
|
205
|
+
source_model = model_name.safe_constantize
|
|
206
|
+
error_message "model '#{model_name}' could not be found" if source_model.blank?
|
|
207
|
+
|
|
208
|
+
if source_model.respond_to?(method_name)
|
|
209
|
+
notification_message "method '#{method_name}' recognized as a class method"
|
|
210
|
+
|
|
211
|
+
parameter_values = source_model.send(method_name)
|
|
212
|
+
|
|
213
|
+
error_message "method '#{method_name}' did not return an array of values" if parameter_values.blank? || !parameter_values.kind_of?(Array)
|
|
214
|
+
|
|
215
|
+
if parameter_values.select { |parameter_value| !parameter_value.has_key?(:name) || !parameter_value.has_key?(:search_value) }.any?
|
|
216
|
+
error_message "method '#{method_name}' does not adhere to the interface"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
parameter_values.map{|parameter_value| find_or_create_parameter_value(parameter, parameter_value[:search_value], parameter_value[:name], parameter_value[:parameter_value_category]) }
|
|
220
|
+
else
|
|
221
|
+
notification_message "method '#{method_name}' is not recognized as a class method, will try it on instance.."
|
|
222
|
+
error_message "model '#{model_name}' does no respond to :all method" if !source_model.respond_to?(:all)
|
|
223
|
+
|
|
224
|
+
search_value_sources = source_model.send(:all)
|
|
225
|
+
error_message "model '#{method_name}' did not return an array of instances" if search_value_sources.blank?
|
|
226
|
+
|
|
227
|
+
search_value_sources.each do |row|
|
|
228
|
+
error_message "model '#{model_name}' instance does no respond to #{method_name} method" if !row.respond_to?(method_name)
|
|
229
|
+
find_or_create_parameter_value(parameter, row.send(method_name))
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def load_parameter_value_categories_from_source(parameter, hash)
|
|
235
|
+
error_message 'parameter value category definition was not provided' if hash.blank?
|
|
236
|
+
|
|
237
|
+
model_name = hash[:model]
|
|
238
|
+
method_name = hash[:method]
|
|
239
|
+
error_message "model and method must be defined for parameter value category source" if model_name.blank? || method_name.blank?
|
|
240
|
+
|
|
241
|
+
source_model = model_name.safe_constantize
|
|
242
|
+
error_message "model '#{model_name}' could not be found" if source_model.blank?
|
|
243
|
+
|
|
244
|
+
if source_model.respond_to?(method_name)
|
|
245
|
+
notification_message "method '#{method_name}' recognized as a class method"
|
|
246
|
+
|
|
247
|
+
parameter_value_categories = source_model.send(method_name)
|
|
248
|
+
|
|
249
|
+
error_message "method '#{method_name}' did not return an array of values" if parameter_value_categories.blank? || !parameter_value_categories.kind_of?(Array)
|
|
250
|
+
|
|
251
|
+
if parameter_value_categories.select { |parameter_value_category| !parameter_value_category.has_key?(:name) || !parameter_value_category.has_key?(:unique_identifier)}.any?
|
|
252
|
+
error_message "method '#{method_name}' does not adhere to the interface"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
parameter_value_categories.map{|parameter_value_category| find_or_create_parameter_value_category(parameter, parameter_value_category[:unique_identifier], parameter_value_category[:name], parameter_value_category[:display_order], parameter_value_category[:collapse])}
|
|
256
|
+
else
|
|
257
|
+
notification_message "method '#{method_name}' is not recognized as a class method, will try it on instance.."
|
|
258
|
+
error_message "model '#{model_name}' does no respond to :all method" if !source_model.respond_to?(:all)
|
|
259
|
+
|
|
260
|
+
parameter_value_category_sources = source_model.send(:all)
|
|
261
|
+
error_message "model '#{method_name}' did not return an array of instances" if parameter_value_category_sources.blank?
|
|
262
|
+
|
|
263
|
+
parameter_value_category_sources.each do |row|
|
|
264
|
+
error_message "model '#{model_name}' instance does no respond to #{method_name} method" if !row.respond_to?(method_name)
|
|
265
|
+
find_or_create_parameter_value_category(parameter, row.send(method_name))
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def parse_operators(str)
|
|
271
|
+
hash_from_file = YAML.load(str)
|
|
272
|
+
|
|
273
|
+
operators_from_file = hash_from_file[:operators]
|
|
274
|
+
error_message 'No operators detected in the file.' if operators_from_file.blank?
|
|
275
|
+
|
|
276
|
+
Discerner::Operator.transaction do
|
|
277
|
+
operators_from_file.each do |operator_from_file|
|
|
278
|
+
error_message 'unique identifier has to be defined' if operator_from_file[:unique_identifier].blank?
|
|
279
|
+
|
|
280
|
+
operator = Discerner::Operator.find_or_initialize_by_unique_identifier(operator_from_file[:unique_identifier])
|
|
281
|
+
if operator.new_record?
|
|
282
|
+
notification_message "creating operator '#{operator_from_file[:unique_identifier]}'"
|
|
283
|
+
operator.created_at = Time.now
|
|
284
|
+
else
|
|
285
|
+
notification_message "operator '#{operator_from_file[:unique_identifier]}' already exists and will be updated"
|
|
286
|
+
operator.updated_at = Time.now
|
|
287
|
+
end
|
|
288
|
+
operator.deleted_at = operator_from_file[:deleted].blank? ? nil : Time.now
|
|
289
|
+
|
|
290
|
+
unless operator_from_file[:parameter_types].blank?
|
|
291
|
+
operator.parameter_types.destroy_all
|
|
292
|
+
|
|
293
|
+
operator_from_file[:parameter_types].each do |parameter_type_from_file|
|
|
294
|
+
parameter_type = find_or_initialize_parameter_type(parameter_type_from_file)
|
|
295
|
+
operator.parameter_types << parameter_type
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
operator.symbol = operator_from_file[:symbol]
|
|
299
|
+
operator.text = operator_from_file[:text]
|
|
300
|
+
operator.operator_type = operator_from_file[:type]
|
|
301
|
+
#operator.deleted_at = operator_from_file[:deleted].blank? ? nil : Time.now
|
|
302
|
+
error_message "Operator could not be saved: #{operator.errors.full_messages}" unless operator.save
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def find_or_initialize_parameter_type(name)
|
|
308
|
+
error_message "Parameter type name has to be provided" if name.blank?
|
|
309
|
+
error_message "'integer' parameter type has been replaced with 'numeric', please update your dictionary definition" if /integer/.match(name.downcase)
|
|
310
|
+
|
|
311
|
+
## find or initialize parameter type
|
|
312
|
+
parameter_type = Discerner::ParameterType.find_or_initialize_by_name(name.downcase)
|
|
313
|
+
if parameter_type.new_record?
|
|
314
|
+
notification_message "Creating parameter type '#{name}'"
|
|
315
|
+
parameter_type.created_at = Time.now
|
|
316
|
+
else
|
|
317
|
+
notification_message "Parameter type '#{name}' already exists"
|
|
318
|
+
end
|
|
319
|
+
error_message "Parameter type #{name} could not be saved: #{parameter_type.errors.full_messages}" unless parameter_type.save
|
|
320
|
+
return parameter_type
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def find_or_create_parameter_value(parameter, search_value, name=nil, parameter_value_category_identifier=nil, silent=nil)
|
|
324
|
+
error_message "search value was not provided" if search_value.nil?
|
|
325
|
+
search_value = search_value.to_s
|
|
326
|
+
notification_message "processing parameter value '#{search_value}'"
|
|
327
|
+
|
|
328
|
+
parameter_value = Discerner::ParameterValue.where(:search_value => search_value, :parameter_id => parameter.id).first_or_initialize
|
|
329
|
+
if parameter_value.new_record?
|
|
330
|
+
notification_message "creating parameter value ..."
|
|
331
|
+
parameter_value.created_at = Time.now
|
|
332
|
+
else
|
|
333
|
+
notification_message "updating parameter value ..."
|
|
334
|
+
parameter_value.updated_at = Time.now
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
unless parameter_value_category_identifier.blank?
|
|
338
|
+
parameter_value_category = Discerner::ParameterValueCategory.where(:unique_identifier => parameter_value_category_identifier, :parameter_id => parameter.id).first_or_initialize
|
|
339
|
+
if parameter_value_category.blank?
|
|
340
|
+
error_message "parameter value category with unique identifier #{parameter_value_category_identifier} is not found for parameter #{parameter.name}"
|
|
341
|
+
else
|
|
342
|
+
parameter_value.parameter_value_category = parameter_value_category
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
parameter_value.name = name || search_value
|
|
347
|
+
parameter_value.deleted_at = nil
|
|
348
|
+
error_message "parameter value #{search_value} could not be saved: #{parameter_value.errors.full_messages}" unless parameter_value.save
|
|
349
|
+
notification_message 'parameter value saved'
|
|
350
|
+
updated_parameter_values << parameter_value unless silent
|
|
351
|
+
parameter_value
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def find_or_create_parameter_value_category(parameter, unique_identifier, name, display_order=0, collapse=nil)
|
|
355
|
+
error_message "unique_identifier was not provided" if unique_identifier.nil?
|
|
356
|
+
error_message "name was not provided" if name.nil?
|
|
357
|
+
|
|
358
|
+
unique_identifier = unique_identifier.to_s
|
|
359
|
+
notification_message "processing parameter value category '#{unique_identifier}'"
|
|
360
|
+
|
|
361
|
+
parameter_value_category = Discerner::ParameterValueCategory.where(:unique_identifier => unique_identifier, :parameter_id => parameter.id).first_or_initialize
|
|
362
|
+
if parameter_value_category.new_record?
|
|
363
|
+
notification_message "creating parameter value category..."
|
|
364
|
+
parameter_value_category.created_at = Time.now
|
|
365
|
+
else
|
|
366
|
+
notification_message "updating parameter value category ..."
|
|
367
|
+
parameter_value_category.updated_at = Time.now
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
parameter_value_category.name = name
|
|
371
|
+
parameter_value_category.display_order = display_order.to_i
|
|
372
|
+
parameter_value_category.collapse = collapse
|
|
373
|
+
parameter_value_category.deleted_at = nil
|
|
374
|
+
error_message "parameter value category #{unique_identifier} could not be saved: #{parameter_value_category.errors.full_messages}" unless parameter_value_category.save
|
|
375
|
+
notification_message 'parameter value category saved'
|
|
376
|
+
updated_parameter_value_categories << parameter_value_category
|
|
377
|
+
parameter_value_category
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def error_message(str, target=nil)
|
|
381
|
+
errors << "#{target}: #{str}"
|
|
382
|
+
puts "ERROR: #{str}"
|
|
383
|
+
reset_counts
|
|
384
|
+
raise ActiveRecord::Rollback
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def notification_message(str)
|
|
388
|
+
puts str unless self.options[:trace].blank?
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def to_bool(s)
|
|
392
|
+
return true if s == true || s =~ (/^(true|t|yes|y|1)$/i)
|
|
393
|
+
return false if s == false || s.blank? || s =~ (/^(false|f|no|n|0)$/i)
|
|
394
|
+
error_message("invalid value for Boolean: \"#{s}\"")
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def deleted?(param)
|
|
398
|
+
return false if param.blank?
|
|
399
|
+
to_bool(param)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
private
|
|
403
|
+
def cleanup
|
|
404
|
+
cleanup_parameter_value_categories
|
|
405
|
+
cleanup_parameter_values
|
|
406
|
+
cleanup_parameters
|
|
407
|
+
cleanup_categories
|
|
408
|
+
cleanup_dictionaries
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def cleanup_dictionaries
|
|
412
|
+
abandoned_dictionaries = Discerner::Dictionary.order(:id).to_a - updated_dictionaries
|
|
413
|
+
used_dictionaries = abandoned_dictionaries.reject{|d| d.searches.blank?}
|
|
414
|
+
not_used_dictionaries = abandoned_dictionaries - used_dictionaries
|
|
415
|
+
|
|
416
|
+
used_dictionaries.each do |r|
|
|
417
|
+
notification_message("marking dictionary #{r.name} as deleted");
|
|
418
|
+
r.deleted_at = Time.now
|
|
419
|
+
error_message "dictionary could not be updated: #{r.errors.full_messages}", r.name unless r.save
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
unless not_used_dictionaries.blank?
|
|
423
|
+
notification_message("permanently deleting dictionaries #{not_used_dictionaries.map{|r| r.name}.join(', ')}");
|
|
424
|
+
not_used_dictionaries.each{|r| r.destroy}
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def cleanup_categories
|
|
429
|
+
abandoned_categories = Discerner::ParameterCategory.order(:id).to_a - updated_categories
|
|
430
|
+
used_categories = abandoned_categories.reject{|c| c.parameters.blank? || c.parameters.select{|p| p.used_in_search?}.blank?}
|
|
431
|
+
not_used_categories = abandoned_categories - used_categories
|
|
432
|
+
|
|
433
|
+
used_categories.each do |r|
|
|
434
|
+
notification_message("marking parameter category #{r.name} as deleted");
|
|
435
|
+
r.deleted_at = Time.now
|
|
436
|
+
error_message "parameter category could not be deleted: #{r.errors.full_messages}", r.name unless r.save
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
unless not_used_categories.blank?
|
|
440
|
+
notification_message("permanently deleting parameter categories #{not_used_categories.map{|r| r.name}.join(', ')}");
|
|
441
|
+
not_used_categories.each{|r| r.destroy}
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def cleanup_parameters
|
|
446
|
+
abandoned_parameters = Discerner::Parameter.order(:id).to_a - updated_parameters
|
|
447
|
+
used_parameters = abandoned_parameters.select{|p| p.used_in_search?}
|
|
448
|
+
not_used_parameters = abandoned_parameters - used_parameters
|
|
449
|
+
|
|
450
|
+
used_parameters.each do |r|
|
|
451
|
+
notification_message("marking parameter #{r.name} as deleted");
|
|
452
|
+
r.deleted_at = Time.now
|
|
453
|
+
error_message "parameter could not be deleted: #{r.errors.full_messages}", r.name unless r.save
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
unless not_used_parameters.blank?
|
|
457
|
+
notification_message("permanently deleting parameters #{not_used_parameters.map{|r| r.name}.join(', ')}");
|
|
458
|
+
not_used_parameters.each{|r| r.destroy}
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def cleanup_parameter_value_categories
|
|
463
|
+
abandoned_categories = Discerner::ParameterValueCategory.order(:id).to_a - updated_parameter_value_categories
|
|
464
|
+
used_categories = abandoned_categories.reject{|c| c.parameter_values.blank? || c.parameter_values.select{|v| v.used_in_search?}.blank?}
|
|
465
|
+
not_used_categories = abandoned_categories - used_categories
|
|
466
|
+
|
|
467
|
+
used_categories.each do |r|
|
|
468
|
+
notification_message("marking parameter value category #{r.name} as deleted");
|
|
469
|
+
r.deleted_at = Time.now
|
|
470
|
+
error_message "parameter value category could not be deleted: #{r.errors.full_messages}", r.name unless r.save
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
unless not_used_categories.blank?
|
|
474
|
+
notification_message("permanently deleting parameter categories #{not_used_categories.map{|r| r.name}.join(', ')}");
|
|
475
|
+
not_used_categories.each{|r| r.destroy}
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
# this also marks search_parameter_values that reference this value and are chosen as deleted
|
|
479
|
+
# and destroys search_parameter_values that reference this value but are not chosen (list options)
|
|
480
|
+
def cleanup_parameter_values
|
|
481
|
+
abandoned_parameter_values = Discerner::ParameterValue.order(:id).to_a - updated_parameter_values - blank_parameter_values
|
|
482
|
+
used_parameter_values = abandoned_parameter_values.select{|p| p.used_in_search?}
|
|
483
|
+
not_used_parameter_values = abandoned_parameter_values - used_parameter_values
|
|
484
|
+
|
|
485
|
+
used_parameter_values.each do |r|
|
|
486
|
+
notification_message("marking parameter value #{r.name} as deleted");
|
|
487
|
+
r.deleted_at = Time.now
|
|
488
|
+
error_message "parameter value could not be marked as deleted: #{r.errors.full_messages}", r.name unless r.save
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
unless not_used_parameter_values.blank?
|
|
492
|
+
notification_message("permanently deleting parameter values #{not_used_parameter_values.map{|r| r.name}.join(', ')}");
|
|
493
|
+
not_used_parameter_values.each{|r| r.destroy}
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
data/lib/discerner.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Discerner
|
|
2
|
+
class DictionaryGenerator < Rails::Generators::Base
|
|
3
|
+
class_option "no-load", :type => :boolean
|
|
4
|
+
class_option "no-models", :type => :boolean
|
|
5
|
+
class_option "no-views", :type => :boolean
|
|
6
|
+
|
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
8
|
+
argument :dictionary_file_path, :type => :string
|
|
9
|
+
|
|
10
|
+
def parse_dictionary_file
|
|
11
|
+
rake("discerner:setup:dictionaries FILE=#{dictionary_file_path}") unless options["no-load"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_stub_dictionary_files
|
|
15
|
+
Discerner::Dictionary.not_deleted.each do |dictionary|
|
|
16
|
+
create_dictionary_class(dictionary) unless options["no-models"]
|
|
17
|
+
create_dictionary_view(dictionary) unless options["no-views"]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_excel_mime_type
|
|
22
|
+
inject_into_file("#{Rails.root}/config/initializers/mime_types.rb", 'Mime::Type.register "application/xls", :xls', :after => "# Be sure to restart your server when you modify this file.\n")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
def create_dictionary_class(dictionary)
|
|
27
|
+
@class_name = dictionary.parameterized_name.camelize
|
|
28
|
+
template "model.rb", "#{Rails.root}/app/models/#{dictionary.parameterized_name}.rb"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_dictionary_view(dictionary)
|
|
32
|
+
@dictionary_name = dictionary.name
|
|
33
|
+
empty_directory "#{Rails.root}/app/views/discerner/dictionaries/#{dictionary.parameterized_name}"
|
|
34
|
+
template "view.html.haml", "#{Rails.root}/app/views/discerner/dictionaries/#{dictionary.parameterized_name}/_results.html.haml"
|
|
35
|
+
template "show.xls.erb", "#{Rails.root}/app/views/discerner/dictionaries/#{dictionary.parameterized_name}/show.xls.erb"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
|
|
3
|
+
xmlns:o="urn:schemas-microsoft-com:office:office"
|
|
4
|
+
xmlns:x="urn:schemas-microsoft-com:office:excel"
|
|
5
|
+
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
|
|
6
|
+
xmlns:html="http://www.w3.org/TR/REC-html40">
|
|
7
|
+
<Worksheet ss:Name="Sheet1">
|
|
8
|
+
<Table>
|
|
9
|
+
<Row>
|
|
10
|
+
<Cell><Data ss:Type="String">ID</Data></Cell>
|
|
11
|
+
<Cell><Data ss:Type="String">Name</Data></Cell>
|
|
12
|
+
<Cell><Data ss:Type="String">Release Date</Data></Cell>
|
|
13
|
+
<Cell><Data ss:Type="String">Price</Data></Cell>
|
|
14
|
+
</Row>
|
|
15
|
+
</Table>
|
|
16
|
+
</Worksheet>
|
|
17
|
+
<Worksheet ss:Name="Sheet2">
|
|
18
|
+
<Table>
|
|
19
|
+
<Row>
|
|
20
|
+
<Cell><Data ss:Type="String">ID</Data></Cell>
|
|
21
|
+
<Cell><Data ss:Type="String">Name</Data></Cell>
|
|
22
|
+
<Cell><Data ss:Type="String">Release Date</Data></Cell>
|
|
23
|
+
<Cell><Data ss:Type="String">Price</Data></Cell>
|
|
24
|
+
</Row>
|
|
25
|
+
</Table>
|
|
26
|
+
</Worksheet>
|
|
27
|
+
<Worksheet ss:Name="Sheet3">
|
|
28
|
+
<Table>
|
|
29
|
+
<Row>
|
|
30
|
+
<Cell><Data ss:Type="String">ID</Data></Cell>
|
|
31
|
+
<Cell><Data ss:Type="String">Name</Data></Cell>
|
|
32
|
+
<Cell><Data ss:Type="String">Release Date</Data></Cell>
|
|
33
|
+
<Cell><Data ss:Type="String">Price</Data></Cell>
|
|
34
|
+
</Row>
|
|
35
|
+
</Table>
|
|
36
|
+
</Worksheet>
|
|
37
|
+
</Workbook>
|