phrasing 4.0.0rc3 → 4.0.0rc4
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.
- 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)
|