snapimage 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.autotest +3 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +2 -0
  8. data/bin/snapimage_generate_config +63 -0
  9. data/bin/snapimage_server +55 -0
  10. data/lib/snapimage.rb +24 -0
  11. data/lib/snapimage/config.rb +51 -0
  12. data/lib/snapimage/exceptions.rb +25 -0
  13. data/lib/snapimage/image/image.rb +96 -0
  14. data/lib/snapimage/image/image_name_utils.rb +131 -0
  15. data/lib/snapimage/middleware.rb +27 -0
  16. data/lib/snapimage/rack/request.rb +19 -0
  17. data/lib/snapimage/rack/request_file.rb +26 -0
  18. data/lib/snapimage/rack/response.rb +51 -0
  19. data/lib/snapimage/server.rb +50 -0
  20. data/lib/snapimage/server_actions/server_actions.authorize.rb +69 -0
  21. data/lib/snapimage/server_actions/server_actions.delete_resource_images.rb +23 -0
  22. data/lib/snapimage/server_actions/server_actions.generate_image.rb +167 -0
  23. data/lib/snapimage/server_actions/server_actions.list_resource_images.rb +23 -0
  24. data/lib/snapimage/server_actions/server_actions.sync_resource.rb +78 -0
  25. data/lib/snapimage/storage/storage.rb +120 -0
  26. data/lib/snapimage/storage/storage_server.local.rb +120 -0
  27. data/lib/snapimage/storage/storage_server.rb +110 -0
  28. data/lib/snapimage/version.rb +3 -0
  29. data/snapimage.gemspec +27 -0
  30. data/spec/acceptance/delete_resource_images_spec.rb +166 -0
  31. data/spec/acceptance/list_resource_images_spec.rb +158 -0
  32. data/spec/acceptance/modify_spec.rb +165 -0
  33. data/spec/acceptance/sync_spec.rb +260 -0
  34. data/spec/acceptance/upload_spec.rb +235 -0
  35. data/spec/snapimage/config_spec.rb +56 -0
  36. data/spec/snapimage/image/image_name_utils_spec.rb +127 -0
  37. data/spec/snapimage/image/image_spec.rb +71 -0
  38. data/spec/snapimage/middleware_spec.rb +27 -0
  39. data/spec/snapimage/rack/request_file_spec.rb +15 -0
  40. data/spec/snapimage/rack/request_spec.rb +52 -0
  41. data/spec/snapimage/rack/response_spec.rb +33 -0
  42. data/spec/snapimage/server_actions/server_actions.authorize_spec.rb +67 -0
  43. data/spec/snapimage/server_actions/server_actions.generate_image_spec.rb +146 -0
  44. data/spec/snapimage/server_actions/server_actions.sync_resource_spec.rb +91 -0
  45. data/spec/snapimage/server_spec.rb +55 -0
  46. data/spec/snapimage/storage/assets/local/resource_1/12345678-1x1-0x0x1x1-1x1-1.gif +0 -0
  47. data/spec/snapimage/storage/assets/local/resource_1/12345678-1x1-0x0x1x1-300x200-0.jpg +0 -0
  48. data/spec/snapimage/storage/assets/local/resource_1/12345678-1x1.png +0 -0
  49. data/spec/snapimage/storage/assets/local/resource_2/12345678-1x1-0x0x1x1-1x1-1.gif +0 -0
  50. data/spec/snapimage/storage/assets/local/resource_2/12345678-1x1-0x0x1x1-300x200-0.jpg +0 -0
  51. data/spec/snapimage/storage/assets/local/resource_2/12345678-1x1.png +0 -0
  52. data/spec/snapimage/storage/storage_server.local_spec.rb +150 -0
  53. data/spec/snapimage/storage/storage_server_spec.rb +97 -0
  54. data/spec/snapimage/storage/storage_spec.rb +49 -0
  55. data/spec/spec_helper.rb +18 -0
  56. data/spec/support/assets/config.json +8 -0
  57. data/spec/support/assets/config.yml +9 -0
  58. data/spec/support/assets/stub-1x1.png +0 -0
  59. data/spec/support/assets/stub-2048x100.png +0 -0
  60. data/spec/support/assets/stub-300x200.png +0 -0
  61. metadata +272 -0
