flipper-ui 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +17 -0
  5. data/Guardfile +26 -0
  6. data/LICENSE +22 -0
  7. data/README.md +101 -0
  8. data/Rakefile +7 -0
  9. data/examples/basic.ru +44 -0
  10. data/examples/flipper.html +14 -0
  11. data/examples/flipper.png +0 -0
  12. data/flipper-ui.gemspec +21 -0
  13. data/lib/flipper-ui.rb +1 -0
  14. data/lib/flipper/ui.rb +23 -0
  15. data/lib/flipper/ui/action.rb +172 -0
  16. data/lib/flipper/ui/action_collection.rb +20 -0
  17. data/lib/flipper/ui/actions/features.rb +21 -0
  18. data/lib/flipper/ui/actions/file.rb +17 -0
  19. data/lib/flipper/ui/actions/gate.rb +143 -0
  20. data/lib/flipper/ui/actions/index.rb +17 -0
  21. data/lib/flipper/ui/assets/javascripts/application.coffee +305 -0
  22. data/lib/flipper/ui/assets/javascripts/spine/ajax.coffee +223 -0
  23. data/lib/flipper/ui/assets/javascripts/spine/list.coffee +43 -0
  24. data/lib/flipper/ui/assets/javascripts/spine/local.coffee +16 -0
  25. data/lib/flipper/ui/assets/javascripts/spine/manager.coffee +83 -0
  26. data/lib/flipper/ui/assets/javascripts/spine/relation.coffee +148 -0
  27. data/lib/flipper/ui/assets/javascripts/spine/route.coffee +146 -0
  28. data/lib/flipper/ui/assets/javascripts/spine/spine.coffee +542 -0
  29. data/lib/flipper/ui/assets/javascripts/spine/version +1 -0
  30. data/lib/flipper/ui/assets/stylesheets/application.scss +237 -0
  31. data/lib/flipper/ui/decorators/feature.rb +37 -0
  32. data/lib/flipper/ui/decorators/gate.rb +36 -0
  33. data/lib/flipper/ui/error.rb +10 -0
  34. data/lib/flipper/ui/eruby.rb +11 -0
  35. data/lib/flipper/ui/middleware.rb +66 -0
  36. data/lib/flipper/ui/public/css/application.css +183 -0
  37. data/lib/flipper/ui/public/css/images/animated-overlay.gif +0 -0
  38. data/lib/flipper/ui/public/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  39. data/lib/flipper/ui/public/css/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  40. data/lib/flipper/ui/public/css/images/ui-bg_flat_10_000000_40x100.png +0 -0
  41. data/lib/flipper/ui/public/css/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  42. data/lib/flipper/ui/public/css/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  43. data/lib/flipper/ui/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  44. data/lib/flipper/ui/public/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  45. data/lib/flipper/ui/public/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  46. data/lib/flipper/ui/public/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  47. data/lib/flipper/ui/public/css/images/ui-icons_222222_256x240.png +0 -0
  48. data/lib/flipper/ui/public/css/images/ui-icons_228ef1_256x240.png +0 -0
  49. data/lib/flipper/ui/public/css/images/ui-icons_ef8c08_256x240.png +0 -0
  50. data/lib/flipper/ui/public/css/images/ui-icons_ffd27a_256x240.png +0 -0
  51. data/lib/flipper/ui/public/css/images/ui-icons_ffffff_256x240.png +0 -0
  52. data/lib/flipper/ui/public/css/jquery-ui-1.10.3.slider.min.css +5 -0
  53. data/lib/flipper/ui/public/images/logo.png +0 -0
  54. data/lib/flipper/ui/public/images/remove.png +0 -0
  55. data/lib/flipper/ui/public/js/application.js +544 -0
  56. data/lib/flipper/ui/public/js/handlebars.js +1992 -0
  57. data/lib/flipper/ui/public/js/jquery-ui-1.10.3.slider.min.js +6 -0
  58. data/lib/flipper/ui/public/js/jquery.js +9555 -0
  59. data/lib/flipper/ui/public/js/jquery.min.js +4 -0
  60. data/lib/flipper/ui/public/js/jquery.min.map +1 -0
  61. data/lib/flipper/ui/public/js/spine/ajax.js +320 -0
  62. data/lib/flipper/ui/public/js/spine/list.js +72 -0
  63. data/lib/flipper/ui/public/js/spine/local.js +29 -0
  64. data/lib/flipper/ui/public/js/spine/manager.js +157 -0
  65. data/lib/flipper/ui/public/js/spine/relation.js +260 -0
  66. data/lib/flipper/ui/public/js/spine/route.js +223 -0
  67. data/lib/flipper/ui/public/js/spine/spine.js +927 -0
  68. data/lib/flipper/ui/util.rb +12 -0
  69. data/lib/flipper/ui/version.rb +5 -0
  70. data/lib/flipper/ui/views/index.erb +9 -0
  71. data/lib/flipper/ui/views/layout.erb +161 -0
  72. data/script/bootstrap +21 -0
  73. data/script/server +19 -0
  74. data/script/test +30 -0
  75. data/spec/flipper/ui/decorators/feature_spec.rb +59 -0
  76. data/spec/flipper/ui/decorators/gate_spec.rb +47 -0
  77. data/spec/flipper/ui/util_spec.rb +18 -0
  78. data/spec/flipper/ui_spec.rb +470 -0
  79. data/spec/helper.rb +35 -0
  80. metadata +168 -0
