snapimage 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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