@@ -0,0 +1,146 @@
1
+ require "spec_helper"
2
+
3
+ describe SnapImage::ServerActions::GenerateImage do
4
+ before do
5
+ @image_path = File.join(RSpec.root, "support/assets/stub-300x200.png")
6
+ @storage = double("storage")
7
+ @config = { "max_width" => 1024, "max_height" => 2048 }
8
+ @config.stub(:storage).and_return(@storage)
9
+ @request = double("request")
10
+ @response = double("response")
11
+ @generate_image = SnapImage::ServerActions::GenerateImage.new(@config, @request, @response)
12
+ end
13
+
14
+ describe "#source_image_defined?" do
15
+ it "returns false if file and url are not defined" do
16
+ @request.stub(:file).and_return(nil)
17
+ @request.stub(:json).and_return({})
18
+ @generate_image.send(:source_image_defined?).should be_false
19
+ end
20
+
21
+ it "returns true when file is defined" do
22
+ @request.stub(:file).and_return("file")
23
+ @request.stub(:json).and_return({})
24
+ @generate_image.send(:source_image_defined?).should be_true
25
+ end
26
+
27
+ it "returns true when url is defined" do
28
+ @request.stub(:file).and_return(nil)
29
+ @request.stub(:json).and_return({"url" => "something"})
30
+ @generate_image.send(:source_image_defined?).should be_true
31
+ end
32
+ end
33
+
34
+ describe "#get_max_width?" do
35
+ it "returns the server max width when JSON 'max_width' is not defined" do
36
+ @request.stub(:json).and_return({})
37
+ @generate_image.send(:get_max_width).should eq 1024
38
+ end
39
+
40
+ it "returns the server max width when JSON 'max_width' is larger" do
41
+ @request.stub(:json).and_return({"max_width" => "4096"})
42
+ @generate_image.send(:get_max_width).should eq 1024
43
+ end
44
+
45
+ it "returns the JSON 'max_width' when server max width is larger" do
46
+ @request.stub(:json).and_return({"max_width" => "640"})
47
+ @generate_image.send(:get_max_width).should eq 640
48
+ end
49
+ end
50
+
51
+ describe "#get_max_height?" do
52
+ it "returns the server max height when JSON 'max_height' is not defined" do
53
+ @request.stub(:json).and_return({})
54
+ @generate_image.send(:get_max_height).should eq 2048
55
+ end
56
+
57
+ it "returns the server max height when JSON 'max_height' is larger" do
58
+ @request.stub(:json).and_return({"max_height" => "4096"})
59
+ @generate_image.send(:get_max_height).should eq 2048
60
+ end
61
+
62
+ it "returns the JSON 'max_height' when server max height is larger" do
63
+ @request.stub(:json).and_return({"max_height" => "640"})
64
+ @generate_image.send(:get_max_height).should eq 640
65
+ end
66
+ end
67
+
68
+ describe "#get_image_for_modification" do
69
+ context "upload" do
70
+ before do
71
+ @generate_image.stub(:upload?).and_return(true)
72
+ end
73
+
74
+ it "uploads the image and returns it when a file is specified" do
75
+ @request.stub(:file).and_return("file")
76
+ @request.stub(:json).and_return({"resource_identifier" => "abc123"})
77
+ @storage.should_receive(:add_upload).with("file", "abc123").once.and_return("image")
78
+ @generate_image.send(:get_image_for_modification).should eq "image"
79
+ end
80
+
81
+ it "downloads the image and returns it when a local url is specified" do
82
+ @request.stub(:file).and_return(nil)
83
+ @request.stub(:json).and_return({"url" => "http://example.com/12345678-1024x768.png", "resource_identifier" => "abc123"})
84
+ @storage.should_receive(:add_url).with("http://example.com/12345678-1024x768.png", "abc123").once.and_return("image")
85
+ @generate_image.send(:get_image_for_modification).should eq "image"
86
+ end
87
+ end
88
+
89
+ context "modify" do
90
+ before do
91
+ @generate_image.stub(:upload?).and_return(false)
92
+ end
93
+
94
+ it "gets the base image" do
95
+ @request.stub(:json).and_return("url" => "http://example.com/fi3k2od0-1027x768-1x2x640x480-300x200-1.png")
96
+ @storage.should_receive(:get).with("http://example.com/fi3k2od0-1027x768.png").once.and_return("image")
97
+ @generate_image.send(:get_image_for_modification).should eq "image"
98
+ end
99
+ end
100
+ end
101
+
102
+ describe "#modify_image" do
103
+ before do
104
+ @image = SnapImage::Image.from_path(@image_path, "http://example.com/12345678-300x200.png")
105
+ end
106
+
107
+ it "does nothing when there are no modifications" do
108
+ @request.stub(:json).and_return({})
109
+ result = @generate_image.send(:modify_image, @image)
110
+ result[:image].should be @image
111
+ result[:name].should eq "12345678-300x200.png"
112
+ end
113
+
114
+ it "crops" do
115
+ @request.stub(:json).and_return({"crop_x" => 10, "crop_y" => 20, "crop_width" => 50, "crop_height" => 100})
116
+ result = @generate_image.send(:modify_image, @image)
117
+ result[:image].width.should eq 50
118
+ result[:image].height.should eq 100
119
+ result[:name].should eq "12345678-300x200-10x20x50x100-50x100-0.png"
120
+ end
121
+
122
+ it "resizes" do
123
+ @request.stub(:json).and_return({"width" => 400, "height" => 50})
124
+ result = @generate_image.send(:modify_image, @image)
125
+ result[:image].width.should eq 400
126
+ result[:image].height.should eq 50
127
+ result[:name].should eq "12345678-300x200-0x0x300x200-400x50-0.png"
128
+ end
129
+
130
+ it "resizes to fit" do
131
+ @request.stub(:json).and_return({"width" => 2048, "height" => 100})
132
+ result = @generate_image.send(:modify_image, @image)
133
+ result[:image].width.should eq 1024
134
+ result[:image].height.should eq 50
135
+ result[:name].should eq "12345678-300x200-0x0x300x200-1024x50-0.png"
136
+ end
137
+
138
+ it "sharpens" do
139
+ @request.stub(:json).and_return({"sharpen" => true})
140
+ result = @generate_image.send(:modify_image, @image)
141
+ result[:image].width.should eq 300
142
+ result[:image].height.should eq 200
143
+ result[:name].should eq "12345678-300x200-0x0x300x200-300x200-1.png"
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+
3
+ describe SnapImage::ServerActions::SyncResource do
4
+ before do
5
+ @image_path = File.join(RSpec.root, "support/assets/stub-300x200.png")
6
+ @storage = double("storage")
7
+ @config = { "max_width" => 1024, "max_height" => 2048 }
8
+ @config.stub(:storage).and_return(@storage)
9
+ @request = double("request")
10
+ @response = double("response")
11
+ @sync_resource = SnapImage::ServerActions::SyncResource.new(@config, @request, @response)
12
+ end
13
+
14
+ describe "#request_valid?" do
15
+ it "returns false when content is not valid" do
16
+ @sync_resource.stub(:content_valid?).and_return(false)
17
+ @sync_resource.send(:request_valid?).should be_false
18
+ end
19
+
20
+ it "returns false when sync_date_time is not defined" do
21
+ @sync_resource.stub(:content_valid?).and_return(true)
22
+ @request.stub(:json).and_return({})
23
+ @sync_resource.send(:request_valid?).should be_false
24
+ end
25
+
26
+ it "returns true when the request is valid" do
27
+ @request.stub(:json).and_return({"sync_date_time" => DateTime.now.iso8601})
28
+ @sync_resource.stub(:content_valid?).and_return(true)
29
+ @sync_resource.send(:request_valid?).should be_true
30
+ end
31
+ end
32
+
33
+ describe "#content_valid?" do
34
+ it "returns false when content is not defined" do
35
+ @request.stub(:json).and_return({})
36
+ @sync_resource.send(:content_valid?).should be_false
37
+ end
38
+
39
+ it "returns false when content is not a hash" do
40
+ @request.stub(:json).and_return({"content" => "content"})
41
+ @sync_resource.send(:content_valid?).should be_false
42
+ end
43
+
44
+ it "returns false when content is empty" do
45
+ @request.stub(:json).and_return({"content" => {}})
46
+ @sync_resource.send(:content_valid?).should be_false
47
+ end
48
+
49
+ it "returns true when content is valid" do
50
+ @request.stub(:json).and_return({"content" => {"body" => "test"}})
51
+ @sync_resource.send(:content_valid?).should be_true
52
+ end
53
+ end
54
+
55
+ describe "#get_content" do
56
+ it "returns all the content concatenated" do
57
+ @request.stub(:json).and_return({"content" => {"first" => "hello", "second" => "world"}})
58
+ @sync_resource.send(:get_content).should eq "helloworld"
59
+ end
60
+ end
61
+
62
+ describe "#urls_to_keep" do
63
+ it "returns all urls that match" do
64
+ @storage.stub(:url_regexps).and_return([
65
+ /(\/\/example\.com\/storage\/.+?\.(png|gif|jpg))/,
66
+ /\/\/snapeditor\.com\/.+?\.(png|gif|jpg)/,
67
+ /\/\/my-bucket\.s3\.amazonaws\.com\/my-images\/.+?\.(png|gif|jpg)/
68
+ ])
69
+ url_1 = "http://example.com/storage/abc/123/12345678-1024x768.png"
70
+ url_2 = "http://snapeditor.com/abc/123/12345678-1024x768-10x40x200x300-400x500-0.gif"
71
+ url_3 = "//my-bucket.s3.amazonaws.com/my-images/abc/123/12345678-1024x768-10x40x200x300-400x500-0.jpg"
72
+ url_4 = "//my-bucket.s3.amazonaws.com/my-images/abc/123/12345678-1024x768-10x40x200x300-400x500-1.jpg"
73
+ @sync_resource.stub(:get_content).and_return(
74
+ <<-CONTENT
75
+ This is some fake content with images. <img src="#{url_1}" /><img src="http://example.com/abc/123/image.png" />
76
+ Some more ["#{url_2}"] but maybe not this http://snapeditor.com/.
77
+ #{url_3} and //my-bucket.s3.amazonaws.com/my-images/abc/123/image.jpeg and #{url_4}
78
+ CONTENT
79
+ )
80
+ keep = @sync_resource.send(:urls_to_keep)
81
+ keep.size.should eq 7
82
+ keep.include?(url_1)
83
+ keep.include?(url_2)
84
+ keep.include?(url_3)
85
+ keep.include?(url_4)
86
+ keep.include?("http://snapeditor.com/abc/123/12345678-1024x768.gif")
87
+ keep.include?("//my-bucket.s3.amazonaws.com/my-images/abc/123/12345678-1024x768.jpg")
88
+ keep.include?("//my-bucket.s3.amazonaws.com/my-images/abc/123/12345678-1024x768.jpg")
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ describe SnapImage::Server do
4
+ before do
5
+ @request = double("request")
6
+ @config = double("config")
7
+ @config.stub(:storage).and_return("storage")
8
+ @server = SnapImage::Server.new(@request, @config)
9
+ end
10
+
11
+ describe "#call" do
12
+ it "returns a bad request when the request is bad" do
13
+ @request.stub(:bad_request?).and_return(true)
14
+ response = @server.call
15
+ response[0].should eq 200
16
+ response[1]["Content-Type"].should eq "text/json"
17
+ response[2].body.should eq ['{"status_code":400,"message":"Bad Request"}']
18
+ end
19
+
20
+ it "returns invalid resource identifier when the resource identifier is invalid" do
21
+ @request.stub(:bad_request?).and_return(false)
22
+ @request.stub(:json).and_return({"resource_identifier" => "abc?123"})
23
+ response = @server.call
24
+ response[0].should eq 200
25
+ response[1]["Content-Type"].should eq "text/json"
26
+ response[2].body.should eq ['{"status_code":404,"message":"Invalid Resource Identifier"}']
27
+ end
28
+
29
+ it "returns not implemented when the action does not exist" do
30
+ @request.stub(:bad_request?).and_return(false)
31
+ @request.stub(:json).and_return({"action" => "test", "resource_identifier" => "abc/123"})
32
+ response = @server.call
33
+ response[0].should eq 200
34
+ response[1]["Content-Type"].should eq "text/json"
35
+ response[2].body.should eq ['{"status_code":501,"message":"Not Implemented"}']
36
+ end
37
+
38
+ it "calls the action" do
39
+ response = double("response")
40
+ response.stub(:finish)
41
+ generate_image = double("Generate Image")
42
+ generate_image.should_receive(:call).once.and_return(response)
43
+ SnapImage::ServerActions::GenerateImage.stub(:new).and_return(generate_image)
44
+ @request.stub(:bad_request?).and_return(false)
45
+ @request.stub(:json).and_return({"action" => "generate_image", "resource_identifier" => "abc/123"})
46
+ @server.call
47
+ end
48
+ end
49
+
50
+ describe "#get_action_class" do
51
+ it "returns the correct class" do
52
+ @server.send(:get_action_class, "generate_image").should be SnapImage::ServerActions::GenerateImage
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,150 @@
1
+ require "spec_helper"
2
+
3
+ describe SnapImage::StorageServer::Local do
4
+ before do
5
+ @local_root = File.join(RSpec.root, "storage")
6
+ @image_path = File.join(RSpec.root, "support/assets/stub-1x1.png")
7
+ @server = SnapImage::StorageServer::Local.new(
8
+ "name" => "Test",
9
+ "public_url" => "//example.com/storage",
10
+ "local_root" => @local_root,
11
+ "max_width" => 1024,
12
+ "max_height" => 2048
13
+ )
14
+ end
15
+
16
+ after do
17
+ FileUtils.rm_rf(@local_root)
18
+ end
19
+
20
+ describe "#store_file" do
21
+ before do
22
+ SnapImage::ImageNameUtils.stub(:generate_image_name).and_return("test_image.png")
23
+ @file = File.new(@image_path, "rb")
24
+ @server.store_file(@file, "png", "abc/123")
25
+ end
26
+
27
+ it "copies the contents of the file" do
28
+ contents = File.new(File.join(@local_root, "abc/123/test_image.png"), "rb").read
29
+ blob = SnapImage::Image.from_blob(File.new(@image_path, "rb").read).blob
30
+ contents.should eq blob
31
+ end
32
+ end
33
+
34
+ describe "#get" do
35
+ before do
36
+ SnapImage::ImageNameUtils.stub(:generate_image_name).and_return("test_image.png")
37
+ @server.store_file(File.new(@image_path), "png", "abc/123")
38
+ end
39
+
40
+ it "raises an error when the file doesn't exist" do
41
+ expect { @server.get("http://example.com/storage/abc/123/test_image.gif") }.should raise_error SnapImage::FileDoesNotExist
42
+ end
43
+
44
+ it "returns a SnapImage::Image" do
45
+ image = @server.get("http://example.com/storage/abc/123/test_image.png")
46
+ image.is_a?(SnapImage::Image).should be_true
47
+ end
48
+ end
49
+
50
+ describe "#store" do
51
+ before do
52
+ SnapImage::ImageNameUtils.stub(:generate_image_name).and_return("test_image.png")
53
+ @image = @server.send(:store, SnapImage::Image.from_blob(File.new(@image_path, "rb").read), "test_image.png", "abc/123")
54
+ end
55
+
56
+ it "creates a new file in the storage" do
57
+ File.exists?(File.join(@local_root, "abc/123/test_image.png")).should be_true
58
+ end
59
+
60
+ it "writes the contents to the file" do
61
+ contents = File.new(File.join(@local_root, "abc/123/test_image.png"), "rb").read
62
+ blob = SnapImage::Image.from_blob(File.new(@image_path, "rb").read).blob
63
+ contents.should eq blob
64
+ end
65
+
66
+ it "returns the image" do
67
+ @image.is_a?(SnapImage::Image).should be_true
68
+ @image.public_url.should eq "//example.com/storage/abc/123/test_image.png"
69
+ end
70
+ end
71
+
72
+ describe "#root" do
73
+ it "creates the root when it doesn't exist" do
74
+ @server.send(:root)
75
+ File.directory?(@local_root)
76
+ end
77
+
78
+ it "returns the root" do
79
+ @server.send(:root).should eq @local_root
80
+ end
81
+ end
82
+
83
+ describe "#get_local_path_parts" do
84
+ it "returns nil when the the path is not a local path" do
85
+ @server.send(:get_local_path_parts, "some/random/path/image.png").should be_nil
86
+ end
87
+
88
+ it "returns the parts when the path is a local path" do
89
+ parts = @server.send(:get_local_path_parts, File.join(@local_root, "abc/123/image.png"))
90
+ parts[:resource_id].should eq "abc/123"
91
+ parts[:filename].should eq "image.png"
92
+ end
93
+ end
94
+
95
+ describe "#local_path_to_public_url" do
96
+ it "returns the public url corresponding to the local path" do
97
+ local_path = File.join(@local_root, "abc/123/image.png")
98
+ @server.send(:local_path_to_public_url, local_path).should eq "//example.com/storage/abc/123/image.png"
99
+ end
100
+ end
101
+
102
+ describe "#public_url_to_local_path" do
103
+ it "returns the local path corresponding to the public url" do
104
+ url = "http://example.com/storage/abc/123/image.png"
105
+ @server.send(:public_url_to_local_path, url).should eq File.join(@local_root, "abc/123/image.png")
106
+ end
107
+ end
108
+
109
+ describe "#get_resource_filenames" do
110
+ before do
111
+ @old_local_root = @local_root
112
+ @local_root = File.join(File.expand_path(File.dirname(__FILE__)), "assets/local")
113
+ @server = SnapImage::StorageServer::Local.new(
114
+ "name" => "Test",
115
+ "public_url" => "//example.com/storage",
116
+ "local_root" => @local_root,
117
+ "max_width" => 1024,
118
+ "max_height" => 2048
119
+ )
120
+ end
121
+
122
+ after do
123
+ @local_root = @old_local_root
124
+ end
125
+
126
+ it "returns only images for the given resource id" do
127
+ filenames = @server.send(:get_resource_filenames, "resource_1")
128
+ filenames.size.should eq 3
129
+ filenames.include?(File.join(@local_root, "resource_1/12345678-1x1.png")).should be_true
130
+ filenames.include?(File.join(@local_root, "resource_1/12345678-1x1-0x0x1x1-300x200-0.jpg")).should be_true
131
+ filenames.include?(File.join(@local_root, "resource_1/12345678-1x1-0x0x1x1-1x1-1.gif")).should be_true
132
+ end
133
+ end
134
+
135
+ describe "#file_modified_before_timestamp??" do
136
+ it "returns true when no timestamp is given" do
137
+ @server.send(:file_modified_before_timestamp?, @image_path).should be_true
138
+ end
139
+
140
+ it "returns true when the file was modified before the timestamp" do
141
+ timestamp = DateTime.parse(File.mtime(@image_path).iso8601) + 100
142
+ @server.send(:file_modified_before_timestamp?, @image_path, timestamp).should be_true
143
+ end
144
+
145
+ it "returns false when file was not modified before the timestamp" do
146
+ timestamp = DateTime.parse(File.mtime(@image_path).iso8601)
147
+ @server.send(:file_modified_before_timestamp?, @image_path, timestamp).should be_false
148
+ end
149
+ end
150
+ end