populate-me 0.0.33 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/LICENSE +1 -1
  4. data/README.md +399 -4
  5. data/Rakefile +14 -0
  6. data/example/config.ru +67 -0
  7. data/lib/populate_me.rb +2 -0
  8. data/lib/populate_me/admin.rb +143 -0
  9. data/lib/populate_me/admin/__assets__/css/main.css +174 -0
  10. data/lib/populate_me/admin/__assets__/js/columnav.js +82 -0
  11. data/lib/populate_me/admin/__assets__/js/main.js +251 -0
  12. data/lib/populate_me/admin/__assets__/js/mustache.js +578 -0
  13. data/lib/populate_me/admin/__assets__/js/sortable.js +2 -0
  14. data/lib/populate_me/admin/views/page.erb +163 -0
  15. data/lib/populate_me/api.rb +124 -0
  16. data/lib/populate_me/attachment.rb +182 -0
  17. data/lib/populate_me/document.rb +178 -0
  18. data/lib/populate_me/document_mixins/admin_adapter.rb +131 -0
  19. data/lib/populate_me/document_mixins/callbacks.rb +104 -0
  20. data/lib/populate_me/document_mixins/outcasting.rb +49 -0
  21. data/lib/populate_me/document_mixins/persistence.rb +92 -0
  22. data/lib/populate_me/document_mixins/schema.rb +99 -0
  23. data/lib/populate_me/document_mixins/typecasting.rb +60 -0
  24. data/lib/populate_me/document_mixins/validation.rb +44 -0
  25. data/lib/populate_me/file_system_attachment.rb +40 -0
  26. data/lib/populate_me/grid_fs_attachment.rb +127 -0
  27. data/lib/populate_me/mongo.rb +98 -3
  28. data/lib/populate_me/variation.rb +34 -0
  29. data/lib/populate_me/version.rb +4 -0
  30. data/populate-me.gemspec +20 -12
  31. data/test/helper.rb +37 -0
  32. data/test/test_admin.rb +161 -0
  33. data/test/test_api.rb +246 -0
  34. data/test/test_attachment.rb +155 -0
  35. data/test/test_document.rb +120 -0
  36. data/test/test_document_admin_adapter.rb +43 -0
  37. data/test/test_document_callbacks.rb +107 -0
  38. data/test/test_document_persistence.rb +56 -0
  39. data/test/test_document_typecasting.rb +121 -0
  40. data/test/test_mongo.rb +217 -0
  41. data/test/test_variation.rb +91 -0
  42. data/test/test_version.rb +11 -0
  43. metadata +115 -66
  44. data/lib/populate_me/control.rb +0 -196
  45. data/lib/populate_me/control/_public/css/main.css +0 -207
  46. data/lib/populate_me/control/_public/css/plugin.asmselect.css +0 -63
  47. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_flat_30_cccccc_40x100.png +0 -0
  48. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_flat_50_5c5c5c_40x100.png +0 -0
  49. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_glass_20_555555_1x400.png +0 -0
  50. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_glass_40_0078a3_1x400.png +0 -0
  51. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_glass_40_ffc73d_1x400.png +0 -0
  52. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_gloss-wave_25_333333_500x100.png +0 -0
  53. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_highlight-soft_80_eeeeee_1x100.png +0 -0
  54. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_inset-soft_25_000000_1x100.png +0 -0
  55. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-bg_inset-soft_30_f58400_1x100.png +0 -0
  56. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-icons_222222_256x240.png +0 -0
  57. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-icons_4b8e0b_256x240.png +0 -0
  58. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-icons_a83300_256x240.png +0 -0
  59. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-icons_cccccc_256x240.png +0 -0
  60. data/lib/populate_me/control/_public/css/ui-darkness/images/ui-icons_ffffff_256x240.png +0 -0
  61. data/lib/populate_me/control/_public/css/ui-darkness/jquery-ui-1.8.17.custom.css +0 -430
  62. data/lib/populate_me/control/_public/img/grip.png +0 -0
  63. data/lib/populate_me/control/_public/img/icons-cms-solarized.png +0 -0
  64. data/lib/populate_me/control/_public/img/icons-cms.png +0 -0
  65. data/lib/populate_me/control/_public/img/placeholder.png +0 -0
  66. data/lib/populate_me/control/_public/img/placeholder.stash_thumb.gif +0 -0
  67. data/lib/populate_me/control/_public/img/placeholder.stash_thumb.png +0 -0
  68. data/lib/populate_me/control/_public/img/small-loader.gif +0 -0
  69. data/lib/populate_me/control/_public/js/addon.timepicker.js +0 -20
  70. data/lib/populate_me/control/_public/js/jquery-ui.js +0 -114
  71. data/lib/populate_me/control/_public/js/jquery.js +0 -167
  72. data/lib/populate_me/control/_public/js/jquery.mustache.js +0 -559
  73. data/lib/populate_me/control/_public/js/main.js +0 -144
  74. data/lib/populate_me/control/_public/js/plugin.asmselect.js +0 -407
  75. data/lib/populate_me/control/_public/js/plugin.form.js +0 -11
  76. data/lib/populate_me/control/_public/js/plugin.quicksearch.js +0 -1
  77. data/lib/populate_me/control/_public/js/plugin.underwood.js +0 -4
  78. data/lib/populate_me/control/views/populate_me_layout.erb +0 -75
  79. data/lib/populate_me/ext.rb +0 -16
  80. data/lib/populate_me/mongo/backend_api_plug.rb +0 -78
  81. data/lib/populate_me/mongo/crushyform.rb +0 -243
  82. data/lib/populate_me/mongo/mutation.rb +0 -324
  83. data/lib/populate_me/mongo/plug.rb +0 -134
  84. data/lib/populate_me/mongo/stash.rb +0 -171
  85. data/test/spec_ext.rb +0 -29
  86. data/test/spec_mongo_mutation.rb +0 -279
