pinkman 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +28 -0
- data/app/assets/javascripts/pinkman.js +10 -0
- data/app/assets/javascripts/pinkman_base/ajax.coffee +90 -0
- data/app/assets/javascripts/pinkman_base/collection.coffee +387 -0
- data/app/assets/javascripts/pinkman_base/common.coffee +56 -0
- data/app/assets/javascripts/pinkman_base/controller.coffee +164 -0
- data/app/assets/javascripts/pinkman_base/glue.coffee +15 -0
- data/app/assets/javascripts/pinkman_base/handlebars.js +4608 -0
- data/app/assets/javascripts/pinkman_base/hogan.js +576 -0
- data/app/assets/javascripts/pinkman_base/markup.js +483 -0
- data/app/assets/javascripts/pinkman_base/mixins.coffee +37 -0
- data/app/assets/javascripts/pinkman_base/object.js.coffee.erb +195 -0
- data/app/assets/javascripts/pinkman_base/pinkman.coffee +131 -0
- data/app/assets/javascripts/pinkman_base/render.coffee.erb +80 -0
- data/app/assets/javascripts/pinkman_base/tools.coffee +4 -0
- data/app/helpers/pinkman_helper.rb +48 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/generators/pinkman/USAGE +16 -0
- data/lib/generators/pinkman/api_generator.rb +60 -0
- data/lib/generators/pinkman/install_generator.rb +25 -0
- data/lib/generators/pinkman/model_generator.rb +41 -0
- data/lib/generators/pinkman/resource_generator.rb +15 -0
- data/lib/generators/pinkman/serializer_generator.rb +33 -0
- data/lib/generators/pinkman/templates/api.rb.erb +87 -0
- data/lib/generators/pinkman/templates/api_controller.rb +2 -0
- data/lib/generators/pinkman/templates/collection.js.erb +5 -0
- data/lib/generators/pinkman/templates/object.js.erb +3 -0
- data/lib/generators/pinkman/templates/serializer.rb.erb +15 -0
- data/lib/pinkman.rb +57 -0
- data/lib/pinkman/serializer.rb +6 -0
- data/lib/pinkman/serializer/base.rb +42 -0
- data/lib/pinkman/serializer/scope.rb +48 -0
- data/lib/pinkman/version.rb +3 -0
- data/pinkman.gemspec +46 -0
- data/public/javascripts/pinkman.min.js +1 -0
- data/public/jquery.pinkman.min.js +0 -0
- data/public/pinkman.min.js +29 -0
- metadata +242 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
window.Pinkman.mixin = (name,mix) ->
|
2
|
+
if name? and typeof name == 'string' and mix? and typeof mix == 'object'
|
3
|
+
Pinkman.mixins = new Pinkman.collection unless Pinkman.mixins?
|
4
|
+
mixin = new Pinkman.object
|
5
|
+
mixin.set 'mix', mix
|
6
|
+
mixin.set 'name',name
|
7
|
+
Pinkman.mixins.push mixin
|
8
|
+
|
9
|
+
window.Pinkman.mixit = (c,name) ->
|
10
|
+
mixin = Pinkman.mixins.getBy('name',name)
|
11
|
+
if mixin?
|
12
|
+
for n, method of mixin.mix
|
13
|
+
method['super'] = c.prototype[n]
|
14
|
+
c.prototype[n] = method
|
15
|
+
return(true)
|
16
|
+
else
|
17
|
+
false
|
18
|
+
|
19
|
+
|
20
|
+
# --- Mixins - Usage --- #
|
21
|
+
|
22
|
+
# Pinkman.mixin 'shitty',
|
23
|
+
# shit: (a) ->
|
24
|
+
# "#{a} is shit"
|
25
|
+
|
26
|
+
|
27
|
+
# class window.Foo
|
28
|
+
# Pinkman.mixit this, 'shitty'
|
29
|
+
|
30
|
+
# class window.Bar extends Pinkman.object
|
31
|
+
# @mixit 'shitty'
|
32
|
+
|
33
|
+
# a = new Foo
|
34
|
+
# a.shit(abc) -> 'abc is shit'
|
35
|
+
|
36
|
+
# a = new Bar
|
37
|
+
# a.shit(abc) -> 'abc is shit'
|
@@ -0,0 +1,195 @@
|
|
1
|
+
class window.PinkmanObject extends window.PinkmanCommon
|
2
|
+
|
3
|
+
@pinkmanType: 'object'
|
4
|
+
|
5
|
+
constructor: (attributesObject) ->
|
6
|
+
|
7
|
+
@isPink = true
|
8
|
+
@isObject = true
|
9
|
+
@pinkmanType = 'object'
|
10
|
+
|
11
|
+
@collections = new PinkmanCollection
|
12
|
+
@pinkey = Pinkman.all.length
|
13
|
+
|
14
|
+
Pinkman.objects.push(this)
|
15
|
+
Pinkman.all.push(this)
|
16
|
+
|
17
|
+
@initialize(attributesObject)
|
18
|
+
|
19
|
+
initialize: (attributesObject) ->
|
20
|
+
if typeof attributesObject == 'object'
|
21
|
+
for key, value of attributesObject
|
22
|
+
@set(key,value) if PinkmanObject.privateAttributes.indexOf(key) is -1
|
23
|
+
|
24
|
+
# Desc: assign attributes from a pure javascript object
|
25
|
+
# Usage: a.assign( {attribute: value} )
|
26
|
+
assign: (obj) ->
|
27
|
+
if typeof obj == 'object'
|
28
|
+
for k,v of obj
|
29
|
+
if typeof v == 'object' and this[k]? and this[k].isPink? and not v.isPink
|
30
|
+
this[k].assign(v)
|
31
|
+
else
|
32
|
+
this.set(k,v)
|
33
|
+
return true
|
34
|
+
else
|
35
|
+
return false
|
36
|
+
|
37
|
+
# Desc: returns a javascript object version of this
|
38
|
+
# Usage: a.attributes() #-> {key: value, key: value, ...}
|
39
|
+
attributes: () ->
|
40
|
+
a = new Object
|
41
|
+
for k,v of this
|
42
|
+
if PinkmanObject.privateAttributes.indexOf(k) == -1 and typeof v != 'function'
|
43
|
+
if v.isPink
|
44
|
+
a[k] = v.attributes()
|
45
|
+
else
|
46
|
+
a[k] = v
|
47
|
+
return a
|
48
|
+
|
49
|
+
# Desc: returns a array of attributes keys
|
50
|
+
# Usage:
|
51
|
+
# a.set('a','b'); a.set('x',value)
|
52
|
+
# a.attributesKeys() #-> ['a','x']
|
53
|
+
attributesKeys: ->
|
54
|
+
Object.keys(@attributes())
|
55
|
+
|
56
|
+
# Desc: Alias for attributesKeys
|
57
|
+
keys: () ->
|
58
|
+
@attributesKeys()
|
59
|
+
|
60
|
+
toString: () ->
|
61
|
+
array = []
|
62
|
+
array.push("(#{@className()})")
|
63
|
+
for a in @attributesKeys()
|
64
|
+
array.push "#{a}: #{this[a]};"
|
65
|
+
array.join(" ")
|
66
|
+
|
67
|
+
# --- Collection related functions --- #
|
68
|
+
|
69
|
+
# Desc: return the next obj (after this) in a collection
|
70
|
+
# Try to guess the collection if not provided
|
71
|
+
next: (collection='') ->
|
72
|
+
if collection.isPink and collection.pinkmanType == 'collection'
|
73
|
+
collection.next(this)
|
74
|
+
else if Pinkman.isNumber(collection)
|
75
|
+
c = @collections.get(collection)
|
76
|
+
c.next(this) if c?
|
77
|
+
else if not collection? or collection==''
|
78
|
+
@collections.first().next(this) if @collections.any()
|
79
|
+
|
80
|
+
# Desc: return the previous obj (before this) in a collection
|
81
|
+
# Try to guess the collection if not provided
|
82
|
+
prev: (collection='') ->
|
83
|
+
if collection.isPink and collection.pinkmanType == 'collection'
|
84
|
+
collection.prev(this)
|
85
|
+
else if Pinkman.isNumber(collection)
|
86
|
+
c = @collections.get(collection)
|
87
|
+
c.prev(this) if c?
|
88
|
+
else if not collection? or collection==''
|
89
|
+
@collections.first().prev(this) if @collections.any()
|
90
|
+
|
91
|
+
# Desc: remove this object from all collections
|
92
|
+
clearCollections: ->
|
93
|
+
c.remove(this) for c in @collections.collection
|
94
|
+
|
95
|
+
# alias for clearCollections
|
96
|
+
removeFromCollections: ->
|
97
|
+
@clearCollections()
|
98
|
+
|
99
|
+
# alias for clearCollections
|
100
|
+
removeFromAllCollections: ->
|
101
|
+
@clearCollections()
|
102
|
+
|
103
|
+
# alias for clearCollections
|
104
|
+
removeAllCollections: ->
|
105
|
+
@clearCollections()
|
106
|
+
|
107
|
+
# --- Error related --- #
|
108
|
+
|
109
|
+
# Desc: return first error message
|
110
|
+
firstError: ->
|
111
|
+
if @errors?
|
112
|
+
attr = Object.keys(@errors)[0]
|
113
|
+
"#{attr} #{@errors[attr][0]}"
|
114
|
+
|
115
|
+
# --- Ajax --- #
|
116
|
+
|
117
|
+
@find: (id, callback='') ->
|
118
|
+
obj = new this
|
119
|
+
Pinkman.ajax.get
|
120
|
+
url: obj.api() + id,
|
121
|
+
complete: (response) ->
|
122
|
+
obj.assign(response)
|
123
|
+
if obj.error? or obj.errors?
|
124
|
+
throw new Error(obj.firstError())
|
125
|
+
return false
|
126
|
+
else
|
127
|
+
callback(obj) if callback? and typeof callback == 'function'
|
128
|
+
return(obj)
|
129
|
+
|
130
|
+
create: (callback='') ->
|
131
|
+
unless @id?
|
132
|
+
Pinkman.ajax.post
|
133
|
+
url: @api()
|
134
|
+
data: { pink_obj: @attributes() }
|
135
|
+
complete: (response) =>
|
136
|
+
@assign(response)
|
137
|
+
delete @errors unless response.errors?
|
138
|
+
callback(this) if callback? and typeof callback=='function'
|
139
|
+
return(this)
|
140
|
+
|
141
|
+
update: (callback='') ->
|
142
|
+
console.log this
|
143
|
+
if @id?
|
144
|
+
Pinkman.ajax.patch
|
145
|
+
url: @api() + @id
|
146
|
+
data: { pink_obj: @attributes() }
|
147
|
+
complete: (response) =>
|
148
|
+
@assign(response)
|
149
|
+
delete @errors unless response.errors?
|
150
|
+
callback(this) if callback? and typeof callback=='function'
|
151
|
+
return(this)
|
152
|
+
|
153
|
+
reload: (callback) ->
|
154
|
+
Pinkman.ajax.get
|
155
|
+
url: @api() + @id,
|
156
|
+
complete: (response) =>
|
157
|
+
@assign(response)
|
158
|
+
if @error? or @errors?
|
159
|
+
throw new Error(@firstError())
|
160
|
+
return false
|
161
|
+
else
|
162
|
+
callback(this) if callback? and typeof callback == 'function'
|
163
|
+
return(this)
|
164
|
+
|
165
|
+
updateAttributes: (obj) ->
|
166
|
+
if this.assign(obj)
|
167
|
+
this.save (obj) ->
|
168
|
+
return(obj)
|
169
|
+
else
|
170
|
+
return false
|
171
|
+
|
172
|
+
|
173
|
+
save: (callback='') ->
|
174
|
+
if @id? and @id!='' then @update(callback) else @create(callback)
|
175
|
+
|
176
|
+
|
177
|
+
destroy: (callback='') ->
|
178
|
+
if @id?
|
179
|
+
Pinkman.ajax.delete
|
180
|
+
url: @api() + @id
|
181
|
+
complete: (response) =>
|
182
|
+
@assign(response)
|
183
|
+
delete @errors unless response.errors?
|
184
|
+
if @errors?
|
185
|
+
throw new Error(@firstError())
|
186
|
+
return(false)
|
187
|
+
else
|
188
|
+
@id = null
|
189
|
+
@removeFromAllCollections()
|
190
|
+
callback(this) if typeof callback=='function'
|
191
|
+
return(this)
|
192
|
+
else
|
193
|
+
return(false)
|
194
|
+
|
195
|
+
window.Pinkman.object = window.PinkmanObject
|
@@ -0,0 +1,131 @@
|
|
1
|
+
class window.Pinkman
|
2
|
+
|
3
|
+
# --- objects and collections manipulation
|
4
|
+
|
5
|
+
@collections = []
|
6
|
+
@objects = []
|
7
|
+
@all = []
|
8
|
+
|
9
|
+
@get: (pinkey) ->
|
10
|
+
this.all[pinkey]
|
11
|
+
|
12
|
+
@count: () ->
|
13
|
+
@all.length
|
14
|
+
|
15
|
+
# --- predicate
|
16
|
+
|
17
|
+
@isNumber: (n) ->
|
18
|
+
!isNaN(parseFloat(n)) && isFinite(n)
|
19
|
+
|
20
|
+
@isArray: (array) ->
|
21
|
+
array? and array.constructor is Array
|
22
|
+
|
23
|
+
@isString: (str) ->
|
24
|
+
str? and typeof str == 'string'
|
25
|
+
|
26
|
+
# --- tools and facilities
|
27
|
+
|
28
|
+
@doAfter: (ds, callback, timer = '_pinkman_doAfter') ->
|
29
|
+
window[timer] = window.setTimeout(callback, ds*100)
|
30
|
+
|
31
|
+
@clearTimeout: (timerName = '_pinkman_doAfter') ->
|
32
|
+
window.clearTimeout(window[timerName])
|
33
|
+
|
34
|
+
@defineGlobalVar: (variable,value) ->
|
35
|
+
window[variable] = value
|
36
|
+
|
37
|
+
@onlyOnce: (f,args...) ->
|
38
|
+
if typeof f == 'function'
|
39
|
+
|
40
|
+
func = new Object
|
41
|
+
func.f = f.toString()
|
42
|
+
func.args = args
|
43
|
+
|
44
|
+
if @calledFunctions.indexOf(func) == -1
|
45
|
+
@calledFunctions.push(func)
|
46
|
+
f(args...)
|
47
|
+
|
48
|
+
@calledFunctions = []
|
49
|
+
|
50
|
+
# --- Ajax
|
51
|
+
|
52
|
+
@ajax:
|
53
|
+
|
54
|
+
request: (options) ->
|
55
|
+
if options.url?
|
56
|
+
ajax = jQuery.ajax options.url,
|
57
|
+
beforeSend: (xhr) ->
|
58
|
+
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
|
59
|
+
type: options.type
|
60
|
+
dataType: 'json'
|
61
|
+
data: options.data
|
62
|
+
ajax.done (response) =>
|
63
|
+
if response.errors?
|
64
|
+
options.error(this) if options.error? and typeof options.error == 'function'
|
65
|
+
return(false)
|
66
|
+
else
|
67
|
+
options.success(response) if options.success? and typeof options.success == 'function'
|
68
|
+
options.complete(response) if options.complete? and typeof options.complete == 'function'
|
69
|
+
return(true)
|
70
|
+
else
|
71
|
+
return false
|
72
|
+
|
73
|
+
get: (options) ->
|
74
|
+
options.type = 'GET'
|
75
|
+
@request(options)
|
76
|
+
|
77
|
+
post: (options) ->
|
78
|
+
options.type = 'POST'
|
79
|
+
@request(options)
|
80
|
+
|
81
|
+
put: (options) ->
|
82
|
+
options.type = 'PUT'
|
83
|
+
@request(options)
|
84
|
+
|
85
|
+
patch: (options) ->
|
86
|
+
options.type = 'PATCH'
|
87
|
+
@request(options)
|
88
|
+
|
89
|
+
delete: (options) ->
|
90
|
+
options.type = 'DELETE'
|
91
|
+
@request(options)
|
92
|
+
|
93
|
+
file: (options) ->
|
94
|
+
if options.url?
|
95
|
+
ajax = jQuery.ajax options.url,
|
96
|
+
beforeSend: (xhr) ->
|
97
|
+
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
|
98
|
+
xhr: ->
|
99
|
+
myXhr = $.ajaxSettings.xhr()
|
100
|
+
myXhr.upload.addEventListener 'progress', (e) ->
|
101
|
+
if e.lengthComputable
|
102
|
+
options.progress e.loaded/e.total if options.progress?
|
103
|
+
, false
|
104
|
+
myXhr.addEventListener 'progress', (e) ->
|
105
|
+
if e.lengthComputable
|
106
|
+
options.progress e.loaded/e.total if options.progress?
|
107
|
+
, false
|
108
|
+
return myXhr
|
109
|
+
type: "POST"
|
110
|
+
dataType: 'json'
|
111
|
+
data: options.data
|
112
|
+
processData: false
|
113
|
+
contentType: false
|
114
|
+
ajax.done (response) =>
|
115
|
+
if response? and response.errors?
|
116
|
+
options.error(this) if options.error? and typeof options.error == 'function'
|
117
|
+
return false
|
118
|
+
else
|
119
|
+
options.success(response) if options.success? and typeof options.success == 'function'
|
120
|
+
options.complete(response) if options.complete? and typeof options.complete == 'function'
|
121
|
+
return this
|
122
|
+
else
|
123
|
+
return false
|
124
|
+
|
125
|
+
upload: (options...) ->
|
126
|
+
@file(options...)
|
127
|
+
|
128
|
+
$(document).ready ->
|
129
|
+
unless Pinkman.pathname?
|
130
|
+
Pinkman.pathname = window.location.pathname
|
131
|
+
Pinkman.pathname = Pinkman.pathname + '/' if Pinkman.pathname.charAt([Pinkman.pathname.length] - 1) != "/"
|
@@ -0,0 +1,80 @@
|
|
1
|
+
PinkmanCollection.prototype.renderQueue = new PinkmanCollection
|
2
|
+
PinkmanObject.prototype.renderQueue = new PinkmanCollection
|
3
|
+
|
4
|
+
Pinkman.templates = new Pinkman.collection
|
5
|
+
|
6
|
+
Pinkman.template_engine = '<%= Pinkman.configuration.js_template_engine %>'
|
7
|
+
|
8
|
+
Pinkman.template_engines =
|
9
|
+
handlebars: (options) ->
|
10
|
+
if options? and options.template? and $('#' + options.template).length
|
11
|
+
if Pinkman.templates.include(template: options.template, engine: Pinkman.template_engine)
|
12
|
+
template = Pinkman.templates.get(template: options.template, engine: Pinkman.template_engine)
|
13
|
+
else
|
14
|
+
template = new Pinkman.object(template: options.template, engine: Pinkman.template_engine)
|
15
|
+
template.handlebars = Handlebars.compile $('#' + options.template).html()
|
16
|
+
Pinkman.templates.push(template)
|
17
|
+
|
18
|
+
options.object = options.context if options.context?
|
19
|
+
content = template.handlebars(options.object)
|
20
|
+
|
21
|
+
|
22
|
+
if options.target?
|
23
|
+
$('#' + options.target).html(content)
|
24
|
+
$('#' + options.target).attr('data-pinkey',options.object.pinkey) if options.object? and typeof options.object == 'object' and options.object.isPink
|
25
|
+
|
26
|
+
options.callback(options.object,content) if options.callback? and typeof options.callback == 'function'
|
27
|
+
return(content)
|
28
|
+
|
29
|
+
hogan: (options) ->
|
30
|
+
if options? and options.template? and $('#' + options.template).length
|
31
|
+
if Pinkman.templates.include(template: options.template, engine: Pinkman.template_engine)
|
32
|
+
template = Pinkman.templates.get(template: options.template, engine: Pinkman.template_engine)
|
33
|
+
else
|
34
|
+
template = new Pinkman.object(template: options.template, engine: Pinkman.template_engine)
|
35
|
+
template.hogan = Hogan.compile $('#' + options.template).html()
|
36
|
+
Pinkman.templates.push(template)
|
37
|
+
|
38
|
+
options.object = options.context if options.context?
|
39
|
+
content = template.hogan.render(options.object)
|
40
|
+
|
41
|
+
if options.target?
|
42
|
+
$('#' + options.target).html(content)
|
43
|
+
$('#' + options.target).attr('data-pinkey',options.object.pinkey) if options.object? and typeof options.object == 'object' and options.object.isPink
|
44
|
+
|
45
|
+
options.callback(options.object,content) if options.callback? and typeof options.callback == 'function'
|
46
|
+
return(content)
|
47
|
+
|
48
|
+
markup: (options) ->
|
49
|
+
if options? and options.template? and $('#' + options.template).length
|
50
|
+
if Pinkman.templates.include(template: options.template, engine: Pinkman.template_engine)
|
51
|
+
template = Pinkman.templates.get(template: options.template, engine: Pinkman.template_engine)
|
52
|
+
else
|
53
|
+
template = new Pinkman.object(template: options.template, engine: Pinkman.template_engine)
|
54
|
+
template.markup = (args...) -> Mark.up(args...)
|
55
|
+
Pinkman.templates.push(template)
|
56
|
+
|
57
|
+
options.object = options.context if options.context?
|
58
|
+
content = template.markup($('#'+options.template).html(),options.object)
|
59
|
+
|
60
|
+
if options.target?
|
61
|
+
$('#' + options.target).html(content)
|
62
|
+
$('#' + options.target).attr('data-pinkey',options.object.pinkey) if options.object? and typeof options.object == 'object' and options.object.isPink
|
63
|
+
|
64
|
+
options.callback(options.object,content) if options.callback? and typeof options.callback == 'function'
|
65
|
+
return(content)
|
66
|
+
|
67
|
+
Pinkman.render = (options) ->
|
68
|
+
options.reRender = true unless options.reRender? and options.reRender == false
|
69
|
+
options.collectionReRender = true unless options.collectionReRender? and options.collectionReRender == false
|
70
|
+
|
71
|
+
engine = Pinkman.template_engines[Pinkman.template_engine]
|
72
|
+
content = engine(options)
|
73
|
+
options.object.renderQueue.directPush(options) if options.reRender and options.object? and options.object.isPink
|
74
|
+
|
75
|
+
Pinkman.reRender = (object) ->
|
76
|
+
engine = Pinkman.template_engines[Pinkman.template_engine]
|
77
|
+
engine(options) for options in object.renderQueue.collection
|
78
|
+
Pinkman.reRender(collection) for collection in object.collections.collection if object.collections? and options.collectionReRender
|
79
|
+
object.renderQueue.any()
|
80
|
+
|