editmode 1.1.4 → 1.1.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d4e7090d2da1935453260320513e82439c6815295621646a1ebdc7378895abe
4
- data.tar.gz: 8e96d36c0c479a4ebd89c4efb74896270c3cbae9ea75353adf491e00fdd51943
3
+ metadata.gz: fbdfaaca69c2832debd4fb728c6684b48abe8e9337cbaa8f4323a20cf175cf77
4
+ data.tar.gz: 51841cadd3a89aea58a0089b9e7c541a286f9da46fa2d7db0f32e9d7a07797ed
5
5
  SHA512:
6
- metadata.gz: 28475295b70e1e65c3472f74d0b3ee4a5765bb52727843dc478c1239f9fd09fe399d43fd0db2c63814e14623f5f225344aea474421e3c73ff6feba3ec84123e6
7
- data.tar.gz: d3d787c71c390a115b9470c06f2e48cdd5089fb7a788dc03e862d6090453b0a41a75a41812297b4a66dddae77b86a59e7415a8145245a0edebd72740c79ae1df
6
+ metadata.gz: 9966ac6b514c9c237db8e2e1f77613431a365f2c4259565422892b9abdd749a2c294575bfd0e1242d9ac682a5340b940bacde295560fa1845e93ddd03ef500f1
7
+ data.tar.gz: fee7262f68522bb8e4f76d0de9f9da8c267c2a311105e89c72d7989408d46f124f6bbbf4170d712c0544aa4453b17f2837525c93f47da9afae2a729a0a2c47a5
data/README.md CHANGED
@@ -1,20 +1,127 @@
1
+ <p align="center">
2
+ <img src="https://editmode.s3-eu-west-1.amazonaws.com/static/editmode-full-navy-bg-transparent.png" width="260" />
3
+ </p>
4
+ <br />
1
5
 
2
- # Editmode Rails Gem
6
+ # EditMode for Rails
3
7
 
4
- To be completed
8
+ Editmode is a smarter way to manage copy and other content in your rails app. It's syntax is similar to i18n, but it's built for copy updates, not internationalization (yet). It's built around the idea of moving your content out of your codebase.
5
9
 
6
10
  ## Installation
7
11
 
8
- ## How It Works
9
- - Autoload editmode.js
10
- - Expose view helper
12
+ #### 1. Add the gem to your Gemfile:
13
+ ```ruby
14
+ gem 'editmode'
15
+ ```
16
+ And run `bundle install`.
17
+
18
+ #### 2. Create an initializer with your project_id
19
+
20
+ <small>Don't have a project id? Sign up for one [here](https://editmode.com/rails?s=ghreadme)</small>
21
+
22
+ ```sh
23
+ rails generate editmode:config YOUR-PROJECT-ID
24
+ ```
25
+ This command produces an initializer file
26
+ ```ruby
27
+ # config/initializers/editmode.rb
28
+ Editmode.setup do |config|
29
+ config.project_id={project_id}
30
+ end
31
+ ```
32
+
33
+ That's it, you're all set up. By default Editmode will now include editmode.js in every page of your rails application, unless you disable auto-include.
34
+ <hr/>
35
+
36
+ ## Rendering Content
37
+
38
+ Editmode provides helper methods for use in your rails views and controllers.
39
+
40
+ ### Render the content of a chunk
41
+ ```erb
42
+ <%= e('cnk_x4ts............') %> # Using a chunk identifier
43
+ <%= e('marketing_page_headline') %> # Using a content key
44
+ ```
45
+
46
+ ### Render an *Editable* chunk. Wait, [what?](https://editmode.com/rails)
47
+ ```erb
48
+ <%= E('cnk_x4ts............') %> # Using a chunk identifier
49
+ <%= E('marketing_page_headline') %> # Using a content key
50
+ ```
51
+
52
+ ### Content can also be accessed in Controllers
53
+ ```ruby
54
+ @page_title = e("cnk_x4ts............") # Using a chunk identifier
55
+ @page_title = e("marketing_page_seo_title") # Using a content key
56
+ ```
57
+
58
+ ### Directly get the value of a custom field
59
+ This works when a chunk is part of a collection.
60
+ ```ruby
61
+ @email_subject = e("welcome_email","Subject")
62
+ @email_content = e("welcome_email","Content")
63
+ ```
64
+
65
+ ### Working with variables
66
+ ```ruby
67
+ variable_values = { first_name: "Dexter", last_name: "Morgan"}
68
+
69
+ # Assume chunk content is "Hi {{first_name}} {{last_name}}"
70
+
71
+ # Single Chunk with Variables
72
+ e("cnk_d36415052285997e079b", variables: variable_values)
73
+
74
+ # Collection Field with Variables
75
+ e("cnk_16e04a02d577afb610ce", "Email Content", variables: variable_values)
76
+
77
+ # Response: "Hi Dexter Morgan"
78
+ ```
79
+
80
+ ### Use collections for repeatable content
81
+ ```erb
82
+ <%= c('col_j8fbs...') do |chunk| %>
83
+ <div class="user-profile">
84
+ <h3 class="name">
85
+ <%= F("Name") %>
86
+ </h3>
87
+ <p class="description">
88
+ <%= f("Description") %>
89
+ </p>
90
+ </div>
91
+ <% end %>
92
+ ```
93
+
94
+ |Parameter|Type|Description|
95
+ |---|---|---|
96
+ | identifier | string | The first argument of `c` takes the id of the collection you want to loop through |
97
+ | limit | int/string |`optional` The number of collection items you want to display |
98
+ | tags | array |`optional` Filter collection items based on tags listed in this parameter |
99
+ | class | string | `optional` Class name(s) that will be added along with "chunks-collection-wrapper" to the main collection `<div>` element |
100
+ | item_class | string | `optional` Class name(s) that will be added along with "chunks-collection-item--wrapper" to all collection items |
11
101
 
