phrasing 3.2.10 → 4.0.0rc1

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -11
  3. data/CHANGELOG.md +0 -20
  4. data/Gemfile +4 -1
  5. data/README-3.md +134 -0
  6. data/README.md +10 -14
  7. data/Release_notes_version_4.md +26 -0
  8. data/app/assets/javascripts/phrasing.js.erb +37 -17
  9. data/app/assets/stylesheets/phrasing_edit_mode_bubble.css.scss +3 -3
  10. data/app/controllers/phrasing_phrase_versions_controller.rb +4 -4
  11. data/app/controllers/phrasing_phrases_controller.rb +42 -75
  12. data/app/helpers/inline_helper.rb +24 -31
  13. data/app/models/phrasing_phrase.rb +29 -23
  14. data/app/views/phrasing/_production_warning.html.haml +0 -2
  15. data/app/views/phrasing_phrases/edit.html.haml +2 -2
  16. data/app/views/phrasing_phrases/import_export.html.haml +0 -5
  17. data/config/routes.rb +11 -9
  18. data/lib/generators/phrasing/phrasing_generator.rb +26 -0
  19. data/lib/generators/phrasing/templates/app/helpers/phrasing_helper.rb +12 -0
  20. data/lib/generators/phrasing/templates/config/initializers/phrasing.rb +11 -0
  21. data/{db/migrate/20131010101010_create_phrasing_phrase_versions.rb → lib/generators/phrasing/templates/db/migrate/create_phrasing_phrase_versions.rb} +2 -2
  22. data/{db/migrate/20120313191745_create_phrasing_phrases.rb → lib/generators/phrasing/templates/db/migrate/create_phrasing_phrases.rb} +2 -2
  23. data/lib/phrasing.rb +11 -37
  24. data/lib/phrasing/version.rb +2 -2
  25. data/phrasing.gemspec +1 -3
  26. data/spec/features/dummy_spec.rb +27 -24
  27. data/spec/features/phrasing_spec.rb +128 -84
  28. data/spec/lib/phrasing_spec.rb +50 -50
  29. metadata +14 -26
  30. data/4.0.0_changes.md +0 -1
  31. data/lib/phrasing/implementation.rb +0 -21
  32. data/lib/phrasing/simple.rb +0 -3
  33. data/lib/tasks/phrasing_tasks.rake +0 -69
@@ -1,9 +1,9 @@
1
- class PhrasingPhraseVersionsController < Phrasing.parent_controller.constantize
1
+ class PhrasingPhraseVersionsController < ActionController::Base
2
2
 
3
3
  def destroy
4
- @phrasing_phrase_version = PhrasingPhraseVersion.find(params[:id])
5
- @phrasing_phrase_version.destroy
6
- redirect_to edit_phrasing_phrase_path(@phrasing_phrase_version.phrasing_phrase.id)
4
+ phrase_version = PhrasingPhraseVersion.find(params[:id])
5
+ phrase_version.destroy
6
+ redirect_to edit_phrasing_phrase_path(phrase_version.phrasing_phrase.id)
7
7
  end
8
8
 
9
9
  end
@@ -1,4 +1,4 @@
1
- class PhrasingPhrasesController < Phrasing.parent_controller.constantize
1
+ class PhrasingPhrasesController < ActionController::Base
2
2
 
3
3
  layout 'phrasing'
4
4
 
@@ -8,18 +8,21 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
8
8
 
9
9
  before_filter :authorize_editor
10
10
 
11
+ def import_export; end
12
+ def help; end
13
+
11
14
  def index
12
15
  params[:locale] ||= I18n.default_locale
13
- query = PhrasingPhrase
14
- query = query.order(:key)
15
- query = query.where(locale: params[:locale]) unless params[:locale].blank?
16
-
17
- if params[:search] and !params[:search].blank?
18
- key_like = PhrasingPhrase.arel_table[:key].matches("%#{params[:search]}%")
19
- value_like = PhrasingPhrase.arel_table[:value].matches("%#{params[:search]}%")
20
- @phrasing_phrases = query.where(key_like.or(value_like))
16
+ query = PhrasingPhrase.order("phrasing_phrases.key")
17
+ query = query.where(locale: params[:locale]) if params[:locale].present?
18
+
19
+ @phrasing_phrases = if params[:search].present?
20
+ key_like = PhrasingPhrase.arel_table[:key].matches("%#{params[:search]}%")
21
+ value_like = PhrasingPhrase.arel_table[:value].matches("%#{params[:search]}%")
22
+ query.where(key_like.or(value_like))
21
23
  else
