phrasing 4.0.0rc3 → 4.0.0rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -8
- data/CHANGELOG.md +8 -0
- data/Gemfile +1 -2
- data/app/assets/javascripts/phrasing.js +153 -0
- data/app/assets/stylesheets/phrasing.css.scss +5 -5
- data/app/controllers/phrasing_phrases_controller.rb +40 -44
- data/app/helpers/inline_helper.rb +27 -30
- data/app/views/phrasing_phrases/edit.html.haml +2 -2
- data/lib/phrasing.rb +4 -4
- data/lib/phrasing/version.rb +2 -2
- data/phrasing.gemspec +1 -1
- data/spec/controllers/phrasing_phrases_controller.rb +34 -0
- data/spec/dummy/app/views/site/index.html.erb +3 -1
- data/spec/lib/phrasing_serializer_spec.rb +13 -20
- data/spec/spec_helper.rb +1 -0
- metadata +5 -4
- data/app/assets/javascripts/phrasing.js.erb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ec5cf82a1f8789b4f8c7f25f1fd93ec9c370ded
|
4
|
+
data.tar.gz: 9d92db89baa70f2adf1fd122ced6cb949ee4e44e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ab0220b8ad7cf33ec522ba8155d2aeb65ffed693bb2c4eed8a8ce736efc0ae8b96d859618829092b6c3a6984dd4a13affe8cc9935fafc199f08ea3f9afd717b
|
7
|
+
data.tar.gz: 61f3907edde2443d5b50f050f01edfec95f258614601aebc3d77e02db8795868a6067b4e17e7ab6318cf7ce9436dd666271e80edcf7812b9bf470261d40b9197
|
data/.travis.yml
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
env:
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
|
3
|
+
matrix:
|
4
|
+
include:
|
5
|
+
- rvm: 1.9.3
|
6
|
+
env: "RAILS_VERSION=3.2.0"
|
7
|
+
- rvm: 2.0.0
|
8
|
+
env: "RAILS_VERSION=4.0.0"
|
9
|
+
- rvm: 2.1.2
|
10
|
+
env: "RAILS_VERSION=4.1.0"
|
11
|
+
- rvm: 2.2.0
|
12
|
+
env: "RAILS_VERSION=4.2.0"
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Phrasing Change Log
|
2
2
|
|
3
|
+
## 3.2.9 (January 7th, 2015)
|
4
|
+
|
5
|
+
Require only haml, not haml-rails.
|
6
|
+
|
7
|
+
## 3.2.8 (January 6th, 2015)
|
8
|
+
|
9
|
+
Fix confirm dialogs when deleting phrases and phrase versions.
|
10
|
+
|
3
11
|
## 3.2.7 (October 3rd, 2014)
|
4
12
|
|
5
13
|
Add a config option to set a parent controller to Phrasing Engine Controllers.
|
data/Gemfile
CHANGED
@@ -8,10 +8,9 @@ rails_version = ENV["RAILS_VERSION"] || "4.1.4"
|
|
8
8
|
gem "rails", "~> #{rails_version}"
|
9
9
|
|
10
10
|
gem 'factory_girl_rails'
|
11
|
-
gem 'haml-rails'
|
12
11
|
gem 'sqlite3'
|
13
12
|
gem 'rspec-rails'
|
14
13
|
gem 'capybara', '~> 2.3.0'
|
15
14
|
gem 'jasmine-rails'
|
16
15
|
gem 'pry-rails'
|
17
|
-
gem 'sass', '3.4.0'
|
16
|
+
gem 'sass', '3.4.0'
|
@@ -0,0 +1,153 @@
|
|
1
|
+
//= require editor
|
2
|
+
|
3
|
+
var Phrasing = {
|
4
|
+
Bus : $({}),
|
5
|
+
EDIT_MODE_KEY : 'editing-mode'
|
6
|
+
};
|
7
|
+
|
8
|
+
function StatusBubbleWidget(options){
|
9
|
+
this.$statusText = options.$statusText;
|
10
|
+
this.$statusIndicator = options.$statusIndicator;
|
11
|
+
this.$editModeChechbox = options.$editModeChechbox;
|
12
|
+
this._init();
|
13
|
+
}
|
14
|
+
|
15
|
+
StatusBubbleWidget.prototype = {
|
16
|
+
_init : function(){
|
17
|
+
this.$editModeChechbox.on('change', function(){
|
18
|
+
if(this.checked){
|
19
|
+
Phrasing.Bus.trigger('phrasing:edit-mode:on');
|
20
|
+
}else{
|
21
|
+
Phrasing.Bus.trigger('phrasing:edit-mode:off');
|
22
|
+
}
|
23
|
+
});
|
24
|
+
},
|
25
|
+
|
26
|
+
_alterStatus : function(text, color){
|
27
|
+
this.$statusText.text(text);
|
28
|
+
this.$statusIndicator.css('background-color', color);
|
29
|
+
},
|
30
|
+
|
31
|
+
saving : function(){
|
32
|
+
this._alterStatus('Saving', 'orange');
|
33
|
+
},
|
34
|
+
|
35
|
+
saved : function(){
|
36
|
+
this._alterStatus('Saved', '#56AE45');
|
37
|
+
},
|
38
|
+
|
39
|
+
error : function(){
|
40
|
+
this._alterStatus('Error', 'red');
|
41
|
+
}
|
42
|
+
};
|
43
|
+
|
44
|
+
var phrasing_setup = function(){
|
45
|
+
var statusBubbleWidget = new StatusBubbleWidget({
|
46
|
+
$statusText : $('#phrasing-edit-mode-bubble #phrasing-saved-status-headline p'),
|
47
|
+
$statusIndicator : $('#phrasing-saved-status-indicator-circle'),
|
48
|
+
$editModeChechbox : $('#edit-mode-onoffswitch')
|
49
|
+
});
|
50
|
+
|
51
|
+
Phrasing.Bus.on('phrasing:edit-mode:on', function(){
|
52
|
+
$('.phrasable').addClass("phrasable-on").attr("contenteditable", 'true');
|
53
|
+
localStorage.setItem(Phrasing.EDIT_MODE_KEY, 'true');
|
54
|
+
});
|
55
|
+
|
56
|
+
Phrasing.Bus.on('phrasing:edit-mode:off', function(){
|
57
|
+
$('.phrasable').removeClass("phrasable-on").attr("contenteditable", "false");
|
58
|
+
localStorage.setItem(Phrasing.EDIT_MODE_KEY, "false");
|
59
|
+
});
|
60
|
+
|
61
|
+
// Initialize the editing bubble
|
62
|
+
editor.init();
|
63
|
+
|
64
|
+
// Making sure to send csrf token from layout file.
|
65
|
+
$(document).ajaxSend(function(e, xhr, options) {
|
66
|
+
var token = $("meta[name='csrf-token']").attr("content");
|
67
|
+
xhr.setRequestHeader("X-CSRF-Token", token);
|
68
|
+
});
|
69
|
+
|
70
|
+
// Hash size function
|
71
|
+
Object.size = function(obj){
|
72
|
+
var size = 0, key;
|
73
|
+
for (key in obj) { if (obj.hasOwnProperty(key)) size++; }
|
74
|
+
return size;
|
75
|
+
};
|
76
|
+
|
77
|
+
// Trigger AJAX on textchange
|
78
|
+
var userTriggeredPhrasingDOMChange = true;
|
79
|
+
var timer = {};
|
80
|
+
var timer_status = {};
|
81
|
+
|
82
|
+
$('.phrasable').on('DOMNodeInserted DOMNodeRemoved DOMCharacterDataModified', function(e){
|
83
|
+
if (userTriggeredPhrasingDOMChange === false){
|
84
|
+
return;
|
85
|
+
}
|
86
|
+
|
87
|
+
statusBubbleWidget.saving();
|
88
|
+
|
89
|
+
var record = this;
|
90
|
+
|
91
|
+
clearTimeout(timer[$(record).data("url")]);
|
92
|
+
timer_status[$(record).data("url")] = 0;
|
93
|
+
|
94
|
+
timer[$(record).data("url")] = setTimeout(function(){
|
95
|
+
savePhraseViaAjax(record);
|
96
|
+
delete timer_status[$(record).data("url")];
|
97
|
+
},2500);
|
98
|
+
|
99
|
+
timer_status[$(record).data("url")] = 1;
|
100
|
+
});
|
101
|
+
|
102
|
+
// AJAX Request
|
103
|
+
function savePhraseViaAjax(record){
|
104
|
+
|
105
|
+
var url = $(record).data("url");
|
106
|
+
|
107
|
+
var content = record.innerHTML;
|
108
|
+
|
109
|
+
if(content.length === 0){
|
110
|
+
content = "Empty";
|
111
|
+
}
|
112
|
+
|
113
|
+
$.ajax({
|
114
|
+
type: "PUT",
|
115
|
+
url: url,
|
116
|
+
data: { new_value: content },
|
117
|
+
success: function(e){
|
118
|
+
userTriggeredPhrasingDOMChange = false;
|
119
|
+
if(content === "Empty"){
|
120
|
+
$('span.phrasable[data-url="'+ url +'"]').html(content);
|
121
|
+
}else{
|
122
|
+
// Not to lose the cursor on the current contenteditable element
|
123
|
+
$('span.phrasable[data-url="'+ url +'"]').not(record).html(content);
|
124
|
+
}
|
125
|
+
userTriggeredPhrasingDOMChange = true;
|
126
|
+
|
127
|
+
if (Object.size(timer_status) === 0){
|
128
|
+
statusBubbleWidget.saved();
|
129
|
+
}
|
130
|
+
},
|
131
|
+
error: function(e){
|
132
|
+
statusBubbleWidget.error();
|
133
|
+
}
|
134
|
+
});
|
135
|
+
}
|
136
|
+
|
137
|
+
if(localStorage.getItem(Phrasing.EDIT_MODE_KEY) === undefined){
|
138
|
+
localStorage.setItem(Phrasing.EDIT_MODE_KEY, 'true');
|
139
|
+
}
|
140
|
+
|
141
|
+
if(localStorage.getItem(Phrasing.EDIT_MODE_KEY) == 'true'){
|
142
|
+
$('#edit-mode-onoffswitch').prop('checked', true).change();
|
143
|
+
}else{
|
144
|
+
$('#edit-mode-onoffswitch').prop('checked', false).change();
|
145
|
+
}
|
146
|
+
|
147
|
+
};
|
148
|
+
|
149
|
+
$(document).ready(phrasing_setup);
|
150
|
+
|
151
|
+
$(document).on('page:before-change', function() {
|
152
|
+
Phrasing.Bus.off();
|
153
|
+
});
|
@@ -8,14 +8,14 @@
|
|
8
8
|
overflow: hidden;
|
9
9
|
}*/
|
10
10
|
|
11
|
-
.
|
11
|
+
.phrasable.phrasable-on{
|
12
12
|
border-bottom: 1px dashed black;
|
13
|
-
|
13
|
+
|
14
14
|
a{
|
15
15
|
pointer-events: none;
|
16
16
|
cursor: default;
|
17
17
|
}
|
18
|
-
|
18
|
+
|
19
19
|
&:focus{
|
20
20
|
outline: none;
|
21
21
|
}
|
@@ -172,7 +172,7 @@ span.no-overflow {
|
|
172
172
|
margin-right: 0px;
|
173
173
|
opacity: 0;
|
174
174
|
}
|
175
|
-
|
175
|
+
|
176
176
|
input{
|
177
177
|
border-left: 2px solid transparent;
|
178
178
|
padding-right: 5px;
|
@@ -248,4 +248,4 @@ span.no-overflow {
|
|
248
248
|
|
249
249
|
.url {
|
250
250
|
-webkit-font-smoothing: antialiased;
|
251
|
-
}
|
251
|
+
}
|
@@ -8,8 +8,11 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
8
8
|
|
9
9
|
before_filter :authorize_editor
|
10
10
|
|
11
|
-
def import_export
|
12
|
-
|
11
|
+
def import_export
|
12
|
+
end
|
13
|
+
|
14
|
+
def help
|
15
|
+
end
|
13
16
|
|
14
17
|
def index
|
15
18
|
params[:locale] ||= I18n.default_locale
|
@@ -22,33 +25,29 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
22
25
|
end
|
23
26
|
|
24
27
|
def update
|
25
|
-
|
26
|
-
xhr_phrase_update
|
27
|
-
else
|
28
|
-
phrase_update
|
29
|
-
end
|
28
|
+
request.xhr? ? xhr_phrase_update : phrase_update
|
30
29
|
end
|
31
30
|
|
32
31
|
def download
|
33
32
|
app_name = Rails.application.class.to_s.split("::").first
|
34
33
|
app_env = Rails.env
|
35
|
-
|
34
|
+
time = Time.now.strftime('%Y_%m_%d_%H_%M_%S')
|
35
|
+
filename = "phrasing_phrases_#{app_name}_#{app_env}_#{time}.yml"
|
36
36
|
send_data Phrasing::Serializer.export_yaml, filename: filename
|
37
37
|
end
|
38
38
|
|
39
39
|
def upload
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
render action: 'import_export', status: 400
|
40
|
+
number_of_changes = Phrasing::Serializer.import_yaml(params["file"].tempfile)
|
41
|
+
redirect_to phrasing_phrases_path, notice: "YAML file uploaded successfully! Number of phrases changed: #{number_of_changes}."
|
42
|
+
rescue => e
|
43
|
+
message = if params[:file].nil?
|
44
|
+
'Please choose a file.'
|
45
|
+
else
|
46
|
+
'Please upload a valid YAML file.'
|
47
|
+
end
|
48
|
+
|
49
|
+
flash[:alert] = "There was an error processing your upload! #{message}"
|
50
|
+
render action: 'import_export', status: 400
|
52
51
|
end
|
53
52
|
|
54
53
|
def destroy
|
@@ -59,33 +58,30 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
59
58
|
|
60
59
|
private
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
def authorize_editor
|
62
|
+
redirect_to root_path unless can_edit_phrases?
|
63
|
+
end
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
class_object = klass.classify.constantize
|
71
|
-
@object = class_object.where(id: params[:id]).first
|
72
|
-
@object.send("#{attribute}=",params[:new_value])
|
73
|
-
@object.save!
|
74
|
-
render json: @object
|
75
|
-
else
|
76
|
-
render status: 403, text: "Attribute not whitelisted!"
|
77
|
-
end
|
78
|
-
|
79
|
-
rescue ActiveRecord::RecordInvalid => e
|
80
|
-
render status: 403, text: e
|
81
|
-
end
|
65
|
+
def xhr_phrase_update
|
66
|
+
klass, attribute = params[:klass], params[:attribute]
|
67
|
+
|
68
|
+
return render status: 403, text: 'Phrase not whitelisted' unless Phrasing.whitelisted?(klass, attribute)
|
82
69
|
|
83
|
-
|
84
|
-
phrase = PhrasingPhrase.find(params[:id])
|
85
|
-
phrase.value = params[:phrasing_phrase][:value]
|
86
|
-
phrase.save!
|
70
|
+
record = klass.classify.constantize.find(params[:id])
|
87
71
|
|
88
|
-
|
72
|
+
if record.update(attribute => params[:new_value])
|
73
|
+
render json: record
|
74
|
+
else
|
75
|
+
render status: 403, json: record.error.full_messages
|
89
76
|
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def phrase_update
|
80
|
+
phrase = PhrasingPhrase.find(params[:id])
|
81
|
+
phrase.value = params[:phrasing_phrase][:value]
|
82
|
+
phrase.save!
|
83
|
+
|
84
|
+
redirect_to phrasing_phrases_path, notice: "#{phrase.key} updated!"
|
85
|
+
end
|
90
86
|
|
91
87
|
end
|
@@ -1,14 +1,13 @@
|
|
1
1
|
module InlineHelper
|
2
|
-
# Normal phrase
|
3
|
-
# phrase("headline", url: www.infinum.co/yabadaba, inverse: true, scope: "models.errors")
|
4
|
-
|
5
|
-
# Data model phrase
|
6
|
-
# phrase(record, :title, inverse: true, class: phrase-record-title)
|
7
2
|
|
3
|
+
# Normal phrase
|
4
|
+
# phrase("headline", url: www.infinum.co/yabadaba, inverse: true, scope: "models.errors")
|
5
|
+
# Data model phrase
|
6
|
+
# phrase(record, :title, inverse: true, class: phrase-record-title)
|
8
7
|
def phrase(*args)
|
9
|
-
if args[0].class
|
10
|
-
key, options = args[0].to_s, args[1]
|
11
|
-
|
8
|
+
if args[0].class.in? [String, Symbol]
|
9
|
+
key, options = args[0].to_s, (args[1] || {})
|
10
|
+
inline(extract_record(key, options), :value, options)
|
12
11
|
else
|
13
12
|
record, attribute, options = args[0], args[1], args[2]
|
14
13
|
inline(record, attribute, options || {})
|
@@ -17,33 +16,31 @@ module InlineHelper
|
|
17
16
|
|
18
17
|
private
|
19
18
|
|
20
|
-
|
21
|
-
|
19
|
+
def inline(record, attribute, options = {})
|
20
|
+
return uneditable_phrase(record, attribute) unless can_edit_phrases?
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
klass = 'phrasable'
|
23
|
+
klass += ' inverse' if options[:inverse]
|
24
|
+
klass += options[:class] if options[:class]
|
26
25
|
|
27
|
-
|
26
|
+
url = phrasing_polymorphic_url(record, attribute)
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
28
|
+
content_tag(:span, class: klass, contenteditable: true, spellcheck: false, 'data-url' => url) do
|
29
|
+
(record.send(attribute) || record.try(:key)).to_s.html_safe
|
32
30
|
end
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
33
|
+
def extract_record(key, options = {})
|
34
|
+
key = options[:scope] ? "#{options[:scope]}.#{key}" : key
|
35
|
+
PhrasingPhrase.find_phrase(key)
|
36
|
+
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
38
|
+
def uneditable_phrase(record, attribute)
|
39
|
+
record.public_send(attribute).to_s.html_safe
|
40
|
+
end
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
def phrasing_polymorphic_url(record, attribute)
|
43
|
+
phrasing_phrase_path(klass: record.class.to_s, id: record.id, attribute: attribute)
|
44
|
+
end
|
48
45
|
|
49
|
-
end
|
46
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
.edit
|
2
2
|
%h2= @phrasing_phrase.key
|
3
|
-
= link_to "Delete Phrase", phrasing_phrase_path(@phrasing_phrase), class: "btn btn-danger delete-phrase", method: :delete,
|
3
|
+
= link_to "Delete Phrase", phrasing_phrase_path(@phrasing_phrase), class: "btn btn-danger delete-phrase", method: :delete, data: { confirm: 'Are you sure?' }
|
4
4
|
= form_for @phrasing_phrase, url: { action: "update" } do |f|
|
5
5
|
= f.text_area :value, rows: 12
|
6
6
|
= f.submit "Update", class: "btn btn-success submit-edit-phrase"
|
@@ -20,4 +20,4 @@
|
|
20
20
|
%td.phrasing-version-value= version.value.html_safe
|
21
21
|
%td.phrasing-version-created_at= version.created_at.strftime("%d-%m-%Y %H:%M:%S")
|
22
22
|
%td= link_to "Revert", phrasing_phrase_path(@phrasing_phrase.id, phrasing_phrase: {value: version.value}), class: "btn btn-success", method: :put
|
23
|
-
%td= link_to "Delete", phrasing_phrase_version_path(version.id), class: "btn btn-danger", method: :delete,
|
23
|
+
%td= link_to "Delete", phrasing_phrase_version_path(version.id), class: "btn btn-danger", method: :delete, data: { confirm: 'Are you sure?' }
|
data/lib/phrasing.rb
CHANGED
@@ -3,7 +3,7 @@ require 'phrasing/serializer'
|
|
3
3
|
require 'phrasing/rails/engine'
|
4
4
|
require 'jquery-rails'
|
5
5
|
require 'jquery-cookie-rails'
|
6
|
-
require 'haml
|
6
|
+
require 'haml'
|
7
7
|
|
8
8
|
module Phrasing
|
9
9
|
mattr_accessor :allow_update_on_all_models_and_attributes
|
@@ -33,7 +33,7 @@ module Phrasing
|
|
33
33
|
@@whitelist = whitelist
|
34
34
|
end
|
35
35
|
|
36
|
-
def self.
|
37
|
-
allow_update_on_all_models_and_attributes == true
|
36
|
+
def self.whitelisted?(klass, attribute)
|
37
|
+
allow_update_on_all_models_and_attributes == true || whitelist.include?("#{klass}.#{attribute}")
|
38
38
|
end
|
39
|
-
end
|
39
|
+
end
|
data/lib/phrasing/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Phrasing
|
2
|
-
VERSION = "4.0.
|
3
|
-
end
|
2
|
+
VERSION = "4.0.0rc4"
|
3
|
+
end
|
data/phrasing.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.add_dependency "rails", ">= 3.2.0"
|
20
20
|
s.add_dependency "railties", ">= 3.2"
|
21
|
-
s.add_dependency "haml
|
21
|
+
s.add_dependency "haml"
|
22
22
|
s.add_dependency "jquery-rails"
|
23
23
|
s.add_dependency "jquery-cookie-rails"
|
24
24
|
s.add_dependency "sass"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PhrasingPhrasesController do
|
4
|
+
describe 'GET #new' do
|
5
|
+
it 'renders index template' do
|
6
|
+
get :index
|
7
|
+
expect(response).to render_template(:index)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:phrase) { FactoryGirl.create(:phrasing_phrase, value: 'old_value') }
|
11
|
+
|
12
|
+
it 'updates phrase' do
|
13
|
+
expect do
|
14
|
+
xhr(:put, :update, klass: 'PhrasingPhrase',
|
15
|
+
attribute: 'value',
|
16
|
+
id: phrase.id,
|
17
|
+
new_value: 'new_value')
|
18
|
+
end.to change { phrase.reload.value }.from('old_value').to('new_value')
|
19
|
+
|
20
|
+
expect(response.code).to eq('200')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'updates phrase' do
|
24
|
+
expect do
|
25
|
+
xhr(:put, :update, klass: 'PhrasingPhrase',
|
26
|
+
attribute: 'locale',
|
27
|
+
id: phrase.id,
|
28
|
+
new_value: 'de')
|
29
|
+
end.to_not change { phrase.reload.locale }
|
30
|
+
|
31
|
+
expect(response.code).to eq('403')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -6,6 +6,8 @@
|
|
6
6
|
|
7
7
|
<div><%= phrase('test', scope: 'models.errors') %></div>
|
8
8
|
|
9
|
+
<div><%= phrase('test', scope: 'models.errors') %></div>
|
10
|
+
|
9
11
|
<div><%= phrase 'test', scope: :site %></div>
|
10
12
|
|
11
|
-
<div><%= distance_of_time_in_words(Time.now, Time.now) %></div>
|
13
|
+
<div><%= distance_of_time_in_words(Time.now, Time.now) %></div>
|
@@ -2,7 +2,6 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Phrasing::Serializer do
|
5
|
-
|
6
5
|
let(:english_phrases) do
|
7
6
|
PhrasingPhrase.where(locale: :en)
|
8
7
|
end
|
@@ -12,15 +11,14 @@ describe Phrasing::Serializer do
|
|
12
11
|
end
|
13
12
|
|
14
13
|
describe 'flatten_the_hash' do
|
15
|
-
|
16
14
|
it 'with simple data' do
|
17
|
-
hash = { "site.intro" => "Hello", "site.outro" => "KthnxBye", "site.overture" => "The Butler"}
|
15
|
+
hash = { "site.intro" => "Hello", "site.outro" => "KthnxBye", "site.overture" => "The Butler" }
|
18
16
|
new_hash = Phrasing::Serializer.flatten_the_hash(hash)
|
19
17
|
expect(new_hash).to eq(hash)
|
20
18
|
end
|
21
19
|
|
22
20
|
it 'with nested data' do
|
23
|
-
hash = { "site.text" => {"intro" => "Hello", "outro" => "KthnxBye"}}
|
21
|
+
hash = { "site.text" => { "intro" => "Hello", "outro" => "KthnxBye" } }
|
24
22
|
new_hash = Phrasing::Serializer.flatten_the_hash(hash)
|
25
23
|
|
26
24
|
predicted_new_hash = { "site.text.intro" => "Hello", "site.text.outro" => "KthnxBye" }
|
@@ -28,17 +26,24 @@ describe Phrasing::Serializer do
|
|
28
26
|
end
|
29
27
|
|
30
28
|
it 'with deeply nested data' do
|
31
|
-
hash = { "site" => {"text" => {"intro" => "Hello",
|
29
|
+
hash = { "site" => { "text" => { "intro" => "Hello",
|
30
|
+
"outro" => "KthnxBye",
|
31
|
+
"overture" => { "intro" => "Overture intro",
|
32
|
+
"outro" => "Overture outro" }
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
32
36
|
new_hash = Phrasing::Serializer.flatten_the_hash(hash)
|
33
37
|
|
34
|
-
predicted_new_hash = { "site.text.intro" => "Hello",
|
38
|
+
predicted_new_hash = { "site.text.intro" => "Hello",
|
39
|
+
"site.text.outro" => "KthnxBye",
|
40
|
+
"site.text.overture.intro" => "Overture intro",
|
41
|
+
"site.text.overture.outro" => "Overture outro" }
|
35
42
|
expect(new_hash).to eq(predicted_new_hash)
|
36
43
|
end
|
37
|
-
|
38
44
|
end
|
39
45
|
|
40
46
|
context 'import_yaml' do
|
41
|
-
|
42
47
|
it 'simply formatted phrases' do
|
43
48
|
yaml = <<-YAML
|
44
49
|
en:
|
@@ -84,7 +89,6 @@ describe Phrasing::Serializer do
|
|
84
89
|
expect(english_phrases.where(key: "site.text.overture.outro").first.value).to eq("Overture outro")
|
85
90
|
end
|
86
91
|
|
87
|
-
|
88
92
|
it 'overrides old values' do
|
89
93
|
FactoryGirl.create(:phrasing_phrase, key: "site.intro", value: "Go Home")
|
90
94
|
FactoryGirl.create(:phrasing_phrase, key: "site.outro", value: "Kthnx Bye")
|
@@ -104,10 +108,8 @@ describe Phrasing::Serializer do
|
|
104
108
|
|
105
109
|
expect(number_of_changes).to eq(1)
|
106
110
|
end
|
107
|
-
|
108
111
|
end
|
109
112
|
|
110
|
-
|
111
113
|
context 'imports and exports' do
|
112
114
|
it 'without changing, should return 0 number of changes' do
|
113
115
|
FactoryGirl.create(:phrasing_phrase, key: "site.intro", value: "Go Home")
|
@@ -126,7 +128,6 @@ describe Phrasing::Serializer do
|
|
126
128
|
end
|
127
129
|
end
|
128
130
|
|
129
|
-
|
130
131
|
context 'export_yaml' do
|
131
132
|
it 'flattened phrases' do
|
132
133
|
FactoryGirl.create(:phrasing_phrase, key: "site.intro", value: "Go Home")
|
@@ -153,13 +154,5 @@ describe Phrasing::Serializer do
|
|
153
154
|
expect(yaml).to match(/en:\s*\n\s*site.intro:\sHello/)
|
154
155
|
expect(yaml).to match(/fr:\s*\n\s*site.intro:\sBonjour/)
|
155
156
|
end
|
156
|
-
|
157
157
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
161
158
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phrasing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.0rc4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomislav Car
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-05-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '3.2'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name: haml
|
43
|
+
name: haml
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - ">="
|
@@ -118,7 +118,7 @@ files:
|
|
118
118
|
- app/assets/fonts/icomoon.woff
|
119
119
|
- app/assets/images/phrasing_icon_edit_all.png
|
120
120
|
- app/assets/javascripts/editor.js
|
121
|
-
- app/assets/javascripts/phrasing.js
|
121
|
+
- app/assets/javascripts/phrasing.js
|
122
122
|
- app/assets/javascripts/phrasing_engine.js
|
123
123
|
- app/assets/stylesheets/phrasing.css.scss
|
124
124
|
- app/assets/stylesheets/phrasing_edit_mode_bubble.css.scss
|
@@ -149,6 +149,7 @@ files:
|
|
149
149
|
- lib/phrasing/serializer.rb
|
150
150
|
- lib/phrasing/version.rb
|
151
151
|
- phrasing.gemspec
|
152
|
+
- spec/controllers/phrasing_phrases_controller.rb
|
152
153
|
- spec/dummy/Rakefile
|
153
154
|
- spec/dummy/app/assets/javascripts/application.js
|
154
155
|
- spec/dummy/app/assets/stylesheets/application.css
|
@@ -1,127 +0,0 @@
|
|
1
|
-
//= require editor
|
2
|
-
|
3
|
-
function StatusBubble(){
|
4
|
-
var $headline = $('#phrasing-edit-mode-bubble #phrasing-saved-status-headline p');
|
5
|
-
var $circle = $('#phrasing-saved-status-indicator-circle');
|
6
|
-
|
7
|
-
var alterStatus = function(text, color){
|
8
|
-
$headline.text(text);
|
9
|
-
$circle.css('background-color', color);
|
10
|
-
}
|
11
|
-
|
12
|
-
this.saving = function(){
|
13
|
-
alterStatus('Saving', 'orange');
|
14
|
-
}
|
15
|
-
|
16
|
-
this.saved = function(){
|
17
|
-
alterStatus('Saved', '#56AE45');
|
18
|
-
}
|
19
|
-
|
20
|
-
this.error = function(){
|
21
|
-
alterStatus('Error', 'red');
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
var phrasing_setup = function(){
|
26
|
-
var status = new StatusBubble();
|
27
|
-
|
28
|
-
// Initialize the editing bubble
|
29
|
-
editor.init();
|
30
|
-
|
31
|
-
// Making sure to send csrf token from layout file.
|
32
|
-
$(document).ajaxSend(function(e, xhr, options) {
|
33
|
-
var token = $("meta[name='csrf-token']").attr("content");
|
34
|
-
xhr.setRequestHeader("X-CSRF-Token", token);
|
35
|
-
});
|
36
|
-
|
37
|
-
// Hash size function
|
38
|
-
Object.size = function(obj){
|
39
|
-
var size = 0, key;
|
40
|
-
for (key in obj) { if (obj.hasOwnProperty(key)) size++; }
|
41
|
-
return size;
|
42
|
-
};
|
43
|
-
|
44
|
-
// Trigger AJAX on textchange
|
45
|
-
var trigger_binded_events_for_phrasable_class = 1;
|
46
|
-
var timer = {}
|
47
|
-
var timer_status = {}
|
48
|
-
|
49
|
-
$('.phrasable').on('DOMNodeInserted DOMNodeRemoved DOMCharacterDataModified', function(e){
|
50
|
-
status.saving();
|
51
|
-
|
52
|
-
if (trigger_binded_events_for_phrasable_class == 1){
|
53
|
-
|
54
|
-
var record = this;
|
55
|
-
|
56
|
-
clearTimeout(timer[$(record).data("url")]);
|
57
|
-
timer_status[$(record).data("url")] = 0;
|
58
|
-
|
59
|
-
timer[$(record).data("url")] = setTimeout(function(){
|
60
|
-
savePhraseViaAjax(record);
|
61
|
-
delete timer_status[$(record).data("url")]
|
62
|
-
},2500)
|
63
|
-
timer_status[$(record).data("url")] = 1;
|
64
|
-
}
|
65
|
-
|
66
|
-
});
|
67
|
-
|
68
|
-
// AJAX Request
|
69
|
-
function savePhraseViaAjax(record){
|
70
|
-
|
71
|
-
var url = $(record).data("url");
|
72
|
-
|
73
|
-
var content = record.innerHTML;
|
74
|
-
|
75
|
-
if(content.length == 0){
|
76
|
-
content = "Empty"
|
77
|
-
}
|
78
|
-
|
79
|
-
$.ajax({
|
80
|
-
type: "PUT",
|
81
|
-
url: url,
|
82
|
-
data: { new_value: content },
|
83
|
-
success: function(e){
|
84
|
-
trigger_binded_events_for_phrasable_class = 0;
|
85
|
-
if(content === "Empty"){
|
86
|
-
$('span.phrasable[data-url="'+ url +'"]').html(content)
|
87
|
-
}else{
|
88
|
-
// Not to lose the cursor on the current contenteditable element
|
89
|
-
$('span.phrasable[data-url="'+ url +'"]').not(record).html(content)
|
90
|
-
}
|
91
|
-
trigger_binded_events_for_phrasable_class = 1;
|
92
|
-
|
93
|
-
if (Object.size(timer_status) == 0){
|
94
|
-
status.saved();
|
95
|
-
}
|
96
|
-
},
|
97
|
-
error: function(e){
|
98
|
-
status.error();
|
99
|
-
}
|
100
|
-
});
|
101
|
-
}
|
102
|
-
|
103
|
-
// Edit Mode On/Off Button
|
104
|
-
$('#edit-mode-onoffswitch').on('change', function(){
|
105
|
-
if(this.checked){
|
106
|
-
$('.phrasable').addClass("phrasable_on").attr("contenteditable", "true");
|
107
|
-
$.cookie("editing_mode", "true");
|
108
|
-
}
|
109
|
-
else{
|
110
|
-
$('.phrasable').removeClass("phrasable_on").attr("contenteditable", "false");
|
111
|
-
$.cookie("editing_mode", "false");
|
112
|
-
}
|
113
|
-
});
|
114
|
-
|
115
|
-
if($.cookie("editing_mode") === null){
|
116
|
-
$.cookie("editing_mode", "true");
|
117
|
-
$('#edit-mode-onoffswitch').prop('checked', true).change();
|
118
|
-
}
|
119
|
-
else if($.cookie("editing_mode") == "true"){
|
120
|
-
$('#edit-mode-onoffswitch').prop('checked', true).change();
|
121
|
-
}else{
|
122
|
-
$('#edit-mode-onoffswitch').prop('checked', false).change();
|
123
|
-
}
|
124
|
-
|
125
|
-
};
|
126
|
-
|
127
|
-
$(document).ready(phrasing_setup)
|