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
@@ -0,0 +1,2 @@
1
+ "use strict";!function(a){var b,c,d=a();a.fn.sortable=function(e){var f=String(e);return e=a.extend({connectWith:!1,placeholder:null,dragImage:null},e),this.each(function(){if("reload"===f&&a(this).children(e.items).off("dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s"),/^enable|disable|destroy$/.test(f)){var g=a(this).children(a(this).data("items")).attr("draggable","enable"===f);return void("destroy"===f&&(a(this).off("sortupdate"),a(this).removeData("opts"),g.add(this).removeData("connectWith items").off("dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s").off("sortupdate")))}var h=a(this).data("opts");"undefined"==typeof h?a(this).data("opts",e):e=h;var i,j,k,l,m=a(this).children(e.items),n=null===e.placeholder?a("<"+(/^ul|ol$/i.test(this.tagName)?"li":"div")+' class="sortable-placeholder"/>'):a(e.placeholder).addClass("sortable-placeholder");m.find(e.handle).mousedown(function(){i=!0}).mouseup(function(){i=!1}),a(this).data("items",e.items),d=d.add(n),e.connectWith&&a(e.connectWith).add(this).data("connectWith",e.connectWith),m.attr("role","option"),m.attr("aria-grabbed","false"),m.attr("draggable","true").on("dragstart.h5s",function(d){if(d.stopImmediatePropagation(),e.handle&&!i)return!1;i=!1;var f=d.originalEvent.dataTransfer;f.effectAllowed="move",f.setData("text",""),e.dragImage&&f.setDragImage&&f.setDragImage(e.dragImage,0,0),j=(b=a(this)).addClass("sortable-dragging").attr("aria-grabbed","true").index(),c=b.outerHeight(),k=a(this).parent()}).on("dragend.h5s",function(){b&&(b.removeClass("sortable-dragging").attr("aria-grabbed","false").show(),d.detach(),l=a(this).parent(),(j!==b.index()||k.get(0)!==l.get(0))&&b.parent().triggerHandler("sortupdate",{item:b,oldindex:j,startparent:k,endparent:l}),b=null,c=null)}).not("a[href], img").on("selectstart.h5s",function(){return e.handle&&!i?!0:(this.dragDrop&&this.dragDrop(),!1)}).end().add([this,n]).on("dragover.h5s dragenter.h5s drop.h5s",function(f){if(!m.is(b)&&e.connectWith!==a(b).parent().data("connectWith"))return!0;if("drop"===f.type)return f.stopPropagation(),d.filter(":visible").after(b),b.trigger("dragend.h5s"),!1;if(f.preventDefault(),f.originalEvent.dataTransfer.dropEffect="move",m.is(this)){var g=a(this).outerHeight();if(e.forcePlaceholderSize&&n.height(c),g>c){var h=g-c,i=a(this).offset().top;if(n.index()<a(this).index()&&f.originalEvent.pageY<i+h)return!1;if(n.index()>a(this).index()&&f.originalEvent.pageY>i+g-h)return!1}b.hide(),a(this)[n.index()<a(this).index()?"after":"before"](n),d.not(n).detach()}else d.is(this)||a(this).children(e.items).length||(d.detach(),a(this).append(n));return!1})})}}(jQuery);
2
+ //# sourceMappingURL=html.sortable.min.js.map
@@ -0,0 +1,163 @@
1
+ <%# encoding: utf-8 %>
2
+ <!DOCTYPE HTML>
3
+ <html>
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <title><%= settings.meta_title %></title>
7
+ <link rel="stylesheet" href="<%= request.script_name %>/__assets__/css/main.css" type="text/css" media='screen' />
8
+ </head>
9
+ <body>
10
+
11
+ <ol id='finder'><li class='column'>
12
+ <h1>Welcome<br><%= label_for_field user_name %></h1>
13
+ <% if settings.logout_path? %>
14
+ <p><a href="<%= request.script_name %><%= settings.logout_path %>">Logout</a></p>
15
+ <% end %>
16
+ </li></ol>
17
+
18
+ <!-- Templates -->
19
+
20
+ <script id="template-menu" type="x-tmpl-mustache">
21
+ <h1>{{page_title}}</h1>
22
+ <ol class='menu'>
23
+ {{#items}}
24
+ <li><a href="{{href}}" class='column-push' title='Open'>{{title}}</a></li>
25
+ {{/items}}
26
+ </ol>
27
+ </script>
28
+
29
+ <script id="template-list" type="x-tmpl-mustache">
30
+ <h1>
31
+ {{page_title}}
32
+ </h1>
33
+ <p class='new-document-btn-wrap'>
34
+ <a href="<%= request.script_name %>/form/{{dasherized_class_name}}{{#new_data}}?{{new_data}}{{/new_data}}" class='column-push new-document-btn' title='Create'>+</a>
35
+ </p>
36
+ <ol class='documents' data-sort-field='{{sort_field}}' data-sort-url='<%= request.script_name %>/api/{{dasherized_class_name}}'>
37
+ {{#items}}{{#custom_partial_or_default}}template_document{{/custom_partial_or_default}}{{/items}}
38
+ </ol>
39
+ </script>
40
+
41
+ <script id="template-document" type="x-tmpl-mustache">
42
+ <li class='admin-list-item' data-id='{{id}}'>
43
+ <header>
44
+ {{#sort_field}}
45
+ <span class='handle' title='Drag and drop'>&#8597;</span>
46
+ {{/sort_field}}
47
+ <button type='button' class='admin-delete' title='Delete' value='<%= request.script_name %>/api/{{admin_url}}'>&times;</button>
48
+ </header>
49
+ <a href="<%= request.script_name %>/form/{{admin_url}}" class='column-push' title='Edit'>
50
+ {{title}}
51
+ {{#image_url}}
52
+ <br />
53
+ <img src='{{image_url}}{{cache_buster}}' alt='{{title}}' width='300' />
54
+ {{/image_url}}
55
+ </a>
56
+ <ol class='local-menu'>
57
+ {{#local_menu}}
58
+ <li>&rarr; <a href="{{href}}" class='column-push' title='Open'>{{title}}</a></li>
59
+ {{/local_menu}}
60
+ </ol>
61
+ </li>
62
+ </script>
63
+
64
+ <script id="template-form" type="x-tmpl-mustache">
65
+ <h1>{{page_title}}</h1>
66
+ <form action="<%= request.script_name %>/api/{{admin_url}}" method="POST" accept-charset="utf-8" class='admin-{{#is_new}}post{{/is_new}}{{^is_new}}put{{/is_new}}'>
67
+ {{#custom_partial_or_default}}template_form_fields{{/custom_partial_or_default}}
68
+ {{^is_new}}
69
+ <input type="hidden" name="_method" value="PUT" />
70
+ {{/is_new}}
71
+ <input type="submit" value="{{#is_new}}Create{{/is_new}}{{^is_new}}Update{{/is_new}}" />
72
+ </form>
73
+ </script>
74
+
75
+ <script id="template-nested-form" type="x-tmpl-mustache">
76
+ <li>
77
+ <header>
78
+ <span class='handle' title='Drag and drop'>&#8597;</span><button type='button' class='admin-delete-nested' title='Delete'>&times;</button>
79
+ </header>
80
+ {{#custom_partial_or_default}}template_form_fields{{/custom_partial_or_default}}
81
+ </li>
82
+ </script>
83
+
84
+ <script id="template-form-fields" type="x-tmpl-mustache">
85
+ {{#fields}}
86
+ {{>template_form_field}}
87
+ {{/fields}}
88
+ </script>
89
+
90
+ <script id="template-form-field" type="x-tmpl-mustache">
91
+ {{#wrap}}
92
+ <div class='field' data-field-name='{{field_name}}'>
93
+ <label>{{label}}</label>
94
+ <br>
95
+ {{#adapted_field}}{{/adapted_field}}
96
+ </div>
97
+ {{/wrap}}
98
+ {{^wrap}}
99
+ {{#adapted_field}}{{/adapted_field}}
100
+ {{/wrap}}
101
+ </script>
102
+
103
+ <script id="template-string-field" type="x-tmpl-mustache">
104
+ <input name='{{input_name}}' value='{{input_value}}' {{#required}}required{{/required}}{{{build_input_attributes}}} />
105
+ </script>
106
+
107
+ <script id="template-text-field" type="x-tmpl-mustache">
108
+ <textarea name='{{input_name}}' {{#required}}required{{/required}}{{{build_input_attributes}}}>{{input_value}}</textarea>
109
+ </script>
110
+
111
+ <script id="template-boolean-field" type="x-tmpl-mustache">
112
+ <select name='{{input_name}}' {{{build_input_attributes}}}>
113
+ <option value='false' {{^input_value}}selected{{/input_value}}>No</option>
114
+ <option value='true' {{#input_value}}selected{{/input_value}}>Yes</option>
115
+ </select>
116
+ </script>
117
+
118
+ <script id="template-select-field" type="x-tmpl-mustache">
119
+ <select name='{{input_name}}' {{#multiple}}multiple{{/multiple}}{{{build_input_attributes}}}>
120
+ {{#select_options}}
121
+ <option value='{{value}}' {{#selected}}selected{{/selected}}>{{description}}</option>
122
+ {{/select_options}}
123
+ </select>
124
+ </script>
125
+
126
+ <script id="template-attachment-field" type="x-tmpl-mustache">
127
+ {{#url}}
128
+ <img src='{{url}}{{cache_buster}}' alt='Preview' width='150' />
129
+ <button class='attachment-deleter'>x</button>
130
+ <br />
131
+ {{/url}}
132
+ <input type='file' name='{{input_name}}' {{{build_input_atrributes}}} />
133
+ </script>
134
+
135
+ <script id="template-list-field" type="x-tmpl-mustache">
136
+ <fieldset class='field' data-field-name='{{field_name}}'>
137
+ <legend>{{label}}</legend>
138
+ {{#dasherized_class_name}}
139
+ <p>
140
+ <a href="<%= request.script_name %>/form/{{dasherized_class_name}}?input_name_prefix={{input_name}}[]&nested=true" class='new-nested-document-btn'>+</a>
141
+ </p>
142
+ {{/dasherized_class_name}}
143
+ <ol class='nested-documents'>
144
+ {{#items}}
145
+ {{>template_nested_form}}
146
+ {{/items}}
147
+ </ol>
148
+ </fieldset>
149
+ </script>
150
+
151
+ <!-- JS -->
152
+ <script src="//code.jquery.com/jquery-1.10.2.min.js" type="text/javascript" charset="utf-8"></script>
153
+ <script src="<%= request.script_name %>/__assets__/js/mustache.js" type="text/javascript" charset="utf-8"></script>
154
+ <script src="<%= request.script_name %>/__assets__/js/columnav.js" type="text/javascript" charset="utf-8"></script>
155
+ <script src="<%= request.script_name %>/__assets__/js/sortable.js" type="text/javascript" charset="utf-8"></script>
156
+ <script type="text/javascript">
157
+ window.admin_path = "<%= request.script_name %>";
158
+ window.index_path = "<%= settings.index_path %>";
159
+ </script>
160
+ <script src="<%= request.script_name %>/__assets__/js/main.js" type="text/javascript" charset="utf-8"></script>
161
+ </body>
162
+ </html>
163
+
@@ -0,0 +1,124 @@
1
+ require 'sinatra/base'
2
+ require 'web_utils'
3
+ require 'populate_me/version'
4
+ require 'json'
5
+
6
+ class PopulateMe::API < Sinatra::Base
7
+
8
+ use Rack::MethodOverride
9
+
10
+ set :show_exceptions, false
11
+
12
+ before do
13
+ content_type :json
14
+ end
15
+
16
+ after do
17
+ redirect(params['_destination']) unless params['_destination'].nil?
18
+ end
19
+
20
+ get '/' do
21
+ status 200
22
+ {'success'=>true}.to_json
23
+ end
24
+
25
+ get '/version' do
26
+ status 200
27
+ {'success'=>true, 'version'=>PopulateMe::VERSION}.to_json
28
+ end
29
+
30
+ get '/:model' do
31
+ end
32
+
33
+ post '/:model' do
34
+ model_class = resolve_model_class params[:model]
35
+ model_instance = model_class.new.set_from_hash((params[:data]||{}), typecast: true)
36
+ if model_instance.valid?
37
+ model_instance.save
38
+ status 201
39
+ {
40
+ 'success'=>true,'message'=>'Created Successfully',
41
+ 'data'=>model_instance.to_h
42
+ }.to_json
43
+ else
44
+ status 400
45
+ {
46
+ 'success'=>false,'message'=>'Invalid Document',
47
+ 'data'=>model_instance.error_report
48
+ }.to_json
49
+ end
50
+ end
51
+
52
+ put '/:model' do
53
+ pass unless params[:action]=='sort'
54
+ model_class = resolve_model_class params[:model]
55
+ model_class.set_indexes(params[:field].to_sym,params[:ids])
56
+ {'success'=>true,'message'=>'Sorted Successfully'}.to_json
57
+ end
58
+
59
+ get '/:model/:id' do
60
+ model_class = resolve_model_class params[:model]
61
+ model_instance = resolve_model_instance model_class, params[:id]
62
+ {'success'=>true,'data'=>model_instance.to_h}.to_json
63
+ end
64
+
65
+ put '/:model/:id' do
66
+ model_class = resolve_model_class params[:model]
67
+ model_instance = resolve_model_instance model_class, params[:id]
68
+ model_instance.set_from_hash params[:data], typecast: true
69
+ if model_instance.valid?
70
+ model_instance.save
71
+ {
72
+ 'success'=>true,'message'=>'Updated Successfully',
73
+ 'data'=>model_instance.to_h
74
+ }.to_json
75
+ else
76
+ status 400
77
+ {
78
+ 'success'=>false,'message'=>'Invalid Document',
79
+ 'data'=>model_instance.error_report
80
+ }.to_json
81
+ end
82
+ end
83
+
84
+ delete '/:model/:id' do
85
+ model_class = resolve_model_class params[:model]
86
+ model_instance = resolve_model_instance model_class, params[:id]
87
+ model_instance.delete
88
+ {'success'=>true,'message'=>'Deleted Successfully','data'=>model_instance.to_h}.to_json
89
+ end
90
+
91
+ not_found do
92
+ response.headers['X-Cascade'] = 'pass'
93
+ {'success'=>false,'message'=>'Not Found'}.to_json
94
+ end
95
+
96
+ error do
97
+ puts
98
+ puts env['sinatra.error'].inspect
99
+ puts
100
+ {'success'=>false,'message'=>env['sinatra.error'].message}.to_json
101
+ end
102
+
103
+ module Helpers
104
+
105
+ include WebUtils
106
+
107
+ def resolve_model_class name
108
+ model_class = resolve_dasherized_class_name(name) rescue nil
109
+ halt(404) unless model_class.respond_to?(:admin_get)
110
+ model_class
111
+ end
112
+
113
+ def resolve_model_instance model_class, id
114
+ instance = model_class.admin_get id
115
+ halt(404) if instance.nil?
116
+ instance
117
+ end
118
+
119
+ end
120
+
121
+ helpers Helpers
122
+
123
+ end
124
+
@@ -0,0 +1,182 @@
1
+ require 'web_utils'
2
+ require 'populate_me/variation'
3
+ require 'fileutils'
4
+ require 'ostruct'
5
+
6
+ module PopulateMe
7
+
8
+ class Attachment
9
+
10
+ class << self
11
+
12
+ attr_accessor :settings
13
+
14
+ # inheritable settings
15
+ def set name, value
16
+ self.settings[name] = value
17
+ end
18
+
19
+ def inherited sub
20
+ super
21
+ sub.settings = settings.dup
22
+ sub::Middleware.parent = sub
23
+ end
24
+
25
+ def middleware
26
+ Rack::Static
27
+ end
28
+
29
+ def middleware_options
30
+ [
31
+ {
32
+ urls: [settings.url_prefix],
33
+ root: settings.root
34
+ }
35
+ ]
36
+ end
37
+
38
+ end
39
+
40
+ attr_accessor :document, :field
41
+
42
+ def initialize doc, field
43
+ @document = doc
44
+ @field = field
45
+ end
46
+
47
+ def settings
48
+ self.class.settings
49
+ end
50
+
51
+ def field_value
52
+ self.document.__send__(field)
53
+ end
54
+
55
+ def variations
56
+ self.document.class.fields[self.field][:variations]
57
+ end
58
+
59
+ def field_filename variation_name=:original
60
+ return nil if WebUtils.blank?(self.field_value)
61
+ return self.field_value if variation_name==:original
62
+ v = self.variations.find{|var|var.name==variation_name}
63
+ WebUtils.filename_variation(self.field_value, v.name, v.ext)
64
+ end
65
+
66
+ def attachee_prefix
67
+ WebUtils.dasherize_class_name self.document.class.name
68
+ end
69
+
70
+ def url variation_name=:original
71
+ return nil if WebUtils.blank?(self.field_filename(variation_name))
72
+ "#{settings.url_prefix.sub(/\/$/,'')}/#{self.attachee_prefix}/#{self.field_filename(variation_name)}"
73
+ end
74
+
75
+ def location_root
76
+ File.join(
77
+ settings.root,
78
+ settings.url_prefix,
79
+ self.attachee_prefix
80
+ )
81
+ end
82
+
83
+ def location variation_name=:original
84
+ self.location_for_filename self.field_filename(variation_name)
85
+ end
86
+ def location_for_filename filename
87
+ File.join self.location_root, filename
88
+ end
89
+
90
+ def ensure_local_path
91
+ self.location
92
+ end
93
+
94
+ def create form_hash
95
+ self.delete
96
+ future_field_value = perform_create form_hash
97
+ create_variations form_hash.merge({future_field_value: future_field_value})
98
+ future_field_value
99
+ end
100
+
101
+ def create_variations hash
102
+ return if self.variations.nil?
103
+ tmppath = hash[:tempfile].path
104
+ path = self.location_for_filename hash[:future_field_value]
105
+ FileUtils.mkdir_p(File.dirname(path))
106
+ variations.each do |v|
107
+ self.delete v.name
108
+ v_path = WebUtils.filename_variation path, v.name, v.ext
109
+ v.job.call tmppath, v_path
110
+ self.perform_create hash.merge({variation: v, variation_path: v_path})
111
+ end
112
+ end
113
+
114
+ def perform_create hash
115
+ return File.basename(hash[:variation_path]) unless WebUtils.blank?(hash[:variation_path])
116
+ # Rack 1.6 deletes multipart files after request
117
+ # So we have to create a copy
118
+ FileUtils.mkdir_p self.location_root
119
+ tmppath = hash[:tempfile].path
120
+ unique_prefix = File.basename(tmppath, File.extname(tmppath))
121
+ unique_filename = "#{unique_prefix}-#{hash[:filename]}"
122
+ path = self.location_for_filename unique_filename
123
+ FileUtils.copy_entry(tmppath, path)
124
+ unique_filename
125
+ end
126
+
127
+ def deletable? variation_name=:original
128
+ !WebUtils.blank?(self.field_filename(variation_name)) and File.exist?(self.location(variation_name))
129
+ end
130
+
131
+ def delete variation_name=:original
132
+ if self.deletable?(variation_name)
133
+ perform_delete variation_name
134
+ end
135
+ end
136
+
137
+ def delete_all
138
+ to_delete = [:original] + variations.map(&:name)
139
+ to_delete.each do |v_name|
140
+ self.delete v_name
141
+ end
142
+ end
143
+
144
+ def perform_delete variation_name=:original
145
+ FileUtils.rm self.location(variation_name)
146
+ end
147
+
148
+ self.settings = OpenStruct.new
149
+ set :root, File.join(Dir.tmpdir, 'populate-me')
150
+ set :url_prefix, '/attachment'
151
+ FileUtils.mkdir_p self.settings.root
152
+
153
+ class Middleware
154
+
155
+ # Used for proxing a rack middleware adapted to the attachment system.
156
+ # The options are then taken from the attachment class.
157
+ # It can be used in rackup file as:
158
+ #
159
+ # use AtachmentClass::Middleware
160
+
161
+ class << self
162
+ attr_accessor :parent
163
+ end
164
+ self.parent = PopulateMe::Attachment
165
+ def parent
166
+ self.class.parent
167
+ end
168
+
169
+ def initialize app
170
+ @proxied = parent.middleware.new(app,*parent.middleware_options)
171
+ end
172
+
173
+ def call env
174
+ @proxied.call env
175
+ end
176
+
177
+ end
178
+
179
+ end
180
+
181
+ end
182
+