kablam 0.0.4 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/app/assets/javascripts/kablam/ajax.js +531 -0
  4. data/app/assets/javascripts/kablam/forms.js +167 -0
  5. data/app/assets/javascripts/kablam/messaging.js +95 -0
  6. data/app/assets/stylesheets/kablam.scss +25 -0
  7. data/app/channels/chat_channel.rb +12 -0
  8. data/app/controllers/concerns/api_settings.rb +41 -0
  9. data/app/controllers/data_controller.rb +133 -0
  10. data/app/views/data/create.js.erb +10 -0
  11. data/app/views/data/destroy.js.erb +12 -0
  12. data/app/views/data/form.html.erb +1 -0
  13. data/app/views/data/undo.js.erb +2 -0
  14. data/app/views/data/update.js.erb +9 -0
  15. data/app/views/kablam_forms/_form_generator.html.erb +1 -1
  16. data/app/views/kablam_forms/fields/_checkbox_array.html.erb +3 -3
  17. data/app/views/kablam_forms/fields/_checkbox_boolean.html.erb +3 -3
  18. data/app/views/kablam_forms/fields/_file_upload.html.erb +2 -2
  19. data/app/views/kablam_forms/fields/_input.html.erb +2 -2
  20. data/app/views/kablam_forms/fields/_multi_inputs.html.erb +2 -2
  21. data/app/views/kablam_forms/fields/_pretext.html.erb +1 -1
  22. data/app/views/kablam_forms/fields/_select.html.erb +3 -3
  23. data/app/views/kablam_forms/fields/_text.html.erb +3 -3
  24. data/kablam.gemspec +2 -1
  25. data/lib/generators/kablam/USAGE +5 -2
  26. data/lib/generators/kablam/install_generator.rb +37 -0
  27. data/lib/generators/kablam/messaging_generator.rb +40 -0
  28. data/lib/generators/kablam/templates/chat.rb +44 -0
  29. data/lib/generators/kablam/templates/kablam.rb +45 -0
  30. data/lib/generators/kablam/templates/message.rb +53 -0
  31. data/lib/kablam.rb +35 -0
  32. data/lib/kablam/forms.rb +26 -21
  33. data/lib/kablam/kablam_record.rb +61 -0
  34. metadata +32 -2
  35. data/lib/generators/kablam/kablam_generator.rb +0 -12
