pinkman 0.9.0
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.
- 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
|
+
|