icentris-rules 0.9

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 (57) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +33 -0
  5. data/app/assets/images/pyr_rules/ui-anim_basic_16x16.gif +0 -0
  6. data/app/assets/javascripts/pyr_rules/rules.js +135 -0
  7. data/app/assets/stylesheets/pyr_rules/rules.css.scss +258 -0
  8. data/app/assets/stylesheets/scaffold.css +57 -0
  9. data/app/controllers/pyr_rules/actions_controller.rb +22 -0
  10. data/app/controllers/pyr_rules/events_controller.rb +17 -0
  11. data/app/controllers/pyr_rules/rules_controller.rb +233 -0
  12. data/app/helpers/pyr_rules/actions_helper.rb +2 -0
  13. data/app/helpers/pyr_rules/events_helper.rb +95 -0
  14. data/app/helpers/pyr_rules/rules_helper.rb +10 -0
  15. data/app/models/pyr_rules.rb +3 -0
  16. data/app/models/pyr_rules/action.rb +116 -0
  17. data/app/models/pyr_rules/rule.rb +201 -0
  18. data/app/rules/pyr_rules/action_handler.rb +94 -0
  19. data/app/rules/pyr_rules/controller_event_emitter.rb +34 -0
  20. data/app/rules/pyr_rules/email_action_handler.rb +6 -0
  21. data/app/rules/pyr_rules/event_concerns.rb +23 -0
  22. data/app/rules/pyr_rules/event_config_loader.rb +97 -0
  23. data/app/rules/pyr_rules/event_subscriber.rb +36 -0
  24. data/app/rules/pyr_rules/model_creation_handler.rb +25 -0
  25. data/app/rules/pyr_rules/model_event_emitter.rb +80 -0
  26. data/app/rules/pyr_rules/rule_context.rb +20 -0
  27. data/app/rules/pyr_rules/rules_config.rb +87 -0
  28. data/app/rules/pyr_rules/rules_engine.rb +38 -0
  29. data/app/rules/pyr_rules/web_request_logger.rb +6 -0
  30. data/app/views/pyr_rules/actions/index.html.erb +12 -0
  31. data/app/views/pyr_rules/events/_sidebar.html.erb +8 -0
  32. data/app/views/pyr_rules/events/index.html.erb +72 -0
  33. data/app/views/pyr_rules/events/show.html.erb +11 -0
  34. data/app/views/pyr_rules/rules/_form.html.erb +28 -0
  35. data/app/views/pyr_rules/rules/_ready_headers.js.erb +11 -0
  36. data/app/views/pyr_rules/rules/_rule_context_field.html.erb +10 -0
  37. data/app/views/pyr_rules/rules/_rules.html.erb +34 -0
  38. data/app/views/pyr_rules/rules/add_action_mapping.js.erb +13 -0
  39. data/app/views/pyr_rules/rules/edit.html.erb +14 -0
  40. data/app/views/pyr_rules/rules/index.html.erb +5 -0
  41. data/app/views/pyr_rules/rules/lookup_sub_properties.js.erb +13 -0
  42. data/app/views/pyr_rules/rules/new.html.erb +11 -0
  43. data/app/views/pyr_rules/rules/remove_action_mapping.js.erb +15 -0
  44. data/app/views/pyr_rules/rules/show.html.erb +98 -0
  45. data/app/views/shared/_action.html.erb +41 -0
  46. data/app/views/shared/_action_field_mapping.html.erb +33 -0
  47. data/app/views/shared/_event.html.erb +21 -0
  48. data/app/views/shared/_rule.html.erb +27 -0
  49. data/config/routes.rb +32 -0
  50. data/config/security.yml +9 -0
  51. data/db/seeds.rb +1 -0
  52. data/lib/pyr_rules.rb +34 -0
  53. data/lib/pyr_rules/engine.rb +72 -0
  54. data/lib/pyr_rules/generators/dummy_generator.rb +27 -0
  55. data/lib/pyr_rules/version.rb +3 -0
  56. data/lib/tasks/pyr_rules_tasks.rake +57 -0
  57. metadata +224 -0
