ires 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -9
- data/ext/Gopkg.lock +36 -2
- data/ext/ires/image.go +133 -103
- data/ext/ires/ires.go +91 -70
- data/ext/ires/uri.go +52 -62
- data/ext/main.go +24 -21
- data/lib/ires.rb +1 -1
- data/lib/ires/core.rb +9 -3
- data/lib/ires/mode.rb +7 -0
- data/lib/ires/os.rb +7 -9
- data/lib/ires/service.rb +12 -10
- data/lib/ires/type.rb +7 -0
- data/lib/ires/version.rb +1 -1
- data/lib/ires/view_helper.rb +12 -5
- data/lib/tasks/ires.rake +2 -2
- data/shared/darwin/ires.h +1 -1
- data/shared/darwin/ires.so +0 -0
- data/shared/linux/ires.h +15 -3
- data/shared/linux/ires.so +0 -0
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51c855f31ee4c3154e6f11d0c58e47b1f4b1e360
|
4
|
+
data.tar.gz: 9529a56c17f521b8cc669c5169d2416a02e96915
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74b58d89cb8f5062b1b1030d26f8ed4db13d298307cabab331b65e5294cc90e6665d4902d1f9775cfaecced305f63eb81d1ef47dff06aa5714d178f08171e3c8
|
7
|
+
data.tar.gz: 7a3d4a7eb98710e4ab36f17b96515e8449bb4ea5151de5ea325d3d48c956c83b95b31490aa07fa814b8840084eebb3b31ab1fabf626ad01008e33c55745b7af7
|
data/README.md
CHANGED
@@ -14,31 +14,40 @@
|
|
14
14
|
<%= ires_tag( path: "image_01.jpg", width: 90, height: 120 ) %>
|
15
15
|
|
16
16
|
<!-- Using image_tag options -->
|
17
|
-
<%= ires_tag( path: "http://example.com/image_02.jpg", width: 200, height: 200,
|
17
|
+
<%= ires_tag( path: "http://example.com/image_02.jpg", width: 200, height: 200, Ires::Mode::CROP, alt: "example image" ) %>
|
18
18
|
```
|
19
19
|
|
20
20
|
### Get resize path
|
21
21
|
|
22
22
|
```ruby
|
23
|
-
Ires::Service.path(
|
23
|
+
Ires::Service.path(path: '<FULL IMAGE PATH>', width: 400, height: 300)
|
24
24
|
=> /ires/<resize image path>
|
25
25
|
```
|
26
26
|
|
27
27
|
### Select mode
|
28
28
|
|
29
|
-
| info |
|
30
|
-
|
31
|
-
| Resize |
|
32
|
-
| Cropping |
|
33
|
-
| Rsize after Cropping |
|
29
|
+
| info | mode |
|
30
|
+
|:---------------------------|:---------------------------------|
|
31
|
+
| Resize | Ires::Mode::RESIZE (default) |
|
32
|
+
| Cropping | Ires::Mode::CROP |
|
33
|
+
| Rsize after Cropping | Ires::Mode::RESIZE_TO_CROP |
|
34
34
|
|
35
|
+
### Select type
|
36
|
+
|
37
|
+
Filter of resize image.
|
38
|
+
|
39
|
+
| info | type |
|
40
|
+
|:---------------------------|:---------------------------------|
|
41
|
+
| All | Ires::Type::ALL (default) |
|
42
|
+
| Smaller than | Ires::Type::SMALLER |
|
43
|
+
| Larger than | Ires::Type::LARGER |
|
35
44
|
|
36
45
|
### Specify cache expiration
|
37
46
|
|
38
47
|
Default: **30days**
|
39
48
|
|
40
49
|
```erb
|
41
|
-
<%= ires_tag( path:
|
50
|
+
<%= ires_tag( path: '/image.jpg', width: 400, height: 300, expire: 7.days ) %>
|
42
51
|
```
|
43
52
|
|
44
53
|
### Saved directory
|
@@ -117,7 +126,7 @@ $ dep ensure
|
|
117
126
|
# Output to a shared object.
|
118
127
|
$ CGO_ENABLED=1 GOOS=linux go build -v -buildmode=c-shared -o shared/linux/ires.so ext/main.go
|
119
128
|
```
|
120
|
-
※ In the current Docker, you can build only linux environment.
|
129
|
+
※ In the current Docker, you can build only linux environment.
|
121
130
|
※ If you want to build in other environments, add GCC or install Go on the host side.🙇
|
122
131
|
|
123
132
|
### 2. Start rails server
|
data/ext/Gopkg.lock
CHANGED
@@ -2,50 +2,84 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
[[projects]]
|
5
|
+
digest = "1:8e47871087b94913898333f37af26732faaab30cdb41571136cf7aec9921dae7"
|
5
6
|
name = "github.com/PuerkitoBio/purell"
|
6
7
|
packages = ["."]
|
8
|
+
pruneopts = ""
|
7
9
|
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
8
10
|
version = "v1.1.0"
|
9
11
|
|
10
12
|
[[projects]]
|
11
13
|
branch = "master"
|
14
|
+
digest = "1:331a419049c2be691e5ba1d24342fc77c7e767a80c666a18fd8a9f7b82419c1c"
|
12
15
|
name = "github.com/PuerkitoBio/urlesc"
|
13
16
|
packages = ["."]
|
17
|
+
pruneopts = ""
|
14
18
|
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
15
19
|
|
16
20
|
[[projects]]
|
17
21
|
branch = "master"
|
22
|
+
digest = "1:690e87555de96973ac79aae9fe51fef5e470f7e3cde3b1aa731ae01ec3f9ffe8"
|
18
23
|
name = "github.com/goware/urlx"
|
19
24
|
packages = ["."]
|
25
|
+
pruneopts = ""
|
20
26
|
revision = "e22571dfbd216d3ca34207d9a1d877f38d8f051c"
|
21
27
|
|
22
28
|
[[projects]]
|
23
29
|
branch = "master"
|
30
|
+
digest = "1:74688aecdaaede7d4ea1ead4569dc5f9f8b169b3b9698470f8d107f347a7a2cd"
|
24
31
|
name = "github.com/nfnt/resize"
|
25
32
|
packages = ["."]
|
33
|
+
pruneopts = ""
|
26
34
|
revision = "891127d8d1b52734debe1b3c3d7e747502b6c366"
|
27
35
|
|
28
36
|
[[projects]]
|
37
|
+
digest = "1:8e8c82e661fc70a3dab1d1bb0f92b800ea32112f5926c08da9c13a46a57cc557"
|
29
38
|
name = "github.com/oliamb/cutter"
|
30
39
|
packages = ["."]
|
40
|
+
pruneopts = ""
|
31
41
|
revision = "5cd9c24de6524b7251ac6b8600b0ba8f43a85963"
|
32
42
|
version = "v0.2.0"
|
33
43
|
|
34
44
|
[[projects]]
|
35
45
|
branch = "master"
|
46
|
+
digest = "1:1418c6b5c10b5e07fb4c90a24227f6493e19ad4d31177d66458115fc47669fb5"
|
36
47
|
name = "golang.org/x/net"
|
37
48
|
packages = ["idna"]
|
49
|
+
pruneopts = ""
|
38
50
|
revision = "1087133bc4af3073e18add999345c6ae75918503"
|
39
51
|
|
40
52
|
[[projects]]
|
41
53
|
branch = "master"
|
54
|
+
digest = "1:706747bb1e3f870cf0dc52459cb94324cd347142fff3b61db56c0157a5f598d0"
|
42
55
|
name = "golang.org/x/text"
|
43
|
-
packages = [
|
56
|
+
packages = [
|
57
|
+
"collate",
|
58
|
+
"collate/build",
|
59
|
+
"internal/colltab",
|
60
|
+
"internal/gen",
|
61
|
+
"internal/tag",
|
62
|
+
"internal/triegen",
|
63
|
+
"internal/ucd",
|
64
|
+
"language",
|
65
|
+
"secure/bidirule",
|
66
|
+
"transform",
|
67
|
+
"unicode/bidi",
|
68
|
+
"unicode/cldr",
|
69
|
+
"unicode/norm",
|
70
|
+
"unicode/rangetable",
|
71
|
+
"width",
|
72
|
+
]
|
73
|
+
pruneopts = ""
|
44
74
|
revision = "c01e4764d870b77f8abe5096ee19ad20d80e8075"
|
45
75
|
|
46
76
|
[solve-meta]
|
47
77
|
analyzer-name = "dep"
|
48
78
|
analyzer-version = 1
|
49
|
-
|
79
|
+
input-imports = [
|
80
|
+
"github.com/goware/urlx",
|
81
|
+
"github.com/nfnt/resize",
|
82
|
+
"github.com/oliamb/cutter",
|
83
|
+
]
|
50
84
|
solver-name = "gps-cdcl"
|
51
85
|
solver-version = 1
|
data/ext/ires/image.go
CHANGED
@@ -1,57 +1,99 @@
|
|
1
1
|
package ires
|
2
2
|
|
3
3
|
import (
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
4
|
+
"bytes"
|
5
|
+
"image"
|
6
|
+
"image/gif"
|
7
|
+
"image/jpeg"
|
8
|
+
"image/png"
|
9
|
+
"io"
|
10
|
+
"io/ioutil"
|
11
|
+
"net/http"
|
12
|
+
"os"
|
13
|
+
"path"
|
14
|
+
"strings"
|
15
|
+
"time"
|
16
|
+
|
17
|
+
"github.com/nfnt/resize"
|
18
|
+
"github.com/oliamb/cutter"
|
18
19
|
)
|
19
20
|
|
21
|
+
// Check expiration date
|
22
|
+
func (i *Ires) DeleteExpireImage() {
|
23
|
+
today := time.Now().Format("20060102")
|
24
|
+
dir := i.readImageDir()
|
25
|
+
files, err := ioutil.ReadDir(dir)
|
26
|
+
if err != nil {
|
27
|
+
return
|
28
|
+
}
|
29
|
+
|
30
|
+
for _, file := range files {
|
31
|
+
findName := file.Name()
|
32
|
+
matched, _ := path.Match(today+"_*", findName)
|
33
|
+
if matched {
|
34
|
+
deleteImage(path.Join(dir, findName))
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
// Input image type is Local or HTTP
|
40
|
+
func (i *Ires) CheckLocal() {
|
41
|
+
if strings.Index(i.URI, "http") == -1 {
|
42
|
+
i.IsLocal = true
|
43
|
+
} else {
|
44
|
+
i.IsLocal = false
|
45
|
+
}
|
46
|
+
}
|
20
47
|
|
21
48
|
// Input image
|
22
|
-
func
|
49
|
+
func (i *Ires) inputImage() bool {
|
23
50
|
if i.IsLocal {
|
24
|
-
img, format := localImage(i.
|
25
|
-
|
26
|
-
|
27
|
-
|
51
|
+
img, format := localImage(i.URI)
|
52
|
+
i.InputImage = &InputImage{
|
53
|
+
Image: img,
|
54
|
+
Format: format,
|
55
|
+
URI: i.URI,
|
56
|
+
}
|
57
|
+
i.setConfig()
|
58
|
+
return true
|
28
59
|
}
|
60
|
+
return i.downloadImage()
|
29
61
|
}
|
30
62
|
|
31
|
-
|
32
63
|
// Save http image
|
33
|
-
func
|
34
|
-
res, err := http.Get(i.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
64
|
+
func (i *Ires) downloadImage() bool {
|
65
|
+
res, err := http.Get(i.URI)
|
66
|
+
if err != nil {
|
67
|
+
return false
|
68
|
+
}
|
69
|
+
defer res.Body.Close()
|
70
|
+
|
71
|
+
header, r := copyReader(res.Body)
|
72
|
+
format := formatSearch(r)
|
73
|
+
|
74
|
+
img, _, err := image.Decode(io.MultiReader(header, res.Body))
|
75
|
+
if err != nil {
|
76
|
+
return false
|
77
|
+
}
|
78
|
+
|
79
|
+
distURI := i.imageURI(true)
|
80
|
+
i.InputImage = &InputImage{
|
81
|
+
Image: img,
|
82
|
+
Format: format,
|
83
|
+
URI: distURI,
|
84
|
+
}
|
85
|
+
|
86
|
+
if createImage(img, distURI, format) {
|
87
|
+
i.setConfig()
|
88
|
+
return true
|
89
|
+
}
|
90
|
+
return false
|
48
91
|
}
|
49
92
|
|
50
|
-
|
51
|
-
func createImage(img image.Image, path, format string) image.Image {
|
93
|
+
func createImage(img image.Image, path, format string) bool {
|
52
94
|
file, err := os.Create(path)
|
53
95
|
if err != nil {
|
54
|
-
|
96
|
+
return false
|
55
97
|
}
|
56
98
|
defer file.Close()
|
57
99
|
|
@@ -65,15 +107,14 @@ func createImage(img image.Image, path, format string) image.Image {
|
|
65
107
|
default:
|
66
108
|
jpeg.Encode(file, img, nil)
|
67
109
|
}
|
68
|
-
return
|
110
|
+
return true
|
69
111
|
}
|
70
112
|
|
71
|
-
|
72
113
|
// Load image
|
73
114
|
func localImage(uri string) (image.Image, string) {
|
74
115
|
file, err := os.Open(uri)
|
75
|
-
if err != nil{
|
76
|
-
|
116
|
+
if err != nil {
|
117
|
+
return nil, ""
|
77
118
|
}
|
78
119
|
defer file.Close()
|
79
120
|
|
@@ -83,24 +124,33 @@ func localImage(uri string) (image.Image, string) {
|
|
83
124
|
|
84
125
|
img, _, err := image.Decode(io.MultiReader(header, file))
|
85
126
|
if err != nil {
|
86
|
-
|
127
|
+
return nil, ""
|
87
128
|
}
|
88
129
|
return img, format
|
89
130
|
}
|
90
131
|
|
132
|
+
// Set image config
|
133
|
+
func (i *Ires) setConfig() {
|
134
|
+
file, err := os.Open(i.InputImage.URI)
|
135
|
+
if err != nil {
|
136
|
+
panic(err)
|
137
|
+
}
|
138
|
+
defer file.Close()
|
139
|
+
|
140
|
+
conf, _, err := image.DecodeConfig(file)
|
141
|
+
if err != nil {
|
142
|
+
panic(err)
|
143
|
+
}
|
144
|
+
i.InputImage.Config = conf
|
145
|
+
}
|
91
146
|
|
92
147
|
// Resizing & Cropping
|
93
|
-
func
|
148
|
+
func (i *Ires) resizeToCrop() image.Image {
|
149
|
+
inputImg := i.InputImage.Image
|
94
150
|
var outputImg image.Image
|
95
|
-
|
96
|
-
if i.IsLocal {
|
97
|
-
imagePath = i.Uri
|
98
|
-
} else {
|
99
|
-
imagePath = i.imagePath(IMAGE_MODE_ORIGINAL)
|
100
|
-
}
|
101
|
-
isAsp, conf := isValidAspectRatio(imagePath, i.Size)
|
151
|
+
isAsp, conf := i.isValidAspectRatio()
|
102
152
|
|
103
|
-
width
|
153
|
+
width := i.Size.Width
|
104
154
|
height := i.Size.Height
|
105
155
|
|
106
156
|
if isAsp {
|
@@ -121,36 +171,15 @@ func resizeToCrop(i *Ires, inputImg image.Image) image.Image {
|
|
121
171
|
|
122
172
|
// Cropping
|
123
173
|
outputImg, _ = cutter.Crop(resizeImg, cutter.Config{
|
124
|
-
Width:
|
125
|
-
Height:
|
126
|
-
Mode:
|
174
|
+
Width: width,
|
175
|
+
Height: height,
|
176
|
+
Mode: cutter.Centered,
|
127
177
|
Options: cutter.Copy,
|
128
178
|
})
|
129
|
-
|
130
179
|
}
|
131
180
|
return outputImg
|
132
181
|
}
|
133
182
|
|
134
|
-
|
135
|
-
// Check expiration date
|
136
|
-
func (i *Ires) deleteExpireImage(mode int) {
|
137
|
-
today := time.Now().Format("20060102")
|
138
|
-
dir := i.readImageDir(mode)
|
139
|
-
files, err := ioutil.ReadDir(dir)
|
140
|
-
if err != nil {
|
141
|
-
return
|
142
|
-
}
|
143
|
-
|
144
|
-
for _, file := range files {
|
145
|
-
findName := file.Name()
|
146
|
-
matched,_ := path.Match(today + "_*", findName)
|
147
|
-
if matched {
|
148
|
-
deleteImage(path.Join(dir, findName))
|
149
|
-
}
|
150
|
-
}
|
151
|
-
}
|
152
|
-
|
153
|
-
|
154
183
|
// Delete image
|
155
184
|
func deleteImage(path string) {
|
156
185
|
_, err := os.Stat(path)
|
@@ -161,38 +190,20 @@ func deleteImage(path string) {
|
|
161
190
|
}
|
162
191
|
}
|
163
192
|
|
164
|
-
|
165
193
|
// Verify aspect ratio
|
166
|
-
func
|
167
|
-
conf :=
|
194
|
+
func (i *Ires) isValidAspectRatio() (bool, image.Config) {
|
195
|
+
conf := i.InputImage.Config
|
196
|
+
s := i.Size
|
168
197
|
aspH := (conf.Height * s.Width) / conf.Width
|
169
198
|
if aspH == s.Height {
|
170
199
|
return true, conf
|
171
|
-
} else {
|
172
|
-
return false, conf
|
173
|
-
}
|
174
|
-
}
|
175
|
-
|
176
|
-
|
177
|
-
// Image config
|
178
|
-
func imageConfig(path string) image.Config {
|
179
|
-
file, err := os.Open(path)
|
180
|
-
if err != nil {
|
181
|
-
panic(err)
|
182
|
-
}
|
183
|
-
defer file.Close()
|
184
|
-
|
185
|
-
conf, _, err := image.DecodeConfig(file)
|
186
|
-
if err != nil {
|
187
|
-
panic(err)
|
188
200
|
}
|
189
|
-
return conf
|
201
|
+
return false, conf
|
190
202
|
}
|
191
203
|
|
192
|
-
|
193
204
|
// Select image resize mode
|
194
205
|
func resizeMode(conf image.Config, s Size) int {
|
195
|
-
srcWidth
|
206
|
+
srcWidth := s.Width
|
196
207
|
srcHeight := s.Height
|
197
208
|
|
198
209
|
if conf.Width >= conf.Height && srcWidth >= srcHeight {
|
@@ -207,10 +218,9 @@ func resizeMode(conf image.Config, s Size) int {
|
|
207
218
|
return 0
|
208
219
|
}
|
209
220
|
|
210
|
-
|
211
221
|
// Search image format
|
212
222
|
// if defined, return "jpeg"
|
213
|
-
func formatSearch(r io.Reader) string{
|
223
|
+
func formatSearch(r io.Reader) string {
|
214
224
|
_, format, err := image.DecodeConfig(r)
|
215
225
|
if err != nil {
|
216
226
|
return "jpeg"
|
@@ -218,10 +228,30 @@ func formatSearch(r io.Reader) string{
|
|
218
228
|
return format
|
219
229
|
}
|
220
230
|
|
221
|
-
|
222
231
|
// Copy Reader
|
223
232
|
func copyReader(body io.Reader) (io.Reader, io.Reader) {
|
224
233
|
header := bytes.NewBuffer(nil)
|
225
234
|
r := io.TeeReader(body, header)
|
226
235
|
return header, r
|
227
|
-
}
|
236
|
+
}
|
237
|
+
|
238
|
+
// Valid resize type
|
239
|
+
func (i *Ires) validResizeType() bool {
|
240
|
+
config := i.InputImage.Config
|
241
|
+
valid := false
|
242
|
+
switch i.ResizeType {
|
243
|
+
case All:
|
244
|
+
valid = true
|
245
|
+
case Smaller:
|
246
|
+
if config.Width < i.Width && config.Height < i.Height {
|
247
|
+
valid = true
|
248
|
+
}
|
249
|
+
case Larger:
|
250
|
+
if i.Width <= config.Width && i.Height <= config.Height {
|
251
|
+
valid = true
|
252
|
+
}
|
253
|
+
default:
|
254
|
+
valid = true
|
255
|
+
}
|
256
|
+
return valid
|
257
|
+
}
|
data/ext/ires/ires.go
CHANGED
@@ -1,102 +1,123 @@
|
|
1
1
|
package ires
|
2
2
|
|
3
3
|
import (
|
4
|
-
|
5
|
-
|
4
|
+
"image"
|
5
|
+
|
6
|
+
"github.com/nfnt/resize"
|
7
|
+
"github.com/oliamb/cutter"
|
6
8
|
)
|
7
9
|
|
10
|
+
type Mode int
|
8
11
|
const (
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
IMAGE_MODE_ORIGINAL
|
12
|
+
Resize Mode = iota
|
13
|
+
Crop
|
14
|
+
ResizeToCrop
|
13
15
|
)
|
14
16
|
|
15
|
-
type
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Size
|
22
|
-
Dir string
|
23
|
-
Expire string
|
24
|
-
IsLocal bool
|
25
|
-
}
|
17
|
+
type ResizeType int
|
18
|
+
const (
|
19
|
+
All ResizeType = iota
|
20
|
+
Smaller
|
21
|
+
Larger
|
22
|
+
)
|
26
23
|
|
24
|
+
type (
|
25
|
+
// Size is ...
|
26
|
+
Size struct {
|
27
|
+
Width, Height int
|
28
|
+
}
|
29
|
+
|
30
|
+
// InputImage is ...
|
31
|
+
InputImage struct {
|
32
|
+
Image image.Image
|
33
|
+
Config image.Config
|
34
|
+
Format string
|
35
|
+
URI string
|
36
|
+
}
|
37
|
+
|
38
|
+
// Ires is ...
|
39
|
+
Ires struct {
|
40
|
+
Size
|
41
|
+
ResizeType
|
42
|
+
Mode
|
43
|
+
*InputImage
|
44
|
+
URI string
|
45
|
+
Dir string
|
46
|
+
Expire string
|
47
|
+
IsLocal bool
|
48
|
+
}
|
49
|
+
)
|
27
50
|
|
51
|
+
// Resize is ...
|
28
52
|
func (i *Ires) Resize() string {
|
29
|
-
|
30
|
-
i.
|
31
|
-
|
32
|
-
// Delete the expiration date image
|
33
|
-
i.deleteExpireImage(IMAGE_MODE_RESIZE)
|
34
|
-
|
35
|
-
distPath := i.imagePath(IMAGE_MODE_RESIZE)
|
53
|
+
i.Mode = Resize
|
54
|
+
distURI := i.imageURI(false)
|
36
55
|
// When the image exists, return the image path
|
37
|
-
if isExistsImage(
|
38
|
-
return i.
|
56
|
+
if isExistsImage(distURI) {
|
57
|
+
return i.targetImageURI(distURI)
|
39
58
|
}
|
40
59
|
|
41
|
-
|
42
|
-
|
43
|
-
return i.Uri
|
60
|
+
if !i.inputImage() {
|
61
|
+
return i.URI
|
44
62
|
}
|
45
63
|
|
46
|
-
outputImg
|
47
|
-
|
48
|
-
|
64
|
+
var outputImg image.Image
|
65
|
+
if i.validResizeType() {
|
66
|
+
outputImg = resize.Resize(uint(i.Width), uint(i.Height), i.InputImage.Image, resize.Lanczos3)
|
67
|
+
} else {
|
68
|
+
outputImg = i.InputImage.Image
|
69
|
+
}
|
70
|
+
createImage(outputImg, distURI, i.InputImage.Format)
|
71
|
+
return i.targetImageURI(distURI)
|
49
72
|
}
|
50
73
|
|
74
|
+
// Crop is Crop ...
|
51
75
|
func (i *Ires) Crop() string {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
// Delete the expiration date image
|
56
|
-
i.deleteExpireImage(IMAGE_MODE_CROP)
|
57
|
-
|
58
|
-
distPath := i.imagePath(IMAGE_MODE_CROP)
|
76
|
+
i.Mode = Crop
|
77
|
+
distURI := i.imageURI(false)
|
59
78
|
// When the image exists, return the image path
|
60
|
-
if isExistsImage(
|
61
|
-
|
62
|
-
|
79
|
+
if isExistsImage(distURI) {
|
80
|
+
return i.targetImageURI(distURI)
|
81
|
+
}
|
63
82
|
|
64
|
-
|
65
|
-
|
66
|
-
return i.Uri
|
83
|
+
if !i.inputImage() {
|
84
|
+
return i.URI
|
67
85
|
}
|
68
86
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
87
|
+
var outputImg image.Image
|
88
|
+
if i.validResizeType() {
|
89
|
+
outputImg, _ = cutter.Crop(i.InputImage.Image, cutter.Config{
|
90
|
+
Width: i.Width,
|
91
|
+
Height: i.Height,
|
92
|
+
Mode: cutter.Centered,
|
93
|
+
Options: cutter.Copy,
|
94
|
+
})
|
95
|
+
} else {
|
96
|
+
outputImg = i.InputImage.Image
|
97
|
+
}
|
98
|
+
createImage(outputImg, distURI, i.InputImage.Format)
|
99
|
+
return i.targetImageURI(distURI)
|
78
100
|
}
|
79
101
|
|
102
|
+
// ResizeToCrop is ...
|
80
103
|
func (i *Ires) ResizeToCrop() string {
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
// Delete the expiration date image
|
85
|
-
i.deleteExpireImage(IMAGE_MODE_RESIZE_TO_CROP)
|
86
|
-
|
87
|
-
distPath := i.imagePath(IMAGE_MODE_RESIZE_TO_CROP)
|
104
|
+
i.Mode = ResizeToCrop
|
105
|
+
distURI := i.imageURI(false)
|
88
106
|
// When the image exists, return the image path
|
89
|
-
if isExistsImage(
|
90
|
-
return i.
|
107
|
+
if isExistsImage(distURI) {
|
108
|
+
return i.targetImageURI(distURI)
|
91
109
|
}
|
92
110
|
|
93
|
-
|
94
|
-
|
95
|
-
return i.Uri
|
111
|
+
if !i.inputImage() {
|
112
|
+
return i.URI
|
96
113
|
}
|
97
114
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
115
|
+
var outputImg image.Image
|
116
|
+
if i.validResizeType() {
|
117
|
+
outputImg = i.resizeToCrop()
|
118
|
+
} else {
|
119
|
+
outputImg = i.InputImage.Image
|
120
|
+
}
|
121
|
+
createImage(outputImg, distURI, i.InputImage.Format)
|
122
|
+
return i.targetImageURI(distURI)
|
102
123
|
}
|
data/ext/ires/uri.go
CHANGED
@@ -9,48 +9,8 @@ import (
|
|
9
9
|
"github.com/goware/urlx"
|
10
10
|
)
|
11
11
|
|
12
|
-
|
13
|
-
// Input image type is Local or HTTP
|
14
|
-
func (i *Ires) isLocalFile() {
|
15
|
-
if strings.Index(i.Uri, "http") == -1 {
|
16
|
-
i.IsLocal = true
|
17
|
-
} else {
|
18
|
-
i.IsLocal = false
|
19
|
-
}
|
20
|
-
}
|
21
|
-
|
22
|
-
|
23
|
-
// Generate image name
|
24
|
-
func imageName(i *Ires, mode int) string {
|
25
|
-
splitPath := strings.Split(i.Uri, "/")
|
26
|
-
|
27
|
-
// ex. sample.jpg
|
28
|
-
fileName := splitPath[len(splitPath)-1]
|
29
|
-
// ex. .jpg
|
30
|
-
ext := filepath.Ext(fileName)
|
31
|
-
|
32
|
-
name := strings.Replace(fileName, ext, "", 1)
|
33
|
-
|
34
|
-
extInfo := strings.Split(ext, "?")
|
35
|
-
if len(extInfo) > 1 {
|
36
|
-
ext = extInfo[0]
|
37
|
-
name += "_" + strings.Join(extInfo[1:], "")
|
38
|
-
}
|
39
|
-
|
40
|
-
var prefix string
|
41
|
-
switch mode {
|
42
|
-
case 0: prefix = prefixSize(i.Size) + "_resize"
|
43
|
-
case 1: prefix = prefixSize(i.Size) + "_crop"
|
44
|
-
case 2: prefix = prefixSize(i.Size) + "_resize_to_crop"
|
45
|
-
case 3: prefix = "original"
|
46
|
-
}
|
47
|
-
|
48
|
-
return i.Expire + "_" + name + "_" + prefix + ext
|
49
|
-
}
|
50
|
-
|
51
|
-
|
52
12
|
// Generate image path
|
53
|
-
func (i *Ires)
|
13
|
+
func (i *Ires) imageURI(original bool) string {
|
54
14
|
paths := []rune(i.Dir)
|
55
15
|
pathsLastIndex := len(paths) - 1
|
56
16
|
lastChar := string(paths[pathsLastIndex])
|
@@ -61,9 +21,9 @@ func (i *Ires) imagePath(mode int) string {
|
|
61
21
|
|
62
22
|
var oDir string
|
63
23
|
if i.IsLocal {
|
64
|
-
oDir = localPath(
|
24
|
+
oDir = localPath(i.Mode)
|
65
25
|
} else {
|
66
|
-
oDir = remotePath(i)
|
26
|
+
oDir = remotePath(i.URI)
|
67
27
|
}
|
68
28
|
|
69
29
|
// Create directory
|
@@ -74,10 +34,43 @@ func (i *Ires) imagePath(mode int) string {
|
|
74
34
|
}
|
75
35
|
}
|
76
36
|
|
77
|
-
name := imageName(
|
37
|
+
name := i.imageName(original)
|
78
38
|
return filepath.Join(oPath, name)
|
79
39
|
}
|
80
40
|
|
41
|
+
// Generate image name
|
42
|
+
func (i *Ires) imageName(original bool) string {
|
43
|
+
splitPath := strings.Split(i.URI, "/")
|
44
|
+
|
45
|
+
// ex. sample.jpg
|
46
|
+
fileName := splitPath[len(splitPath)-1]
|
47
|
+
// ex. .jpg
|
48
|
+
ext := filepath.Ext(fileName)
|
49
|
+
|
50
|
+
name := strings.Replace(fileName, ext, "", 1)
|
51
|
+
|
52
|
+
extInfo := strings.Split(ext, "?")
|
53
|
+
if len(extInfo) > 1 {
|
54
|
+
ext = extInfo[0]
|
55
|
+
name += "_" + strings.Join(extInfo[1:], "")
|
56
|
+
}
|
57
|
+
|
58
|
+
var prefix string
|
59
|
+
if original {
|
60
|
+
prefix = "original"
|
61
|
+
} else {
|
62
|
+
switch i.Mode {
|
63
|
+
case Resize:
|
64
|
+
prefix = prefixSize(i.Size) + "_resize"
|
65
|
+
case Crop:
|
66
|
+
prefix = prefixSize(i.Size) + "_crop"
|
67
|
+
case ResizeToCrop:
|
68
|
+
prefix = prefixSize(i.Size) + "_resize_to_crop"
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
return i.Expire + "_" + name + "_" + prefix + ext
|
73
|
+
}
|
81
74
|
|
82
75
|
// Create prefix by size
|
83
76
|
// ex. 640x480
|
@@ -85,45 +78,43 @@ func prefixSize(s Size) string {
|
|
85
78
|
return strconv.Itoa(s.Width) + "x" + strconv.Itoa(s.Height)
|
86
79
|
}
|
87
80
|
|
88
|
-
|
89
81
|
// リサイズ済みのファイルがあれば、処理せず返す
|
90
82
|
func isExistsImage(path string) bool {
|
91
83
|
_, err := os.Stat(path)
|
92
84
|
if err == nil {
|
93
85
|
return true
|
94
|
-
} else {
|
95
|
-
return false
|
96
86
|
}
|
87
|
+
return false
|
97
88
|
}
|
98
89
|
|
99
|
-
|
100
90
|
// Read directory
|
101
|
-
func (i *Ires) readImageDir(
|
91
|
+
func (i *Ires) readImageDir() string {
|
102
92
|
var dir string
|
103
93
|
if i.IsLocal {
|
104
|
-
dir = localPath(
|
94
|
+
dir = localPath(i.Mode)
|
105
95
|
} else {
|
106
|
-
dir = remotePath(i)
|
96
|
+
dir = remotePath(i.URI)
|
107
97
|
}
|
108
98
|
return filepath.Join(i.Dir, dir)
|
109
99
|
}
|
110
100
|
|
111
|
-
|
112
101
|
// if local image, create ires directory
|
113
|
-
func localPath(mode
|
102
|
+
func localPath(mode Mode) string {
|
114
103
|
var dir string
|
115
104
|
switch mode {
|
116
|
-
case
|
117
|
-
|
118
|
-
case
|
105
|
+
case Resize:
|
106
|
+
dir = "ires/resize"
|
107
|
+
case Crop:
|
108
|
+
dir = "ires/crop"
|
109
|
+
case ResizeToCrop:
|
110
|
+
dir = "ires/resize_to_crop"
|
119
111
|
}
|
120
112
|
return dir
|
121
113
|
}
|
122
114
|
|
123
|
-
|
124
115
|
// if http image, parse URL & make directory
|
125
|
-
func remotePath(
|
126
|
-
u, err := urlx.Parse(
|
116
|
+
func remotePath(uri string) string {
|
117
|
+
u, err := urlx.Parse(uri)
|
127
118
|
dir := []string{"ires"}
|
128
119
|
if err != nil {
|
129
120
|
panic(err)
|
@@ -136,8 +127,7 @@ func remotePath(i *Ires) string {
|
|
136
127
|
return strings.Join(dir, "/")
|
137
128
|
}
|
138
129
|
|
139
|
-
|
140
130
|
// Optimize image path
|
141
|
-
func (i *Ires)
|
142
|
-
return strings.Replace(
|
131
|
+
func (i *Ires) targetImageURI(uri string) string {
|
132
|
+
return strings.Replace(uri, i.Dir, "", -1)
|
143
133
|
}
|
data/ext/main.go
CHANGED
@@ -10,32 +10,35 @@ func init() {}
|
|
10
10
|
func main() {}
|
11
11
|
|
12
12
|
//export iresImagePath
|
13
|
-
func iresImagePath(
|
14
|
-
uri
|
15
|
-
|
16
|
-
|
17
|
-
expire := C.GoString(Expire)
|
13
|
+
func iresImagePath(URI *C.char, width, height, rType, mode int, Dir, Expire *C.char) *C.char {
|
14
|
+
uri := C.GoString(URI)
|
15
|
+
dir := C.GoString(Dir)
|
16
|
+
expire := C.GoString(Expire)
|
18
17
|
|
19
18
|
r := &ires.Ires{
|
20
|
-
Uri: uri,
|
21
19
|
Size: ires.Size{
|
22
|
-
Width:
|
20
|
+
Width: width,
|
23
21
|
Height: height,
|
24
22
|
},
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
ResizeType: ires.ResizeType(rType),
|
24
|
+
URI: uri,
|
25
|
+
Dir: dir,
|
26
|
+
Expire: expire,
|
28
27
|
}
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
case "crop":
|
35
|
-
imagePath = r.Crop()
|
36
|
-
case "resize_to_crop":
|
37
|
-
imagePath = r.ResizeToCrop()
|
38
|
-
}
|
29
|
+
// If local image, True
|
30
|
+
r.CheckLocal()
|
31
|
+
// Delete the expiration date image
|
32
|
+
r.DeleteExpireImage()
|
39
33
|
|
40
|
-
|
41
|
-
|
34
|
+
var distURI string
|
35
|
+
switch ires.Mode(mode) {
|
36
|
+
case ires.Resize:
|
37
|
+
distURI = r.Resize()
|
38
|
+
case ires.Crop:
|
39
|
+
distURI = r.Crop()
|
40
|
+
case ires.ResizeToCrop:
|
41
|
+
distURI = r.ResizeToCrop()
|
42
|
+
}
|
43
|
+
return C.CString(distURI)
|
44
|
+
}
|
data/lib/ires.rb
CHANGED
data/lib/ires/core.rb
CHANGED
@@ -4,11 +4,17 @@ require 'ires/os'
|
|
4
4
|
module Ires
|
5
5
|
module Core
|
6
6
|
extend FFI::Library
|
7
|
-
|
8
7
|
# NOTE: ires.so is golang object
|
9
8
|
ffi_lib File.expand_path("../../shared/#{Ires::Os.current}/ires.so", File.dirname(__FILE__))
|
10
|
-
|
11
9
|
# resize func
|
12
|
-
|
10
|
+
# Type:
|
11
|
+
# path: :string
|
12
|
+
# width: :int
|
13
|
+
# height: :int
|
14
|
+
# type: :int
|
15
|
+
# mode: :int
|
16
|
+
# dir: :string
|
17
|
+
# expire: :string
|
18
|
+
attach_function :iresImagePath, %i[string int int int int string string], :string
|
13
19
|
end
|
14
20
|
end
|
data/lib/ires/mode.rb
ADDED
data/lib/ires/os.rb
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
module Ires
|
2
2
|
module Os
|
3
3
|
class << self
|
4
|
-
|
5
4
|
# Reszie image directory
|
6
5
|
# @return [none(ffi)]
|
7
6
|
def current
|
8
|
-
if [
|
7
|
+
if %w[darwin linux].include?(os)
|
9
8
|
os
|
10
9
|
else
|
11
10
|
logger.fatal "Ires is not supported by this #{os}"
|
12
11
|
nil
|
13
12
|
end
|
14
13
|
end
|
15
|
-
|
16
|
-
private
|
14
|
+
|
15
|
+
private
|
17
16
|
|
18
17
|
# Search OS
|
19
18
|
# @return [String]
|
20
19
|
def os
|
21
|
-
@os ||=
|
22
|
-
|
23
|
-
|
20
|
+
@os ||= begin
|
21
|
+
host_os = RbConfig::CONFIG['host_os']
|
22
|
+
case host_os
|
24
23
|
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
25
24
|
'windows'
|
26
25
|
when /darwin|mac os/
|
@@ -31,10 +30,9 @@ module Ires
|
|
31
30
|
'unix'
|
32
31
|
else
|
33
32
|
'unknown'
|
33
|
+
end
|
34
34
|
end
|
35
|
-
)
|
36
35
|
end
|
37
|
-
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
data/lib/ires/service.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'ires/core'
|
2
2
|
require 'ires/os'
|
3
|
+
require 'ires/mode'
|
4
|
+
require 'ires/type'
|
3
5
|
|
4
6
|
module Ires
|
5
7
|
class Service
|
6
8
|
class << self
|
7
|
-
|
8
9
|
# Resize image path
|
9
10
|
# @return [String]
|
10
|
-
def path(path:, width:, height:, mode:
|
11
|
-
|
11
|
+
def path(path:, width:, height:, type: Type::ALL, mode: Mode::RESIZE, expire: 30.days)
|
12
12
|
os = Ires::Os.current
|
13
13
|
return nil if os.nil?
|
14
14
|
|
@@ -16,7 +16,7 @@ module Ires
|
|
16
16
|
|
17
17
|
# if no image or could not find file path then perform the same action as 'image_tag'
|
18
18
|
return nil if invalid_path?(full_path)
|
19
|
-
|
19
|
+
|
20
20
|
expiration_date = expiration_date(expire)
|
21
21
|
dir = image_dir
|
22
22
|
|
@@ -25,6 +25,7 @@ module Ires
|
|
25
25
|
width: width,
|
26
26
|
height: height,
|
27
27
|
mode: mode,
|
28
|
+
type: type,
|
28
29
|
dir: dir,
|
29
30
|
expire: expiration_date
|
30
31
|
}
|
@@ -53,28 +54,29 @@ module Ires
|
|
53
54
|
# Check file or URI
|
54
55
|
# @return [Bool]
|
55
56
|
def invalid_path?(path)
|
56
|
-
!File.exist?(path) && !path.include?(
|
57
|
+
!File.exist?(path) && !path.include?('http')
|
57
58
|
end
|
58
59
|
|
59
60
|
# Expiration date (default: 7.days)
|
60
61
|
# ex. "20170101"
|
61
62
|
# @return [String]
|
62
63
|
def expiration_date(expire)
|
63
|
-
(
|
64
|
+
(Time.zone.today + expire).strftime('%Y%m%d')
|
64
65
|
end
|
65
66
|
|
66
67
|
# Image path
|
67
68
|
# @return [String]
|
68
69
|
def ires_image_path(ires_element)
|
69
|
-
|
70
|
+
Core.iresImagePath(
|
70
71
|
ires_element[:path],
|
71
72
|
ires_element[:width],
|
72
73
|
ires_element[:height],
|
74
|
+
ires_element[:type],
|
73
75
|
ires_element[:mode],
|
74
76
|
ires_element[:dir],
|
75
|
-
ires_element[:expire]
|
77
|
+
ires_element[:expire]
|
78
|
+
)
|
76
79
|
end
|
77
|
-
|
78
80
|
end
|
79
81
|
end
|
80
|
-
end
|
82
|
+
end
|
data/lib/ires/type.rb
ADDED
data/lib/ires/version.rb
CHANGED
data/lib/ires/view_helper.rb
CHANGED
@@ -1,19 +1,26 @@
|
|
1
1
|
require 'ires/core'
|
2
2
|
require 'ires/service'
|
3
|
+
require 'ires/mode'
|
4
|
+
require 'ires/type'
|
3
5
|
require 'action_view/helpers'
|
4
6
|
|
5
7
|
module Ires
|
6
8
|
module ViewHelper
|
7
|
-
|
8
9
|
# Image resize
|
9
10
|
# @return [image_tag]
|
10
|
-
def ires_tag(path:, width:, height:, mode:
|
11
|
-
image = Ires::Service.path(
|
11
|
+
def ires_tag(path:, width:, height:, type: Type::ALL, mode: Mode::RESIZE, expire: 30.days, **option)
|
12
|
+
image = Ires::Service.path(
|
13
|
+
path: path,
|
14
|
+
width: width,
|
15
|
+
height: height,
|
16
|
+
mode: mode,
|
17
|
+
type: type,
|
18
|
+
expire: expire
|
19
|
+
)
|
12
20
|
return nil if image.nil?
|
13
21
|
|
14
22
|
# Set image_tag
|
15
23
|
image_tag(image, option)
|
16
24
|
end
|
17
|
-
|
18
25
|
end
|
19
|
-
end
|
26
|
+
end
|
data/lib/tasks/ires.rake
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'ires/os'
|
2
2
|
|
3
|
-
desc
|
3
|
+
desc 'Build shared object'
|
4
4
|
namespace :ires do
|
5
5
|
task :build do
|
6
6
|
os = Ires::Os.current
|
7
7
|
return if os.nil?
|
8
8
|
exec "CGO_ENABLED=1 GOOS=\"#{os}\" go build -v -buildmode=c-shared -o shared/\"#{os}\"/ires.so ext/main.go"
|
9
9
|
end
|
10
|
-
end
|
10
|
+
end
|
data/shared/darwin/ires.h
CHANGED
data/shared/darwin/ires.so
CHANGED
Binary file
|
data/shared/linux/ires.h
CHANGED
@@ -1,7 +1,19 @@
|
|
1
|
-
/*
|
1
|
+
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
2
2
|
|
3
3
|
/* package command-line-arguments */
|
4
4
|
|
5
|
+
|
6
|
+
#line 1 "cgo-builtin-prolog"
|
7
|
+
|
8
|
+
#include <stddef.h> /* for ptrdiff_t below */
|
9
|
+
|
10
|
+
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
11
|
+
#define GO_CGO_EXPORT_PROLOGUE_H
|
12
|
+
|
13
|
+
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
14
|
+
|
15
|
+
#endif
|
16
|
+
|
5
17
|
/* Start of preamble from import "C" comments. */
|
6
18
|
|
7
19
|
|
@@ -38,7 +50,7 @@ typedef double _Complex GoComplex128;
|
|
38
50
|
*/
|
39
51
|
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
40
52
|
|
41
|
-
typedef
|
53
|
+
typedef _GoString_ GoString;
|
42
54
|
typedef void *GoMap;
|
43
55
|
typedef void *GoChan;
|
44
56
|
typedef struct { void *t; void *v; } GoInterface;
|
@@ -53,7 +65,7 @@ extern "C" {
|
|
53
65
|
#endif
|
54
66
|
|
55
67
|
|
56
|
-
extern char* iresImagePath(char* p0, GoInt p1, GoInt p2,
|
68
|
+
extern char* iresImagePath(char* p0, GoInt p1, GoInt p2, GoInt p3, GoInt p4, char* p5, char* p6);
|
57
69
|
|
58
70
|
#ifdef __cplusplus
|
59
71
|
}
|
data/shared/linux/ires.so
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ires
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- enta0701
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 4.1.8
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 4.1.8
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: ffi
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rails
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -1112,8 +1112,10 @@ files:
|
|
1112
1112
|
- ext/vendor/golang.org/x/text/width/width.go
|
1113
1113
|
- lib/ires.rb
|
1114
1114
|
- lib/ires/core.rb
|
1115
|
+
- lib/ires/mode.rb
|
1115
1116
|
- lib/ires/os.rb
|
1116
1117
|
- lib/ires/service.rb
|
1118
|
+
- lib/ires/type.rb
|
1117
1119
|
- lib/ires/version.rb
|
1118
1120
|
- lib/ires/view_helper.rb
|
1119
1121
|
- lib/tasks/ires.rake
|