weeler 1.4.0 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +13 -3
- data/CHANGELOG.md +24 -0
- data/Rakefile +4 -0
- data/app/controllers/weeler/translations_controller.rb +13 -7
- data/app/views/weeler/translations/_translation.html.haml +2 -2
- data/app/views/weeler/translations/edit.html.haml +3 -3
- data/app/views/weeler/translations/new.html.haml +2 -2
- data/app/views/weeler/translations/usage_stats.html.haml +10 -4
- data/lib/generators/weeler/templates/migrations/create_weeler_locks.rb +14 -0
- data/lib/generators/weeler/templates/migrations/create_weeler_translation_stats.rb +11 -0
- data/lib/i18n/backend/weeler.rb +15 -6
- data/lib/i18n/backend/weeler/lock.rb +78 -0
- data/lib/i18n/backend/weeler/translation_stat.rb +25 -0
- data/lib/i18n/backend/weeler/usage_logger.rb +17 -1
- data/lib/weeler/action_controller/acts/restful.rb +1 -1
- data/lib/weeler/engine.rb +1 -1
- data/lib/weeler/version.rb +2 -2
- data/spec/.gitignore +21 -0
- data/spec/dummy/config/database.yml +2 -24
- data/spec/dummy/db/migrate/20160330161101_create_weeler_locks.rb +14 -0
- data/spec/dummy/db/migrate/20160330192005_create_weeler_translation_stats.rb +11 -0
- data/spec/dummy/db/schema.rb +31 -11
- data/spec/dummy/log/development.log +780 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/weeler/action_controller/acts/restful_spec.rb +1 -1
- data/spec/weeler/i18n/backend/weeler/lock_spec.rb +89 -0
- data/spec/weeler/i18n/backend/weeler/usage_logger_spec.rb +34 -0
- data/spec/weeler/i18n/backend/weeler_spec.rb +22 -0
- data/weeler.gemspec +2 -2
- metadata +18 -8
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21fd389096c8af80e13c46bf777233d15188fa71
|
4
|
+
data.tar.gz: 783c33428045e4d05b7e76fc10a991cafc3f9b2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39c2ceb91177c876eadeb18b9e03b86fc8367c33a48cc81f4b2e627741290816c5d8ecd8c20188c87e1f776e3e6df7f1f16e19f154007464e4200d9b25123ee1
|
7
|
+
data.tar.gz: e9602c77a0ea9738ae4eb0c8dd91c98bdfb228a594e9ead83d41b0bcbf4bb0a799f19fabeb8c2c479c0e07b0be4b148b0da8b19388d4e8bd47edfb8954f187e4
|
data/.travis.yml
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
language: ruby
|
2
|
+
before_install:
|
3
|
+
- rvm get head
|
4
|
+
services:
|
5
|
+
- postgresql
|
6
|
+
sudo: false
|
7
|
+
before_script:
|
8
|
+
- psql -c 'create database travis_ci_test;' -U postgres
|
2
9
|
rvm:
|
3
10
|
- "2.0.0"
|
4
11
|
- "2.1.0"
|
@@ -11,7 +18,10 @@ rvm:
|
|
11
18
|
- "2.2.0"
|
12
19
|
- "2.2.1"
|
13
20
|
- "2.2.2"
|
14
|
-
- "
|
15
|
-
- "
|
21
|
+
- "2.2.3"
|
22
|
+
- "2.2.4"
|
23
|
+
- "2.3.0"
|
16
24
|
# uncomment this line if your project needs to run something other than `rake`:
|
17
|
-
script:
|
25
|
+
script:
|
26
|
+
- RAILS_ENV=test bundle exec rake db:migrate --trace
|
27
|
+
- bundle exec rspec spec
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
## 1.5.1
|
2
|
+
|
3
|
+
* Fixed version number
|
4
|
+
|
5
|
+
### Contributors
|
6
|
+
|
7
|
+
* Artis Raugulis
|
8
|
+
|
9
|
+
## 1.4.0
|
10
|
+
|
11
|
+
* Added translation usage stats
|
12
|
+
* Fixed a transliteration bug
|
13
|
+
|
14
|
+
### Contributors
|
15
|
+
|
16
|
+
* Artis Raugulis
|
17
|
+
|
18
|
+
## 1.5.0
|
19
|
+
|
20
|
+
* Added Lock
|
21
|
+
* Added translation usage stats export
|
22
|
+
* Dropped jruby support
|
23
|
+
* Dropped rbx suport
|
24
|
+
|
1
25
|
## 1.4.0
|
2
26
|
|
3
27
|
* Added translation usage stats
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ module Weeler
|
|
20
20
|
|
21
21
|
if @translation.save
|
22
22
|
Settings.i18n_updated_at = Time.now
|
23
|
-
redirect_to
|
23
|
+
redirect_to ({ action: :edit, id: @translation }), flash: { success: "Translation saved." }
|
24
24
|
else
|
25
25
|
flash.now[:error] = "Errors in saving."
|
26
26
|
render :edit
|
@@ -33,7 +33,7 @@ module Weeler
|
|
33
33
|
if @translation.update_attributes(translation_params)
|
34
34
|
Settings.i18n_updated_at = Time.now
|
35
35
|
|
36
|
-
redirect_to
|
36
|
+
redirect_to ({ action: :edit, id: @translation }), flash: { success: "Translation updated." }
|
37
37
|
else
|
38
38
|
flash.now[:error] = "Errors in updating."
|
39
39
|
render :edit
|
@@ -46,13 +46,19 @@ module Weeler
|
|
46
46
|
|
47
47
|
Settings.i18n_updated_at = Time.now
|
48
48
|
|
49
|
-
redirect_to
|
49
|
+
redirect_to ({ action: :index }), flash: {success: "Translation succesfully removed."}
|
50
50
|
end
|
51
51
|
|
52
52
|
def usage_stats
|
53
53
|
@used_keys = []
|
54
|
-
|
55
|
-
|
54
|
+
if Settings.log_key_usage == 'dump'
|
55
|
+
I18n::Backend::Weeler::TranslationStat.all.each do |translation_stat|
|
56
|
+
@used_keys << [translation_stat.key, translation_stat.usage_count]
|
57
|
+
end
|
58
|
+
else
|
59
|
+
Weeler.i18n_cache.instance_variable_get(:@data).keys.each do |key|
|
60
|
+
@used_keys << [key, Weeler.i18n_cache.read(key)] if key.start_with?('usage_stats')
|
61
|
+
end
|
56
62
|
end
|
57
63
|
end
|
58
64
|
|
@@ -72,9 +78,9 @@ module Weeler
|
|
72
78
|
|
73
79
|
Settings.i18n_updated_at = Time.now
|
74
80
|
|
75
|
-
redirect_to
|
81
|
+
redirect_to ({ action: :index }), flash: {success: "Translations succesfully imported."}
|
76
82
|
else
|
77
|
-
redirect_to
|
83
|
+
redirect_to ({ action: :index }), flash: {success: "No file choosen"}
|
78
84
|
end
|
79
85
|
end
|
80
86
|
|
@@ -8,5 +8,5 @@
|
|
8
8
|
%td= translation.interpolations
|
9
9
|
%td.text-center= translation.created_at.strftime("%d/%m/%Y")
|
10
10
|
%td
|
11
|
-
= link_to 'Edit',
|
12
|
-
= link_to 'Remove',
|
11
|
+
= link_to 'Edit', { action: :edit, id: translation.id }, class: 'btn btn-default'
|
12
|
+
= link_to 'Remove', { action: :destroy, id: translation }, data: { confirm: 'Are you sure you want to remove?' }, method: :delete, class: 'btn btn-danger'
|
@@ -6,9 +6,9 @@
|
|
6
6
|
.edit_user.container
|
7
7
|
.row
|
8
8
|
.col-lg-12.col-md-12
|
9
|
-
= form_for @translation, {url:
|
9
|
+
= form_for @translation, {url: { action: :update, id: @translation }, html: {class: "form-horizontal", role: "form"}} do |f|
|
10
10
|
= render partial: "form", locals: {translation: @translation, f: f}
|
11
11
|
%p.text-center
|
12
12
|
%button.btn.btn-primary{type: "submit"} Save
|
13
|
-
|
14
|
-
= link_to 'Remove',
|
13
|
+
= link_to 'Cancel', { action: :index }, class: 'btn btn-link'
|
14
|
+
= link_to 'Remove', { action: :destroy, id: @translation}, :confirm => "Are you sure you want remove?", :method => :delete, :class => "btn btn-danger"
|
@@ -6,8 +6,8 @@
|
|
6
6
|
.edit_user.container
|
7
7
|
.row
|
8
8
|
.col-lg-12.col-md-12
|
9
|
-
= form_for @translation, {url:
|
9
|
+
= form_for @translation, {url: { action: :create }, html: {class: "form-horizontal", role: "form"}} do |f|
|
10
10
|
= render partial: "form", locals: {translation: @translation, f: f}
|
11
11
|
%p.text-center
|
12
12
|
%button.btn.btn-primary{type: "submit"} Save
|
13
|
-
|
13
|
+
= link_to 'Cancel', { action: :index }, class: 'btn btn-link'
|
@@ -22,7 +22,13 @@
|
|
22
22
|
%th text
|
23
23
|
%tbody
|
24
24
|
- @used_keys.each do |key, usage|
|
25
|
-
|
26
|
-
%
|
27
|
-
|
28
|
-
|
25
|
+
- begin
|
26
|
+
%tr
|
27
|
+
%td= usage
|
28
|
+
%td= key.to_s.gsub('usage_stats/en/', '')
|
29
|
+
%td= CGI::unescapeHTML(CGI::escapeHTML(t(key.to_s.gsub('usage_stats/en/', ''))))
|
30
|
+
- rescue => e
|
31
|
+
%tr
|
32
|
+
%td= "err #{usage}"
|
33
|
+
%td= key
|
34
|
+
%td= e.message
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateWeelerLocks < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :weeler_locks do |t|
|
4
|
+
t.string :name, :limit => 40
|
5
|
+
t.timestamps
|
6
|
+
end
|
7
|
+
add_index :weeler_locks, :name, :unique => true
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down
|
11
|
+
remove_index :weeler_locks, :column => :name
|
12
|
+
drop_table :weeler_locks
|
13
|
+
end
|
14
|
+
end
|
data/lib/i18n/backend/weeler.rb
CHANGED
@@ -5,6 +5,8 @@ require 'i18n/backend/weeler/html_checker'
|
|
5
5
|
require 'i18n/backend/weeler/exporter'
|
6
6
|
require 'i18n/backend/weeler/importer'
|
7
7
|
require 'i18n/backend/weeler/usage_logger'
|
8
|
+
require 'i18n/backend/weeler/translation_stat'
|
9
|
+
require 'i18n/backend/weeler/lock'
|
8
10
|
|
9
11
|
module I18n
|
10
12
|
module Backend
|
@@ -24,12 +26,14 @@ module I18n
|
|
24
26
|
|
25
27
|
PLURAL_KEYS = ["zero", "one", "other"]
|
26
28
|
|
27
|
-
autoload :HtmlChecker,
|
28
|
-
autoload :Translation,
|
29
|
-
autoload :Translation,
|
30
|
-
autoload :Exporter,
|
31
|
-
autoload :Importer,
|
32
|
-
autoload :UsageLogger,
|
29
|
+
autoload :HtmlChecker, 'i18n/backend/weeler/html_checker'
|
30
|
+
autoload :Translation, 'i18n/backend/weeler/dedupe'
|
31
|
+
autoload :Translation, 'i18n/backend/weeler/translation'
|
32
|
+
autoload :Exporter, 'i18n/backend/weeler/exporter'
|
33
|
+
autoload :Importer, 'i18n/backend/weeler/importer'
|
34
|
+
autoload :UsageLogger, 'i18n/backend/weeler/usage_logger'
|
35
|
+
autoload :TranslationStat, 'i18n/backend/weeler/translation_stat'
|
36
|
+
autoload :Lock, 'i18n/backend/weeler/lock'
|
33
37
|
|
34
38
|
module Implementation
|
35
39
|
include Base, Flatten
|
@@ -72,9 +76,14 @@ module I18n
|
|
72
76
|
|
73
77
|
# log locale/key usage for statistics
|
74
78
|
if Settings.log_key_usage == 'true'
|
79
|
+
i18n_cache.delete([:dump_usage_stats, Process.pid])
|
75
80
|
log_key_usage(locale, key)
|
76
81
|
end
|
77
82
|
|
83
|
+
if Settings.log_key_usage == 'dump'
|
84
|
+
dump_key_usage
|
85
|
+
end
|
86
|
+
|
78
87
|
return nil if i18n_cache.read([:missing, [locale, key]])
|
79
88
|
|
80
89
|
keys = expand_keys key
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module Backend
|
5
|
+
# Weeler model used to store locks
|
6
|
+
#
|
7
|
+
# This model expects a table like the following to be already set up in
|
8
|
+
# your the database:
|
9
|
+
#
|
10
|
+
# def self.up
|
11
|
+
# create_table :weeler_locks do |t|
|
12
|
+
# t.string :name, :limit => 40
|
13
|
+
# t.timestamps
|
14
|
+
# end
|
15
|
+
# add_index :weeler_locks, :name, :unique => true
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def self.down
|
19
|
+
# remove_index :weeler_locks, :column => :name
|
20
|
+
# drop_table :weeler_locks
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
class Weeler
|
24
|
+
class Lock < ActiveRecord::Base
|
25
|
+
|
26
|
+
self.table_name = 'weeler_locks'
|
27
|
+
|
28
|
+
def self.acquire(name)
|
29
|
+
already_acquired = definitely_acquired?(name)
|
30
|
+
|
31
|
+
if already_acquired
|
32
|
+
yield
|
33
|
+
else
|
34
|
+
begin
|
35
|
+
create(:name => name) unless find_by(name: name)
|
36
|
+
rescue ActiveRecord::StatementInvalid
|
37
|
+
# concurrent create is okay
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
result = nil
|
42
|
+
transaction do
|
43
|
+
self.lock(true).find_by(name: name) # this is the call that will block
|
44
|
+
acquired_lock(name)
|
45
|
+
result = yield
|
46
|
+
end
|
47
|
+
|
48
|
+
result
|
49
|
+
ensure
|
50
|
+
maybe_released_lock(name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# if true, the lock is acquired
|
56
|
+
# if false, the lock might still be acquired, because we were in another db transaction
|
57
|
+
def self.definitely_acquired?(name)
|
58
|
+
!!Thread.current[:definitely_acquired_locks] and Thread.current[:definitely_acquired_locks].has_key?(name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.acquired_lock(name)
|
62
|
+
logger.debug("Acquired lock '#{name}'")
|
63
|
+
Thread.current[:definitely_acquired_locks] ||= {}
|
64
|
+
Thread.current[:definitely_acquired_locks][name] = true
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.maybe_released_lock(name)
|
68
|
+
logger.debug("Released lock '#{name}' (if we are not in a bigger transaction)")
|
69
|
+
Thread.current[:definitely_acquired_locks] ||= {}
|
70
|
+
Thread.current[:definitely_acquired_locks].delete(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
private_class_method :acquired_lock, :maybe_released_lock
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module Backend
|
5
|
+
# Weeler model used to store translation key usage statistics
|
6
|
+
#
|
7
|
+
# This model expects a table like the following to be already set up in
|
8
|
+
# your the database:
|
9
|
+
#
|
10
|
+
# create_table :weeler_translation_stats do |t|
|
11
|
+
# t.string :key
|
12
|
+
# t.integer :usage_count, default: 0
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
class Weeler
|
16
|
+
class TranslationStat < ::ActiveRecord::Base
|
17
|
+
|
18
|
+
self.table_name = 'weeler_translation_stats'
|
19
|
+
|
20
|
+
validates :key, uniqueness: true
|
21
|
+
validates :key, :usage_count, presence: true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -11,7 +11,23 @@ module I18n
|
|
11
11
|
value = existing.present? ? (existing.to_i + 1) : 1
|
12
12
|
i18n_cache.write [:usage_stats, [locale, key]], value
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
|
+
def dump_key_usage
|
16
|
+
already_dumped = i18n_cache.read([:dump_usage_stats, Process.pid]).present?
|
17
|
+
unless already_dumped
|
18
|
+
Weeler::Lock.acquire('usage_stats_dump') do
|
19
|
+
i18n_cache.instance_variable_get(:@data).keys.each do |key|
|
20
|
+
if key.start_with?('usage_stats')
|
21
|
+
translation_stat = TranslationStat.find_or_create_by(key: key.gsub('usage_stats/en/', ''))
|
22
|
+
translation_stat.usage_count += i18n_cache.read(key).to_i
|
23
|
+
translation_stat.save!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
i18n_cache.write [:dump_usage_stats, Process.pid], Process.pid
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
15
31
|
Weeler.send(:include, UsageLogger)
|
16
32
|
end
|
17
33
|
end
|
@@ -118,7 +118,7 @@ module Weeler
|
|
118
118
|
permited_params.call(params)
|
119
119
|
elsif permited_params.blank?
|
120
120
|
warning_suggestion = params[parameterized_name.to_sym].is_a?(Hash) ? params[parameterized_name.to_sym].keys.map{ |k| k.to_sym } : "permit_params:"
|
121
|
-
warn "[UNPERMITED PARAMS] To
|
121
|
+
warn "[UNPERMITED PARAMS] To permit #{params[parameterized_name.to_sym].inspect} params, add 'permit_params: #{warning_suggestion}' option to 'acts_as_restful'"
|
122
122
|
else
|
123
123
|
params.require(parameterized_name.to_sym).permit(permited_params)
|
124
124
|
end
|
data/lib/weeler/engine.rb
CHANGED
@@ -10,7 +10,7 @@ module Weeler
|
|
10
10
|
config.weeler = Weeler
|
11
11
|
|
12
12
|
config.i18n.available_locales = [:en] unless config.i18n.available_locales.present?
|
13
|
-
|
13
|
+
config.active_record.raise_in_transactional_callbacks = true
|
14
14
|
config.assets.precompile += ["weeler/init.js", "weeler/init.css"]
|
15
15
|
|
16
16
|
# Load extend Rails classes
|
data/lib/weeler/version.rb
CHANGED
data/spec/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
.coveralls.yml
|
19
|
+
.DS_Store
|
20
|
+
spec/dummy/log/test.log
|
21
|
+
rspec.html
|
@@ -1,25 +1,3 @@
|
|
1
|
-
# SQLite version 3.x
|
2
|
-
# gem install sqlite3
|
3
|
-
#
|
4
|
-
# Ensure the SQLite 3 gem is defined in your Gemfile
|
5
|
-
# gem 'sqlite3'
|
6
|
-
development:
|
7
|
-
adapter: sqlite3
|
8
|
-
database: db/development.sqlite3
|
9
|
-
pool: 5
|
10
|
-
timeout: 5000
|
11
|
-
|
12
|
-
# Warning: The database defined as "test" will be erased and
|
13
|
-
# re-generated from your development database when you run "rake".
|
14
|
-
# Do not set this db to the same as development or production.
|
15
1
|
test:
|
16
|
-
adapter:
|
17
|
-
database:
|
18
|
-
pool: 5
|
19
|
-
timeout: 5000
|
20
|
-
|
21
|
-
production:
|
22
|
-
adapter: sqlite3
|
23
|
-
database: db/production.sqlite3
|
24
|
-
pool: 5
|
25
|
-
timeout: 5000
|
2
|
+
adapter: postgresql
|
3
|
+
database: travis_ci_test
|