@@ -1,196 +0,0 @@
1
- # encoding: utf-8
2
- require 'rack/golem'
3
- require 'json'
4
-
5
- module PopulateMe
6
- module Control
7
- F = ::File
8
- DIR = F.expand_path(F.dirname(__FILE__)+'/control')
9
- BEFORE = proc{
10
- }
11
-
12
- def self.included(klass)
13
- klass.class_eval do
14
- include ::Rack::Golem
15
- extend ClassMethods
16
- end
17
- klass.before(&BEFORE)
18
- end
19
-
20
- module ClassMethods
21
- def new(*)
22
- ::Rack::Static.new(::Rack::MethodOverride.new(super), :urls => ["/_public"], :root => DIR)
23
- end
24
- def config
25
- @config ||= {
26
- :client_name => 'Client Name',
27
- :website_url => 'www.domain.com',
28
- :page_title => 'Populate Me',
29
- :path => '/admin',
30
- :logout_path => '/admin/logout', # sometimes higher in stack
31
- :menu => [['Home', '/admin']],
32
- }
33
- end
34
- end
35
-
36
- def index(class_name=nil, id=nil)
37
- if class_name.nil?
38
- @r.env['erb.location'] = DIR+'/views/'
39
- erb :populate_me_layout
40
- else
41
- http_method = @r.request_method.downcase
42
- if ['post','put','delete'].include?(http_method)
43
- @model_class_name, @id = class_name, id
44
- build_model_vars
45
- return if @res.status==404
46
- @res['Content-Type'] = 'text/json'
47
- __send__(http_method)
48
- else
49
- send_404
50
- '' # Trick for the moment but it needs a throw for 404
51
- end
52
- end
53
- end
54
-
55
- def menu(*levels)
56
- level_menu = config[:menu]
57
- levels.each do |l|
58
- level_menu = level_menu.assoc(::Rack::Utils.unescape(l))[1]
59
- end
60
- items = level_menu.map do |i|
61
- {
62
- 'title'=>i[0],
63
- 'href'=> i[1].kind_of?(String) ?
64
- i[1] :
65
- "#{config[:path]}/menu/#{levels.join('/')}#{'/' unless levels.empty?}#{::Rack::Utils.escape(i[0])}"
66
- }
67
- end
68
- page_title = levels.empty? ? config[:page_title] : ::Rack::Utils.unescape(levels.join(' / '))
69
-
70
- @res['Content-Type'] = 'text/json'
71
- JSON.generate({
72
- 'action'=>'menu',
73
- 'page_title'=>page_title,
74
- 'items'=>items
75
- })
76
- end
77
-
78
- def list(class_name)
79
- @model_class_name = class_name
80
- resolve_model_class
81
- items = @model_class.find(@r['filter']||{}).map {|m| m.to_nutshell }
82
-
83
- @res['Content-Type'] = 'text/json'
84
- JSON.generate({
85
- 'action'=> 'list',
86
- 'page_title'=> @model_class.human_plural_name,
87
- 'class_name'=> class_name,
88
- 'sortable'=> @model_class.sortable_on_that_page?(@r),
89
- 'command_plus'=> !@model_class.populate_config[:no_plus],
90
- 'command_search'=> !@model_class.populate_config[:no_search],
91
- 'items'=> items,
92
- })
93
- end
94
-
95
- def form(class_name=nil, id=nil)
96
- @model_class_name, @id = class_name, id
97
- build_model_vars
98
- return if @res.status==404
99
- @model_instance ||= @model_class.backend_post
100
- @model_instance.backend_put @r['model']
101
- form = @model_instance.backend_form(@r.path.sub(/\/form/, ''), @r['fields'], :destination => @r['_destination'], :submit_text => @r['_submit_text'])
102
-
103
- @res['Content-Type'] = 'text/json'
104
- JSON.generate({
105
- 'action'=> 'form',
106
- 'page_title'=> @model_instance.backend_form_title,
107
- 'form'=>form
108
- })
109
- end
110
-
111
- private
112
-
113
- def config
114
- @config ||= self.class.config.update({
115
- :path => @r.script_name,
116
- :logout_path => "#{@r.script_name}/logout"
117
- }).dup
118
- end
119
-
120
- def resolve_model_class
121
- if @model_class_name.nil?
122
- raise("No model class name provided")
123
- end
124
- unless ::Object.const_defined?(@model_class_name)
125
- raise("#{@model_class_name} is not constant")
126
- end
127
- @model_class = Kernel.const_get(@model_class_name)
128
- unless @model_class.kind_of?(PopulateMe::Mongo::BackendApiPlug::ClassMethods)
129
- raise("Requested constant #{@model_class_name} is not a model class")
130
- end
131
- end
132
-
133
- def build_model_vars
134
- resolve_model_class
135
- @model_instance = @model_class.backend_get(@id) unless @id.nil?
136
- @clone_instance = @model_class.backend_get(@r['clone_id']) unless @r['clone_id'].nil?
137
- unless @clone_instance.nil?
138
- @r['fields'] ||= @clone_instance.cloning_backend_columns
139
- @r['model'] = @clone_instance.backend_values.reject{|k,v| !@r['fields'].include?(k.to_s)}
140
- end
141
- @r['model'] ||= {}
142
- send_404 if @model_instance.nil?&&!@id.nil?
143
- end
144
-
145
- def post
146
- return put unless @id.nil?
147
- @model_instance = @model_class.backend_post(@r['model'])
148
- save_and_respond
149
- end
150
-
151
- def put
152
- if @id.nil? && @r[@model_class_name]
153
- @model_class.sort(@r[@model_class_name])
154
- JSON.generate({'ids'=>@r[@model_class_name]})
155
- else
156
- @model_instance.backend_put(@r['model'])
157
- save_and_respond
158
- end
159
- end
160
-
161
- def delete
162
- @model_instance.backend_delete
163
- @r['_destination'].nil? ? @res.status=204 : @res.redirect(::Rack::Utils::unescape(@r['_destination'])) # 204 No Content
164
- end
165
-
166
- def save_and_respond
167
- if @model_instance.backend_save?
168
- if @r['_destination'].nil?
169
- @res.status=201 # Created
170
- JSON.generate({
171
- 'action'=> 'save',
172
- 'message'=> 'ok'
173
- })
174
- else
175
- @res.redirect(::Rack::Utils::unescape(@r['_destination']))
176
- end
177
- else
178
- form = @model_instance.backend_form(@r.path, @r['fields']||@r['model'].keys, :destination => @r['_destination'], :submit_text => @r['_submit_text'])
179
- # Bad Request 400 does not give content anymore in safari/lion
180
- # So I put 200 back until I find a better code
181
- JSON.generate({
182
- 'action'=> 'validation',
183
- 'form'=>form
184
- })
185
- end
186
- end
187
-
188
- def send_404
189
- @res.status=404 # Not Found
190
- @res.headers['X-Cascade']='pass'
191
- @res.write 'Not Found'
192
- end
193
-
194
- end
195
- end
196
-
@@ -1,207 +0,0 @@
1
- @import url(http://fonts.googleapis.com/css?family=Lato);
2
-
3
- body {
4
- font-family: 'Lato', sans-serif;
5
- font-size: 12px;
6
- color: #fdf6e3;
7
- background-color: #002b36;
8
- padding: 0px;
9
- /* Build column */
10
- height: 100%;
11
- margin: 0px;
12
- text-align: center; /* for IE7 */
13
- -webkit-font-smoothing: antialiased;
14
- }
15
-
16
- img { border: 0px; }
17
- ul { list-style: none; padding: 0px; margin: 0px; }
18
- a { color: #dc322f; }
19
- a:hover { text-decoration: none; }
20
-
21
- noscript div {
22
- padding: 20px;
23
- background-color: #b58900; color: #002b36;
24
- }
25
-
26
- .shadowed {
27
- -moz-box-shadow: 0px 0px 20px black;
28
- -webkit-box-shadow: 0px 0px 20px black;
29
- box-shadow: 0px 0px 20px black;
30
- }
31
- .float-left { float: left; }
32
- .float-right { float: right; }
33
-
34
- #trunk { margin: 0px auto; text-align: left; width: 100%; max-width: 500px; min-width: 320px; }
35
- #page-title {
36
- background-color: #2aa198; color: #002b36; width: 80%; text-align: center;
37
- margin: 0px; padding: 20px 10%;
38
- }
39
- #header { background-color: #2aa198; width: 100%; height: 32px; position: relative; }
40
- #search-wrap { position: absolute; top: 0px; bottom: 0px; left: 32px; right: 32px; }
41
- #search {
42
- width: 80%; height: 22px;
43
- border: 0px; padding: 0px 5%; margin: 5px 5%;
44
- border-radius: 11px;
45
- -moz-border-radius: 11px;
46
- -webkit-border-radius: 11px;
47
- }
48
- #content { position: relative; }
49
-
50
- .btn {
51
- display: block;
52
- width: 32px; height: 32px;
53
- background: transparent url('../img/icons-cms-solarized.png') no-repeat left top;
54
- margin: 0px auto;
55
- }
56
- .btn:hover { background-color: #586e75; }
57
- .back-btn { background-position: 0px -32px; }
58
- .plus-btn { background-position: 0px -64px; }
59
- .cross-btn {
60
- background-position: 0px -96px;
61
- border-radius: 5px;
62
- -moz-border-radius: 5px;
63
- -webkit-border-radius: 5px;
64
- }
65
- .cross-btn:hover { background-color: #dc322f;}
66
- .further-btn { background-position: 0px -128px; }
67
-
68
- .extra-plus {
69
- /* border: 1px dashed #073642; */
70
- margin: 10px;
71
- width: auto;
72
- background-color: #073642;
73
- background-position: center -64px;
74
- border-radius: 5px;
75
- -moz-border-radius: 5px;
76
- -webkit-border-radius: 5px;
77
- }
78
-
79
- /* ========= */
80
- /* = Menus = */
81
- /* ========= */
82
-
83
- ul.menu-list li {
84
- margin: 10px; padding: 0px;
85
- }
86
-
87
- ul.menu-list li a {
88
- display: block; padding: 10px;
89
- background-color: #eee8d5; color: #002b36;
90
- text-decoration: none;
91
- }
92
- ul.menu-list li a:hover {
93
- background-color: #93a1a1;
94
- }
95
-
96
- /* ============= */
97
- /* = Nutshells = */
98
- /* ============= */
99
-
100
- ul.nut-tree { list-style: none; margin: 0px; padding: 0px; }
101
- li.nutshell { margin: 10px; padding: 0px; }
102
-
103
- ul.nutshell-children { list-style: none; margin: 0px; padding: 0px; }
104
- ul.nutshell-children li { margin: 0px 10px; }
105
-
106
- .nutshell-core {
107
- -moz-box-shadow: 0px 0px 20px black;
108
- -webkit-box-shadow: 0px 0px 20px black;
109
- box-shadow: 0px 0px 20px black;
110
- }
111
-
112
- .nutshell-bar {
113
- background-color: #eee8d5;
114
- /* background: #fdf6e3 url('../img/grip.png') repeat left top;*/
115
- height: 32px;
116
- -webkit-border-top-left-radius: 5px;
117
- -webkit-border-top-right-radius: 5px;
118
- -moz-border-radius-topleft: 5px;
119
- -moz-border-radius-topright: 5px;
120
- border-top-left-radius: 5px;
121
- border-top-right-radius: 5px;
122
- }
123
-
124
- .sortable-handle {
125
- background: transparent url('../img/grip.png') repeat left top;
126
- cursor: move;
127
- cursor: ns-resize;
128
- cursor: -webkit-grab;
129
- cursor: -moz-grab;
130
- }
131
-
132
- .further-bar { width: auto; background-color: #2aa198; border-top: 1px dotted black; }
133
-
134
- .nutshell-edit, .further-bar {
135
- display: block; position: relative;
136
- padding: 10px 10px 10px 42px;
137
- font-size: 12px; line-height: 12px; height: 12px;
138
- color: #002b36; text-decoration: none;
139
- }
140
- .nutshell-edit:hover, .further-bar:hover { background-color: #586e75; }
141
-
142
- .nutshell-edit {
143
- background-color: #eee8d5;
144
- padding: 10px 10px 10px 10px;
145
- font-size: 14px; line-height: 14px;
146
- height: auto;
147
- overflow: hidden;
148
- white-space: nowrap;
149
- text-overflow: ellipsis;
150
-
151
- -webkit-border-bottom-right-radius: 5px;
152
- -webkit-border-bottom-left-radius: 5px;
153
- -moz-border-radius-bottomright: 5px;
154
- -moz-border-radius-bottomleft: 5px;
155
- border-bottom-right-radius: 5px;
156
- border-bottom-left-radius: 5px;
157
- }
158
- .nutshell-edit:hover {
159
- background-color: #93a1a1;
160
- }
161
-
162
- /*
163
- .nutshell-bottom {
164
- background-color: #fdf6e3;
165
- height: 10px;
166
- -webkit-border-bottom-right-radius: 5px;
167
- -webkit-border-bottom-left-radius: 5px;
168
- -moz-border-radius-bottomright: 5px;
169
- -moz-border-radius-bottomleft: 5px;
170
- border-bottom-right-radius: 5px;
171
- border-bottom-left-radius: 5px;
172
- }*/
173
-
174
- /* ========= */
175
- /* = Forms = */
176
- /* ========= */
177
-
178
- input:not([type=file]), textarea, select {
179
- border: 0px;
180
- background: #eee8d5; color: #002b36;
181
- }
182
- input[type=submit] {
183
- -webkit-border-radius: 3px;
184
- -moz-border-radius: 3px;
185
- -o-border-radius: 3px;
186
- border-radius: 3px;
187
- background-color: #859900;
188
- cursor: pointer;
189
- }
190
- input[type=submit]:hover { background-color: #586e75; }
191
- .backend-form { margin: 10px; padding: 0px; }
192
- .crushyfield-error input { background-color: #FCC; }
193
- .crushyfield-error-list { color: #F99; }
194
- input[type=text] { width: 70%; padding: 5px; }
195
- textarea { width: 70%; height: 150px; padding: 5px; }
196
- select { padding: 5px; }
197
-
198
- /* ========== */
199
- /* = Footer = */
200
- /* ========== */
201
-
202
- #footer {
203
- border-top: 1px solid #586e75;
204
- color: #586e75; font-size: 10px;
205
- padding: 10px;
206
- }
207
- #footer a { color: #586e75; }
@@ -1,63 +0,0 @@
1
- .asmContainer {
2
- /* container that surrounds entire asmSelect widget */
3
- }
4
-
5
- .asmSelect {
6
- /* the newly created regular 'select' */
7
- display: inline;
8
- }
9
-
10
- .asmOptionDisabled {
11
- /* disabled options in new select */
12
- color: #999;
13
- }
14
-
15
- .asmHighlight {
16
- /* the highlight span */
17
- padding: 0;
18
- margin: 0 0 0 1em;
19
- }
20
-
21
- .asmList {
22
- /* html list that contains selected items */
23
- margin: 0.25em 0 1em 0;
24
- position: relative;
25
- display: block;
26
- padding-left: 0;
27
- list-style: none;
28
- }
29
-
30
- .asmListItem {
31
- /* li item from the html list above */
32
- position: relative;
33
- margin-left: 0;
34
- padding-left: 0;
35
- list-style: none;
36
- background: #ddd;
37
- border: 1px solid #bbb;
38
- width: 100%;
39
- margin: 0 0 -1px 0;
40
- line-height: 1em;
41
- }
42
-
43
- .asmListItem:hover {
44
- background-color: #e5e5e5;
45
- }
46
-
47
- .asmListItemLabel {
48
- /* this is a span that surrounds the text in the item, except for the remove link */
49
- padding: 5px;
50
- display: block;
51
- }
52
-
53
- .asmListSortable .asmListItemLabel {
54
- cursor: move;
55
- }
56
-
57
- .asmListItemRemove {
58
- /* the remove link in each list item */
59
- position: absolute;
60
- right: 0;
61
- top: 0;
62
- padding: 5px;
63
- }