phrasing 3.2.10 → 4.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
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