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.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +97 -0
  4. data/Rakefile +29 -0
  5. data/app/assets/images/discerner/add.png +0 -0
  6. data/app/assets/images/discerner/ajax-loader.gif +0 -0
  7. data/app/assets/images/discerner/bar.gif +0 -0
  8. data/app/assets/images/discerner/bullet_arrow_down.png +0 -0
  9. data/app/assets/images/discerner/bullet_arrow_up.png +0 -0
  10. data/app/assets/images/discerner/cog.png +0 -0
  11. data/app/assets/images/discerner/delete.png +0 -0
  12. data/app/assets/images/discerner/edit.png +0 -0
  13. data/app/assets/images/discerner/excel.png +0 -0
  14. data/app/assets/images/discerner/greencheck.gif +0 -0
  15. data/app/assets/images/discerner/rails.png +0 -0
  16. data/app/assets/images/discerner/show.png +0 -0
  17. data/app/assets/images/discerner/switch_minus.gif +0 -0
  18. data/app/assets/images/discerner/switch_plus.gif +0 -0
  19. data/app/assets/javascripts/discerner/application.js +20 -0
  20. data/app/assets/javascripts/discerner/combobox.js +301 -0
  21. data/app/assets/javascripts/discerner/discerner.js +19 -0
  22. data/app/assets/javascripts/discerner/export_parameters.js +13 -0
  23. data/app/assets/javascripts/discerner/jquery/jquery.blockUI.js +576 -0
  24. data/app/assets/javascripts/discerner/jquery/jquery.form.js +1074 -0
  25. data/app/assets/javascripts/discerner/nested_attributes.js +69 -0
  26. data/app/assets/javascripts/discerner/search_combinations.js +49 -0
  27. data/app/assets/javascripts/discerner/search_parameter.js +131 -0
  28. data/app/assets/javascripts/discerner/search_parameter_value.js +172 -0
  29. data/app/assets/javascripts/discerner/searches.js +93 -0
  30. data/app/assets/javascripts/discerner/url.js +42 -0
  31. data/app/assets/stylesheets/discerner/application.css +16 -0
  32. data/app/assets/stylesheets/discerner/buttons.sass +13 -0
  33. data/app/assets/stylesheets/discerner/categorized_autocompleter.sass +53 -0
  34. data/app/assets/stylesheets/discerner/discerner.sass +51 -0
  35. data/app/assets/stylesheets/discerner/export_parameters.sass +43 -0
  36. data/app/assets/stylesheets/discerner/links.sass +29 -0
  37. data/app/assets/stylesheets/discerner/searches.sass +205 -0
  38. data/app/assets/stylesheets/discerner/tables.sass +32 -0
  39. data/app/controllers/discerner/application_controller.rb +2 -0
  40. data/app/controllers/discerner/export_parameters_controller.rb +5 -0
  41. data/app/controllers/discerner/parameters_controller.rb +5 -0
  42. data/app/controllers/discerner/searches_controller.rb +5 -0
  43. data/app/helpers/discerner/application_helper.rb +4 -0
  44. data/app/helpers/discerner/parameters_helper.rb +5 -0
  45. data/app/helpers/discerner/searches_helper.rb +5 -0
  46. data/app/models/discerner/dictionary.rb +5 -0
  47. data/app/models/discerner/export_parameter.rb +5 -0
  48. data/app/models/discerner/operator.rb +5 -0
  49. data/app/models/discerner/parameter.rb +5 -0
  50. data/app/models/discerner/parameter_category.rb +5 -0
  51. data/app/models/discerner/parameter_type.rb +5 -0
  52. data/app/models/discerner/parameter_value.rb +5 -0
  53. data/app/models/discerner/parameter_value_categorization.rb +5 -0
  54. data/app/models/discerner/parameter_value_category.rb +5 -0
  55. data/app/models/discerner/search.rb +5 -0
  56. data/app/models/discerner/search_combination.rb +5 -0
  57. data/app/models/discerner/search_parameter.rb +5 -0
  58. data/app/models/discerner/search_parameter_value.rb +5 -0
  59. data/app/views/discerner/export_parameters/index.html.haml +55 -0
  60. data/app/views/discerner/parameters/_values_autocompleter.html.haml +49 -0
  61. data/app/views/discerner/parameters/values.html.haml +1 -0
  62. data/app/views/discerner/searches/_form.html.haml +21 -0
  63. data/app/views/discerner/searches/_form_combined_searches.html.haml +32 -0
  64. data/app/views/discerner/searches/_form_controls.html.haml +8 -0
  65. data/app/views/discerner/searches/_form_header.html.haml +39 -0
  66. data/app/views/discerner/searches/_form_search_parameters.html.haml +28 -0
  67. data/app/views/discerner/searches/_list.html.haml +17 -0
  68. data/app/views/discerner/searches/_search_combination_fields.html.haml +22 -0
  69. data/app/views/discerner/searches/_search_parameter_fields.html.haml +48 -0
  70. data/app/views/discerner/searches/_search_parameter_value_fields.html.haml +62 -0
  71. data/app/views/discerner/searches/_summary.html.haml +25 -0
  72. data/app/views/discerner/searches/edit.html.haml +8 -0
  73. data/app/views/discerner/searches/index.html.haml +13 -0
  74. data/app/views/discerner/searches/index.js.haml +1 -0
  75. data/app/views/discerner/searches/new.html.haml +3 -0
  76. data/app/views/discerner/searches/rename.html.haml +6 -0
  77. data/app/views/discerner/searches/update.js.haml +7 -0
  78. data/app/views/discerner/shared/_categorized_autocompleter_items.html.haml +15 -0
  79. data/app/views/discerner/shared/_error_messages.html.haml +5 -0
  80. data/app/views/layouts/discerner/searches.html.erb +17 -0
  81. data/config/cucumber.yml +8 -0
  82. data/config/routes.rb +17 -0
  83. data/db/migrate/20121004040716_create_discerner_dictionaries.rb +11 -0
  84. data/db/migrate/20121004153043_create_discerner_parameter_categories.rb +12 -0
  85. data/db/migrate/20121005194843_create_discerner_parameter_types.rb +11 -0
  86. data/db/migrate/20121005203223_create_discerner_parameters.rb +14 -0
  87. data/db/migrate/20121008154855_create_discerner_operators.rb +13 -0
  88. data/db/migrate/20121008160313_create_discerner_operators_parameter_types.rb +8 -0
  89. data/db/migrate/20121008161455_create_discerner_parameter_values.rb +13 -0
  90. data/db/migrate/20121008180829_create_discerner_searches.rb +11 -0
  91. data/db/migrate/20121008182443_create_discerner_search_parameters.rb +11 -0
  92. data/db/migrate/20121011205130_create_discerner_search_parameter_values.rb +15 -0
  93. data/db/migrate/20121211213215_add_dictionary_id_to_discerner_searches.rb +5 -0
  94. data/db/migrate/20130205193602_create_discerner_search_combinations.rb +12 -0
  95. data/db/migrate/20130211230636_create_discerner_export_parameters.rb +9 -0
  96. data/db/migrate/20130213185818_add_search_columns_to_discerner_parameters.rb +38 -0
  97. data/db/migrate/20130213205255_rename_database_name_to_search_value_in_discerner_parameter_values.rb +5 -0
  98. data/db/migrate/20130215165416_add_searchable_to_discerner_parameters.rb +5 -0
  99. data/db/migrate/20130218230257_add_exclusive_to_discerner_parameters.rb +5 -0
  100. data/db/migrate/20130220163015_replace_integer_type_with_numeric.rb +22 -0
  101. data/db/migrate/20130221172826_add_unique_identifier_to_discerner_parameters.rb +5 -0
  102. data/db/migrate/20130222052924_change_search_attribute_to_method.rb +29 -0
  103. data/db/migrate/20130222070959_add_export_columns_to_discerner_parameters.rb +6 -0
  104. data/db/migrate/20130227031747_add_unique_identifier_to_discerner_operators.rb +16 -0
  105. data/db/migrate/20130306015019_change_discerner_parameter_values_name.rb +9 -0
  106. data/db/migrate/20130306212430_add_deleted_at_to_discerner_search_parameters.rb +5 -0
  107. data/db/migrate/20130306212504_add_deleted_at_to_discerner_search_parameter_values.rb +5 -0
  108. data/db/migrate/20130306212527_add_deleted_at_to_discerner_search_combinations.rb +5 -0
  109. data/db/migrate/20130306212818_add_deleted_at_to_discerner_export_parameters.rb +5 -0
  110. data/db/migrate/20130311190717_add_operator_type_to_discerner_operators.rb +5 -0
  111. data/db/migrate/20131212175110_remove_extra_search_parameter_values.rb +10 -0
  112. data/db/migrate/20140204170625_create_discerner_parameter_value_categories.rb +16 -0
  113. data/db/migrate/20140204170646_create_discerner_parameter_value_categorizations.rb +12 -0
  114. data/db/migrate/20140227191827_remove_blank_parameter_values_from_export_parameters.rb +10 -0
  115. data/db/seeds.rb +0 -0
  116. data/lib/discerner/engine.rb +16 -0
  117. data/lib/discerner/methods/controllers/export_parameters_controller.rb +38 -0
  118. data/lib/discerner/methods/controllers/parameters_controller.rb +30 -0
  119. data/lib/discerner/methods/controllers/searches_controller.rb +204 -0
  120. data/lib/discerner/methods/helpers/searches_helper.rb +145 -0
  121. data/lib/discerner/methods/models/dictionary.rb +54 -0
  122. data/lib/discerner/methods/models/export_parameter.rb +35 -0
  123. data/lib/discerner/methods/models/operator.rb +42 -0
  124. data/lib/discerner/methods/models/parameter.rb +90 -0
  125. data/lib/discerner/methods/models/parameter_category.rb +60 -0
  126. data/lib/discerner/methods/models/parameter_type.rb +33 -0
  127. data/lib/discerner/methods/models/parameter_value.rb +87 -0
  128. data/lib/discerner/methods/models/parameter_value_categorization.rb +21 -0
  129. data/lib/discerner/methods/models/parameter_value_category.rb +45 -0
  130. data/lib/discerner/methods/models/search.rb +147 -0
  131. data/lib/discerner/methods/models/search_combination.rb +50 -0
  132. data/lib/discerner/methods/models/search_parameter.rb +128 -0
  133. data/lib/discerner/methods/models/search_parameter_value.rb +130 -0
  134. data/lib/discerner/methods/models/soft_delete.rb +35 -0
  135. data/lib/discerner/methods/models/warning.rb +15 -0
  136. data/lib/discerner/parser.rb +497 -0
  137. data/lib/discerner/version.rb +3 -0
  138. data/lib/discerner.rb +4 -0
  139. data/lib/generators/discerner/dictionary/dictionary_generator.rb +38 -0
  140. data/lib/generators/discerner/dictionary/templates/model.rb +15 -0
  141. data/lib/generators/discerner/dictionary/templates/show.xls.erb +37 -0
  142. data/lib/generators/discerner/dictionary/templates/view.html.haml +2 -0
  143. data/lib/generators/discerner/install/install_generator.rb +153 -0
  144. data/lib/generators/discerner/install/templates/controllers/export_parameters_controller.rb +20 -0
  145. data/lib/generators/discerner/install/templates/controllers/parameters_controller.rb +15 -0
  146. data/lib/generators/discerner/install/templates/controllers/searches_controller.rb +39 -0
  147. data/lib/generators/discerner/install/templates/dictionaries.yml +236 -0
  148. data/lib/generators/discerner/install/templates/helpers/searches_helper.rb +9 -0
  149. data/lib/generators/discerner/install/templates/models/book.rb +15 -0
  150. data/lib/generators/discerner/install/templates/models/dictionary.rb +9 -0
  151. data/lib/generators/discerner/install/templates/models/export_parameter.rb +9 -0
  152. data/lib/generators/discerner/install/templates/models/operator.rb +9 -0
  153. data/lib/generators/discerner/install/templates/models/parameter.rb +10 -0
  154. data/lib/generators/discerner/install/templates/models/parameter_category.rb +9 -0
  155. data/lib/generators/discerner/install/templates/models/parameter_type.rb +9 -0
  156. data/lib/generators/discerner/install/templates/models/parameter_value.rb +9 -0
  157. data/lib/generators/discerner/install/templates/models/parameter_value_categorization.rb +9 -0
  158. data/lib/generators/discerner/install/templates/models/parameter_value_category.rb +9 -0
  159. data/lib/generators/discerner/install/templates/models/search.rb +9 -0
  160. data/lib/generators/discerner/install/templates/models/search_combination.rb +9 -0
  161. data/lib/generators/discerner/install/templates/models/search_parameter.rb +9 -0
  162. data/lib/generators/discerner/install/templates/models/search_parameter_value.rb +9 -0
  163. data/lib/generators/discerner/install/templates/views/layouts/searches.html.erb +17 -0
  164. data/lib/setup/operators.yml +91 -0
  165. data/lib/tasks/cucumber.rake +65 -0
  166. data/lib/tasks/discerner_tasks.rake +30 -0
  167. 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
@@ -0,0 +1,3 @@
1
+ module Discerner
2
+ VERSION = "1.1.18"
3
+ end
data/lib/discerner.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "discerner/engine"
2
+
3
+ module Discerner
4
+ end
@@ -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,15 @@
1
+ class <%= @class_name %>
2
+ attr_accessor :search
3
+
4
+ def initialize(search)
5
+ @search = search
6
+ end
7
+
8
+ def search(params = {}, options = {})
9
+
10
+ end
11
+
12
+ def export(params = {}, options = {})
13
+
14
+ end
15
+ 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>
@@ -0,0 +1,2 @@
1
+ %p
2
+ Results for search on the `<%=@dictionary_name %>` dictionary can be added here