simple_attachments 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +132 -0
- data/lib/simple_attachments.rb +15 -0
- data/lib/simple_attachments/attachment_model.rb +128 -0
- data/lib/simple_attachments/attachments_controller.rb +100 -0
- data/lib/simple_attachments/container_model.rb +73 -0
- data/lib/simple_attachments/migration_helpers.rb +14 -0
- data/lib/simple_attachments/view_helpers.rb +92 -0
- data/locales/en.yml +5 -0
- data/test/Rakefile +10 -0
- data/test/app/assets/javascripts/attachments.js +18 -0
- data/test/app/assets/stylesheets/attachments.css +17 -0
- data/test/app/controllers/application_controller.rb +3 -0
- data/test/app/controllers/avatars_controller.rb +3 -0
- data/test/app/controllers/documents_controller.rb +3 -0
- data/test/app/controllers/users_controller.rb +18 -0
- data/test/app/models/avatar.rb +5 -0
- data/test/app/models/document.rb +5 -0
- data/test/app/models/user.rb +4 -0
- data/test/app/views/layouts/application.html.erb +12 -0
- data/test/app/views/users/edit.html.erb +6 -0
- data/test/config.ru +4 -0
- data/test/config/application.rb +10 -0
- data/test/config/boot.rb +3 -0
- data/test/config/database.yml +5 -0
- data/test/config/environment.rb +5 -0
- data/test/config/environments/test.rb +12 -0
- data/test/config/initializers/secret_token.rb +7 -0
- data/test/config/initializers/session_store.rb +8 -0
- data/test/config/initializers/wrap_parameters.rb +14 -0
- data/test/config/routes.rb +5 -0
- data/test/db/migrate/1_create_users.rb +6 -0
- data/test/db/migrate/2_create_avatars.rb +8 -0
- data/test/db/migrate/3_create_documents.rb +8 -0
- data/test/db/schema.rb +34 -0
- data/test/db/test.sqlite3 +0 -0
- data/test/log/test.log +11774 -0
- data/test/script/rails +6 -0
- data/test/spec/multiple_spec.rb +36 -0
- data/test/spec/samples/sample.jpg +0 -0
- data/test/spec/samples/sample.pdf +0 -0
- data/test/spec/samples/sample.png +0 -0
- data/test/spec/singleton_spec.rb +54 -0
- data/test/spec/spec_helper.rb +22 -0
- data/test/tmp/cache/2CB/9B1/http%3A%2F%2Flocalhost%3A3000%2Fassets%2Fattachments.js%3F +0 -0
- data/test/tmp/cache/337/6A1/http%3A%2F%2Flocalhost%3A3000%2Fassets%2Fattachments.css%3F +0 -0
- data/test/tmp/cache/A03/C30/86cd82a38406e2cc75af040321554265d148882e +0 -0
- data/test/tmp/cache/BDE/BD0/7d763b3bac537e97ea5f3fb558c2bdbba05b72bc +0 -0
- data/test/tmp/cache/assets/C2E/6F0/sprockets%2F99a77a1d4d8767343010e1153d993839 +0 -0
- data/test/tmp/cache/assets/C5A/590/sprockets%2F2993f5a3d21b3e477143358534c68f55 +0 -0
- data/test/tmp/cache/assets/C5B/6F0/sprockets%2F19824e4169877bb010cd8163319f3f95 +0 -0
- data/test/tmp/cache/assets/CEB/450/sprockets%2Fada5663796684365b3e4ab36529bc86b +0 -0
- data/test/tmp/cache/assets/CF4/E60/sprockets%2F96b74d34978f2759ec2af602cd969d00 +0 -0
- data/test/tmp/cache/assets/D0B/600/sprockets%2F0b1b45e6991800f2911a6bcccf54a679 +0 -0
- data/test/tmp/cache/assets/D48/590/sprockets%2F522d019dab47a2617f2c2a99e98a99ed +0 -0
- data/test/tmp/cache/assets/D64/BF0/sprockets%2Fbd0d2396f3797000ffd901eba90b4d0e +0 -0
- data/test/tmp/cache/assets/D6B/150/sprockets%2F0b6cebd1717e9da4183329be9a2d77c0 +0 -0
- data/test/tmp/cache/assets/D70/120/sprockets%2F0f56b864e2eba6c4199d4171bdf391ae +0 -0
- data/test/tmp/cache/assets/D91/580/sprockets%2Fb5e4430a5a0af8eee00d262f82b565ed +0 -0
- data/test/tmp/cache/assets/D9A/760/sprockets%2F8df77fdd2c3c1f3d04f450c72a635be2 +0 -0
- data/test/tmp/cache/assets/DA1/380/sprockets%2F5e06b3974a8f9ca4452d4dfea337dba5 +0 -0
- data/test/tmp/cache/assets/DA9/EC0/sprockets%2F73f61f04eb2d87aced83787a0f14cf8e +0 -0
- data/test/uploads/1330732189.40513.sample.jpg +0 -0
- data/test/uploads/1330732621.7381.sample.jpg +0 -0
- data/test/uploads/1330732672.36763.sample.jpg +0 -0
- data/test/uploads/1330732847.98348.sample.jpg +0 -0
- data/test/uploads/1330732967.05269.sample.jpg +0 -0
- data/test/uploads/1330733025.52707.sample.jpg +0 -0
- data/test/uploads/1330733381.60733.sample.jpg +0 -0
- data/test/uploads/1330733822.51761.sample.jpg +0 -0
- data/test/uploads/1330733887.44114.sample.jpg +0 -0
- data/test/uploads/1330738615.68437.sample.jpg +0 -0
- data/test/uploads/1330738616.82401.sample.jpg +0 -0
- data/test/uploads/1330738618.00278.sample.jpg +0 -0
- data/test/uploads/1330738771.92631.sample.jpg +0 -0
- data/test/uploads/1330738772.94099.sample.jpg +0 -0
- data/test/uploads/1330738901.69933.sample.jpg +0 -0
- data/test/uploads/1330738904.3815.sample.jpg +0 -0
- data/test/uploads/1330739046.59901.sample.jpg +0 -0
- data/test/uploads/1330739125.79463.sample.jpg +0 -0
- data/test/uploads/1330739131.09163.sample.pdf +0 -0
- data/test/uploads/1330739735.52707.sample.jpg +0 -0
- data/test/uploads/1330739738.11937.sample.jpg +0 -0
- data/test/uploads/1330739742.76692.sample.jpg +0 -0
- data/test/uploads/1330739911.78046.sample.jpg +0 -0
- data/test/uploads/1330739914.46047.sample.jpg +0 -0
- data/test/uploads/1330739918.80359.sample.jpg +0 -0
- data/test/uploads/1330739921.39005.sample.jpg +0 -0
- data/test/uploads/1330739988.04537.sample.jpg +0 -0
- data/test/uploads/1330739990.65212.sample.jpg +0 -0
- data/test/uploads/1330739994.98225.sample.jpg +0 -0
- data/test/uploads/1330739997.45622.sample.jpg +0 -0
- data/test/uploads/1330740765.24516.sample.jpg +0 -0
- data/test/uploads/1330740767.71798.sample.jpg +0 -0
- data/test/uploads/1330740771.9889.sample.jpg +0 -0
- data/test/uploads/1330740774.56759.sample.jpg +0 -0
- data/test/uploads/1330741058.17571.sample.jpg +0 -0
- data/test/uploads/1330741060.65483.sample.jpg +0 -0
- data/test/uploads/1330741067.79574.sample.jpg +0 -0
- data/test/uploads/1330741154.42055.sample.jpg +0 -0
- data/test/uploads/1330741157.28718.sample.jpg +0 -0
- data/test/uploads/1330741164.64669.sample.jpg +0 -0
- data/test/uploads/1330741776.05725.sample.jpg +0 -0
- data/test/uploads/1330741778.80256.sample.jpg +0 -0
- data/test/uploads/1330741785.96345.sample.jpg +0 -0
- data/test/uploads/1330742286.07361.sample.jpg +0 -0
- data/test/uploads/1330742288.59918.sample.jpg +0 -0
- data/test/uploads/1330742295.83963.sample.jpg +0 -0
- data/test/uploads/1330742576.6398.sample.jpg +0 -0
- data/test/uploads/1330742579.54181.sample.jpg +0 -0
- data/test/uploads/1330742586.88996.sample.jpg +0 -0
- data/test/uploads/1330743983.91544.shen-logic-part1.pdf +0 -0
- data/test/uploads/1330744294.55912.sample.jpg +0 -0
- data/test/uploads/1330744296.94811.sample.jpg +0 -0
- data/test/uploads/1330744304.25529.sample.jpg +0 -0
- data/test/uploads/1330744307.08498.sample.pdf +0 -0
- data/test/uploads/1330744309.25872.sample.pdf +0 -0
- data/test/uploads/1330744443.49578.sample.jpg +0 -0
- data/test/uploads/1330744446.09032.sample.jpg +0 -0
- data/test/uploads/1330744453.55965.sample.jpg +0 -0
- data/test/uploads/1330744456.32318.sample.pdf +0 -0
- data/test/uploads/1330744458.42931.sample.pdf +0 -0
- data/test/uploads/1330744533.52631.sample.jpg +0 -0
- data/test/uploads/1330744536.13208.sample.jpg +0 -0
- data/test/uploads/1330744543.29535.sample.jpg +0 -0
- data/test/uploads/1330744546.16477.sample.pdf +0 -0
- data/test/uploads/1330744548.27125.sample.pdf +0 -0
- data/test/uploads/1330744696.43953.sample.jpg +0 -0
- data/test/uploads/1330744698.95043.sample.jpg +0 -0
- data/test/uploads/1330744706.28901.sample.jpg +0 -0
- data/test/uploads/1330744711.24284.sample.pdf +0 -0
- data/test/uploads/1330744713.6281.sample.pdf +0 -0
- data/test/uploads/1330744782.91673.sample.jpg +0 -0
- data/test/uploads/1330744785.53684.sample.jpg +0 -0
- data/test/uploads/1330744792.74599.sample.jpg +0 -0
- data/test/uploads/1330744797.90608.sample.pdf +0 -0
- data/test/uploads/1330744800.55736.sample.pdf +0 -0
- data/test/uploads/1330771358.91882.sample.jpg +0 -0
- data/test/uploads/1330771359.23929.sample.jpg +0 -0
- data/test/uploads/1330771359.70574.sample.jpg +0 -0
- data/test/uploads/1330771360.07645.sample.png +0 -0
- data/test/uploads/1330771364.83802.sample.pdf +0 -0
- data/test/uploads/1330771367.18498.sample.pdf +0 -0
- data/test/uploads/1330771514.28703.sample.jpg +0 -0
- data/test/uploads/1330771514.66497.sample.jpg +0 -0
- data/test/uploads/1330771515.12563.sample.jpg +0 -0
- data/test/uploads/1330771515.72515.sample.png +0 -0
- data/test/uploads/1330771519.30014.sample.pdf +0 -0
- data/test/uploads/1330771519.55784.sample.pdf +0 -0
- data/test/uploads/1330771519.89044.sample.pdf +0 -0
- data/test/uploads/1330771522.50763.sample.pdf +0 -0
- data/test/uploads/1330771522.75458.sample.pdf +0 -0
- data/test/uploads/1330775627.9105.sample.pdf +0 -0
- data/test/uploads/1330775628.40671.sample.pdf +0 -0
- data/test/uploads/1330775628.68645.sample.pdf +0 -0
- data/test/uploads/1330775631.48519.sample.pdf +0 -0
- data/test/uploads/1330775631.9586.sample.pdf +0 -0
- data/test/uploads/1330775676.5146.sample.pdf +0 -0
- data/test/uploads/1330775703.20143.sample.pdf +0 -0
- data/test/uploads/1330775704.36145.sample.pdf +0 -0
- data/test/uploads/1330775705.27831.sample.pdf +0 -0
- data/test/uploads/1330775706.66539.sample.pdf +0 -0
- data/test/uploads/1330776988.21579.sample.pdf +0 -0
- data/test/uploads/1330776988.69155.sample.pdf +0 -0
- data/test/uploads/1330776989.12114.sample.pdf +0 -0
- data/test/uploads/1330776992.1201.sample.pdf +0 -0
- data/test/uploads/1330776992.57634.sample.pdf +0 -0
- data/test/uploads/1330777152.6597.sample.jpg +0 -0
- data/test/uploads/1330777153.00459.sample.jpg +0 -0
- data/test/uploads/1330777153.45871.sample.jpg +0 -0
- data/test/uploads/1330777153.86376.sample.png +0 -0
- data/test/uploads/1330777157.91465.sample.pdf +0 -0
- data/test/uploads/1330777158.23816.sample.pdf +0 -0
- data/test/uploads/1330777158.74465.sample.pdf +0 -0
- data/test/uploads/1330777161.59586.sample.pdf +0 -0
- data/test/uploads/1330777161.93334.sample.pdf +0 -0
- data/test/uploads/1330777833.73591.sample.jpg +0 -0
- data/test/uploads/1330777834.05449.sample.jpg +0 -0
- data/test/uploads/1330777834.50661.sample.jpg +0 -0
- data/test/uploads/1330777834.87967.sample.jpg +0 -0
- data/test/uploads/1330777835.22949.sample.jpg +0 -0
- data/test/uploads/1330777835.55546.sample.png +0 -0
- data/test/uploads/1330777840.54959.sample.jpg +0 -0
- data/test/uploads/1330777844.18911.sample.pdf +0 -0
- data/test/uploads/1330777844.58364.sample.pdf +0 -0
- data/test/uploads/1330777844.96998.sample.pdf +0 -0
- data/test/uploads/1330777847.62838.sample.pdf +0 -0
- data/test/uploads/1330777848.0192.sample.pdf +0 -0
- data/test/uploads/1330778979.41703.sample.jpg +0 -0
- data/test/uploads/1330778979.74093.sample.jpg +0 -0
- data/test/uploads/1330778980.16769.sample.jpg +0 -0
- data/test/uploads/1330778980.54831.sample.jpg +0 -0
- data/test/uploads/1330778980.91033.sample.jpg +0 -0
- data/test/uploads/1330778981.23582.sample.png +0 -0
- data/test/uploads/1330778986.50795.sample.jpg +0 -0
- data/test/uploads/1330778989.07307.sample.pdf +0 -0
- data/test/uploads/1330778989.59241.sample.pdf +0 -0
- data/test/uploads/1330778990.06415.sample.pdf +0 -0
- data/test/uploads/1330778990.44233.sample.pdf +0 -0
- data/test/uploads/1330779134.25647.sample.jpg +0 -0
- data/test/uploads/1330779134.67957.sample.jpg +0 -0
- data/test/uploads/1330779135.15349.sample.jpg +0 -0
- data/test/uploads/1330779135.6793.sample.jpg +0 -0
- data/test/uploads/1330779136.14714.sample.jpg +0 -0
- data/test/uploads/1330779136.70598.sample.png +0 -0
- data/test/uploads/1330779141.75878.sample.jpg +0 -0
- data/test/uploads/1330779145.79811.sample.pdf +0 -0
- data/test/uploads/1330779146.40945.sample.pdf +0 -0
- data/test/uploads/1330779146.75038.sample.pdf +0 -0
- data/test/uploads/1330779149.67101.sample.pdf +0 -0
- data/test/uploads/1330779150.033.sample.pdf +0 -0
- data/test/uploads/1330779251.92468.sample.jpg +0 -0
- data/test/uploads/1330779252.33759.sample.jpg +0 -0
- data/test/uploads/1330779252.79211.sample.jpg +0 -0
- data/test/uploads/1330779253.16365.sample.jpg +0 -0
- data/test/uploads/1330779253.51459.sample.jpg +0 -0
- data/test/uploads/1330779253.82932.sample.png +0 -0
- data/test/uploads/1330779259.04186.sample.jpg +0 -0
- data/test/uploads/1330779262.79998.sample.pdf +0 -0
- data/test/uploads/1330779263.19534.sample.pdf +0 -0
- data/test/uploads/1330779263.51726.sample.pdf +0 -0
- data/test/uploads/1330779266.23965.sample.pdf +0 -0
- data/test/uploads/1330779266.65453.sample.pdf +0 -0
- data/test/uploads/1330780162.71983.1.gif +0 -0
- data/test/uploads/1330780370.60224.sample.jpg +0 -0
- data/test/uploads/1330780370.95202.sample.jpg +0 -0
- data/test/uploads/1330780371.44543.sample.jpg +0 -0
- data/test/uploads/1330780371.88658.sample.jpg +0 -0
- data/test/uploads/1330780372.30293.sample.jpg +0 -0
- data/test/uploads/1330780372.79723.sample.png +0 -0
- data/test/uploads/1330780378.19075.sample.jpg +0 -0
- data/test/uploads/1330780382.07736.sample.pdf +0 -0
- data/test/uploads/1330780382.61497.sample.pdf +0 -0
- data/test/uploads/1330780383.10458.sample.pdf +0 -0
- data/test/uploads/1330780385.85251.sample.pdf +0 -0
- data/test/uploads/1330780386.24973.sample.pdf +0 -0
- data/test/uploads/1330781258.12109.sample.pdf +0 -0
- data/test/uploads/1330781258.63862.sample.pdf +0 -0
- data/test/uploads/1330781259.10465.sample.pdf +0 -0
- data/test/uploads/1330781262.08459.sample.pdf +0 -0
- data/test/uploads/1330781262.52448.sample.pdf +0 -0
- data/test/uploads/1330781278.37307.sample.jpg +0 -0
- data/test/uploads/1330781278.7309.sample.jpg +0 -0
- data/test/uploads/1330781279.18693.sample.jpg +0 -0
- data/test/uploads/1330781279.55913.sample.jpg +0 -0
- data/test/uploads/1330781279.92436.sample.jpg +0 -0
- data/test/uploads/1330781280.27391.sample.png +0 -0
- data/test/uploads/1330886711.00348.sample.pdf +0 -0
- data/test/uploads/1330886711.78495.sample.pdf +0 -0
- data/test/uploads/1330886712.4033.sample.pdf +0 -0
- data/test/uploads/1330886716.09532.sample.pdf +0 -0
- data/test/uploads/1330886716.7663.sample.pdf +0 -0
- data/test/uploads/1330886735.38676.sample.jpg +0 -0
- data/test/uploads/1330886736.28739.sample.jpg +0 -0
- data/test/uploads/1330886736.84868.sample.jpg +0 -0
- data/test/uploads/1330886737.64167.sample.jpg +0 -0
- data/test/uploads/1330886738.33938.sample.jpg +0 -0
- data/test/uploads/1330886738.92271.sample.png +0 -0
- data/test/uploads/1330886985.6679.sample.pdf +0 -0
- data/test/uploads/1330886986.4568.sample.pdf +0 -0
- data/test/uploads/1330886987.00302.sample.pdf +0 -0
- data/test/uploads/1330886990.4604.sample.pdf +0 -0
- data/test/uploads/1330886991.04681.sample.pdf +0 -0
- data/test/uploads/1330887008.68477.sample.jpg +0 -0
- data/test/uploads/1330887009.23077.sample.jpg +0 -0
- data/test/uploads/1330887009.82498.sample.jpg +0 -0
- data/test/uploads/1330887010.48937.sample.jpg +0 -0
- data/test/uploads/1330887011.23496.sample.jpg +0 -0
- data/test/uploads/1330887011.92032.sample.png +0 -0
- data/test/uploads/1330887633.41769.sample.pdf +0 -0
- data/test/uploads/1330887633.81961.sample.pdf +0 -0
- data/test/uploads/1330887634.25582.sample.pdf +0 -0
- data/test/uploads/1330887637.47958.sample.pdf +0 -0
- data/test/uploads/1330887638.14348.sample.pdf +0 -0
- data/test/uploads/1330887655.10619.sample.jpg +0 -0
- data/test/uploads/1330887655.80139.sample.jpg +0 -0
- data/test/uploads/1330887656.35041.sample.jpg +0 -0
- data/test/uploads/1330887656.86876.sample.jpg +0 -0
- data/test/uploads/1330887657.60467.sample.jpg +0 -0
- data/test/uploads/1330887658.23424.sample.png +0 -0
- data/test/uploads/1330888133.62299.WIN.gif +0 -0
- data/test/uploads/1330888504.16714.1.gif +0 -0
- data/test/uploads/1330888586.99975.1.gif +0 -0
- data/test/uploads/1330888607.58249.shen-logic-part1.pdf +0 -0
- data/test/uploads/1330889585.30006.shen-logic-part2.pdf +0 -0
- data/test/uploads/1330890565.79547.shen-logic-part1.pdf +0 -0
- data/test/uploads/1330890992.45693.sample.jpg +0 -0
- data/test/uploads/1330890998.10996.sample.pdf +0 -0
- data/test/uploads/1330890998.76199.sample.pdf +0 -0
- data/test/uploads/1330890999.14516.sample.pdf +0 -0
- data/test/uploads/1330891002.0989.sample.pdf +0 -0
- data/test/uploads/1330891002.49258.sample.pdf +0 -0
- data/test/uploads/1330891004.89888.sample.jpg +0 -0
- data/test/uploads/1330891007.45147.sample.jpg +0 -0
- data/test/uploads/1330891052.00842.sample.jpg +0 -0
- data/test/uploads/1330891056.46428.sample.pdf +0 -0
- data/test/uploads/1330891056.83678.sample.pdf +0 -0
- data/test/uploads/1330891057.31732.sample.pdf +0 -0
- data/test/uploads/1330891057.86036.sample.pdf +0 -0
- data/test/uploads/1330891058.49616.sample.pdf +0 -0
- data/test/uploads/1330891058.91288.sample.pdf +0 -0
- data/test/uploads/1330891138.7408.sample.pdf +0 -0
- data/test/uploads/1330891139.14067.sample.pdf +0 -0
- data/test/uploads/1330891139.46755.sample.pdf +0 -0
- data/test/uploads/1330891142.26432.sample.pdf +0 -0
- data/test/uploads/1330891142.684.sample.pdf +0 -0
- data/test/uploads/1330891158.04426.sample.jpg +0 -0
- data/test/uploads/1330891158.44159.sample.jpg +0 -0
- data/test/uploads/1330891158.85135.sample.jpg +0 -0
- data/test/uploads/1330891159.3765.sample.jpg +0 -0
- data/test/uploads/1330891159.79503.sample.jpg +0 -0
- data/test/uploads/1330891160.15479.sample.png +0 -0
- data/test/uploads/1330900466.37214.sample.pdf +0 -0
- data/test/uploads/1330900466.77443.sample.pdf +0 -0
- data/test/uploads/1330900467.32831.sample.pdf +0 -0
- data/test/uploads/1330900470.07702.sample.pdf +0 -0
- data/test/uploads/1330900470.48767.sample.pdf +0 -0
- data/test/uploads/1330900486.92727.sample.jpg +0 -0
- data/test/uploads/1330900487.4311.sample.jpg +0 -0
- data/test/uploads/1330900488.03152.sample.jpg +0 -0
- data/test/uploads/1330900488.43945.sample.jpg +0 -0
- data/test/uploads/1330900489.01433.sample.jpg +0 -0
- data/test/uploads/1330900489.43854.sample.png +0 -0
- data/vendor/assets/javascripts/simple_attachments.js +138 -0
- data/vendor/assets/stylesheets/simple_attachments.css +23 -0
- metadata +729 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Tsokurov Alex <me@ximik.net>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
= Simple Attachments
|
2
|
+
|
3
|
+
Simple Attachments is a plugin for Ruby on Rails 3 which implements complex solution for working with file attachments.
|
4
|
+
|
5
|
+
== Requirements
|
6
|
+
|
7
|
+
Plugin contains some CSS and JavaScript code, so Asset Pipeline should be switched on (by default).
|
8
|
+
Also you need {jQuery library}[http://jquery.com/] to make JavaScript work.
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
Add this to your Gemfile and run the +bundle+ command.
|
13
|
+
|
14
|
+
gem 'simple_attachments'
|
15
|
+
|
16
|
+
== Getting Started
|
17
|
+
|
18
|
+
Let's say you have a +User+ which has one +Avatar+ and many +Document+.
|
19
|
+
The avatar can be change from user edit form. And the avatar data updates only after submiting the form.
|
20
|
+
The documents are shown on the same page. They can be managed separately with auto saving all changes in background.
|
21
|
+
You can find this app in +test+.
|
22
|
+
|
23
|
+
=== Migrations
|
24
|
+
|
25
|
+
class CreateAvatars < ActiveRecord::Migration
|
26
|
+
def change
|
27
|
+
create_table :avatars do |t|
|
28
|
+
t.references :avatar, :null => true
|
29
|
+
t.attachment
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class CreateDocuments < ActiveRecord::Migration
|
35
|
+
def change
|
36
|
+
create_table :documents do |t|
|
37
|
+
t.references :avatar, :null => true
|
38
|
+
t.attachment
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
*WARNING*: it's important to permit null value for container.
|
44
|
+
|
45
|
+
=== Routes
|
46
|
+
|
47
|
+
resources :avatars, :only => [:create, :show, :destroy]
|
48
|
+
resources :documents, :only => [:create, :show, :destroy]
|
49
|
+
|
50
|
+
=== Models
|
51
|
+
|
52
|
+
class User < ActiveRecord::Base
|
53
|
+
has_one_attachment :avatar, :dependent => :destroy
|
54
|
+
has_many_attachments :books, :dependent => :destroy
|
55
|
+
end
|
56
|
+
|
57
|
+
class Avatar < ActiveRecord::Base
|
58
|
+
attached_to :user
|
59
|
+
validates_mimetype %w(image/png image/gif image/jpeg)
|
60
|
+
validates_filesize :less_than_or_equal_to => 5e4
|
61
|
+
end
|
62
|
+
|
63
|
+
class Document < ActiveRecord::Base
|
64
|
+
attached_to :user
|
65
|
+
validates_mimetype %w(application/pdf)
|
66
|
+
validates_filesize :less_than_or_equal_to => 20e6
|
67
|
+
end
|
68
|
+
|
69
|
+
You can use a lot of options here. Just like with +has_one+, +has_many+ and +belongs_to+.
|
70
|
+
|
71
|
+
=== Controllers
|
72
|
+
|
73
|
+
class AvatarsController < ApplicationController
|
74
|
+
attachment_controller
|
75
|
+
end
|
76
|
+
|
77
|
+
class DocumentsController < ApplicationController
|
78
|
+
attachment_controller
|
79
|
+
end
|
80
|
+
|
81
|
+
=== View
|
82
|
+
|
83
|
+
<%= form_for @user do |f| %>
|
84
|
+
<%= f.file :avatar, :auto_associate => false %>
|
85
|
+
<% end %>
|
86
|
+
|
87
|
+
<%= files @user, :documents %>
|
88
|
+
|
89
|
+
*WARNING*: you also need +csrf_meta_tags+ in your layout to work with +protect_from_forgery+ filter.
|
90
|
+
|
91
|
+
=== JavaScript
|
92
|
+
|
93
|
+
Plugin doesn't provide any implementation of the file appearance.
|
94
|
+
Instead it triggers events, which are easy to handle.
|
95
|
+
|
96
|
+
//= require_self
|
97
|
+
//= require simple_attachments
|
98
|
+
|
99
|
+
$(function() {
|
100
|
+
// Div is loaded and engine is ready.
|
101
|
+
$(".simple_attachments_div").bind("init", function() {
|
102
|
+
// You can add your own init features here.
|
103
|
+
});
|
104
|
+
// New field was created.
|
105
|
+
// Field is a div displaying attachment information.
|
106
|
+
$(".simple_attachments_div").bind("new_field", function(event, field) {
|
107
|
+
// You have to set field in the right place and add binds.
|
108
|
+
field.appendTo(this);
|
109
|
+
// Attachment was loaded
|
110
|
+
// data contains attachment properties and some little support variables
|
111
|
+
field.bind("loaded", function(event, data) {
|
112
|
+
// You have to create and/or set field content here
|
113
|
+
data.destroy_link.appendTo(this);
|
114
|
+
data.attachment_link.html(data.filename).appendTo(this);
|
115
|
+
});
|
116
|
+
// Attachment upload was failed
|
117
|
+
// errors is an array of strings
|
118
|
+
field.bind("failed", function(event, errors) {
|
119
|
+
// You can do something here before field would be deleted
|
120
|
+
});
|
121
|
+
});
|
122
|
+
});
|
123
|
+
|
124
|
+
*WARNING*: +simple_attachments.js+ should be required after binds.
|
125
|
+
|
126
|
+
=== CSS
|
127
|
+
|
128
|
+
+simple_attachments.css+ contains some code to decorate default file input. So better require it, but it's not so necessary.
|
129
|
+
|
130
|
+
---
|
131
|
+
|
132
|
+
(c) 2012 Tsokurov Alex <{me@ximik.net}[mailto:me@ximik.net]>, released under the MIT license
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# :main: README.rdoc
|
2
|
+
|
3
|
+
module SimpleAttachments # :nodoc:
|
4
|
+
class Engine < ::Rails::Engine # :nodoc:
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
9
|
+
I18n.load_path << File.join(dir, '..', 'locales', 'en.yml')
|
10
|
+
|
11
|
+
require File.join(dir, 'simple_attachments', 'attachment_model')
|
12
|
+
require File.join(dir, 'simple_attachments', 'container_model')
|
13
|
+
require File.join(dir, 'simple_attachments', 'attachments_controller')
|
14
|
+
require File.join(dir, 'simple_attachments', 'view_helpers')
|
15
|
+
require File.join(dir, 'simple_attachments', 'migration_helpers')
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module SimpleAttachments::AttachmentModel
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
# Mark model as attachment and create association.
|
6
|
+
#
|
7
|
+
# Syntax is identical to +belongs_to+.
|
8
|
+
#
|
9
|
+
# attached_to :post
|
10
|
+
#
|
11
|
+
# Do not use more than one time in one model.
|
12
|
+
# However you can use polymorphic associations.
|
13
|
+
#
|
14
|
+
# attached_to :container, :polymorphic => true
|
15
|
+
|
16
|
+
def attached_to(name, options = {})
|
17
|
+
reflection = belongs_to name, options
|
18
|
+
self.class.send(:attr_accessor, :container_name)
|
19
|
+
self.container_name = reflection.name
|
20
|
+
before_save :save_file
|
21
|
+
after_destroy :destroy_file
|
22
|
+
send :extend, ClassMethods
|
23
|
+
send :include, InstanceMethods
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
|
30
|
+
# Validate mimetype of uploaded file.
|
31
|
+
#
|
32
|
+
# validates_mimetype %w(image/png image/gif image/jpeg), :message => 'should be an image'
|
33
|
+
#
|
34
|
+
# :message is optional.
|
35
|
+
|
36
|
+
def validates_mimetype(types, options = {})
|
37
|
+
options[:message] ||= I18n.t('simple_attachments.mimetype_isnt_allowed')
|
38
|
+
validates :mimetype, :inclusion => { :in => types, :message => options[:message] }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Validate filesize. Syntax is identical to numericality validator. Filesize is given in bytes.
|
42
|
+
#
|
43
|
+
# validates_filesize :less_than_or_equal_to => 12e6, :message => 'file is too large'
|
44
|
+
#
|
45
|
+
# :message is optional.
|
46
|
+
|
47
|
+
def validates_filesize(options)
|
48
|
+
options[:message] ||= I18n.t('simple_attachments.file_is_too_large')
|
49
|
+
validates :filesize, :numericality => options
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
module InstanceMethods
|
55
|
+
|
56
|
+
# Set file for attachment.
|
57
|
+
# +file+ is +UploadedFile+ object
|
58
|
+
|
59
|
+
def file=(file)
|
60
|
+
if file.nil?
|
61
|
+
uploading_error
|
62
|
+
else
|
63
|
+
@file = file
|
64
|
+
self.mimetype = @file.content_type
|
65
|
+
self.filesize = @file.tempfile.size
|
66
|
+
self.filename = File.basename(@file.original_filename) # old browsers hack
|
67
|
+
self.filepath = file_path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Generate path to file.
|
72
|
+
# Redefine this method if you want another naming logic.
|
73
|
+
|
74
|
+
def file_path
|
75
|
+
"#{Time.now.to_f}.#{filename}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Generate full path to file.
|
79
|
+
# Redefine this method if you have another place to hold files.
|
80
|
+
|
81
|
+
def full_file_path
|
82
|
+
Rails.root.join('uploads', filepath).to_s
|
83
|
+
end
|
84
|
+
|
85
|
+
# Save file in the filesystem.
|
86
|
+
|
87
|
+
def save_file
|
88
|
+
File.open(full_file_path, 'w') { |file| file.write @file.read } unless @file.nil?
|
89
|
+
rescue
|
90
|
+
uploading_error
|
91
|
+
end
|
92
|
+
|
93
|
+
# Delete file from the filesystem.
|
94
|
+
|
95
|
+
def destroy_file
|
96
|
+
File.delete full_file_path
|
97
|
+
rescue
|
98
|
+
end
|
99
|
+
|
100
|
+
def serializable_hash # :nodoc:
|
101
|
+
data = super
|
102
|
+
data['filepath'] = path # changing filepath to file url
|
103
|
+
data
|
104
|
+
end
|
105
|
+
|
106
|
+
# Test if attached to any container.
|
107
|
+
|
108
|
+
def attached?
|
109
|
+
not send(self.class.container_name).nil?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generate error connected to any server problems.
|
113
|
+
|
114
|
+
def uploading_error
|
115
|
+
errors[:base] << I18n.t('simple_attachments.uploading_error')
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get url to the attachment.
|
119
|
+
|
120
|
+
def path
|
121
|
+
Rails.application.routes.url_helpers.send(self.class.to_s.underscore.concat('_path'), id)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
ActiveRecord::Base.extend SimpleAttachments::AttachmentModel::Helpers
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module SimpleAttachments::AttachmentsController
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
# Mark controller as controller for attachment resource.
|
6
|
+
#
|
7
|
+
# Just put at any place of your controller.
|
8
|
+
#
|
9
|
+
# attachment_controller
|
10
|
+
#
|
11
|
+
# Sometimes you have to specify your resource manually.
|
12
|
+
#
|
13
|
+
# attachment_controller :resource => MyNamespace::Attachment
|
14
|
+
|
15
|
+
def attachment_controller(options = {})
|
16
|
+
before_filter :load_attachment, :except => :create
|
17
|
+
self.class.send(:attr_accessor, :attachment_model)
|
18
|
+
self.attachment_model = (options[:resource] or controller_path.classify.constantize)
|
19
|
+
send :include, InstanceMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
|
26
|
+
def create # :nodoc:
|
27
|
+
@attachment = self.class.attachment_model.new
|
28
|
+
@attachment.file = params[:file]
|
29
|
+
if @attachment.save and params[:container_id] != 'null'
|
30
|
+
begin
|
31
|
+
container = params[:container_model].classify.constantize.find params[:container_id]
|
32
|
+
associate(container, params[:method])
|
33
|
+
container.save
|
34
|
+
rescue
|
35
|
+
@attachment.uploading_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
if @attachment.errors.any?
|
39
|
+
succeed = false
|
40
|
+
data = @attachment.errors.messages.values.flatten
|
41
|
+
@attachment.destroy
|
42
|
+
else
|
43
|
+
succeed = true
|
44
|
+
data = @attachment.serializable_hash
|
45
|
+
end
|
46
|
+
render_answer(succeed, data)
|
47
|
+
end
|
48
|
+
|
49
|
+
def show # :nodoc:
|
50
|
+
send_file @attachment.full_file_path, :type => @attachment.mimetype, :filename => @attachment.filename
|
51
|
+
end
|
52
|
+
|
53
|
+
def destroy # :nodoc:
|
54
|
+
@attachment.destroy
|
55
|
+
render :nothing => true
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Before filter to load attachment resource.
|
61
|
+
#
|
62
|
+
# If you use another way to load it then just skip this filter.
|
63
|
+
#
|
64
|
+
# skip_before_filter :load_attachment
|
65
|
+
|
66
|
+
def load_attachment # :doc:
|
67
|
+
@attachment = self.class.attachment_model.find params[:id]
|
68
|
+
rescue
|
69
|
+
raise ActionController::RoutingError.new('Not Found')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Associate attachment with container.
|
73
|
+
# Works only if you use +:auto_associate+ option (switched on by default).
|
74
|
+
#
|
75
|
+
# Redefine it if you want to have some extra control. E.g. CanCan authorization.
|
76
|
+
#
|
77
|
+
# def associate(container, method)
|
78
|
+
# authorize! :update, container
|
79
|
+
# super
|
80
|
+
# end
|
81
|
+
|
82
|
+
def associate(container, method) # :doc:
|
83
|
+
container.add_attachment method, @attachment
|
84
|
+
end
|
85
|
+
|
86
|
+
# Renders answer for javascript.
|
87
|
+
|
88
|
+
def render_answer(succeed, data) # :nodoc:
|
89
|
+
render :text => {'succeed' => succeed, 'data' => data}.to_json
|
90
|
+
end
|
91
|
+
|
92
|
+
def handle_unverified_request # :nodoc:
|
93
|
+
render :nothing => true
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
ActionController::Base.extend SimpleAttachments::AttachmentsController::Helpers
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module SimpleAttachments::ContainerModel
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
# Mark model as container and create association.
|
6
|
+
#
|
7
|
+
# Syntax is identical to +has_many+.
|
8
|
+
#
|
9
|
+
# has_many_attachments :attachments
|
10
|
+
|
11
|
+
def has_many_attachments(name, options = {})
|
12
|
+
reflection = has_many name, options
|
13
|
+
send(:define_method, reflection.plural_name + '_=') do |attachment_ids|
|
14
|
+
method, model = recover_vars(__method__)
|
15
|
+
old_attachment_ids = send method.singularize.concat('_ids')
|
16
|
+
attachment_ids = attachment_ids.map { |id| id.to_i }
|
17
|
+
model.destroy_all :id => (old_attachment_ids - attachment_ids)
|
18
|
+
(attachment_ids - old_attachment_ids).each do |attachment_id|
|
19
|
+
find_and_add_attachment(method, model, attachment_id)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
send :include, InstanceMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
# Mark model as container and create association.
|
26
|
+
#
|
27
|
+
# Syntax is identical to +has_one+.
|
28
|
+
#
|
29
|
+
# has_one_attachment :attachment
|
30
|
+
|
31
|
+
def has_one_attachment(name, options = {})
|
32
|
+
reflection = has_one name, options
|
33
|
+
send(:define_method, reflection.name.to_s + '_=') do |attachment_id|
|
34
|
+
method, model = recover_vars(__method__)
|
35
|
+
find_and_add_attachment(method, model, attachment_id)
|
36
|
+
end
|
37
|
+
send :include, InstanceMethods
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
|
44
|
+
def add_attachment(method, attachment) # :nodoc:
|
45
|
+
if reflections[method.to_sym].instance_variable_get :@collection
|
46
|
+
send(method) << attachment
|
47
|
+
else
|
48
|
+
old_attachment = send(method)
|
49
|
+
old_attachment.destroy unless old_attachment.nil?
|
50
|
+
send (method + '='), attachment
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def recover_vars(method) # :nodoc:
|
57
|
+
method = method.to_s.chomp('_=')
|
58
|
+
model = reflections[method.to_sym].klass
|
59
|
+
[method, model]
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_and_add_attachment(method, model, attachment_id) # :nodoc:
|
63
|
+
attachment = model.find_by_id attachment_id
|
64
|
+
return if attachment.nil?
|
65
|
+
return if attachment.attached?
|
66
|
+
add_attachment method, attachment
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
ActiveRecord::Base.extend SimpleAttachments::ContainerModel::Helpers
|