vidibus-pureftpd 0.1.0 → 1.0.0
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.
- data/README.md +74 -35
- data/Rakefile +1 -1
- data/config/locales/de.yml +8 -0
- data/config/locales/en.yml +8 -0
- data/lib/vidibus-pureftpd.rb +3 -1
- data/lib/vidibus/pureftpd.rb +15 -52
- data/lib/vidibus/pureftpd/user.rb +106 -29
- metadata +36 -2
data/README.md
CHANGED
|
@@ -1,43 +1,69 @@
|
|
|
1
1
|
# Vidibus::Pureftpd
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Provides an ActiveModel-based abstraction of Pure-FTPd's [virtual users](http://download.pureftpd.org/pub/pure-ftpd/doc/README.Virtual-Users). [Pure-FTPd](http://www.pureftpd.org/project/pure-ftpd) is a free, secure, production-quality and standard-conformant FTP server.
|
|
4
4
|
|
|
5
|
-
This gem is part of [Vidibus](http://vidibus.org), an open source toolset for building distributed (video) applications.
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
Add the dependency to the Gemfile of your application: `gem 'vidibus-pureftpd'`. Then call bundle install on your console.
|
|
5
|
+
This gem is part of [Vidibus](http://vidibus.org), an open source toolset for building distributed (video) applications. It has been tested with Ruby 1.8.7 and 1.9.3.
|
|
10
6
|
|
|
11
7
|
|
|
12
8
|
## Usage
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+
Basic CRUD operations are available for `Vidibus::Pureftpd::User`:
|
|
15
11
|
|
|
16
|
-
```
|
|
17
|
-
Vidibus::Pureftpd.
|
|
18
|
-
:login => '
|
|
12
|
+
```ruby
|
|
13
|
+
user = Vidibus::Pureftpd::User.new(
|
|
14
|
+
:login => 'my_user',
|
|
19
15
|
:password => 'verysecret',
|
|
20
|
-
:directory => '/
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
:directory => '/home/my_user'
|
|
17
|
+
)
|
|
18
|
+
# => returns a new user instance
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
user = Vidibus::Pureftpd::User.create(
|
|
21
|
+
:login => 'my_user',
|
|
22
|
+
:password => 'verysecret',
|
|
23
|
+
:directory => '/home/my_user'
|
|
24
|
+
)
|
|
25
|
+
# => creates a new user and returns the instance
|
|
25
26
|
|
|
27
|
+
user = Vidibus::Pureftpd::User.find_by_login('my_user')
|
|
28
|
+
# => reads user from database and returns an user instance
|
|
29
|
+
|
|
30
|
+
user.save
|
|
31
|
+
# => saves user to database and returns true
|
|
32
|
+
|
|
33
|
+
user.destroy
|
|
34
|
+
# => removes user from database
|
|
26
35
|
```
|
|
27
|
-
Vidibus::Pureftpd.delete_user(:login => 'someuser')
|
|
28
|
-
```
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
Additionally, some methods are provided for your convenience:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
user.valid?
|
|
41
|
+
# => returns true if user is valid
|
|
42
|
+
|
|
43
|
+
user.persisted?
|
|
44
|
+
# => returns true if user has been saved to database
|
|
31
45
|
|
|
46
|
+
user.reload
|
|
47
|
+
# => reloads user from database
|
|
32
48
|
```
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
|
|
50
|
+
By default, `Vidibus::Pureftpd` will use these settings which you may override:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
Vidibus::Pureftpd.settings[:sysuser] # => 'pureftpd_user'
|
|
54
|
+
Vidibus::Pureftpd.settings[:sysgroup] # => 'pureftpd_group'
|
|
55
|
+
Vidibus::Pureftpd.settings[:password_file] # => '/etc/pure-ftpd/pureftpd.passwd'
|
|
37
56
|
```
|
|
38
57
|
|
|
39
58
|
|
|
40
|
-
##
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
Add the dependency to the Gemfile of your application: `gem 'vidibus-pureftpd'`. Then call bundle install on your console.
|
|
62
|
+
|
|
63
|
+
Installation of the Pure-FTPd server itself is quite simple:
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### Install Pure-FTPd on Debian Lenny
|
|
41
67
|
|
|
42
68
|
Get the package:
|
|
43
69
|
|
|
@@ -112,13 +138,13 @@ Finally, (re)start Pure-FTPd:
|
|
|
112
138
|
For more instructions, please [check this resource](http://linux.justinhartman.com/PureFTPd_Installation_and_Setup).
|
|
113
139
|
|
|
114
140
|
|
|
115
|
-
|
|
141
|
+
### Install Pure-FTPd on OSX (required for testing this gem)
|
|
116
142
|
|
|
117
143
|
```
|
|
118
144
|
brew install pure-ftpd
|
|
119
145
|
```
|
|
120
146
|
|
|
121
|
-
Create the user `pureftpd_user` with ID 483:
|
|
147
|
+
In order to perform the tests, a certain user is required. Create the user `pureftpd_user` with ID 483:
|
|
122
148
|
|
|
123
149
|
```
|
|
124
150
|
sudo dscl . create /Users/pureftpd_user uid 483
|
|
@@ -127,7 +153,7 @@ sudo dscl . create /Users/pureftpd_user UserShell /etc/pure-ftpd
|
|
|
127
153
|
sudo dscl . create /Users/pureftpd_user NFSHomeDirectory /dev/null
|
|
128
154
|
```
|
|
129
155
|
|
|
130
|
-
|
|
156
|
+
Then create the group `pureftpd_group`, also with ID 483:
|
|
131
157
|
|
|
132
158
|
```
|
|
133
159
|
sudo dscl . create /Groups/pureftpd_group gid 483
|
|
@@ -141,21 +167,14 @@ dscacheutil -q user
|
|
|
141
167
|
dscacheutil -q group
|
|
142
168
|
```
|
|
143
169
|
|
|
144
|
-
Ensure that the database exists and is writable for the user that executes RSpec:
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
sudo mkdir /etc/pure-ftpd/
|
|
148
|
-
sudo touch /etc/pure-ftpd/pureftpd.passwd
|
|
149
|
-
sudo chown -R `whoami` /etc/pure-ftpd/
|
|
150
|
-
```
|
|
151
170
|
|
|
152
|
-
|
|
171
|
+
Not needed for testing, but to start the server, type:
|
|
153
172
|
|
|
154
173
|
```
|
|
155
174
|
sudo /usr/local/sbin/pure-ftpd &
|
|
156
175
|
```
|
|
157
176
|
|
|
158
|
-
You should be able to connect via ftp:
|
|
177
|
+
You should now be able to connect via ftp:
|
|
159
178
|
|
|
160
179
|
```
|
|
161
180
|
ftp localhost
|
|
@@ -164,7 +183,27 @@ ftp localhost
|
|
|
164
183
|
Shut it down with:
|
|
165
184
|
|
|
166
185
|
```
|
|
167
|
-
pkill pure-ftpd
|
|
186
|
+
sudo pkill pure-ftpd
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
## TODO
|
|
191
|
+
|
|
192
|
+
Implement all user options offered by Pure-FTPd:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
-t <download bandwidth>
|
|
196
|
+
-T <upload bandwidth>
|
|
197
|
+
-n <max number of files>
|
|
198
|
+
-N <max Mbytes>
|
|
199
|
+
-q <upload ratio>
|
|
200
|
+
-Q <download ratio>
|
|
201
|
+
-r <allow client host>
|
|
202
|
+
-R <deny client host>
|
|
203
|
+
-i <allow local host>
|
|
204
|
+
-I <deny local host>
|
|
205
|
+
-y <max number of concurrent sessions>
|
|
206
|
+
-z <hhmm>-<hhmm>
|
|
168
207
|
```
|
|
169
208
|
|
|
170
209
|
|
data/Rakefile
CHANGED
data/lib/vidibus-pureftpd.rb
CHANGED
data/lib/vidibus/pureftpd.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
module Vidibus
|
|
2
2
|
module Pureftpd
|
|
3
|
-
VERSION = '0.1.0'
|
|
4
|
-
|
|
5
3
|
class Error < StandardError; end
|
|
6
4
|
|
|
7
5
|
class << self
|
|
@@ -17,60 +15,25 @@ module Vidibus
|
|
|
17
15
|
}
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# :login, :password, :directory
|
|
23
|
-
def add_user(options)
|
|
24
|
-
unless options.keys?(:login, :password, :directory)
|
|
25
|
-
raise ArgumentError.new('Required options are :login, :password, :directory')
|
|
26
|
-
end
|
|
27
|
-
password = options.delete(:password)
|
|
28
|
-
cmd = 'pure-pw useradd %{login} -f %{password_file} -u %{sysuser} -g %{sysgroup} -d %{directory} -m' % settings.merge(options)
|
|
29
|
-
perform(cmd) do |stdin, stdout, stderr|
|
|
30
|
-
stdin.puts(password)
|
|
31
|
-
stdin.puts(password)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Deletes an existing user.
|
|
36
|
-
# Required options:
|
|
37
|
-
# :login
|
|
38
|
-
def delete_user(options)
|
|
39
|
-
unless options.key?(:login)
|
|
40
|
-
raise ArgumentError.new('Required option is :login')
|
|
41
|
-
end
|
|
42
|
-
cmd = 'pure-pw userdel %{login} -f %{password_file} -m' % settings.merge(options)
|
|
43
|
-
perform(cmd)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Changes password of existing user.
|
|
47
|
-
# Required options:
|
|
48
|
-
# :login, :password
|
|
49
|
-
def change_password(options)
|
|
50
|
-
unless options.keys?(:login, :password)
|
|
51
|
-
raise ArgumentError.new('Required options are :login, :password')
|
|
52
|
-
end
|
|
53
|
-
password = options.delete(:password)
|
|
54
|
-
cmd = 'pure-pw passwd %{login} -f %{password_file} -m' % settings.merge(options)
|
|
55
|
-
perform(cmd) do |stdin, stdout, stderr|
|
|
56
|
-
stdin.puts(password)
|
|
57
|
-
stdin.puts(password)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
protected
|
|
62
|
-
|
|
63
|
-
# Performs given command. Accepts a block with |stdin, stdout, stderr|.
|
|
18
|
+
# Performs given command prefixed with pure-pw.
|
|
19
|
+
# Accepts a block with |stdin, stdout, stderr|.
|
|
64
20
|
def perform(cmd, &block)
|
|
21
|
+
cmd = "pure-pw #{cmd}"
|
|
65
22
|
error = ''
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
23
|
+
output = nil
|
|
24
|
+
|
|
25
|
+
pid, stdin, stdout, stderr = POSIX::Spawn::popen4(cmd)
|
|
26
|
+
yield(stdin, stdout, stderr) if block_given?
|
|
27
|
+
error = stderr.read
|
|
28
|
+
output = stdout.read
|
|
29
|
+
|
|
70
30
|
unless error == ''
|
|
71
|
-
raise Error.new("
|
|
31
|
+
raise Error.new("Pure-FTPd returned an error:\n#{cmd}\n\n#{error}")
|
|
72
32
|
end
|
|
73
|
-
|
|
33
|
+
output
|
|
34
|
+
ensure
|
|
35
|
+
[stdin, stdout, stderr].each { |io| io.close if !io.closed? }
|
|
36
|
+
Process::waitpid(pid)
|
|
74
37
|
end
|
|
75
38
|
end
|
|
76
39
|
end
|
|
@@ -4,27 +4,56 @@ module Vidibus
|
|
|
4
4
|
include ActiveModel::Validations
|
|
5
5
|
include ActiveModel::Dirty
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
class Error < Vidibus::Pureftpd::Error; end
|
|
8
|
+
class DocumentNotFound < Error; end
|
|
9
|
+
|
|
10
|
+
ACCESSSORS = [:login, :password, :directory]
|
|
11
|
+
|
|
12
|
+
ACCESSSORS.each do |attribute|
|
|
13
|
+
class_eval <<-EOS
|
|
14
|
+
def #{attribute}=(value)
|
|
15
|
+
unless @#{attribute} == value
|
|
16
|
+
#{attribute}_will_change!
|
|
17
|
+
@#{attribute} = value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def #{attribute}
|
|
22
|
+
@#{attribute}
|
|
23
|
+
end
|
|
24
|
+
EOS
|
|
25
|
+
end
|
|
8
26
|
|
|
9
|
-
|
|
10
|
-
|
|
27
|
+
define_attribute_methods ACCESSSORS
|
|
28
|
+
|
|
29
|
+
validates :password, :directory, :presence => true
|
|
30
|
+
validates :login, :format => { :with => /^[a-z_]+$/ }
|
|
31
|
+
validate :unique_login?, :if => :login_changed?
|
|
32
|
+
validate :valid_directory?, :if => :directory_changed?
|
|
11
33
|
|
|
12
34
|
def initialize(values = {})
|
|
13
|
-
attributes = values
|
|
35
|
+
self.attributes = values
|
|
36
|
+
@persisted = false
|
|
14
37
|
end
|
|
15
38
|
|
|
16
39
|
def save
|
|
40
|
+
return false unless valid? && changed?
|
|
17
41
|
persisted? ? update : create
|
|
42
|
+
@previously_changed = changes
|
|
43
|
+
@changed_attributes.clear
|
|
44
|
+
@persisted = true
|
|
18
45
|
end
|
|
19
46
|
|
|
20
47
|
def destroy
|
|
21
|
-
|
|
48
|
+
return false unless persisted? && login_was
|
|
49
|
+
cmd = "userdel #{login_was} -f %{password_file} -m" % settings
|
|
22
50
|
perform(cmd)
|
|
23
|
-
|
|
51
|
+
instance_variable_set('@persisted', false)
|
|
52
|
+
self
|
|
24
53
|
end
|
|
25
54
|
|
|
26
55
|
def persisted?
|
|
27
|
-
@
|
|
56
|
+
@persisted
|
|
28
57
|
end
|
|
29
58
|
|
|
30
59
|
def attributes
|
|
@@ -35,50 +64,91 @@ module Vidibus
|
|
|
35
64
|
hash
|
|
36
65
|
end
|
|
37
66
|
|
|
38
|
-
def attributes=(
|
|
39
|
-
|
|
67
|
+
def attributes=(hash)
|
|
68
|
+
hash.each do |key, value|
|
|
69
|
+
send("#{key}=", value)
|
|
70
|
+
end
|
|
40
71
|
end
|
|
41
72
|
|
|
42
73
|
def reload
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
74
|
+
if persisted? && login_was && (user = User.find_by_login(login_was))
|
|
75
|
+
self.attributes = user.attributes
|
|
76
|
+
self
|
|
77
|
+
else
|
|
78
|
+
raise DocumentNotFound
|
|
79
|
+
end
|
|
48
80
|
end
|
|
49
81
|
|
|
50
82
|
class << self
|
|
51
83
|
|
|
52
|
-
def
|
|
53
|
-
cmd =
|
|
54
|
-
|
|
84
|
+
def find_by_login(login)
|
|
85
|
+
cmd = "show #{login} -f %{password_file}" %
|
|
86
|
+
Vidibus::Pureftpd.settings
|
|
87
|
+
begin
|
|
88
|
+
data = Vidibus::Pureftpd.perform(cmd)
|
|
89
|
+
rescue Vidibus::Pureftpd::Error => e
|
|
90
|
+
if e.message.match('Unable to fetch info about user')
|
|
91
|
+
return nil
|
|
92
|
+
else
|
|
93
|
+
raise e
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
55
97
|
attributes = {}
|
|
98
|
+
data.scan(/(#{ACCESSSORS.join('|')})\s*:\s*(.+)/i).each do |key, value|
|
|
99
|
+
attributes[key.downcase] = value
|
|
100
|
+
end
|
|
101
|
+
|
|
56
102
|
User.new(attributes).tap do |user|
|
|
57
|
-
user.instance_variable_set('@
|
|
103
|
+
user.instance_variable_set('@persisted', true)
|
|
58
104
|
end
|
|
59
105
|
end
|
|
60
106
|
|
|
61
107
|
def create(attributes)
|
|
62
|
-
|
|
108
|
+
new(attributes).tap do |user|
|
|
109
|
+
user.save
|
|
110
|
+
end
|
|
63
111
|
end
|
|
64
112
|
end
|
|
65
113
|
|
|
66
114
|
private
|
|
67
115
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
def settings
|
|
117
|
+
attributes.merge(Vidibus::Pureftpd.settings)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def unique_login?
|
|
121
|
+
return unless login.present?
|
|
122
|
+
if User.find_by_login(login)
|
|
123
|
+
self.errors.add(:login, :taken)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def valid_directory?
|
|
128
|
+
return unless directory.present?
|
|
129
|
+
if !File.exist?(directory)
|
|
130
|
+
self.errors.add(:directory, :not_existent)
|
|
131
|
+
elsif !File.directory?(directory)
|
|
132
|
+
self.errors.add(:directory, :not_a_directory)
|
|
133
|
+
elsif !File.readable?(directory)
|
|
134
|
+
self.errors.add(:directory, :not_readable)
|
|
135
|
+
elsif !File.writable?(directory)
|
|
136
|
+
self.errors.add(:directory, :not_writable)
|
|
137
|
+
end
|
|
71
138
|
end
|
|
72
139
|
|
|
73
140
|
def update
|
|
74
141
|
if login_changed?
|
|
75
142
|
a = attributes
|
|
143
|
+
destroy
|
|
144
|
+
User.create(a)
|
|
76
145
|
end
|
|
77
146
|
if password_changed?
|
|
78
147
|
update_password
|
|
79
148
|
end
|
|
80
|
-
|
|
81
|
-
|
|
149
|
+
if changes.except(:login, :password).any?
|
|
150
|
+
update_attributes
|
|
151
|
+
end
|
|
82
152
|
end
|
|
83
153
|
|
|
84
154
|
def create
|
|
@@ -87,14 +157,22 @@ module Vidibus
|
|
|
87
157
|
end
|
|
88
158
|
|
|
89
159
|
def update_password
|
|
90
|
-
cmd = '
|
|
160
|
+
cmd = 'passwd %{login} -f %{password_file} -m' % settings
|
|
91
161
|
perform_with_password_input(cmd)
|
|
92
162
|
end
|
|
93
163
|
|
|
164
|
+
def update_attributes
|
|
165
|
+
cmd = 'usermod %{login} -d %{directory} -f %{password_file} -m' % settings
|
|
166
|
+
perform(cmd)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# TODO: Dry this up. But args << &Proc.new isn't possible.
|
|
94
170
|
def perform(cmd)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
171
|
+
if block_given?
|
|
172
|
+
Vidibus::Pureftpd.perform(cmd, &Proc.new)
|
|
173
|
+
else
|
|
174
|
+
Vidibus::Pureftpd.perform(cmd)
|
|
175
|
+
end
|
|
98
176
|
end
|
|
99
177
|
|
|
100
178
|
def perform_with_password_input(cmd)
|
|
@@ -103,7 +181,6 @@ module Vidibus
|
|
|
103
181
|
stdin.puts(password)
|
|
104
182
|
end
|
|
105
183
|
end
|
|
106
|
-
|
|
107
184
|
end
|
|
108
185
|
end
|
|
109
186
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vidibus-pureftpd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -11,6 +11,22 @@ bindir: bin
|
|
|
11
11
|
cert_chain: []
|
|
12
12
|
date: 2012-09-19 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: posix-spawn
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
14
30
|
- !ruby/object:Gem::Dependency
|
|
15
31
|
name: vidibus-core_extensions
|
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -27,6 +43,22 @@ dependencies:
|
|
|
27
43
|
- - ! '>='
|
|
28
44
|
- !ruby/object:Gem::Version
|
|
29
45
|
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: activemodel
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ! '>='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
30
62
|
- !ruby/object:Gem::Dependency
|
|
31
63
|
name: bundler
|
|
32
64
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -133,6 +165,8 @@ files:
|
|
|
133
165
|
- lib/vidibus/pureftpd/version.rb
|
|
134
166
|
- lib/vidibus/pureftpd.rb
|
|
135
167
|
- lib/vidibus-pureftpd.rb
|
|
168
|
+
- config/locales/de.yml
|
|
169
|
+
- config/locales/en.yml
|
|
136
170
|
- LICENSE
|
|
137
171
|
- README.md
|
|
138
172
|
- Rakefile
|
|
@@ -150,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
150
184
|
version: '0'
|
|
151
185
|
segments:
|
|
152
186
|
- 0
|
|
153
|
-
hash:
|
|
187
|
+
hash: 848917452145310950
|
|
154
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
189
|
none: false
|
|
156
190
|
requirements:
|