22
- @phrasing_phrases = query.where("value is not null") + query.where("value is null")
24
+ # because we want to have non nil values first.
25
+ query.where("value is not null") + query.where("value is null")
23
26
  end
24
27
 
25
28
  @locale_names = PhrasingPhrase.uniq.pluck(:locale)
@@ -30,24 +33,13 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
30
33
  end
31
34
 
32
35
  def update
33
- @phrasing_phrase = PhrasingPhrase.find(params[:id])
34
- @phrasing_phrase.value = params[:phrasing_phrase][:value]
35
- @phrasing_phrase.save!
36
-
37
- respond_to do |format|
38
- format.html do
39
- redirect_to phrasing_phrases_path, notice: "#{@phrasing_phrase.key} updated!"
40
- end
41
-
42
- format.js do
43
- render json: @phrasing_phrase
44
- end
36
+ if request.xhr?
37
+ xhr_phrase_update
38
+ else
39
+ phrase_update
45
40
  end
46
41
  end
47
42
 
48
- def import_export
49
- end
50
-
51
43
  def download
52
44
  app_name = Rails.application.class.to_s.split("::").first
53
45
  app_env = Rails.env
@@ -58,7 +50,7 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
58
50
  def upload
59
51
  number_of_changes = Phrasing::Serializer.import_yaml(params["file"].tempfile)
60
52
  redirect_to phrasing_phrases_path, notice: "YAML file uploaded successfully! Number of phrases changed: #{number_of_changes}."
61
- rescue Exception => e
53
+ rescue => e
62
54
  logger.info "\n#{e.class}\n#{e.message}"
63
55
  message = if params[:file].nil?
64
56
  "Please choose a file."
@@ -71,65 +63,40 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
71
63
  end
72
64
 
73
65
  def destroy
74
- @phrasing_phrase = PhrasingPhrase.find(params[:id])
75
- @phrasing_phrase.destroy
76
- redirect_to phrasing_phrases_path, notice: "#{@phrasing_phrase.key} deleted!"
66
+ phrasing_phrase = PhrasingPhrase.find(params[:id])
67
+ phrasing_phrase.destroy
68
+ redirect_to phrasing_phrases_path, notice: "#{phrasing_phrase.key} deleted!"
77
69
  end
78
70
 
79
- def help
80
- end
71
+ private
81
72
 
82
- def sync
83
- if Phrasing.staging_server_endpoint.nil?
84
- redirect_to :back, alert: "You didn't set your source server"
85
- else
86
- yaml = read_remote_yaml(Phrasing.staging_server_endpoint)
87
-
88
- if yaml
89
- Phrasing::Serializer.import_yaml(yaml)
90
- redirect_to :back, notice: "Translations synced from source server"
91
- else
92
- redirect_to :back
93
- end
94
-
95
- end
96
- end
97
-
98
- def remote_update_phrase
99
- klass, attribute = params[:klass], params[:attribute]
100
-
101
- if Phrasing.is_whitelisted?(klass, attribute)
102
- class_object = klass.classify.constantize
103
- @object = class_object.where(id: params[:id]).first
104
- @object.send("#{attribute}=",params[:new_value])
105
- @object.save!
106
- render json: @object
107
- else
108
- render status: 403, text: "Attribute not whitelisted!"
73
+ def authorize_editor
74
+ redirect_to root_path unless can_edit_phrases?
109
75
  end
110
76
 
111
- rescue ActiveRecord::RecordInvalid => e
112
- render status: 403, text: e
113
- end
77
+ def xhr_phrase_update
78
+ klass, attribute = params[:klass], params[:attribute]
114
79
 
