kablam 0.0.4 → 0.0.8

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 (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