question_chain 0.0.1
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.
- data/.gitignore +4 -0
- data/Gemfile +18 -0
- data/Licence.txt +20 -0
- data/README.rdoc +34 -0
- data/Rakefile +2 -0
- data/config/initializers/mustache.rb +2 -0
- data/lib/question_chain/answerable.rb +142 -0
- data/lib/question_chain/answers.rb +290 -0
- data/lib/question_chain/models/answers/question_view.rb +21 -0
- data/lib/question_chain/models/answers/ui_object_view.rb +67 -0
- data/lib/question_chain/models/answers/ui_objects_check_box_view.rb +17 -0
- data/lib/question_chain/models/answers/ui_objects_drop_down_view.rb +16 -0
- data/lib/question_chain/models/answers/ui_objects_hidden_field_view.rb +8 -0
- data/lib/question_chain/models/answers/ui_objects_object_reference_drop_down_view.rb +18 -0
- data/lib/question_chain/models/answers/ui_objects_object_search_view.rb +22 -0
- data/lib/question_chain/models/answers/ui_objects_relatable_category_drop_down_view.rb +18 -0
- data/lib/question_chain/models/answers/ui_objects_text_field_view.rb +8 -0
- data/lib/question_chain/models/chain_template.rb +43 -0
- data/lib/question_chain/models/question.rb +37 -0
- data/lib/question_chain/models/relatable_category_filter.rb +15 -0
- data/lib/question_chain/models/rule.rb +27 -0
- data/lib/question_chain/models/rules/attribute_change.rb +28 -0
- data/lib/question_chain/models/rules/choice_genenerator.rb +5 -0
- data/lib/question_chain/models/rules/populate_drop_down.rb +30 -0
- data/lib/question_chain/models/rules/search.rb +19 -0
- data/lib/question_chain/models/rules/value_change.rb +22 -0
- data/lib/question_chain/models/ui_group.rb +71 -0
- data/lib/question_chain/models/ui_object.rb +92 -0
- data/lib/question_chain/models/ui_object_answer.rb +17 -0
- data/lib/question_chain/models/ui_objects/check_box.rb +8 -0
- data/lib/question_chain/models/ui_objects/drop_down.rb +17 -0
- data/lib/question_chain/models/ui_objects/hidden_field.rb +5 -0
- data/lib/question_chain/models/ui_objects/object_reference_drop_down.rb +67 -0
- data/lib/question_chain/models/ui_objects/object_search.rb +44 -0
- data/lib/question_chain/models/ui_objects/radio_button.rb +5 -0
- data/lib/question_chain/models/ui_objects/radio_button_group.rb +5 -0
- data/lib/question_chain/models/ui_objects/relatable_category_drop_down.rb +70 -0
- data/lib/question_chain/models/ui_objects/text_field.rb +11 -0
- data/lib/question_chain/mongo_serialization.rb +62 -0
- data/lib/question_chain/mustache_handler.rb +16 -0
- data/lib/question_chain/mustache_rails.rb +50 -0
- data/lib/question_chain/state_machine.rb +29 -0
- data/lib/question_chain/stored_template.rb +30 -0
- data/lib/question_chain/version.rb +3 -0
- data/lib/question_chain/views/answers/_edit.html.haml +62 -0
- data/lib/question_chain/views/answers/_new.html.haml +62 -0
- data/lib/question_chain/views/answers/_question.html.mustache +11 -0
- data/lib/question_chain/views/answers/_ui_objects_check_box.html.mustache +19 -0
- data/lib/question_chain/views/answers/_ui_objects_drop_down.html.mustache +26 -0
- data/lib/question_chain/views/answers/_ui_objects_hidden_field.html.mustache +3 -0
- data/lib/question_chain/views/answers/_ui_objects_object_reference_drop_down.html.mustache +27 -0
- data/lib/question_chain/views/answers/_ui_objects_object_search.html.mustache +20 -0
- data/lib/question_chain/views/answers/_ui_objects_relatable_category_drop_down.html.mustache +26 -0
- data/lib/question_chain/views/answers/_ui_objects_text_field.html.mustache +19 -0
- data/lib/question_chain/views/layouts/application.html.haml +10 -0
- data/lib/question_chain.rb +35 -0
- data/question_chain.gemspec +31 -0
- data/test_app/.gitignore +4 -0
- data/test_app/Gemfile +29 -0
- data/test_app/Rakefile +16 -0
- data/test_app/app/controllers/answers_controller.rb +13 -0
- data/test_app/app/controllers/application_controller.rb +4 -0
- data/test_app/app/models/container.rb +10 -0
- data/test_app/app/models/flight.rb +20 -0
- data/test_app/app/views/answers/edit.html.haml +1 -0
- data/test_app/app/views/answers/new.html.haml +1 -0
- data/test_app/config/application.rb +18 -0
- data/test_app/config/boot.rb +13 -0
- data/test_app/config/database.yml +15 -0
- data/test_app/config/environment.rb +5 -0
- data/test_app/config/initializers/app.rb +5 -0
- data/test_app/config/initializers/cookie_verification_secret.rb +7 -0
- data/test_app/config/initializers/mongodb.rb +2 -0
- data/test_app/config/initializers/session_store.rb +3 -0
- data/test_app/config/routes.rb +30 -0
- data/test_app/config.ru +2 -0
- data/test_app/environments/development.rb +19 -0
- data/test_app/environments/test.rb +30 -0
- data/test_app/lib/tasks/rspec.rake +69 -0
- data/test_app/lib/tasks/yard.rake +4 -0
- data/test_app/public/.gitkeep +0 -0
- data/test_app/script/rails +10 -0
- data/test_app/spec/acceptance/new_spec +30 -0
- data/test_app/spec/factories.rb +81 -0
- data/test_app/spec/models/chain_template_spec.rb +53 -0
- data/test_app/spec/models/flight_spec.rb +101 -0
- data/test_app/spec/models/question_spec.rb +13 -0
- data/test_app/spec/models/rules/value_change_spec.rb +31 -0
- data/test_app/spec/models/ui_group_spec.rb +55 -0
- data/test_app/spec/models/ui_object_spec.rb +33 -0
- data/test_app/spec/models/ui_objects/drop_down_spec.rb +28 -0
- data/test_app/spec/models/ui_objects/relatable_category_drop_down_spec.rb +13 -0
- data/test_app/spec/spec_helper.rb +25 -0
- metadata +325 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Answers
|
|
2
|
+
class UiObjectsRelatableCategoryDropDownView < Answers::UiObjectView
|
|
3
|
+
|
|
4
|
+
def prompt
|
|
5
|
+
ui_object.prompt
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# @return [Array<Hash<value => value, name => name>>]
|
|
9
|
+
def options
|
|
10
|
+
if answer_params
|
|
11
|
+
ui_object.options.map{|option| option.merge!(:selected => option["value"].to_s == value.to_s)}
|
|
12
|
+
else
|
|
13
|
+
ui_object.options
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class ChainTemplate
|
|
2
|
+
include MongoMapper::Document
|
|
3
|
+
include MongoMapper::StateMachine
|
|
4
|
+
|
|
5
|
+
# == Keys
|
|
6
|
+
key :context, Hash # [Hash<content => [Array<MongoIds>]>]
|
|
7
|
+
key :account_id, ObjectId # used to override any specific question essentially
|
|
8
|
+
key :for_resource, String
|
|
9
|
+
key :parent_resource, String
|
|
10
|
+
key :active_at, Time
|
|
11
|
+
|
|
12
|
+
# == State Machine
|
|
13
|
+
state_machine :initial => :pending do
|
|
14
|
+
state :pending
|
|
15
|
+
state :active
|
|
16
|
+
|
|
17
|
+
event :activate do
|
|
18
|
+
transitions :to => :active, :from => [:pending], :on_transition => :set_active_at
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
event :deactivate do
|
|
22
|
+
transitions :to => :pending, :from => [:active]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# == Indexes
|
|
27
|
+
ensure_index :for_resource
|
|
28
|
+
|
|
29
|
+
# == Validations
|
|
30
|
+
validates_presence_of :for_resource
|
|
31
|
+
validates_presence_of :context
|
|
32
|
+
validates_true_for :context, :logic => Proc.new{ !context.empty?}
|
|
33
|
+
|
|
34
|
+
def self.attributes_for_api
|
|
35
|
+
%w(id name for_resource account_id parent_resource)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
def set_active_at
|
|
40
|
+
self.active_at = Time.now
|
|
41
|
+
self.save!
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class Question
|
|
2
|
+
include MongoMapper::Document
|
|
3
|
+
include MongoMapper::Serialize
|
|
4
|
+
|
|
5
|
+
# == Keys
|
|
6
|
+
key :name, String
|
|
7
|
+
key :label, String
|
|
8
|
+
key :_type, String
|
|
9
|
+
key :description, String
|
|
10
|
+
key :calculator_id, ObjectId
|
|
11
|
+
key :computation_id, ObjectId
|
|
12
|
+
timestamps!
|
|
13
|
+
|
|
14
|
+
# == Indexes
|
|
15
|
+
ensure_index :names
|
|
16
|
+
ensure_index :label
|
|
17
|
+
|
|
18
|
+
# == Validations
|
|
19
|
+
validates_presence_of :name
|
|
20
|
+
validates_uniqueness_of :name
|
|
21
|
+
|
|
22
|
+
# == Associations
|
|
23
|
+
many :ui_groups, :order => :position.asc, :dependent => :destroy
|
|
24
|
+
|
|
25
|
+
# == Hooks
|
|
26
|
+
def self.attributes_for_api
|
|
27
|
+
%w(id name description computation_id ui_groups label calculator_id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_json
|
|
31
|
+
attributes_for_api_resource.to_json
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_hash
|
|
35
|
+
Hashie::Mash.new(attributes_for_api_resources)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class RelatableCategoryFilter
|
|
2
|
+
include MongoMapper::Document
|
|
3
|
+
include MongoMapper::Serialize
|
|
4
|
+
|
|
5
|
+
# == Keys
|
|
6
|
+
key :filters, Array
|
|
7
|
+
key :ui_group_id, ObjectId
|
|
8
|
+
|
|
9
|
+
# == Associations
|
|
10
|
+
belongs_to :ui_group
|
|
11
|
+
|
|
12
|
+
def self.attributes_for_api
|
|
13
|
+
%w(id filters ui_group_id)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class Rule
|
|
2
|
+
include MongoMapper::EmbeddedDocument
|
|
3
|
+
include MongoMapper::Serialize
|
|
4
|
+
|
|
5
|
+
# == Keys
|
|
6
|
+
key :fire_value, String
|
|
7
|
+
key :negate_value, :default => false
|
|
8
|
+
key :_type, String
|
|
9
|
+
|
|
10
|
+
def ui_object_id
|
|
11
|
+
_parent_document.id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# have to have this here to declear all subclass not being used?
|
|
15
|
+
def self.attributes_for_api
|
|
16
|
+
%w(id fire_value _type ui_object_id negate_value)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# this will manipulate the question hash with the updated
|
|
20
|
+
# attributes for the affecting ui objects that are
|
|
21
|
+
# attached to this rule
|
|
22
|
+
#
|
|
23
|
+
# @return [TrueClass] when the fire! is actually doing that
|
|
24
|
+
def fire!(value, question_hash = {})
|
|
25
|
+
raise NotImplementedError, "Need to set the fire! method"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Rules
|
|
2
|
+
class AttributeChange < Rule
|
|
3
|
+
# {:id => {:atttibute_name => "new_state"}, :id => {:atttibute_name => "new_state"}}
|
|
4
|
+
key :affecting_ui_objects, Hash
|
|
5
|
+
key :compare_text_value, :default => false
|
|
6
|
+
|
|
7
|
+
def self.attributes_for_api
|
|
8
|
+
%w(id fire_value _type affecting_ui_objects ui_object_id negate_value compare_text_value)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Checks to determine the value and the fire_value if cool
|
|
12
|
+
# then the ui objects are updated depending on the affecting values
|
|
13
|
+
#
|
|
14
|
+
# @param [UiObject] ui_object the ui object that this rul is observing
|
|
15
|
+
# @param [String] value the value that the ui_object has
|
|
16
|
+
# @param [Array<Hash>] ui_objects
|
|
17
|
+
def fire!(value, ui_objects)
|
|
18
|
+
if negate_value ? value != self.fire_value : value == self.fire_value
|
|
19
|
+
affecting_ui_objects.each_pair do |key, value|
|
|
20
|
+
if ui_object = ui_objects.detect{|ui| ui["id"] == key}
|
|
21
|
+
ui_object["ui_attributes"].merge!(value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Rules
|
|
2
|
+
class PopulateDropDown < Rule
|
|
3
|
+
|
|
4
|
+
# == Keys
|
|
5
|
+
key :ui_object_attribute_check, Hash, :default => nil
|
|
6
|
+
|
|
7
|
+
def fire!(value = nil, ui_objects_hash = {})
|
|
8
|
+
# does not matter what the value is in this instance
|
|
9
|
+
# it is used to get opions for the drop_down_target
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def drop_down_target_id
|
|
13
|
+
_parent_document.drop_down_target_id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# ask the parent document to get the options
|
|
17
|
+
def get_options(object_ids = [])
|
|
18
|
+
options = []
|
|
19
|
+
_parent_document.get_target_drop_down_options(object_ids).each_pair do |key ,value|
|
|
20
|
+
options << {:name => value, :value => key}
|
|
21
|
+
end
|
|
22
|
+
options.sort_by{|option| option[:name]}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.attributes_for_api
|
|
26
|
+
%w(id fire_value _type ui_object_id drop_down_target_id ui_object_attribute_check negate_value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Rules
|
|
2
|
+
class Search < Rule
|
|
3
|
+
|
|
4
|
+
def fire!(value = nil, ui_objects_hash = {})
|
|
5
|
+
# does not matter what the value is in this instance
|
|
6
|
+
# it is used to get opions for the drop_down_target
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# ask the parent document to get the options
|
|
10
|
+
def get_options(filter, relatable_category_names = [])
|
|
11
|
+
_parent_document.options(filter, relatable_category_names)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.attributes_for_api
|
|
15
|
+
%w(id fire_value _type ui_object_id negate_value)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Rules
|
|
2
|
+
class ValueChange < Rule
|
|
3
|
+
|
|
4
|
+
# == Keys
|
|
5
|
+
key :affecting_ui_object_id, ObjectId
|
|
6
|
+
key :change_value, String
|
|
7
|
+
|
|
8
|
+
# == Validations
|
|
9
|
+
validates_presence_of :affecting_ui_object_id
|
|
10
|
+
validates_presence_of :change_value
|
|
11
|
+
|
|
12
|
+
def fire!(value = nil, ui_objects_hash = {})
|
|
13
|
+
# does not matter what the value is in this instance
|
|
14
|
+
# it is used to get opions for the drop_down_target
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.attributes_for_api
|
|
18
|
+
%w(id fire_value _type ui_object_id affecting_ui_object_id negate_value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# holds a group of ui objects basically
|
|
2
|
+
# keep simple to start of with
|
|
3
|
+
class UiGroup
|
|
4
|
+
include MongoMapper::Document
|
|
5
|
+
include MongoMapper::Serialize
|
|
6
|
+
|
|
7
|
+
# == Keys
|
|
8
|
+
key :name, String
|
|
9
|
+
key :description, String
|
|
10
|
+
key :label, String
|
|
11
|
+
key :parent_id, ObjectId
|
|
12
|
+
key :parent_ids, Array
|
|
13
|
+
key :question_id, ObjectId
|
|
14
|
+
key :ui_attributes, Hash
|
|
15
|
+
key :css_classes, Array, :default => ["single"]
|
|
16
|
+
timestamps!
|
|
17
|
+
|
|
18
|
+
# == Indexes
|
|
19
|
+
ensure_index :parent_ids
|
|
20
|
+
ensure_index :name
|
|
21
|
+
|
|
22
|
+
# == Validations
|
|
23
|
+
validates_presence_of :name
|
|
24
|
+
validates_presence_of :label
|
|
25
|
+
validates_presence_of :question_id
|
|
26
|
+
|
|
27
|
+
# == Associations
|
|
28
|
+
belongs_to :parent
|
|
29
|
+
belongs_to :question
|
|
30
|
+
many :ui_objects, :order => :position.asc
|
|
31
|
+
many :object_searches, :class_name => "UiObjects::ObjectSearch"
|
|
32
|
+
many :text_fields, :class_name => "UiObjects::TextField"
|
|
33
|
+
many :drop_downs, :class_name => "UiObjects::DropDown"
|
|
34
|
+
many :checkboxes, :class_name => "UiObjects::CheckBox"
|
|
35
|
+
one :object_reference_drop_down, :class_name => "UiObjects::ObjectReferenceDropDown"
|
|
36
|
+
many :relatable_category_drop_downs, :class_name => "UiObjects::RelatableCategoryDropDown"
|
|
37
|
+
many :hidden_fields, :class_name => "UiObjects::HiddenField"
|
|
38
|
+
many :children, :class_name => 'UiGroup', :foreign_key => 'parent_id'
|
|
39
|
+
one :relatable_category_filter, :class_name => "RelatableCategoryFilter"
|
|
40
|
+
|
|
41
|
+
# thats right only ever one object_reference in a ui_group
|
|
42
|
+
# its a constraint we just need
|
|
43
|
+
one :object_reference_drop_down, :class_name => "UiObjects::ObjectReferenceDropDown"
|
|
44
|
+
|
|
45
|
+
# == hooks
|
|
46
|
+
before_save :set_parents
|
|
47
|
+
|
|
48
|
+
def self.attributes_for_api
|
|
49
|
+
%w(id name label question_id relatable_category_filter ui_objects ui_attributes default_styles css_classes)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# this should go into the mustache view basically
|
|
53
|
+
def default_styles
|
|
54
|
+
default_styles = ""
|
|
55
|
+
self.ui_attributes.each_pair do |key, value|
|
|
56
|
+
if key.to_s == "visible" && value == false
|
|
57
|
+
default_styles << "display:none;visibility:hidden;"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
default_styles
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def css_classes
|
|
64
|
+
read_attribute(:css_classes).join(" ")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
protected
|
|
68
|
+
def set_parents
|
|
69
|
+
self.parent_ids = (parent.parent_ids || []) << parent_id if parent?
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require "observer"
|
|
2
|
+
class UiObject
|
|
3
|
+
include ::Observable
|
|
4
|
+
include MongoMapper::Document
|
|
5
|
+
include MongoMapper::Serialize
|
|
6
|
+
|
|
7
|
+
# == Keys
|
|
8
|
+
key :_type, String
|
|
9
|
+
key :name, String
|
|
10
|
+
key :label, String
|
|
11
|
+
key :description, String
|
|
12
|
+
key :default_value, String
|
|
13
|
+
key :ui_attributes, Hash
|
|
14
|
+
key :ui_group_id, ObjectId
|
|
15
|
+
key :position, Integer
|
|
16
|
+
key :extra_info, String
|
|
17
|
+
key :per_page, Integer, :default => 50
|
|
18
|
+
key :css_classes, Array, :default => ["single"]
|
|
19
|
+
timestamps!
|
|
20
|
+
|
|
21
|
+
# == Indexes
|
|
22
|
+
ensure_index :label
|
|
23
|
+
ensure_index "rules.name"
|
|
24
|
+
|
|
25
|
+
# == Attrs
|
|
26
|
+
attr_accessor :value
|
|
27
|
+
class_inheritable_accessor :default_values
|
|
28
|
+
|
|
29
|
+
# Class set values
|
|
30
|
+
self.default_values = {:visible => true, :enabled => true}
|
|
31
|
+
|
|
32
|
+
# == Validations
|
|
33
|
+
validates_presence_of :ui_group_id
|
|
34
|
+
validates_presence_of :label
|
|
35
|
+
validates_presence_of :ui_attributes
|
|
36
|
+
validates_true_for :ui_attributes, :logic => Proc.new{
|
|
37
|
+
!ui_attributes.empty?
|
|
38
|
+
}
|
|
39
|
+
validates_associated :rules
|
|
40
|
+
|
|
41
|
+
# == Associations
|
|
42
|
+
belongs_to :ui_group
|
|
43
|
+
many :rules
|
|
44
|
+
|
|
45
|
+
# == Hooks
|
|
46
|
+
before_save :add_rule_observers!
|
|
47
|
+
before_validation_on_create :set_default_attributes
|
|
48
|
+
|
|
49
|
+
def self.attributes_for_api
|
|
50
|
+
%w(id name _type label description default_value ui_attributes ui_group_id rules extra_info css_classes)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# == need as added a little late in the day basically
|
|
54
|
+
def per_page
|
|
55
|
+
read_attribute(:per_page) || 50
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def initialize(*args)
|
|
59
|
+
super
|
|
60
|
+
add_rule_observers!
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# fires the rules that are attached the ui object
|
|
64
|
+
def change_value!(ui_objects = [])
|
|
65
|
+
if ui_object = ui_objects.detect{|ui| ui["id"] == self.id.to_s}
|
|
66
|
+
value = ui_object["ui_attributes"] && ui_object["ui_attributes"]["value"] || ""
|
|
67
|
+
@observer_state = true
|
|
68
|
+
notify_observers(value, ui_objects)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def visible?
|
|
73
|
+
ui_attributes[:visible]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def enabled?
|
|
77
|
+
ui_attributes[:enabled]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
protected
|
|
81
|
+
def set_default_attributes
|
|
82
|
+
self.ui_attributes = self.class.default_values.merge(:value => self.default_value || "").merge(self.ui_attributes || {})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Adding the obervers so we can fire rules
|
|
86
|
+
def add_rule_observers!
|
|
87
|
+
rules.each do |rule|
|
|
88
|
+
self.add_observer(rule, :fire!)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class UiObjectAnswer
|
|
2
|
+
|
|
3
|
+
# @todo refactor make not as ugly as a bulldog eating a wasp
|
|
4
|
+
# @param [String] json
|
|
5
|
+
# @return [String] json formatted String manipulate from ui changes
|
|
6
|
+
def self.update_answers!(answer_json = "")
|
|
7
|
+
@answer_hash = JSON.parse(answer_json)
|
|
8
|
+
if @ui_objects = @answer_hash.delete("ui_objects")
|
|
9
|
+
uis = UiObject.find(ui_object_ids)
|
|
10
|
+
uis.each do |ui_object|
|
|
11
|
+
ui_object.change_value!(@ui_objects)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
@answer_hash["ui_objects"] = @ui_objects
|
|
15
|
+
answer_json = @answer_hash.to_json
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module UiObjects
|
|
2
|
+
class DropDown < UiObject
|
|
3
|
+
|
|
4
|
+
# == keys
|
|
5
|
+
key :options, Array
|
|
6
|
+
key :prompt, String
|
|
7
|
+
key :max_options, Integer, :default => 20
|
|
8
|
+
key :populate, Boolean, :default => true
|
|
9
|
+
key :order, String
|
|
10
|
+
key :filter, Set
|
|
11
|
+
|
|
12
|
+
def self.attributes_for_api
|
|
13
|
+
%w(id name _type populate label order filter description default_value ui_attributes options prompt max_options rules extra_info css_classes)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module UiObjects
|
|
2
|
+
class ObjectReferenceDropDown < UiObject
|
|
3
|
+
|
|
4
|
+
# == Keys
|
|
5
|
+
key :prompt, String
|
|
6
|
+
key :max_options, Integer, :default => 20
|
|
7
|
+
|
|
8
|
+
# :id => :attribute_name # not quite sure about the option trackers
|
|
9
|
+
key :object_name, String
|
|
10
|
+
key :filters, Set # filters
|
|
11
|
+
key :filter_attribute, String
|
|
12
|
+
key :filter_value, String
|
|
13
|
+
key :order, String
|
|
14
|
+
key :drop_down_target_id, ObjectId
|
|
15
|
+
key :drop_down_target_options_filters, Array # == Array of filters for formula inputs
|
|
16
|
+
key :populate, Boolean, :default => true
|
|
17
|
+
key :relatable_options, Boolean, :default => false
|
|
18
|
+
key :attribute_for_value, String, :default => "id"
|
|
19
|
+
key :attribute_for_display, String, :default => "identifier"
|
|
20
|
+
key :external, Boolean, :default => true
|
|
21
|
+
|
|
22
|
+
# == Indexes
|
|
23
|
+
ensure_index :object_name
|
|
24
|
+
|
|
25
|
+
# == Validations
|
|
26
|
+
validates_presence_of :object_name
|
|
27
|
+
|
|
28
|
+
def options
|
|
29
|
+
return [] if !populate
|
|
30
|
+
@options ||= get_options(object_name, filters || [])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.attributes_for_api
|
|
34
|
+
%w(id name _type label description rules default_value ui_attributes options prompt max_options object_name drop_down_target_id extra_info css_classes)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# object_reference drop down can only target its formula units basically
|
|
38
|
+
# only 1 object in this instance
|
|
39
|
+
def get_target_drop_down_options(object_ids = [])
|
|
40
|
+
if object_id = object_ids.first
|
|
41
|
+
formula_inputs = QuestionChain.calculated_session.formula_inputs_for_generic_object(object_id.to_s)
|
|
42
|
+
if respond_to?(:drop_down_target_options_filters) && !drop_down_target_options_filters.empty?
|
|
43
|
+
selected_formula_inputs = formula_inputs.select{|f| drop_down_target_options_filters.map(&:downcase).include?(f.name.downcase)}
|
|
44
|
+
selected_formula_inputs.inject({}){|var, fi| var.merge!({fi.name => fi.label_input_units}); var}
|
|
45
|
+
else
|
|
46
|
+
formula_inputs.inject({}){|var, fi| var.merge!({fi.name => fi.label_input_units}); var}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
# Based on the fact we dont know what co2_platform
|
|
53
|
+
# we are using how can the options be found!
|
|
54
|
+
#
|
|
55
|
+
# @return [Array<Array<String, String>>] tuples [value, display]
|
|
56
|
+
def get_options(name, filters = [])
|
|
57
|
+
options = {:per_page => per_page}
|
|
58
|
+
options.merge!(:filter_attribute => self.filter_attribute, :filter_value => self.filter_value) if self.filter_attribute && self.filter_value
|
|
59
|
+
response = QuestionChain.calculated_session.generic_objects_for_object_template(name, options)
|
|
60
|
+
options = response.generic_objects.select{|obj| (filters.empty? || (!filters.empty? && filters.include?(obj.id.to_s)))}.map do |obj|
|
|
61
|
+
{:value => obj.send(self.attribute_for_value), :name => obj.send(self.attribute_for_display)}
|
|
62
|
+
end
|
|
63
|
+
options.sort_by{|obj| obj[:name]}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module UiObjects
|
|
2
|
+
class ObjectSearch < UiObject
|
|
3
|
+
|
|
4
|
+
# == keys
|
|
5
|
+
key :object_name, String
|
|
6
|
+
key :max_options, Integer, :default => 20
|
|
7
|
+
key :prompt, String
|
|
8
|
+
key :attribute_for_value, String, :default => "id"
|
|
9
|
+
key :attribute_for_display, String, :default => "identifier"
|
|
10
|
+
|
|
11
|
+
# == Validations
|
|
12
|
+
validates_presence_of :object_name
|
|
13
|
+
|
|
14
|
+
# == Hooks
|
|
15
|
+
after_create :add_default_rule
|
|
16
|
+
|
|
17
|
+
# == Attrs
|
|
18
|
+
attr_accessor_with_default :default_rule, true
|
|
19
|
+
|
|
20
|
+
def self.attributes_for_api
|
|
21
|
+
%w(id name _type label position description rules ui_attributes prompt max_options object_name extra_info css_classes)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def options(filter, relatable_category_names = [])
|
|
25
|
+
@options ||= get_options(object_name, filter || "", relatable_category_names)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
def get_options(object_name, filter, relatable_category_names)
|
|
30
|
+
response = QuestionChain::calculated_session.generic_objects_for_object_template_with_filter(object_name, filter, :relatable_category_values => relatable_category_names, :per_page => per_page)
|
|
31
|
+
response.generic_objects.map do |obj|
|
|
32
|
+
{:value => obj.send(self.attribute_for_value), :name => obj.identifier}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def add_default_rule
|
|
37
|
+
if default_rule
|
|
38
|
+
self.rules << Rules::Search.new
|
|
39
|
+
self.save
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module UiObjects
|
|
2
|
+
class RelatableCategoryDropDown < UiObject
|
|
3
|
+
|
|
4
|
+
# == Keys
|
|
5
|
+
key :related_attribute, String
|
|
6
|
+
key :object_name, String
|
|
7
|
+
key :filters, Set
|
|
8
|
+
key :order, String
|
|
9
|
+
key :prompt, String
|
|
10
|
+
key :max_options, Integer, :default => 20
|
|
11
|
+
key :drop_down_target_id, ObjectId
|
|
12
|
+
key :drop_down_target_is_relatable, Boolean, :default => false
|
|
13
|
+
key :populate, Boolean, :default => true
|
|
14
|
+
key :attribute_for_value, String, :default => "id"
|
|
15
|
+
key :attribute_for_display, String, :default => "label"
|
|
16
|
+
key :external, Boolean, :default => true
|
|
17
|
+
|
|
18
|
+
# == Indexes
|
|
19
|
+
ensure_index :filters
|
|
20
|
+
ensure_index :object_name
|
|
21
|
+
ensure_index :related_attribute
|
|
22
|
+
|
|
23
|
+
# == Validations
|
|
24
|
+
validates_presence_of :related_attribute
|
|
25
|
+
validates_presence_of :object_name
|
|
26
|
+
|
|
27
|
+
# == Associations
|
|
28
|
+
belongs_to :drop_down_target, :class_name => "UiObject", :foreign_key => "drop_down_target_id"
|
|
29
|
+
|
|
30
|
+
# == Hooks
|
|
31
|
+
after_create :add_default_rule
|
|
32
|
+
|
|
33
|
+
# == Attrs
|
|
34
|
+
attr_accessor_with_default :default_rule, true
|
|
35
|
+
|
|
36
|
+
def options
|
|
37
|
+
return [] if !populate
|
|
38
|
+
@options ||= relatable_categories.select{|obj| (filters.empty? || (!filters.empty? && filters.include?(obj.id.to_s)))}.map do |obj|
|
|
39
|
+
{:value => obj.id, :name => obj.name}
|
|
40
|
+
end.sort_by{|option| option[:name]}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_target_drop_down_options(relatable_category_ids = [])
|
|
44
|
+
if drop_down_target_is_relatable
|
|
45
|
+
id = relatable_category_ids.shift
|
|
46
|
+
QuestionChain.calculated_session.related_categories_from_relatable_category(id.to_s, drop_down_target.related_attribute, {:relatable_category_ids => relatable_category_ids, :per_page => per_page})
|
|
47
|
+
else
|
|
48
|
+
QuestionChain.calculated_session.related_objects_from_relatable_categories(self.object_name, relatable_category_ids, :per_page => per_page)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.attributes_for_api
|
|
53
|
+
%w(id name _type label populate drop_down_target_is_relatable related_attribute order rules filters description drop_down_target_id default_value ui_attributes options prompt max_options object_name order extra_info css_classes)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
protected
|
|
57
|
+
def relatable_categories(related_attribute = self.related_attribute)
|
|
58
|
+
@relatable_categories ||= QuestionChain.calculated_session.relatable_categories_for_object_template(object_name, related_attribute, :per_page => per_page).relatable_categories
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @todo move to after save; check for existing
|
|
62
|
+
def add_default_rule
|
|
63
|
+
if default_rule
|
|
64
|
+
self.rules << Rules::PopulateDropDown.new
|
|
65
|
+
self.save
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
end
|