115
- protected
116
-
117
- def read_remote_yaml(url)
118
- output = nil
119
- begin
120
- open(url, http_basic_authentication: [Phrasing.username, Phrasing.password]) do |remote|
121
- output = remote.read()
122
- end
123
- rescue Exception => e
124
- logger.fatal e
125
- flash[:alert] = "Syncing failed: #{e}"
80
+ if Phrasing.is_whitelisted?(klass, attribute)
81
+ class_object = klass.classify.constantize
82
+ @object = class_object.where(id: params[:id]).first
83
+ @object.send("#{attribute}=",params[:new_value])
84
+ @object.save!
85
+ render json: @object
86
+ else
87
+ render status: 403, text: "Attribute not whitelisted!"
126
88
  end
127
- output
89
+
90
+ rescue ActiveRecord::RecordInvalid => e
91
+ render status: 403, text: e
128
92
  end
129
93
 
94
+ def phrase_update
95
+ phrase = PhrasingPhrase.find(params[:id])
96
+ phrase.value = params[:phrasing_phrase][:value]
97
+ phrase.save!
130
98
 
131
- def authorize_editor
132
- redirect_to root_path unless can_edit_phrases?
99
+ redirect_to phrasing_phrases_path, notice: "#{phrase.key} updated!"
133
100
  end
134
101
 
135
102
  end
@@ -3,58 +3,51 @@ module InlineHelper
3
3
  # phrase("headline", url: www.infinum.co/yabadaba, inverse: true, interpolation: {min: 15, max: 20}, scope: "models.errors")
4
4
 
5
5
  # Data model phrase
6
- # phrase(@record, :title, inverse: true, class: phrase-record-title)
6
+ # phrase(record, :title, inverse: true, class: phrase-record-title)
7
7
 
8
8
  def phrase(*args)
9
9
  if args[0].class == String or args[0].class == Symbol
10
10
  key, options = args[0].to_s, args[1]
11
11
  phrasing_phrase(key,options || {})
12
12
  else
13
- record, field_name, options = args[0], args[1], args[2]
14
- inline(record, field_name, options || {})
13
+ record, attribute, options = args[0], args[1], args[2]
14
+ inline(record, attribute, options || {})
15
15
  end
16
16
  end
17
17
 
18
- def inline(record, field_name, options={})
19
- return record.send(field_name).to_s.html_safe unless can_edit_phrases?
18
+ private
20
19
 
21
- klass = 'phrasable'
22
- klass += ' phrasable_on' if edit_mode_on?
23
- klass += ' inverse' if options[:inverse]
24
- klass += options[:class] if options[:class]
20
+ def inline(record, attribute, options={})
21
+ return uneditable_phrase(record, attribute, options) unless can_edit_phrases?
25
22
 
26
- url = phrasing_polymorphic_url(record, field_name)
23
+ klass = 'phrasable phrasable_on'
24
+ klass += ' inverse' if options[:inverse]
25
+ klass += options[:class] if options[:class]
27
26
 
28
- content_tag(:span, { class: klass, contenteditable: edit_mode_on?, spellcheck: false, "data-url" => url}) do
29
- (record.send(field_name) || record.try(:key)).to_s.html_safe
30
- end
31
- end
27
+ url = phrasing_polymorphic_url(record, attribute)
32
28
 
33
- alias_method :model_phrase, :inline
34
-
35
- private
29
+ content_tag(:span, { class: klass, contenteditable: true, spellcheck: false, "data-url" => url }) do
30
+ (record.send(attribute) || record.try(:key)).to_s.html_safe
31
+ end
32
+ end
36
33
 
37
34
  def phrasing_phrase(key, options = {})
38
- key = options[:scope] ? "#{options[:scope]}.#{key}" : key.to_s
39
- if can_edit_phrases?
40
- @record = PhrasingPhrase.where(key: key, locale: I18n.locale.to_s).first || PhrasingPhrase.search_i18n_and_create_phrase(key)
41
- inline(@record, :value, options)
42
- else
43
- options.try(:[], :interpolation) ? t(key, options[:interpolation]).html_safe : t(key).html_safe
44
- end
35
+ key = options[:scope] ? "#{options[:scope]}.#{key}" : key
36
+ record = PhrasingPhrase.find_phrase(key)
37
+ inline(record, :value, options)
45
38
  end
46
39
 
