discerner 1.1.18

Sign up to get free protection for your applications and to get access to all the features.
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,128 @@
1
+ module Discerner
2
+ module Methods
3
+ module Models
4
+ module SearchParameter
5
+ def self.included(base)
6
+ base.send :include, SoftDelete
7
+ base.send :include, Warning
8
+
9
+ # Associations
10
+ base.send :belongs_to, :search, :inverse_of => :search_parameters
11
+ base.send :belongs_to, :parameter, :inverse_of => :search_parameters
12
+ base.send :has_many, :search_parameter_values, :dependent => :destroy, :inverse_of => :search_parameter
13
+
14
+ # Scopes
15
+ base.send(:scope, :by_parameter_category, ->(parameter_category) { base.includes(:parameter).where('discerner_parameters.parameter_category_id' => parameter_category.id) unless parameter_category.blank?})
16
+ base.send(:scope, :ordered_by_display_order, -> { base.order('discerner_search_parameters.display_order ASC') })
17
+ base.send(:scope, :ordered, -> { base.order('discerner_search_parameters.id ASC') })
18
+
19
+ #Validations
20
+ base.send :validates_presence_of, :search, :parameter
21
+
22
+ # Nested attributes
23
+ base.send :accepts_nested_attributes_for, :search_parameter_values, :allow_destroy => true
24
+
25
+ # Hooks
26
+ base.send :after_commit, :update_associations, :on => :update, :if => Proc.new { |record| record.previous_changes.include?('deleted_at') }
27
+
28
+ # Whitelisting attributes
29
+ base.send :attr_accessible, :search, :search_id, :parameter, :parameter_id, :search_parameter_values_attributes, :display_order
30
+ end
31
+
32
+ # Instance Methods
33
+ def initialize(*args)
34
+ super(*args)
35
+ end
36
+
37
+ def check_search_parameters
38
+ if self.search_parameters.size < 1 || self.search_parameters.all?{|search_parameter| search_parameter.marked_for_destruction? }
39
+ errors.add(:base,"Search should have at least one search criteria.")
40
+ end
41
+ end
42
+
43
+ def parameterized_name
44
+ name.blank? ? 'no_name_specified' : name.parameterize.underscore
45
+ end
46
+
47
+ def prepare_sql
48
+ sql = {}
49
+ parameter_type = parameter.parameter_type.name
50
+ case parameter_type
51
+ when 'list'
52
+ values = [search_parameter_values.chosen.map { |spv| spv.parameter_value.search_value }]
53
+ predicate = "#{parameter.search_method} in (?)"
54
+ when 'combobox'
55
+ values = [search_parameter_values.map { |spv| spv.parameter_value.search_value unless spv.parameter_value.nil? }.compact]
56
+ predicate = "#{parameter.search_method} in (?)" unless values.blank?
57
+ else # 'numeric','date', 'text', 'string
58
+ spvs = []
59
+ values = []
60
+ search_parameter_values.map {|spv| spvs << spv.to_sql}
61
+
62
+ predicates = spvs.map { |s| s[:predicates] }.join(' or ')
63
+ predicate = "(#{predicates})"
64
+
65
+ spvs.each do |spv|
66
+ if spv[:values].is_a?(Array)
67
+ spv[:values].each do |v|
68
+ values << v
69
+ end
70
+ else
71
+ values << spv[:values] unless spv[:values].blank?
72
+ end
73
+ end
74
+ end
75
+ sql[:predicates] = predicate
76
+ sql[:values] = values
77
+ sql
78
+ end
79
+
80
+ def search_model_class
81
+ return if parameter.search_model.blank? || parameter.search_method.blank?
82
+ search_model_class = parameter.search_model.safe_constantize
83
+ raise "Search model #{parameter.search_model} could not be found" if search_model_class.blank?
84
+ search_model_class
85
+ end
86
+
87
+ def search_model_attribute_method?
88
+ search_model_class && search_model_class.attribute_method?(parameter.search_method)
89
+ end
90
+
91
+ def to_sql
92
+ sql = prepare_sql
93
+ if search_model_class && !search_model_attribute_method?
94
+ raise "Search model #{parameter.search_model} does not respond to search method #{parameter.search_method}" unless search_model_class.respond_to?(parameter.search_method)
95
+ sql = search_model_class.send(parameter.search_method, sql)
96
+ end
97
+ sql
98
+ end
99
+
100
+ def disabled?
101
+ return false unless persisted?
102
+ if parameter.blank?
103
+ warnings.add(:base, "Parameter has to be selected")
104
+ return true
105
+ elsif parameter.deleted?
106
+ warnings.add(:base, "Parameter has been deleted and has to be removed from the search")
107
+ return true
108
+ elsif search_parameter_values.blank? || (parameter.parameter_type.name == 'list' && search_parameter_values.select{|spv| spv.chosen?}.empty?)
109
+ warnings.add(:base, "Parameter value has to be selected")
110
+ return true
111
+ elsif deleted? || search_parameter_values.select{ |spv| spv.disabled?}.any?
112
+ return true
113
+ end
114
+ end
115
+
116
+
117
+
118
+ private
119
+ def update_associations
120
+ search_parameter_values.each do |r|
121
+ r.deleted_at = Time.now
122
+ r.save
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,130 @@
1
+ module Discerner
2
+ module Methods
3
+ module Models
4
+ module SearchParameterValue
5
+ def self.included(base)
6
+ base.send :include, SoftDelete
7
+ base.send :include, Warning
8
+
9
+ # Associations
10
+ base.send :belongs_to, :search_parameter, :inverse_of => :search_parameter_values
11
+ base.send :belongs_to, :parameter_value, :inverse_of => :search_parameter_values
12
+ base.send :belongs_to, :operator, :inverse_of => :search_parameter_values
13
+
14
+ # Scopes
15
+ base.send(:scope, :chosen, -> { base.where(:chosen => true) })
16
+ base.send(:scope, :ordered_by_display_order, -> { base.order('discerner_search_parameter_values.display_order ASC') })
17
+
18
+ #Validations
19
+ base.send :validate, :validate_operator
20
+ base.send :validates, :search_parameter, :presence => true
21
+
22
+ # Hooks
23
+ base.send :before_validation, :cleanup_parameter_values
24
+ base.send :after_commit, :destroy_if_deleted_parameter_value, :on => :update
25
+
26
+ # Whitelisting attributes
27
+ base.send :attr_accessible, :additional_value, :chosen, :display_order, :operator_id, :parameter_value_id, :search_parameter_id, :value, :parameter_value, :operator, :search_parameter
28
+ end
29
+
30
+ # Instance Methods
31
+ def initialize(*args)
32
+ super(*args)
33
+ end
34
+
35
+ def to_sql
36
+ sql = {}
37
+ raise "Search operator has to be defined in order to run 'to_sql' method on search_parameter_value" if operator.blank?
38
+ case operator.operator_type
39
+ when 'comparison'
40
+ sql[:predicates] = "#{search_parameter.parameter.search_method} #{operator.symbol} ?"
41
+ sql[:values] = formatted_values.first
42
+ when 'range'
43
+ sql[:predicates] = "#{search_parameter.parameter.search_method} #{operator.symbol} ? and ?"
44
+ sql[:values] = formatted_values
45
+ when 'text_comparison'
46
+ sql[:predicates] = "#{search_parameter.parameter.search_method} #{operator.symbol} ?"
47
+ sql[:values] = "%#{formatted_values.first}%"
48
+ when 'presence'
49
+ sql[:predicates] = "#{search_parameter.parameter.search_method} #{operator.symbol}"
50
+ end
51
+ sql
52
+ end
53
+
54
+ def formatted_values
55
+ all_values = [value, additional_value].compact
56
+ case search_parameter.parameter.parameter_type.name
57
+ when 'date'
58
+ all_values.map{|v| v.to_date}
59
+ when 'numeric'
60
+ all_values.map{|v| v.to_f}
61
+ else
62
+ all_values
63
+ end
64
+ end
65
+
66
+ def disabled?
67
+ return false unless persisted?
68
+ if parameter_value.blank? && value.blank? && operator && operator.operator_type != 'presence'
69
+ warnings.add(:base, "Parameter value has to be selected")
70
+ return true
71
+ end
72
+ if chosen? && parameter_value.blank? || parameter_value && parameter_value.deleted?
73
+ warnings.add(:base, "Parameter value has been deleted and has to be removed from the search")
74
+ return true
75
+ end
76
+ if search_parameter && search_parameter.parameter && search_parameter.parameter.parameter_type.name == 'date' && !validate_dates_format
77
+ warnings.add(:base, "Provided date is not valid")
78
+ return true
79
+ end
80
+ if parameter_value.blank? && search_parameter.parameter.parameter_type.name == 'combobox'
81
+ warnings.add(:base, "Parameter value has to be selected")
82
+ return true
83
+ end
84
+ warnings.clear
85
+ return false
86
+ end
87
+
88
+ def validate_dates_format
89
+ validate_date(value) && validate_date(additional_value)
90
+ end
91
+
92
+ private
93
+ def destroy_if_deleted_parameter_value
94
+ return if parameter_value.blank? || search_parameter.blank? || search_parameter.parameter.blank? || search_parameter.parameter.parameter_type.blank?
95
+ #return unless ['list', 'combobox'].include?(search_parameter.parameter.parameter_type.name)
96
+ destroy if parameter_value.deleted? && search_parameter.parameter.parameter_type.name == 'list' && !chosen?
97
+ end
98
+
99
+ def validate_date(date)
100
+ begin
101
+ unless date.blank?
102
+ parsed_date = date.to_date
103
+ #http://www.karaszi.com/sqlserver/info_datetime.asp#Why1753
104
+ return false if parsed_date.year < 1753
105
+ end
106
+ rescue => e
107
+ return false
108
+ end
109
+ return true
110
+ end
111
+
112
+ def cleanup_parameter_values
113
+ if operator && operator.operator_type == 'presence'
114
+ self.value = nil
115
+ self.additional_value = nil
116
+ self.parameter_value = nil
117
+ end
118
+ end
119
+
120
+ def validate_operator
121
+ if search_parameter && search_parameter.parameter && search_parameter.parameter.parameter_type && ['list', 'combobox'].include?(search_parameter.parameter.parameter_type.name)
122
+ self.operator = nil
123
+ else
124
+ errors.add(:base, "Operator has to be selected for parameter values that do not belong to list or combobox") if operator.blank?
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,35 @@
1
+ module Discerner
2
+ module Methods
3
+ module Models
4
+ module SoftDelete
5
+ def self.included(base)
6
+ attr_accessor :soft_delete
7
+ base.send(:scope, :not_deleted, -> { base.where(:deleted_at => nil) })
8
+ end
9
+
10
+ def process_soft_delete
11
+ self.deleted_at = Time.zone.now
12
+ end
13
+
14
+ def soft_delete=(removed)
15
+ if (removed.is_a?(TrueClass) || removed.to_s == 't' || removed.to_s == '1')
16
+ process_soft_delete
17
+ end
18
+ end
19
+
20
+ def soft_delete
21
+ !self.deleted_at.blank?
22
+ end
23
+
24
+ def soft_delete!
25
+ process_soft_delete
26
+ save!
27
+ end
28
+
29
+ def deleted?
30
+ !deleted_at.blank?
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module Discerner
2
+ module Methods
3
+ module Models
4
+ module Warning
5
+ def self.included(base)
6
+ attr_accessor :force_save
7
+ end
8
+
9
+ def warnings
10
+ @warnings ||= ActiveModel::Errors.new(self)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end