12
- ## Helper methods
13
- - chunk_display
14
- - raw_chunk
15
- - chunk_list (coming soon)
16
102
 
17
103
  ## Caching
18
- - All chunks cached by default using Rails.cache
19
- - GET /editmode/clear_cache?identifier={} to clear cache
104
+ In order to keep your application speedy, Editmode minimizes the amount of network calls it makes by caching content where it can.
105
+
106
+ #### What's cached
107
+ - Any embedded content returned by the `e`, `E`, `f`, or `F` view helpers.
108
+
109
+ #### Expiring the cache
110
+
111
+ The editmode gem exposes a cache expiration endpoint in your application at `/editmode/clear_cache`.
112
+
113
+ 1. GET `/editmode/clear_cache?identifier={chunk_id}` clears the cache for a specific chunk.
114
+ 2. GET `/editmode/clear_cache?full=1` clears the full Editmode cache.
115
+
116
+ - Editmode.js will automatically hit this endpoint when you update a chunk through your frontend.
117
+ - You can configure cache expiration webhooks in Editmode.com to ensure your application is notified when content changes happen on Editmode.com
118
+
119
+ The cache expiration endpoint is currently **not** authenticated.
120
+
121
+ ## Disabling editmode.js auto-include
122
+
123
+ To disable automatic insertion for a particular controller or action you can:
124
+ ```ruby
125
+ skip_after_action :editmode_auto_include
126
+ ```
20
127
 
@@ -36,7 +36,7 @@ module Editmode
36
36
  begin
37
37
  Editmode::ChunkValue.new(identifier, **options )
38
38
  rescue => er
39
- raise er
39
+ puts er
40
40
  end
41
41
  end
42
42
  end
@@ -19,6 +19,9 @@ module Editmode
19
19
  branch_id = params[:em_branch_id].presence
20
20
  tags = options[:tags].presence || []
21
21
  limit = options[:limit].presence
22
+
23
+ parent_class = options[:class] || ""
24
+ item_class = options[:item_class] || ""
22
25
 
23
26
  begin