@@ -0,0 +1,57 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ div.field, div.actions {
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ #notice {
24
+ color: green;
25
+ }
26
+
27
+ .field_with_errors {
28
+ padding: 2px;
29
+ background-color: red;
30
+ display: table;
31
+ }
32
+
33
+ #error_explanation {
34
+ width: 450px;
35
+ border: 2px solid red;
36
+ padding: 7px;
37
+ padding-bottom: 0;
38
+ margin-bottom: 20px;
39
+ background-color: #f0f0f0;
40
+ }
41
+
42
+ #error_explanation h2 {
43
+ text-align: left;
44
+ font-weight: bold;
45
+ padding: 5px 5px 5px 15px;
46
+ font-size: 12px;
47
+ margin: -7px;
48
+ margin-bottom: 0px;
49
+ background-color: #c00;
50
+ color: #fff;
51
+ }
52
+
53
+ #error_explanation ul li {
54
+ font-size: 12px;
55
+ list-style: square;
56
+ }
57
+
@@ -0,0 +1,22 @@
1
+ class PyrRules::ActionsController < ApplicationController
2
+
3
+ def create
4
+ @rule = PyrRules::Rule.find(params[:rule_id])
5
+ @action = @rule.actions.create!(params[:pyr_rules_action])
6
+
7
+ redirect_to (params[:event_id].presence ? pyr_rules_event_rule_url(@rule, event_id: params[:event_id]) : pyr_rules_rule_url(@rule)), :notice => "Action created!"
8
+ end
9
+
10
+ def destroy
11
+ @rule = PyrRules::Rule.find(params[:rule_id])
12
+ @rule.actions.delete(@rule.actions.find(params[:id]))
13
+
14
+ redirect_to (params[:event_id].presence ? pyr_rules_event_rule_url(@rule, event_id: params[:event_id]) : pyr_rules_rule_url(@rule)), :notice => "Action deleted!"
15
+ end
16
+
17
+ def index
18
+ @actions_config = PyrRules::ActionHandler.actions_config
19
+ @actions = @actions_config.map{|type,needs| a= PyrRules::Action.new;a.type=type;a}
20
+ end
21
+
22
+ end
@@ -0,0 +1,17 @@
1
+ class PyrRules::EventsController < ApplicationController
2
+ def index
3
+ @events_tree = PyrRules::RulesConfig.events_tree
4
+ respond_to do |format|
5
+ format.html
6
+ end
7
+ end
8
+
9
+ def show
10
+ @event = PyrRules::RulesConfig.event params[:id]
11
+ @pyr_rules_rules = PyrRules::Rule.where(event: @event[:name]).all
12
+ respond_to do |format|
13
+ format.html
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,233 @@
1
+ module PyrRules
2
+ class RulesController < ApplicationController
3
+
4
+ # before_filter :admin_required # If you include Rules, you want this controller SECURE!!!
5
+
6
+ def lookup_events
7
+ r = Regexp.new(".*#{params[:q]}.*", "i")
8
+ list = PyrRules::RulesConfig.events.grep r
9
+ render json: list.first(10).map{|e| {id: e.gsub("::","__") ,value: e}}
10
+ end
11
+
12
+ def add_event
13
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
14
+ event = params[:event].gsub("__","::") rescue nil
15
+ @pyr_rules_rule.events << event unless @pyr_rules_rule.events.index event
16
+ if @pyr_rules_rule.save
17
+ redirect_to :back, notice: "#{event} is a trigger for this rule"
18
+ else
19
+ redirect_to :back, error: "Could not add event: #{event}. Suggest you check the logs and maybe this will help, too: #{@pyr_rules_rule.errors}"
20
+ end
21
+ end
22
+
23
+ def remove_event
24
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
25
+ event = params[:event].gsub("__","::") rescue nil
26
+ @pyr_rules_rule.events.delete event
27
+ if @pyr_rules_rule.save
28
+ redirect_to :back, notice: "#{event} is no longer a trigger for this rule"
29
+ else
30
+ redirect_to :back, error: "Could not remove event: #{event}. Suggest you check the logs and maybe this will help, too: #{@pyr_rules_rule.errors}"
31
+ end
32
+ end
33
+
34
+ def lookup_actions
35
+ r = Regexp.new(".*#{params[:q]}.*", "i")
36
+ list = PyrRules::ActionHandler.action_listing.map(&:to_s).grep r
37
+ render json: list.first(10).map{|a| {id: a.gsub("::","__") ,value: a}}
38
+ end
39
+
40
+ def add_action
41
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
42
+ action = params[:action_class].gsub("__","::") rescue nil
43
+ @pyr_rules_rule.actions << PyrRules::Action.new(type: action)
44
+ if @pyr_rules_rule.save
45
+ redirect_to :back, notice: "Added event: #{action}"
46
+ else
47
+ redirect_to :back, error: "Could not add event: #{action}. Suggest you check the logs and maybe this will help, too: #{@pyr_rules_rule.errors}"
48
+ end
49
+ end
50
+
51
+ def remove_action
52
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
53
+ action_id = params[:action_id] rescue nil
54
+ action = @pyr_rules_rule.actions.detect{|a| a.id.to_s == action_id}
55
+ action.delete if action
56
+ if @pyr_rules_rule.save
57
+ redirect_to :back, notice: "Action deleted"
58
+ else
59
+ redirect_to :back, error: "Could not remove action. Suggest you check the logs and maybe this will help, too: #{@pyr_rules_rule.errors}"
60
+ end
61
+
62
+ end
63
+
64
+ def lookup_sub_properties
65
+ @rule = PyrRules::Rule.find(params[:rule_id])
66
+ @div_id = request[:div_id]
67
+ @path = params[:path]
68
+ @ctx = @rule.lookup_path(@path)
69
+ respond_to do |format|
70
+ format.js {}
71
+ format.json { render json: @ctx }
72
+ end
73
+
74
+
75
+ end
76
+
77
+ def add_action_mapping
78
+ PyrRules::Action.add_mapping_type_equivalency(:messaging_user, [:user, :tree_user, :contact])
79
+ PyrRules::Action.add_mapping_type_equivalency(:object, [:user, :messaging_user, :contact, :string])
80
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
81
+ action_id = params[:action_id]
82
+ rule_field = params[:rule_field]
83
+ action_field = params[:action_field]
84
+ @action = @pyr_rules_rule.actions.detect{|a| a.id.to_s == action_id}
85
+ @was_mapped = @action.context_mapping[action_field]
86
+ @action_field_name = action_field.split(":").first
87
+ @action_field_type = action_field.split(":").last
88
+ if @action_field_type == :class_lookup
89
+ klazz = rule_field.constantize
90
+ @action.context_mapping[action_field] = rule_field
91
+ @pyr_rules_rule.save
92
+ else
93
+ @rule_field_name = rule_field.split(":").first
94
+ rule_type = rule_field.split(":").last
95
+ if PyrRules::Action.check_mapping_type?(@action_field_type, rule_type)
96
+ @action.context_mapping[action_field] = rule_field
97
+ @pyr_rules_rule.save
98
+ else
99
+ @error = "Illegal field type mapping from #{rule_type} to #{@action_field_type}"
100
+ end
101
+ end
102
+ respond_to do |format|
103
+ format.html { redirect_to pyr_rules_rule_path(@pyr_rules_rule) }
104
+ format.js {}
105
+ end
106
+ end
107
+
108
+
109
+ def remove_action_mapping
110
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
111
+ action_id = params[:action_id]
112
+ action_field = params[:action_field]
113
+ @action = @pyr_rules_rule.actions.detect{|a| a.id.to_s == action_id}
114
+ @action_field_name = action_field.split(":").first
115
+ @action_field_type = action_field.split(":").last
116
+ mapped_from = @action.context_mapping[action_field]
117
+ if mapped_from
118
+ @action.context_mapping[action_field] = nil
119
+ @rule_field_name = mapped_from.split(":").first
120
+ @rule_field_type = mapped_from.split(":").last
121
+ @message = "Removed field mapping for #{@action_field_name}"
122
+ else
123
+ @message = "#{action_field} was not mapped"
124
+ end
125
+ @pyr_rules_rule.save
126
+ respond_to do |format|
127
+ format.html { redirect_to pyr_rules_rule_path(@pyr_rules_rule) }
128
+ format.js {}
129
+ end
130
+ end
131
+
132
+ def reload_rules_engine
133
+ c = PyrRules::RulesEngine.reload_configuration
134
+ notice = "Loaded #{c[:events].try(:size) || '0'} Events and #{c[:actions].try(:size) || '0'} Actions "
135
+ redirect_to :back, notice: notice
136
+ end
137
+
138
+ # GET /pyr_rules/rules
139
+ # GET /pyr_rules/rules.json
140
+ def index
141
+ @pyr_rules_rules = params[:event_id].presence ? PyrRules::Rule.where(event: params[:event_id]).all : PyrRules::Rule.all
142
+
143
+ respond_to do |format|
144
+ format.html # index.html.erb
145
+ format.json { render json: @pyr_rules_rules }
146
+ end
147
+ end
148
+
149
+ # GET /pyr_rules/rules/1
150
+ # GET /pyr_rules/rules/1.json
151
+ def show
152
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
153
+
154
+ respond_to do |format|
155
+ format.html # show.html.erb
156
+ format.json { render json: @pyr_rules_rule }
157
+ end
158
+ end
159
+
160
+ # GET /pyr_rules/rules/new
161
+ # GET /pyr_rules/rules/new.json
162
+ def new
163
+ @pyr_rules_rule = PyrRules::Rule.new
164
+ @pyr_rules_rule.event = params[:event_id] if params[:event_id]
165
+
166
+ respond_to do |format|
167
+ format.html # new.html.erb
168
+ format.json { render json: @pyr_rules_rule }
169
+ end
170
+ end
171
+
172
+ # GET /pyr_rules/rules/1/edit
173
+ def edit
174
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
175
+ end
176
+
177
+ # POST /pyr_rules/rules
178
+ # POST /pyr_rules/rules.json
179
+ def create
180
+ @pyr_rules_rule = PyrRules::Rule.new(params[:pyr_rules_rule])
181
+
182
+ respond_to do |format|
183
+ if @pyr_rules_rule.save
184
+ format.html { redirect_to (params[:event_id].presence ? pyr_rules_event_rule_url(@pyr_rules_rule, event_id: params[:event_id]) : pyr_rules_rule_url(@pyr_rules_rule)), notice: 'Rule was successfully created.' }
185
+ format.json { render json: @pyr_rules_rule, status: :created, location: @pyr_rules_rule }
186
+ else
187
+ format.html { render action: "new" }
188
+ format.json { render json: @pyr_rules_rule.errors, status: :unprocessable_entity }
189
+ end
190
+ end
191
+ end
192
+
193
+ # PUT /pyr_rules/rules/1
194
+ # PUT /pyr_rules/rules/1.json
195
+ def update
196
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
197
+ # {"action"=>{"522e0313a4c6b2154c000001"=>{"template"=>{"name"=>"asdf"}}}}
198
+ action_update = params[:pyr_rules_rule].delete(:action) rescue nil
199
+ if action_update
200
+ action_id = action_update.keys.first
201
+ action = @pyr_rules_rule.actions.detect{|a| a.id.to_s == action_id}
202
+ puts "Updating Rule #{@pyr_rules_rule.name}- Action #{action.class}"
203
+ template_name = action_update.values.first[:template].keys.first
204
+ template_value = action_update.values.first[:template].values.first
205
+ puts "TemplateName[#{template_name}] TemplateValue[#{template_value}]"
206
+ action.template[template_name] = template_value
207
+ @pyr_rules_rule.save!
208
+ end
209
+ respond_to do |format|
210
+ if @pyr_rules_rule.update_attributes(params[:pyr_rules_rule])
211
+ format.html { redirect_to (params[:event_id].presence ? pyr_rules_event_rule_url(@pyr_rules_rule, event_id: params[:event_id]) : pyr_rules_rule_url(@pyr_rules_rule)), notice: 'Rule was successfully updated.' }
212
+ format.json { head :no_content }
213
+ else
214
+ format.html { render action: "edit" }
215
+ format.json { render json: @pyr_rules_rule.errors, status: :unprocessable_entity }
216
+ end
217
+ end
218
+ end
219
+
220
+ # DELETE /pyr_rules/rules/1
221
+ # DELETE /pyr_rules/rules/1.json
222
+ def destroy
223
+ @pyr_rules_rule = PyrRules::Rule.find(params[:id])
224
+ @pyr_rules_rule.destroy
225
+
226
+ respond_to do |format|
227
+ format.html { redirect_to (params[:event_id].presence ? pyr_rules_event_rules_url(event_id: params[:event_id]) : pyr_rules_rules_url) }
228
+ format.json { head :no_content }
229
+ end
230
+ end
231
+
232
+ end
233
+ end
@@ -0,0 +1,2 @@
1
+ module PyrRules::ActionsHelper
2
+ end
@@ -0,0 +1,95 @@
1
+ # :model_events=>
2
+ # {"PyrCommunity"=>
3
+ # {"Blog"=>
4
+ # {:context=>
5
+ # {"id"=>{:type=>:integer},
6
+ # "title"=>{:type=>:string},
7
+ # "author_id"=>{:type=>:integer},
8
+ # "disqus_shortname"=>{:type=>:string},
9
+ # "background_tile"=>{:type=>:boolean},
10
+ # "background_blur"=>{:type=>:integer},
11
+ # "footer"=>{:type=>:text},
12
+ # "description"=>{:type=>:text},
13
+ # "twitter"=>{:type=>:string},
14
+ # "google_analytics"=>{:type=>:string},
15
+ # "background_uid"=>{:type=>:string},
16
+ # "logo_uid"=>{:type=>:string},
17
+ # "created_at"=>{:type=>:datetime},
18
+ # "updated_at"=>{:type=>:datetime}},
19
+ # :actions=>[:create, :update, :delete]}
20
+ # }
21
+ # },
22
+ # "PyrCore"=>
23
+ # {"Lead"=>
24
+ # {:context=>
25
+ # {"id"=>{:type=>:integer},
26
+ # "first_name"=>{:type=>:string},
27
+ # "last_name"=>{:type=>:string},
28
+ # "email"=>{:type=>:string},
29
+ # "phone"=>{:type=>:string},
30
+ # "lead_group"=>{:type=>:string},
31
+ # "comments"=>{:type=>:text},
32
+ # "user_id"=>{:type=>:integer},
33
+ # "created_at"=>{:type=>:datetime},
34
+ # "updated_at"=>{:type=>:datetime}},
35
+ # :actions=>[:create, :update, :delete]}}}}
36
+
37
+
38
+
39
+ module PyrRules::EventsHelper
40
+
41
+
42
+ def render_tree(events_tree)
43
+ stack = []
44
+ content_tag :div, id: "events_tree" do
45
+ body = ""
46
+ events_tree.each do |event_type, sub_events|
47
+ body += _render_and_recurse(event_type, sub_events, stack) + "\n"
48
+ end
49
+ body.html_safe
50
+ end
51
+ end
52
+
53
+
54
+ def _render_and_recurse(key,children,stack)
55
+ return "" if key.blank? # escape - don't render context variables
56
+ body = ""
57
+ if key == :actions # render each action as a leaf
58
+ children.each do |action|
59
+ body += _render_action_leaf(action, stack)
60
+ end
61
+ elsif key == :context
62
+ body = content_tag :div, class: "collapse-group", style:"block:inline;margin-left:30px;padding:5px;" do
63
+ content_tag :div, id:"#{stack.join('_')}_context", class: "collapse", style: "block:inline;padding:5px;min-height:30px;" do
64
+ context = children.collect{|k,v| "<b>#{k}</b> (<i>#{v}</i>)"}.join("<br/>")
65
+ inner_body = "<a class='btn btn-mini' data-toggle='collapse' data-target='##{stack.join('_')}_context'>context</a>".html_safe
66
+ inner_body += "<div class='collapse-group'>#{context}</div>".html_safe
67
+ inner_body += "<div style='clear:both;'></div>".html_safe
68
+ end
69
+ end
70
+ else
71
+ stack.push key
72
+ body += content_tag :div, class: "collapse-group", style: "margin-left:30px;position:relative;" do
73
+ content_tag :div, id:"#{stack.join('_')}", class: "collapse", style: "padding:5px;min-height:30px;" do
74
+ inner_body = "<a class='btn btn-mini' data-toggle='collapse' data-target='##{stack.join('_')}'>+</a><span style='font-weight:bold;'>#{key.to_s.titleize}</span>"
75
+ children.each do |child_key, grandkids|
76
+ #inner_body += "&nbsp;" * stack.size
77
+ inner_body += _render_and_recurse(child_key, grandkids, stack) + "\n"
78
+ end
79
+ inner_body.html_safe
80
+ end
81
+ end
82
+ stack.pop
83
+ end
84
+ body.html_safe
85
+ end
86
+
87
+ def _render_action_leaf(action_name, stack)
88
+ name = "#{stack.join('::')}_#{action_name}"
89
+ content_tag :div, id: name, style:"margin-left:30px;padding:5px;" do
90
+ content_tag :input, name: name, type: "checkbox" do
91
+ "<span style='margin-left:10px;'>#{action_name.to_s}</span>".html_safe
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,10 @@
1
+ module PyrRules::RulesHelper
2
+
3
+ # Don't drill into these types
4
+ @@dont_drill_types = [:boolean, :datetime, :integer, :messaging_user, :string, :text]
5
+
6
+ def drillable_rule_type?(check_type)
7
+ !@@dont_drill_types.index(check_type)
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ module PyrRules
2
+
3
+ end