@@ -0,0 +1,260 @@
1
+ (function() {
2
+ var Collection, Instance, Singleton, Spine, isArray, require, singularize, underscore,
3
+ __hasProp = {}.hasOwnProperty,
4
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
5
+
6
+ Spine = this.Spine || require('spine');
7
+
8
+ isArray = Spine.isArray;
9
+
10
+ require = this.require || (function(value) {
11
+ return eval(value);
12
+ });
13
+
14
+ Collection = (function(_super) {
15
+
16
+ __extends(Collection, _super);
17
+
18
+ function Collection(options) {
19
+ var key, value;
20
+ if (options == null) {
21
+ options = {};
22
+ }
23
+ for (key in options) {
24
+ value = options[key];
25
+ this[key] = value;
26
+ }
27
+ }
28
+
29
+ Collection.prototype.all = function() {
30
+ var _this = this;
31
+ return this.model.select(function(rec) {
32
+ return _this.associated(rec);
33
+ });
34
+ };
35
+
36
+ Collection.prototype.first = function() {
37
+ return this.all()[0];
38
+ };
39
+
40
+ Collection.prototype.last = function() {
41
+ var values;
42
+ values = this.all();
43
+ return values[values.length - 1];
44
+ };
45
+
46
+ Collection.prototype.find = function(id) {
47
+ var records,
48
+ _this = this;
49
+ records = this.select(function(rec) {
50
+ return ("" + rec.id) === ("" + id);
51
+ });
52
+ if (!records[0]) {
53
+ throw new Error("\"" + this.model.className + "\" model could not find a record for the ID \"" + id + "\"");
54
+ }
55
+ return records[0];
56
+ };
57
+
58
+ Collection.prototype.findAllByAttribute = function(name, value) {
59
+ var _this = this;
60
+ return this.model.select(function(rec) {
61
+ return _this.associated(rec) && rec[name] === value;
62
+ });
63
+ };
64
+
65
+ Collection.prototype.findByAttribute = function(name, value) {
66
+ return this.findAllByAttribute(name, value)[0];
67
+ };
68
+
69
+ Collection.prototype.select = function(cb) {
70
+ var _this = this;
71
+ return this.model.select(function(rec) {
72
+ return _this.associated(rec) && cb(rec);
73
+ });
74
+ };
75
+
76
+ Collection.prototype.refresh = function(values) {
77
+ var record, records, _i, _j, _len, _len1, _ref;
78
+ _ref = this.all();
79
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
80
+ record = _ref[_i];
81
+ delete this.model.records[record.id];
82
+ }
83
+ records = this.model.fromJSON(values);
84
+ if (!isArray(records)) {
85
+ records = [records];
86
+ }
87
+ for (_j = 0, _len1 = records.length; _j < _len1; _j++) {
88
+ record = records[_j];
89
+ record.newRecord = false;
90
+ record[this.fkey] = this.record.id;
91
+ this.model.records[record.id] = record;
92
+ }
93
+ return this.model.trigger('refresh', this.model.cloneArray(records));
94
+ };
95
+
96
+ Collection.prototype.create = function(record) {
97
+ record[this.fkey] = this.record.id;
98
+ return this.model.create(record);
99
+ };
100
+
101
+ Collection.prototype.associated = function(record) {
102
+ return record[this.fkey] === this.record.id;
103
+ };
104
+
105
+ return Collection;
106
+
107
+ })(Spine.Module);
108
+
109
+ Instance = (function(_super) {
110
+
111
+ __extends(Instance, _super);
112
+
113
+ function Instance(options) {
114
+ var key, value;
115
+ if (options == null) {
116
+ options = {};
117
+ }
118
+ for (key in options) {
119
+ value = options[key];
120
+ this[key] = value;
121
+ }
122
+ }
123
+
124
+ Instance.prototype.exists = function() {
125
+ return this.record[this.fkey] && this.model.exists(this.record[this.fkey]);
126
+ };
127
+
128
+ Instance.prototype.update = function(value) {
129
+ if (!(value instanceof this.model)) {
130
+ value = new this.model(value);
131
+ }
132
+ if (value.isNew()) {
133
+ value.save();
134
+ }
135
+ return this.record[this.fkey] = value && value.id;
136
+ };
137
+
138
+ return Instance;
139
+
140
+ })(Spine.Module);
141
+
142
+ Singleton = (function(_super) {
143
+
144
+ __extends(Singleton, _super);
145
+
146
+ function Singleton(options) {
147
+ var key, value;
148
+ if (options == null) {
149
+ options = {};
150
+ }
151
+ for (key in options) {
152
+ value = options[key];
153
+ this[key] = value;
154
+ }
155
+ }
156
+
157
+ Singleton.prototype.find = function() {
158
+ return this.record.id && this.model.findByAttribute(this.fkey, this.record.id);
159
+ };
160
+
161
+ Singleton.prototype.update = function(value) {
162
+ if (!(value instanceof this.model)) {
163
+ value = this.model.fromJSON(value);
164
+ }
165
+ value[this.fkey] = this.record.id;
166
+ return value.save();
167
+ };
168
+
169
+ return Singleton;
170
+
171
+ })(Spine.Module);
172
+
173
+ singularize = function(str) {
174
+ return str.replace(/s$/, '');
175
+ };
176
+
177
+ underscore = function(str) {
178
+ return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/-/g, '_').toLowerCase();
179
+ };
180
+
181
+ Spine.Model.extend({
182
+ hasMany: function(name, model, fkey) {
183
+ var association;
184
+ if (fkey == null) {
185
+ fkey = "" + (underscore(this.className)) + "_id";
186
+ }
187
+ association = function(record) {
188
+ if (typeof model === 'string') {
189
+ model = require(model);
190
+ }
191
+ return new Collection({
192
+ name: name,
193
+ model: model,
194
+ record: record,
195
+ fkey: fkey
196
+ });
197
+ };
198
+ return this.prototype[name] = function(value) {
199
+ if (value != null) {
200
+ association(this).refresh(value);
201
+ }
202
+ return association(this);
203
+ };
204
+ },
205
+ belongsTo: function(name, model, fkey) {
206
+ var association;
207
+ if (fkey == null) {
208
+ fkey = "" + (singularize(name)) + "_id";
209
+ }
210
+ association = function(record) {
211
+ if (typeof model === 'string') {
212
+ model = require(model);
213
+ }
214
+ return new Instance({
215
+ name: name,
216
+ model: model,
217
+ record: record,
218
+ fkey: fkey
219
+ });
220
+ };
221
+ this.prototype[name] = function(value) {
222
+ if (value != null) {
223
+ association(this).update(value);
224
+ }
225
+ return association(this).exists();
226
+ };
227
+ return this.attributes.push(fkey);
228
+ },
229
+ hasOne: function(name, model, fkey) {
230
+ var association;
231
+ if (fkey == null) {
232
+ fkey = "" + (underscore(this.className)) + "_id";
233
+ }
234
+ association = function(record) {
235
+ if (typeof model === 'string') {
236
+ model = require(model);
237
+ }
238
+ return new Singleton({
239
+ name: name,
240
+ model: model,
241
+ record: record,
242
+ fkey: fkey
243
+ });
244
+ };
245
+ return this.prototype[name] = function(value) {
246
+ if (value != null) {
247
+ association(this).update(value);
248
+ }
249
+ return association(this).find();
250
+ };
251
+ }
252
+ });
253
+
254
+ Spine.Collection = Collection;
255
+
256
+ Spine.Singleton = Singleton;
257
+
258
+ Spine.Instance = Instance;
259
+
260
+ }).call(this);
@@ -0,0 +1,223 @@
1
+ (function() {
2
+ var $, Spine, escapeRegExp, hashStrip, namedParam, splatParam,
3
+ __hasProp = {}.hasOwnProperty,
4
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
5
+ __slice = [].slice;
6
+
7
+ Spine = this.Spine || require('spine');
8
+
9
+ $ = Spine.$;
10
+
11
+ hashStrip = /^#*/;
12
+
13
+ namedParam = /:([\w\d]+)/g;
14
+
15
+ splatParam = /\*([\w\d]+)/g;
16
+
17
+ escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;
18
+
19
+ Spine.Route = (function(_super) {
20
+ var _ref;
21
+
22
+ __extends(Route, _super);
23
+
24
+ Route.extend(Spine.Events);
25
+
26
+ Route.historySupport = ((_ref = window.history) != null ? _ref.pushState : void 0) != null;
27
+
28
+ Route.routes = [];
29
+
30
+ Route.options = {
31
+ trigger: true,
32
+ history: false,
33
+ shim: false
34
+ };
35
+
36
+ Route.add = function(path, callback) {
37
+ var key, value, _results;
38
+ if (typeof path === 'object' && !(path instanceof RegExp)) {
39
+ _results = [];
40
+ for (key in path) {
41
+ value = path[key];
42
+ _results.push(this.add(key, value));
43
+ }
44
+ return _results;
45
+ } else {
46
+ return this.routes.push(new this(path, callback));
47
+ }
48
+ };
49
+
50
+ Route.setup = function(options) {
51
+ if (options == null) {
52
+ options = {};
53
+ }
54
+ this.options = $.extend({}, this.options, options);
55
+ if (this.options.history) {
56
+ this.history = this.historySupport && this.options.history;
57
+ }
58
+ if (this.options.shim) {
59
+ return;
60
+ }
61
+ if (this.history) {
62
+ $(window).bind('popstate', this.change);
63
+ } else {
64
+ $(window).bind('hashchange', this.change);
65
+ }
66
+ return this.change();
67
+ };
68
+
69
+ Route.unbind = function() {
70
+ if (this.options.shim) {
71
+ return;
72
+ }
73
+ if (this.history) {
74
+ return $(window).unbind('popstate', this.change);
75
+ } else {
76
+ return $(window).unbind('hashchange', this.change);
77
+ }
78
+ };
79
+
80
+ Route.navigate = function() {
81
+ var args, lastArg, options, path;
82
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
83
+ options = {};
84
+ lastArg = args[args.length - 1];
85
+ if (typeof lastArg === 'object') {
86
+ options = args.pop();
87
+ } else if (typeof lastArg === 'boolean') {
88
+ options.trigger = args.pop();
89
+ }
90
+ options = $.extend({}, this.options, options);
91
+ path = args.join('/');
92
+ if (this.path === path) {
93
+ return;
94
+ }
95
+ this.path = path;
96
+ this.trigger('navigate', this.path);
97
+ if (options.trigger) {
98
+ this.matchRoute(this.path, options);
99
+ }
100
+ if (options.shim) {
101
+ return;
102
+ }
103
+ if (this.history) {
104
+ return history.pushState({}, document.title, this.path);
105
+ } else {
106
+ return window.location.hash = this.path;
107
+ }
108
+ };
109
+
110
+ Route.getPath = function() {
111
+ var path;
112
+ path = window.location.pathname;
113
+ if (path.substr(0, 1) !== '/') {
114
+ path = '/' + path;
115
+ }
116
+ return path;
117
+ };
118
+
119
+ Route.getHash = function() {
120
+ return window.location.hash;
121
+ };
122
+
123
+ Route.getFragment = function() {
124
+ return this.getHash().replace(hashStrip, '');
125
+ };
126
+
127
+ Route.getHost = function() {
128
+ return (document.location + '').replace(this.getPath() + this.getHash(), '');
129
+ };
130
+
131
+ Route.change = function() {
132
+ var path;
133
+ path = this.history ? this.getPath() : this.getFragment();
134
+ if (path === this.path) {
135
+ return;
136
+ }
137
+ this.path = path;
138
+ return this.matchRoute(this.path);
139
+ };
140
+
141
+ Route.matchRoute = function(path, options) {
142
+ var route, _i, _len, _ref1;
143
+ _ref1 = this.routes;
144
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
145
+ route = _ref1[_i];
146
+ if (!(route.match(path, options))) {
147
+ continue;
148
+ }
149
+ this.trigger('change', route, path);
150
+ return route;
151
+ }
152
+ };
153
+
154
+ function Route(path, callback) {
155
+ var match;
156
+ this.path = path;
157
+ this.callback = callback;
158
+ this.names = [];
159
+ if (typeof path === 'string') {
160
+ namedParam.lastIndex = 0;
161
+ while ((match = namedParam.exec(path)) !== null) {
162
+ this.names.push(match[1]);
163
+ }
164
+ splatParam.lastIndex = 0;
165
+ while ((match = splatParam.exec(path)) !== null) {
166
+ this.names.push(match[1]);
167
+ }
168
+ path = path.replace(escapeRegExp, '\\$&').replace(namedParam, '([^\/]*)').replace(splatParam, '(.*?)');
169
+ this.route = new RegExp("^" + path + "$");
170
+ } else {
171
+ this.route = path;
172
+ }
173
+ }
174
+
175
+ Route.prototype.match = function(path, options) {
176
+ var i, match, param, params, _i, _len;
177
+ if (options == null) {
178
+ options = {};
179
+ }
180
+ match = this.route.exec(path);
181
+ if (!match) {
182
+ return false;
183
+ }
184
+ options.match = match;
185
+ params = match.slice(1);
186
+ if (this.names.length) {
187
+ for (i = _i = 0, _len = params.length; _i < _len; i = ++_i) {
188
+ param = params[i];
189
+ options[this.names[i]] = param;
190
+ }
191
+ }
192
+ return this.callback.call(null, options) !== false;
193
+ };
194
+
195
+ return Route;
196
+
197
+ })(Spine.Module);
198
+
199
+ Spine.Route.change = Spine.Route.proxy(Spine.Route.change);
200
+
201
+ Spine.Controller.include({
202
+ route: function(path, callback) {
203
+ return Spine.Route.add(path, this.proxy(callback));
204
+ },
205
+ routes: function(routes) {
206
+ var key, value, _results;
207
+ _results = [];
208
+ for (key in routes) {
209
+ value = routes[key];
210
+ _results.push(this.route(key, value));
211
+ }
212
+ return _results;
213
+ },
214
+ navigate: function() {
215
+ return Spine.Route.navigate.apply(Spine.Route, arguments);
216
+ }
217
+ });
218
+
219
+ if (typeof module !== "undefined" && module !== null) {
220
+ module.exports = Spine.Route;
221
+ }
222
+
223
+ }).call(this);