24
27
  url_params = {
@@ -49,12 +52,22 @@ module Editmode
49
52
  end
50
53
 
51
54
  if chunks.any?
52
- content_tag :div, class: "chunks-collection-wrapper", data: {chunk_collection_identifier: collection_identifier} do
55
+ content_tag :div, class: "chunks-collection-wrapper #{parent_class}", data: {chunk_collection_identifier: collection_identifier} do
53
56
  chunks.each do |chunk|
54
57
  @custom_field_chunk = chunk
55
- yield
58
+ concat(content_tag(:div, class: "chunks-collection-item--wrapper #{item_class}") do
59
+ yield
60
+ end)
56
61
  end
57
- end
62
+
63
+ # Placeholder element for new collection item
64
+ @custom_field_chunk = chunks.first.merge!({placeholder: true})
65
+ concat(content_tag(:div, class: "chunks-hide chunks-col-placeholder-wrapper") do
66
+ yield
67
+ end)
68
+ end
69
+ else
70
+ content_tag(:span, "&nbsp".html_safe)
58
71
  end
59
72
  end
60
73
  rescue => error
@@ -67,14 +80,23 @@ module Editmode
67
80
  def chunk_field_value(parent_chunk_object, custom_field_identifier, options = {})
68
81
  begin
69
82
  chunk_identifier = parent_chunk_object["identifier"]
70
- custom_field_item = parent_chunk_object["content"].detect {|f| f["custom_field_identifier"] == custom_field_identifier || f["custom_field_name"] == custom_field_identifier }
83
+ custom_field_item = parent_chunk_object["content"].detect do |f|
84
+ f["custom_field_identifier"].try(:downcase) == custom_field_identifier.try(:downcase) || f["custom_field_name"].try(:downcase) == custom_field_identifier.try(:downcase)
85
+ end
86
+
87
+ options[:field] = custom_field_identifier
71
88
 
89
+ if parent_chunk_object[:placeholder]
90
+ custom_field_item["identifier"] = ""
91
+ custom_field_item["content"] = ""
92
+ end
93
+
72
94
  if custom_field_item.present?
73
95
  render_chunk_content(
74
96
  custom_field_item["identifier"],
75
97
  custom_field_item["content"],
76
98
  custom_field_item["chunk_type"],
77
- { parent_identifier: chunk_identifier }.merge(options)
99
+ { parent_identifier: chunk_identifier, custom_field_identifier: custom_field_identifier}.merge(options)
78
100
  )
79
101
  end
80
102
  rescue => errors
@@ -88,6 +110,7 @@ module Editmode
88
110
  begin
89
111
  # Always sanitize the content!!
90
112
  chunk_content = ActionController::Base.helpers.sanitize(chunk_content) unless chunk_type == 'rich_text'
113
+ chunk_content = variable_parse!(chunk_content, options[:variable_fallbacks], options[:variable_values])
91
114
 
92
115
  css_class = options[:class]
93
116
 
@@ -99,9 +122,8 @@ module Editmode
99
122
 
100
123
  chunk_data = { :chunk => chunk_identifier, :chunk_editable => false, :chunk_type => chunk_type }
101
124
 
102
- if options[:parent_identifier].present?
103
- chunk_data.merge!({parent_identifier: options[:parent_identifier]})
104
- end
125
+ chunk_data.merge!({parent_identifier: options[:parent_identifier]}) if options[:parent_identifier].present?
126
+ chunk_data.merge!({custom_field_identifier: options[:custom_field_identifier]}) if options[:custom_field_identifier].present?
105
127
 
106
128
  case display_type
107
129
  when "span"
@@ -115,6 +137,7 @@ module Editmode
115
137
  end
116
138
  end
117
139
  when "image"
140
+ chunk_content = chunk_content.blank? || chunk_content == "/images/original/missing.png" ? 'https://www.editmode.com/upload.png' : chunk_content
118
141
  image_tag(chunk_content, :data => chunk_data, :class => css_class)
119
142
  end
120
143
  rescue => errors
@@ -132,10 +155,11 @@ module Editmode
132
155
  # prevent the page from loading.
133
156
  begin
134
157
  branch_params = branch_id.present? ? "branch_id=#{branch_id}" : ""
135
- cache_identifier = "chunk_#{identifier}#{branch_id}"
158
+ field = options[:field].presence || ""
159
+ cache_identifier = "chunk_#{identifier}#{branch_id}#{field}"
136
160
  url = "#{api_root_url}/chunks/#{identifier}?project_id=#{Editmode.project_id}&#{branch_params}"
137
161
  cached_content_present = Rails.cache.exist?(cache_identifier)
138
-
162
+
139
163
  if !cached_content_present
140
164
  response = HTTParty.get(url)
141
165
  response_received = true if response.code == 200
@@ -144,15 +168,30 @@ module Editmode
144
168
  if !cached_content_present && !response_received
145
169
  raise "No response received"
146
170
  else
147
-
171
+ if field.present? && response.present?
172
+ field_content = response["content"].detect {|f| f["custom_field_identifier"].downcase == field.downcase || f["custom_field_name"].downcase == field.downcase }
173
+ if field_content
174
+ content = field_content["content"]
175
+ type = field_content["chunk_type"]
176
+ identifier = field_content["identifier"]
177
+ end
178
+ end
179
+
180
+ variable_fallbacks = Rails.cache.fetch("#{cache_identifier}_variables") do
181
+ response['variable_fallbacks'].presence || {}
182
+ end
183
+
148
184
  chunk_content = Rails.cache.fetch(cache_identifier) do
149
- response['content']
185
+ content.presence || response["content"]
150
186
  end
151
187
 
152
188
  chunk_type = Rails.cache.fetch("#{cache_identifier}_type") do
153
- response['chunk_type']
189
+ type.presence || response['chunk_type']
154
190
  end
155
191
 
192
+ options[:variable_fallbacks] = variable_fallbacks
193
+ options[:variable_values] = options[:variables].presence || {}
194
+
156
195
  render_chunk_content(identifier,chunk_content,chunk_type, options)
157
196
 
158
197
  end
@@ -168,18 +207,23 @@ module Editmode
168
207
  alias_method :chunk, :chunk_display
169
208
 
170
209
 
171
- def render_custom_field(label, options={})
172
- chunk_field_value(@custom_field_chunk, label, options)
210
+ def render_custom_field(field_name, options={})
211
+ options[:variable_fallbacks] = @custom_field_chunk["variable_fallbacks"] || {}
212
+ options[:variable_values] = options[:variables] || {}
213
+
214
+ chunk_field_value(@custom_field_chunk, field_name, options)
173
215
  end
174
216
  alias_method :F, :render_custom_field
175
217
 
176
- def render_chunk(identifier, options = {}, &block)
218
+ def render_chunk(identifier, *args, &block)
219
+ field, options = parse_arguments(args)
220
+ options[:field] = field
177
221
  chunk_display('label', identifier, options, &block)
178
222
  end
179
223
  alias_method :E, :render_chunk
180
224
 
181
225
 
182
- def variable_parse!(content, variables, values)
226
+ def variable_parse!(content, variables = {}, values = {})
183
227
  tokens = content.scan(/\{{(.*?)\}}/)
184
228
  if tokens.any?
185
229
  tokens.flatten!
@@ -3,9 +3,9 @@ module Editmode
3
3
  include Editmode::ActionViewExtensions::EditmodeHelper
4
4
 
5
5
  attr_accessor :identifier, :variable_values, :branch_id,
6
- :variable_fallbacks, :chunk_type, :project_id,
6
+ :variable_fallbacks, :chunk_type, :project_id,
7
7
  :response
8
-
8
+
9
9
  attr_writer :content
10
10
 
11
11
  def initialize(identifier, **options)
@@ -31,18 +31,26 @@ module Editmode
31
31
  raise require_field_id
32
32
  end
33
33
  else
34
- raise NoMethodError.new "undefined method 'field` for chunk_type: #{chunk_type} \n"
34
+ raise "undefined method 'field` for chunk_type: #{chunk_type} \n"
35
35
  end
36
36
  result || @content
37
37
  end
38
38
 
39
39
  def content
40
- raise NoMethodError.new "undefined method 'content` for chunk_type: collection_item \nDid you mean? field" if chunk_type == 'collection_item'
40
+ raise "undefined method 'content` for chunk_type: collection_item \nDid you mean? field" if chunk_type == 'collection_item'
41
41
 
42
42
  variable_parse!(@content, variable_fallbacks, variable_values)
43
43
  end
44
44
 
45
45
  private
46
+
47
+ def json?(json)
48
+ JSON.parse(json)
49
+ return true
50
+ rescue JSON::ParserError => e
51
+ return false
52
+ end
53
+
46
54
  def get_content
47
55
  branch_params = branch_id.present? ? "branch_id=#{branch_id}" : ""
48
56
  url = "#{api_root_url}/chunks/#{identifier}?project_id=#{Editmode.project_id}&#{branch_params}"
@@ -58,16 +66,16 @@ module Editmode
58
66
  if !cached_content_present && !response_received
59
67
  raise no_response_received(identifier)
60
68
  else
61
- @response = Rails.cache.fetch(cache_identifier) do
62
- http_response
69
+ cached_response = Rails.cache.fetch(cache_identifier) do
70
+ http_response.to_json
63
71
  end
64
72
 
73
+ @response = json?(cached_response) ? JSON.parse(cached_response) : cached_response
74
+
65
75
  @content = response['content']
66
76
  @chunk_type = response['chunk_type']
67
77
  @project_id = response['project_id']
68
- @variable_fallbacks = Rails.cache.fetch("chunk_#{project_id}_variables") do
69
- response['variable_fallbacks']
70
- end
78
+ @variable_fallbacks = response['variable_fallbacks']
71
79
  end
72
80
  end
73
81
 
@@ -12,7 +12,7 @@ module Editmode
12
12
  chunk.content
13
13
  end
14
14
  rescue => er
15
- raise er
15
+ puts er
16
16
  end
17
17
  end
18
18
 
@@ -1,3 +1,3 @@
1
1
  module Editmode
2
- VERSION = "1.1.4"
2
+ VERSION = "1.1.9"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: editmode
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Ennis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-04 00:00:00.000000000 Z
11
+ date: 2020-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler