question_chain 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|