@@ -0,0 +1,167 @@
1
+ function byId(e){return document.getElementById(e);}
2
+ function newEl(tag){return document.createElement(tag);}
3
+ function newTxt(txt){return document.createTextNode(txt);}
4
+ function hide(e) {
5
+ var contentId = document.getElementById(e);
6
+ contentId.style.display = "none";
7
+ }
8
+ // random hex string generator
9
+ function randHex(len) {
10
+ var maxlen = 8,
11
+ min = Math.pow(16,Math.min(len,maxlen)-1)
12
+ max = Math.pow(16,Math.min(len,maxlen)) - 1,
13
+ n = Math.floor( Math.random() * (max-min+1) ) + min,
14
+ r = n.toString(16);
15
+ while ( r.length < len ) {
16
+ r = r + randHex( len - maxlen );
17
+ }
18
+ return r;
19
+ };
20
+
21
+ function insertAndExecute(id, text) {
22
+ byId(id).innerHTML = text;
23
+ var scripts = Array.prototype.slice.call(byId(id).getElementsByTagName("script"));
24
+ for (var i = 0; i < scripts.length; i++) {
25
+ if (scripts[i].src != "") {
26
+ var tag = newEl("script");
27
+ tag.src = scripts[i].src;
28
+ document.getElementsByTagName("head")[0].appendChild(tag);
29
+ }
30
+ else {
31
+ eval(scripts[i].innerHTML);
32
+ }
33
+ }
34
+ }
35
+
36
+ function load(target, url) {
37
+ AjaxRequest.get(
38
+ {
39
+ 'url':url,
40
+ 'onSuccess':function(req){ insertAndExecute(target, req.responseText); }
41
+ }
42
+ );
43
+ }
44
+
45
+ var W3CDOM = (document.createElement && document.getElementsByTagName);
46
+
47
+ function initFileUploads(opt) {
48
+ if (!W3CDOM) return;
49
+ var fakeFileUpload = document.createElement('div');
50
+ fakeFileUpload.className = 'kablam_fakefile';
51
+ var upload_icon = document.createElement('i');
52
+ upload_icon.className = opt['icon'];
53
+ var input = document.createElement('input');
54
+ input.type = "text";
55
+ input.className = opt['class'];
56
+
57
+ fakeFileUpload.appendChild(upload_icon);
58
+ fakeFileUpload.appendChild(input);
59
+
60
+ var x = document.getElementsByClassName('kablam_file');
61
+ for (var i=0;i<x.length;i++) {
62
+ if (x[i].type != 'file') continue;
63
+ if (x[i].parentElement.className != 'kablam_fileinput_init') continue;
64
+
65
+ x[i].parentElement.className = 'kablam_fileinputs';
66
+ var clone = fakeFileUpload.cloneNode(true);
67
+ x[i].parentElement.appendChild(clone);
68
+ x[i].relatedElement = clone.getElementsByTagName('input')[0];
69
+
70
+ x[i].onchange = x[i].onmouseout = function () {
71
+ path = this.value
72
+ path = path.replace( /fakepath\\/, '...' );
73
+ path = path.replace('C:\\', '');
74
+ this.relatedElement.value = path;
75
+ }
76
+ }
77
+ }
78
+
79
+ function addInput(id, name, classes){
80
+ var input_set = document.getElementById(id);
81
+ var new_set = document.createElement('div');
82
+ new_set.id = randHex(12);
83
+ new_set.className = classes['group'];
84
+
85
+ var input = document.createElement('input');
86
+ input.type = "text";
87
+ input.className = classes['input'];
88
+ input.name = name;
89
+ var button = document.createElement('a');
90
+ button.className = classes['btn'];
91
+ button.setAttribute("onclick", "event.preventDefault();rmParent(this);");
92
+ var icon = document.createElement('i');
93
+ icon.className = classes['icon'];
94
+
95
+ button.appendChild(icon);
96
+ new_set.appendChild(input);
97
+ new_set.appendChild(button);
98
+ input_set.appendChild(new_set);
99
+ }
100
+
101
+ function rmParent(el){
102
+ el.parentElement.remove();
103
+ }
104
+
105
+
106
+ Object.defineProperty(HTMLElement, 'From', {
107
+ enumerable: false,
108
+ value: (function (document) {
109
+ //https://www.measurethat.net/Benchmarks/Show/2149/0/element-creation-speed
110
+ var rgx = /(\S+)=(["'])(.*?)(?:\2)|(\w+)/g;
111
+ return function CreateElementFromHTML(html) {
112
+ html = html.trim();
113
+ var bodystart = html.indexOf('>') + 1, bodyend = html.lastIndexOf('<');
114
+ var elemStart = html.substr(0, bodystart);
115
+ var innerHTML = html.substr(bodystart, bodyend - bodystart);
116
+ rgx.lastIndex = 0;
117
+ var elem = document.createElement(rgx.exec(elemStart)[4]);
118
+ var match; while ((match = rgx.exec(elemStart))) {
119
+ if (match[1] === undefined) {
120
+ elem.setAttribute(match[4], "");
121
+ } else {
122
+ elem.setAttribute(match[1], match[3]);
123
+ }
124
+ }
125
+ elem.innerHTML = innerHTML;
126
+ return elem;
127
+ };
128
+ }(window.document))
129
+ });
130
+
131
+
132
+ function submitRemove(form_id){
133
+ var form = document.getElementById(form_id);
134
+ form.addEventListener("ajax:success", function(e) {
135
+ e.preventDefault();
136
+ form.remove();
137
+ });
138
+ }
139
+
140
+ function submitToTop(form_id){
141
+ var form = document.getElementById(form_id);
142
+ form.addEventListener("ajax:success", function(e) {
143
+ e.preventDefault();
144
+ scrollToTop(500);
145
+ });
146
+ }
147
+
148
+ function scrollToTop(scrollDuration) {
149
+ const scrollHeight = window.scrollY,
150
+ scrollStep = Math.PI / ( scrollDuration / 15 ),
151
+ cosParameter = scrollHeight / 2;
152
+ var scrollCount = 0,
153
+ scrollMargin,
154
+ scrollInterval = setInterval( function() {
155
+ if ( window.scrollY != 0 ) {
156
+ scrollCount = scrollCount + 1;
157
+ scrollMargin = cosParameter - cosParameter * Math.cos( scrollCount * scrollStep );
158
+ window.scrollTo( 0, ( scrollHeight - scrollMargin ) );
159
+ }
160
+ else clearInterval(scrollInterval);
161
+ }, 15 );
162
+ }
163
+
164
+ function toggle_visibility(id) {
165
+ var e = document.getElementById(id);
166
+ e.style.display = 'none';
167
+ }
@@ -0,0 +1,95 @@
1
+ var scrollPosition;
2
+ document.addEventListener('turbolinks:load', refreshScroll());
3
+
4
+ function refreshScroll(){
5
+ if (scrollPosition) {
6
+ window.scrollTo.apply(window, scrollPosition);
7
+ scrollPosition = null;
8
+ }
9
+ }
10
+
11
+ function TurboRefresh(){
12
+ scrollPosition = [window.scrollX, window.scrollY];
13
+ Turbolinks.visit(window.location.toString(), { action: 'replace' });
14
+ }
15
+
16
+ function Sound(path) {
17
+ var audio = new Audio(path);
18
+ audio.play();
19
+ }
20
+
21
+ function TopDot(dot_id, dot_class="dot bg-officeblue"){
22
+ var topdotspan = document.getElementById(dot_id);
23
+ topdotspan.className = dot_class;
24
+ }
25
+
26
+
27
+ function RemoveChatsSub(chatid) {
28
+ var all = App.cable.subscriptions.subscriptions;
29
+ var all_chats_sub_id = all.map(function (x) {return JSON.parse(x.identifier).id});
30
+ var index_of_subed_chats = [];
31
+ all_chats_sub_id.forEach(function(item,index) {
32
+ if (chatid != undefined) {
33
+ if (item != undefined && item != chatid) {
34
+ index_of_subed_chats.push(index);
35
+ }
36
+ } else {
37
+ if (item != undefined) {
38
+ index_of_subed_chats.push(index);
39
+ }
40
+ }
41
+ });
42
+ index_of_subed_chats.forEach(function(item) {
43
+ App.cable.subscriptions.remove(all[item]);
44
+ });
45
+ }
46
+
47
+
48
+ function RemoveNotificationDot(dot_id){
49
+ var dotspan = document.getElementById(dot_id);
50
+ dotspan.className = "";
51
+ }
52
+
53
+ function UserChatSub(chat_div_id, chat_id, channel, user_id, options={}){
54
+ RemoveChatsSub();
55
+ var chatdiv = document.getElementById(chat_div_id);
56
+
57
+ App.cable.subscriptions.create({ channel: channel, id: chat_id }, {
58
+ received: function(data) {
59
+ HtmlNode(data, options);
60
+ if (data['chat']['sender_id'] != user_id) {
61
+ TopDot();
62
+ }
63
+ }
64
+ })
65
+ }
66
+
67
+ function HtmlNode(data, options={}){
68
+ options = {
69
+ outerDivClass: options.outerDivClass || "pt3 ph3 ph2-l bg-white br2 shadow1 mb3",
70
+ innerDivClass: options.innerDivClass || "dt w-100 pb2 ph2 mt2",
71
+ imgDivClass: options.imgDivClass || "dtc w2 w3-ns v-mid",
72
+ imgClass: options.imgClass || "ba b--black-10 db br-100 w2 w3-ns h2 h3-ns",
73
+ userDivClass: options.userDivClass || "dtc v-mid pl3",
74
+ userNameClass: options.userNameClass || "f6 f5-ns fw6 lh-title black mv0",
75
+ timestampClass: options.timestampClass || "f6 fw4 mt0 mb0 black-60 i",
76
+ messageDivClass: options.messageDivClass || "ph3-l pb2",
77
+ messageClass: options.messageClass || "f5 lh-copy measure pb3"
78
+ }
79
+
80
+ var new_message = document.createElement("div");
81
+
82
+ new_message.className = options.outerDivClass;
83
+ var content = data['chat'].content.replace(/(?:\r\n|\r|\n)/g, '<br>');
84
+ new_message.innerHTML = `<div class="${options.innerDivClass}">
85
+ <div class="${options.imgDivClass}">
86
+ <img src="${data['chat'].image}" class="${options.imgClass}"/>
87
+ </div>
88
+ <div class="${options.userDivClass}">
89
+ <h1 class="${options.userNameClass}">${data['chat'].username}</h1>
90
+ <h2 class="${options.timestampClass}">${data['chat'].created_at}</h2>
91
+ </div>
92
+ </div>
93
+ <div class="${options.messageDivClass}"><p class="${options.messageClass}">${content}</p></div>`;
94
+ chatdiv.insertAdjacentElement('afterbegin', new_message);
95
+ }
@@ -0,0 +1,25 @@
1
+ div.kablam_fileinputs {
2
+ position: relative;
3
+ }
4
+
5
+ div.kablam_fakefile {
6
+ position: absolute;
7
+ top: 0px;
8
+ left: 0px;
9
+ z-index: 1;
10
+ width: 100%;
11
+ height: 100%;
12
+ text-align: center;
13
+ }
14
+
15
+ input.kablam_file {
16
+ position: relative;
17
+ text-align: right;
18
+ -moz-opacity:0 ;
19
+ filter: opacity(0);
20
+ opacity: 0;
21
+ z-index: 2;
22
+ }
23
+ .hidden {
24
+ display: none;
25
+ }
@@ -0,0 +1,12 @@
1
+ class ChatChannel < ApplicationCable::Channel
2
+ def subscribed
3
+ # stream_from "some_channel"
4
+ stream_from "ChatChannel_#{params[:id]}"
5
+
6
+ puts "subscribed to chat #{params[:id]}"
7
+ end
8
+
9
+ def unsubscribed
10
+ # Any cleanup needed when channel is unsubscribed
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ module Concerns
2
+ module ApiSettings
3
+ extend ActiveSupport::Concern
4
+ require 'securerandom'
5
+ protected
6
+ def send_to_bucket(bucket, upload_item)
7
+ puts "uploading..."
8
+ path = ENV.fetch("BUCKET_TRANSFER_API")
9
+ bucket = ENV.fetch("BUCKET_NAME")
10
+
11
+
12
+ post_params = {"oss"=> {
13
+ "bucket" => bucket,
14
+ "user_id" => "TEST",
15
+ "attachment" => upload_item,
16
+ "encrypted_name" => SecureRandom.hex
17
+ }}
18
+ response = RestClient.post(path, post_params)
19
+ JSON.parse(response.body)["url"]
20
+ end
21
+
22
+ def slack_it(url, content)
23
+ # url must be a SLACK webhook
24
+ message =
25
+ {
26
+ "attachments": [
27
+ {
28
+ "fallback": "",
29
+ "color": "#36a64f",
30
+ "pretext": "#{content[:pretext] || '---'}",
31
+ "author_name": "#{content[:author] || '-no_author_provided-'}",
32
+ "title": "#{content[:title] || '-no_title_provided-'}",
33
+ "text": "#{content[:text] || '-no_text_provided-'}"
34
+ }
35
+ ]
36
+ }.to_json
37
+
38
+ RestClient.post(url, message)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,133 @@
1
+ require 'rest-client'
2
+
3
+ class DataController < ApplicationController
4
+ protect_from_forgery with: :exception
5
+ before_action :set_model, only: [:create, :update, :undo, :destroy, :form]
6
+ before_action :set_object, only: [:update, :destroy]
7
+ before_action :set_undo_object, only: [:undo]
8
+ include Concerns::ApiSettings
9
+
10
+ def form
11
+ # Automatically Render Vanilla Form for use w/ JS.load
12
+ if params[:id].present?
13
+ set_object
14
+ else
15
+ @object = @model.new
16
+ set_ref
17
+ end
18
+ render layout: false
19
+ end
20
+
21
+ def create
22
+ @object = @model.new(model_params)
23
+
24
+ if @object.save
25
+ slack?(:create)
26
+ respond_to do |format|
27
+ format.js
28
+ format.html do
29
+ redirect_to request.referrer
30
+ flash[:notice] = :Created
31
+ end
32
+ end
33
+ else
34
+ # TODO: Add some failure handling 👍
35
+ end
36
+ end
37
+
38
+ def update
39
+ if @object.update(model_params)
40
+ slack?(:update)
41
+ respond_to do |format|
42
+ format.js
43
+ format.html do
44
+ redirect_to request.referrer
45
+ flash[:notice] = :Destroyed
46
+ end
47
+ end
48
+ else
49
+ # ADD FAILURE HANDLING
50
+ end
51
+ end
52
+
53
+ def destroy
54
+ if @object.destroy
55
+ slack?(:destroy)
56
+ respond_to do |format|
57
+ format.js
58
+ format.html do
59
+ redirect_to request.referrer
60
+ flash[:notice] = :Destroyed
61
+ end
62
+ end
63
+ else
64
+ # TODO: Add some failure handling 👍
65
+ end
66
+ end
67
+
68
+ def undo
69
+ @object.update(undo_params)
70
+ end
71
+
72
+ def slack
73
+ render status: 200, json: params['challenge']
74
+ end
75
+
76
+ private
77
+
78
+ def slack?(crud_type)
79
+ if @model.slack_hook.present? && @object.slack_message[crud_type].present?
80
+ slack_it(@model.slack_hook, @object.slack_message[crud_type])
81
+ end
82
+ end
83
+
84
+ def set_object
85
+ @object = @model.find(params[:id])
86
+ end
87
+
88
+ def set_undo_object
89
+ @object = @model.unscoped.where.not(destroyed_at: nil).find(params[:id])
90
+ end
91
+
92
+ def set_model
93
+ db_index = Hash[ActiveRecord::Base.connection.tables.collect{|c| [c, c.classify]}].except("schema_migrations", "ar_internal_metadata")
94
+ @model = db_index[params[:name]].constantize # "users" => User
95
+ end
96
+
97
+ def undo_params
98
+ params.require(@model.to_s.underscore.to_sym).permit :destroyed_at
99
+ end
100
+
101
+ def base_params
102
+ arr = @model.attribute_names.map { |e| e.to_sym }
103
+ arr.delete(:id)
104
+ arr.delete(:created_at)
105
+ arr.delete(:updated_at)
106
+ arr.map! do |attr|
107
+ new_val = (@model.new.send(attr) == []) ? {attr=>[]} : attr
108
+ new_val
109
+ end
110
+ params.require(@model.to_s.underscore.to_sym).permit(arr)
111
+ end
112
+
113
+ def model_params
114
+ result = base_params
115
+ upload_items = base_params.select{|k,v| v.is_a? ActionDispatch::Http::UploadedFile }
116
+ upload_items.each do |k,v|
117
+ result[k] = send_to_bucket(v)
118
+ end
119
+ result
120
+ end
121
+
122
+ def set_ref
123
+ owners = @model.reflect_on_all_associations(:belongs_to) || []
124
+ if owners.count < 1
125
+ return true
126
+ elsif owners.count == 1
127
+ foreign_key = owners.first.name.to_s+"_id"
128
+ @object.write_attribute(foreign_key, params[:ref_id])
129
+ else
130
+ p "NOT ENOUGH REF'S ACCOUNTED FOR! REWRITE FORMS??"
131
+ end
132
+ end
133
+ end