47
- def edit_mode_on?
48
- if cookies["editing_mode"].nil?
49
- cookies['editing_mode'] = "true"
50
- true
40
+ def uneditable_phrase(record, attribute, options={})
41
+ record_value = if options[:interpolation]
42
+ I18n.interpolate(record.send(attribute), options[:interpolation])
51
43
  else
52
- cookies['editing_mode'] == "true"
44
+ record.send(attribute)
53
45
  end
46
+ record_value.to_s.html_safe
54
47
  end
55
48
 
56
49
  def phrasing_polymorphic_url(record, attribute)
57
- remote_update_phrase_phrasing_phrases_path(klass: record.class.to_s, id: record.id, attribute: attribute)
50
+ phrasing_phrase_path(klass: record.class.to_s, id: record.id, attribute: attribute)
58
51
  end
59
52
 
60
- end
53
+ end
@@ -1,39 +1,45 @@
1
1
  class PhrasingPhrase < ActiveRecord::Base
2
2
 
3
3
  validates_presence_of :key, :locale
4
+ # validate :uniqueness_of_key_on_locale_scope, on: :create
5
+ validates_uniqueness_of :key, scope: [:locale]
4
6
 
5
- validate :uniqueness_of_key_on_locale_scope, on: :create
6
-
7
- has_many :versions, dependent: :destroy, class_name: "PhrasingPhraseVersion"
7
+ has_many :versions, dependent: :destroy, class_name: PhrasingPhraseVersion
8
8
 
9
9
  after_update :version_it
10
10
 
11
- def self.search_i18n_and_create_phrase key
12
- begin
13
- value = I18n.t key, raise: true
14
- PhrasingPhrase.where(key: key, locale: I18n.locale).first
15
- rescue I18n::MissingTranslationData
16
- create_phrase(key)
17
- end
18
- end
19
-
20
- def self.create_phrase key, value = nil
21
- phrasing_phrase = PhrasingPhrase.new
22
- phrasing_phrase.locale = I18n.locale.to_s
23
- phrasing_phrase.key = key.to_s
24
- phrasing_phrase.value = value || key.to_s
25
- phrasing_phrase.save
26
- phrasing_phrase
11
+ def self.find_phrase(key)
12
+ where(key: key, locale: I18n.locale.to_s).first || search_i18n_and_create_phrase(key)
27
13
  end
28
14
 
29
15
  private
30
16
 
17
+ def self.search_i18n_and_create_phrase(key)
18
+ begin
19
+ value = I18n.t(key, raise: true)
20
+ create_phrase(key, value)
21
+ rescue I18n::MissingTranslationData
22
+ create_phrase(key)
23
+ end
24
+ end
25
+
26
+ def self.create_phrase(key, value=nil)
27
+ phrasing_phrase = PhrasingPhrase.new
28
+ phrasing_phrase.locale = I18n.locale.to_s
29
+ phrasing_phrase.key = key.to_s
30
+ phrasing_phrase.value = value || key.to_s
31
+ phrasing_phrase.save
32
+ phrasing_phrase
33
+ end
34
+
31
35
  def uniqueness_of_key_on_locale_scope
32
- errors.add(:key, "Duplicate entry #{key} for locale #{locale}") unless PhrasingPhrase.where(key: key, locale: locale).empty?
36
+ if PhrasingPhrase.where(key: key, locale: locale).any?
37
+ errors.add(:key, "Duplicate entry #{key} for locale #{locale}")
38
+ end
33
39
  end
34
40
 
35
41
  def version_it
36
- PhrasingPhraseVersion.create_version(id, value) if value_was != value
42
+ PhrasingPhraseVersion.create_version(id, value_was) if value_was != value
37
43
  end
38
-
39
- end
44
+
45
+ end
@@ -1,4 +1,2 @@
1
1
  .alert
2
2
  %h1 You're in production!
3
- - unless Phrasing.staging_server_endpoint.nil?
4
- %p You should do your changes in #{link_to("staging", Phrasing.staging_server_endpoint.gsub("/download", ""))} and then #{link_to("sync them",sync_phrasing_phrases_path)}.
@@ -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, data: { confirm: 'Are you sure?' }
3
+ = link_to "Delete Phrase", phrasing_phrase_path(@phrasing_phrase), class: "btn btn-danger delete-phrase", method: :delete, onclick: "return 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, data: { confirm: 'Are you sure?' }
23
+ %td= link_to "Delete", phrasing_phrase_version_path(version.id), class: "btn btn-danger", method: :delete, onclick: "return confirm('Are you sure?');"
@@ -8,11 +8,6 @@
8
8
  = file_field_tag "file"
9
9
  = submit_tag "Upload"
10
10
 
11
- - unless Phrasing.staging_server_endpoint.nil? || Rails.env.staging?
12
- %h2 Staging syncing
13
- %div
14
- = link_to "Sync from staging server", sync_phrasing_phrases_path
15
-
16
11
  %h2 Export
17
12
  %div
18
13
  = link_to "Download as YAML", download_phrasing_phrases_path
@@ -1,13 +1,15 @@
1
1
  Rails.application.routes.draw do
2
- resources Phrasing.route, as: 'phrasing_phrases', controller: 'phrasing_phrases', only: [:index, :edit, :update, :destroy] do
2
+ resources Phrasing.route,
3
+ as: :phrasing_phrases,
4
+ controller: :phrasing_phrases,
5
+ only: [:index, :edit, :update, :destroy] do
3
6
  collection do
4
- get 'help'
5
- get 'import_export'
6
- get 'sync'
7
- get 'download'
8
- post 'upload'
9
- put 'remote_update_phrase'
7
+ get :help
8
+ get :import_export
9
+ get :download
10
+ post :upload
10
11
  end
11
12
  end
12
- resources :phrasing_phrase_versions, as: 'phrasing_phrase_versions', controller: 'phrasing_phrase_versions', only: [:destroy]
13
- end
13
+
14
+ resources :phrasing_phrase_versions, only: :destroy
15
+ end
@@ -0,0 +1,26 @@
1
+ class PhrasingGenerator < Rails::Generators::Base
2
+ include Rails::Generators::Migration
3
+ source_root File.expand_path('../templates', __FILE__)
4
+
5
+ def create_initializer_file
6
+ initializer_location = "config/initializers/phrasing.rb"
7
+ copy_file initializer_location, initializer_location
8
+ end
9
+
10
+ def create_helper_file
11
+ helper_location = "app/helpers/phrasing_helper.rb"
12
+ copy_file helper_location, helper_location
13
+ end
14
+
15
+ def create_migrations
16
+ phrasing_phrase_migration = "db/migrate/create_phrasing_phrases.rb"
17
+ migration_template phrasing_phrase_migration, phrasing_phrase_migration
18
+ phrase_versions_migration = "db/migrate/create_phrasing_phrase_versions.rb"
19
+ migration_template phrase_versions_migration, phrase_versions_migration
20
+ end
21
+
22
+ def self.next_migration_number(path)
23
+ sleep 1 # migration numbers should differentiate
24
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module PhrasingHelper
2
+ # You must implement the can_edit_phrases? method.
3
+ # Example:
4
+ #
5
+ # def can_edit_phrases?
6
+ # current_user.is_admin?
7
+ # end
8
+
9
+ def can_edit_phrases?
10
+ raise NotImplementedError.new("You must implement the can_edit_phrases? method")
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ Phrasing.setup do |config|
2
+ config.route = 'phrasing'
3
+
4
+ # List all the model attributes you wish to edit with Phrasing, example:
5
+ # config.whitelist = ["Post.title", "Post.description"]
6
+ config.whitelist = []
7
+
8
+ # You can whitelist all models, but it's not recommended.
9
+ # Read here: https://github.com/infinum/phrasing#security
10
+ config.allow_update_on_all_models_and_attributes = false
11
+ end
@@ -1,10 +1,10 @@
1
1
  class CreatePhrasingPhraseVersions < ActiveRecord::Migration
2
2
  def change
3
- create_table :phrasing_phrase_versions do |t|
3
+ create_table :phrasing_phrase_versions do |t|
4
4
  t.integer :phrasing_phrase_id
5
5
  t.text :value
6
6
  t.timestamps
7
7
  end
8
8
  add_index :phrasing_phrase_versions, :phrasing_phrase_id
9
9
  end
10
- end
10
+ end