editmode 1.1.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2b0dd9951ef0e48376081729783e5f722f291818ce523db1867a7e6ecc30eb83
4
+ data.tar.gz: b83d8a8b26ce25c881fe848197917d0318fc5c32cab12105a14263e1cac78900
5
+ SHA512:
6
+ metadata.gz: 4c9c3b5e77d2a30f30986009c9998c2746d38c9c95668ff05425ca006d0356728c049395ef7661c8d2158e68f1e2867a3a16b4b43f4643b0a42f830c02bdfce1
7
+ data.tar.gz: ae797ecca5f90c38ce43f5aa8cafa8ca39890ff336a441969065ea12e41f5b441057cd4454894462657eb23698508cb01f1e3948259d09f0f438710cc66a8c99
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chunks.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+
2
+ # Editmode Rails Gem
3
+
4
+ To be completed
5
+
6
+ ## Installation
7
+
8
+ ## How It Works
9
+ - Autoload editmode.js
10
+ - Expose view helper
11
+
12
+ ## Helper methods
13
+ - chunk_display
14
+ - raw_chunk
15
+ - chunk_list (coming soon)
16
+
17
+ ## Caching
18
+ - All chunks cached by default using Rails.cache
19
+ - GET /editmode/clear_cache?identifier={} to clear cache
20
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,21 @@
1
+ class EditmodeController < ApplicationController
2
+
3
+ def clear_cache
4
+ if params[:full]
5
+ Rails.cache.clear
6
+ render status: 200, json: {:response => "success"}
7
+ elsif params[:variable_cache_project_id]
8
+ project_id = params[:variable_cache_project_id]
9
+ Rails.cache.delete("chunk_#{project_id}_variables")
10
+ render status: 200, json: {:response => "success"}
11
+ elsif params[:identifier]
12
+ Rails.cache.delete("chunk_#{params[:identifier]}")
13
+ Rails.cache.delete("chunk_#{params[:identifier]}_type")
14
+ render status: 200, json: {:response => "success"}
15
+ else
16
+ render status: 404, json: {:response => "no identifier specified"}
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,6 @@
1
+ Rails.application.routes.draw do
2
+ # Use a namespaced url because /:generic can easily be overwritten by app default routes
3
+ get "/editmode/clear_cache" => "editmode#clear_cache" , :as => :editmode_clear_cache
4
+ # Support older route structure. To be expired
5
+ get "/chunksapp/clear_cache" => "editmode#clear_cache" , :as => :chunksapp_clear_cache
6
+ end
@@ -0,0 +1,52 @@
1
+ require "active_support/dependencies"
2
+ require "editmode/version"
3
+ require 'editmode/script_tag'
4
+ require 'editmode/action_view_extensions/editmode_helper'
5
+ require 'editmode/auto_include_filter'
6
+ require 'editmode/chunk_value'
7
+ require 'editmode/railtie' if defined? Rails
8
+ require 'editmode/engine' if defined?(Rails)
9
+ # Todo: Implement RSPEC
10
+ module Editmode
11
+ class << self
12
+ include Editmode::ActionViewExtensions::EditmodeHelper
13
+
14
+ def project_id=(id)
15
+ config.project_id = id
16
+ end
17
+
18
+ def project_id
19
+ config.project_id
20
+ end
21
+
22
+ def access_token
23
+ config.access_token
24
+ end
25
+
26
+ def config
27
+ @config ||= Configuration.new
28
+ end
29
+
30
+ def setup
31
+ yield config
32
+ end
33
+
34
+ def chunk_value(identifier, **options)
35
+ begin
36
+ Editmode::ChunkValue.new(identifier, **options )
37
+ rescue => er
38
+ raise er
39
+ end
40
+ end
41
+ end
42
+
43
+ class Configuration
44
+ attr_accessor :access_token, :variable
45
+ attr_reader :project_id
46
+
47
+ def project_id=(id)
48
+ @project_id = id
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,176 @@
1
+ module Editmode
2
+ module ActionViewExtensions
3
+ module EditmodeHelper
4
+
5
+ require 'httparty'
6
+
7
+ def api_version
8
+ # Todo Add Header Version
9
+ end
10
+
11
+ def api_root_url
12
+ ENV["EDITMODE_OVERRIDE_API_URL"] || "https://api.editmode.com"
13
+ end
14
+
15
+ def chunk_collection(collection_identifier, **options)
16
+ branch_params = params[:em_branch_id].present? ? "branch_id=#{params[:em_branch_id]}" : ""
17
+ branch_id = params[:em_branch_id].presence
18
+ tags = options[:tags].presence || []
19
+ limit = options[:limit].presence
20
+
21
+ begin
22
+ url_params = {
23
+ :collection_identifier => collection_identifier,
24
+ :branch_id => branch_id,
25
+ :limit => limit,
26
+ :tags => tags
27
+ }.to_query
28
+
29
+ url = URI(api_root_url)
30
+ url.path = '/chunks'
31
+ url.query = url_params
32
+
33
+ response = HTTParty.get(url)
34
+
35
+ raise "No response received" unless response.code == 200
36
+ chunks = response["chunks"]
37
+
38
+ return chunks
39
+ rescue => error
40
+ puts error
41
+ return []
42
+ end
43
+ end
44
+
45
+ def chunk_field_value(parent_chunk_object, custom_field_identifier,options={})
46
+
47
+ begin
48
+ chunk_identifier = parent_chunk_object["identifier"]
49
+ custom_field_item = parent_chunk_object["content"].detect {|f| f["custom_field_identifier"] == custom_field_identifier }
50
+
51
+ if custom_field_item.present?
52
+ render_chunk_content(
53
+ custom_field_item["identifier"],
54
+ custom_field_item["content"],
55
+ custom_field_item["chunk_type"],
56
+ { parent_identifier: chunk_identifier }.merge(options)
57
+ )
58
+ end
59
+ rescue => errors
60
+ puts errors
61
+ content_tag(:span, "&nbsp".html_safe)
62
+ end
63
+
64
+ end
65
+
66
+ def render_chunk_content(chunk_identifier,chunk_content,chunk_type,options={})
67
+
68
+ begin
69
+ # Always sanitize the content!!
70
+ chunk_content = ActionController::Base.helpers.sanitize(chunk_content) unless chunk_type == 'rich_text'
71
+
72
+ css_class = options[:class]
73
+
74
+ if chunk_type == "image"
75
+ display_type = "image"
76
+ else
77
+ display_type = options[:display_type] || "span"
78
+ end
79
+
80
+ chunk_data = { :chunk => chunk_identifier, :chunk_editable => false, :chunk_type => chunk_type }
81
+
82
+ if options[:parent_identifier].present?
83
+ chunk_data.merge!({parent_identifier: options[:parent_identifier]})
84
+ end
85
+
86
+ case display_type
87
+ when "span"
88
+ if chunk_type == "rich_text"
89
+ content = content_tag("em-span", :class => "editmode-richtext-editor #{css_class}", :data => chunk_data.merge!({:chunk_editable => true}) ) do
90
+ chunk_content.html_safe
91
+ end
92
+ else
93
+ content_tag("em-span", :class => css_class, :data => chunk_data.merge!({:chunk_editable => true}) ) do
94
+ chunk_content
95
+ end
96
+ end
97
+ when "image"
98
+ image_tag(chunk_content, :data => chunk_data, :class => css_class)
99
+ end
100
+ rescue => errors
101
+ puts errors
102
+ content_tag("em-span", "&nbsp".html_safe)
103
+ end
104
+
105
+ end
106
+
107
+ def chunk_display(label,identifier,options={},&block)
108
+ branch_id = params[:em_branch_id]
109
+ # This method should never show an error.
110
+ # If anything goes wrong fetching content
111
+ # We should just show blank content, not
112
+ # prevent the page from loading.
113
+ begin
114
+ branch_params = branch_id.present? ? "branch_id=#{branch_id}" : ""
115
+ cache_identifier = "chunk_#{identifier}#{branch_id}"
116
+ url = "#{api_root_url}/chunks/#{identifier}?#{branch_params}"
117
+ cached_content_present = Rails.cache.exist?(cache_identifier)
118
+
119
+ if !cached_content_present
120
+ response = HTTParty.get(url)
121
+ response_received = true if response.code == 200
122
+ end
123
+
124
+ if !cached_content_present && !response_received
125
+ raise "No response received"
126
+ else
127
+
128
+ chunk_content = Rails.cache.fetch(cache_identifier) do
129
+ response['content']
130
+ end
131
+
132
+ chunk_type = Rails.cache.fetch("#{cache_identifier}_type") do
133
+ response['chunk_type']
134
+ end
135
+
136
+ render_chunk_content(identifier,chunk_content,chunk_type, options)
137
+
138
+ end
139
+
140
+ rescue => error
141
+ # Show fallback content by default
142
+ return content_tag("em-span", &block) if block_given?
143
+ # Otherwise show a span with no content to
144
+ # maintain layout
145
+ content_tag("em-span", "&nbsp".html_safe)
146
+ end
147
+
148
+ end
149
+
150
+ alias_method :chunk, :chunk_display
151
+
152
+ def variable_parse!(content, variables, values)
153
+ tokens = content.scan(/\{{(.*?)\}}/)
154
+ if tokens.any?
155
+ tokens.flatten!
156
+ tokens.each do |token|
157
+ token_value = values[token.to_sym] || variables[token] || ""
158
+ sanitized_value = ActionController::Base.helpers.sanitize(token_value)
159
+
160
+ content.gsub!("{{#{token}}}", sanitized_value)
161
+ end
162
+ end
163
+
164
+ content
165
+ end
166
+
167
+ def no_response_received(id = "")
168
+ "Sorry, we can't find a chunk using this identifier: \"#{id}\". This can happen if you've deleted a chunk on editmode.com or if your local cache is out of date. If it persists, try running Rails.cache clear."
169
+ end
170
+
171
+ def require_field_id
172
+ "Field ID or Field Name is required to retrieve a collection item"
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,65 @@
1
+ module Editmode
2
+
3
+ module AutoInclude
4
+
5
+ module Method
6
+ def editmode_auto_include
7
+ Editmode::AutoInclude::Filter.filter(self)
8
+ end
9
+ end
10
+
11
+ class Filter
12
+
13
+ CLOSING_BODY_TAG = %r{</body>}
14
+ def self.filter(controller)
15
+ auto_include_filter = new(controller)
16
+ return unless auto_include_filter.include_javascript?
17
+
18
+ auto_include_filter.include_javascript!
19
+ end
20
+
21
+ attr_reader :controller
22
+
23
+ def initialize(kontroller)
24
+ @controller = kontroller
25
+ end
26
+
27
+ def include_javascript!
28
+ response.body = response.body.gsub(CLOSING_BODY_TAG, editmode_script_tag.output + '\\0')
29
+ end
30
+
31
+ def include_javascript?
32
+ enabled_for_environment? &&
33
+ html_content_type? &&
34
+ response_has_closing_body_tag? &&
35
+ editmode_script_tag.valid?
36
+ end
37
+
38
+ private
39
+ def response
40
+ controller.response
41
+ end
42
+
43
+ def html_content_type?
44
+ response.content_type.try(:include?,'text/html')
45
+ end
46
+
47
+ def response_has_closing_body_tag?
48
+ !!(response.body[CLOSING_BODY_TAG])
49
+ end
50
+
51
+ def editmode_script_tag
52
+ @script_tag ||= Editmode::ScriptTag.new()
53
+ end
54
+
55
+ def enabled_for_environment?
56
+ enabled_environments = ["production","development","staging"]
57
+ return true if enabled_environments.nil?
58
+ enabled_environments.map(&:to_s).include?(Rails.env)
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,75 @@
1
+ module Editmode
2
+ class ChunkValue
3
+ include Editmode::ActionViewExtensions::EditmodeHelper
4
+
5
+ attr_accessor :identifier, :variable_values, :branch_id,
6
+ :variable_fallbacks, :chunk_type, :project_id,
7
+ :response
8
+
9
+ attr_writer :content
10
+
11
+ def initialize(identifier, **options)
12
+ @identifier = identifier
13
+ @branch_id = options[:branch_id].presence
14
+ @variable_values = options[:values].presence || {}
15
+ get_content
16
+ end
17
+
18
+ def field(field = nil)
19
+ # Field ID can be a slug or field_name
20
+ if chunk_type == 'collection_item'
21
+ if field.present?
22
+ field.downcase!
23
+ field_content = content.detect {|f| f["custom_field_identifier"].downcase == field || f["custom_field_name"].downcase == field }
24
+ if field_content.present?
25
+ result = field_content['content']
26
+ result = variable_parse!(result, variable_fallbacks, variable_values)
27
+ else
28
+ raise no_response_received(field)
29
+ end
30
+ else
31
+ raise require_field_id
32
+ end
33
+ else
34
+ raise NoMethodError.new "undefined method 'field` for chunk_type: #{chunk_type} \n"
35
+ end
36
+ result ||= content
37
+ end
38
+
39
+ def content
40
+ raise NoMethodError.new "undefined method 'content` for chunk_type: collection_item \nDid you mean? field" if chunk_type == 'collection_item'
41
+
42
+ variable_parse!(@content, variable_fallbacks, variable_values)
43
+ end
44
+
45
+ private
46
+ def get_content
47
+ branch_params = branch_id.present? ? "branch_id=#{branch_id}" : ""
48
+ url = "#{api_root_url}/chunks/#{identifier}?#{branch_params}"
49
+
50
+ cache_identifier = "chunk_value_#{identifier}#{branch_id}"
51
+ cached_content_present = Rails.cache.exist?(cache_identifier)
52
+
53
+ if !cached_content_present
54
+ http_response = HTTParty.get(url)
55
+ response_received = true if http_response.code == 200
56
+ end
57
+
58
+ if !cached_content_present && !response_received
59
+ raise no_response_received(identifier)
60
+ else
61
+ @response = Rails.cache.fetch(cache_identifier) do
62
+ http_response
63
+ end
64
+
65
+ @content = response['content']
66
+ @chunk_type = response['chunk_type']
67
+ @project_id = response['project_id']
68
+ @variable_fallbacks = Rails.cache.fetch("chunk_#{project_id}_variables") do
69
+ response['variable_fallbacks']
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,8 @@
1
+ module Editmode
2
+
3
+ class Engine < Rails::Engine
4
+
5
+
6
+ end
7
+
8
+ end
@@ -0,0 +1,19 @@
1
+ module Editmode
2
+ class Railtie < Rails::Railtie
3
+ initializer "editmode" do |app|
4
+
5
+ ActiveSupport.on_load :action_view do
6
+ include Editmode::ActionViewExtensions::EditmodeHelper
7
+ end
8
+ ActiveSupport.on_load :action_controller do
9
+ include AutoInclude::Method
10
+
11
+ if respond_to? :after_action
12
+ after_action :editmode_auto_include
13
+ else
14
+ after_filter :editmode_auto_include
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ module Editmode
2
+
3
+ class ScriptTag
4
+
5
+ def self.generate(*args)
6
+ new(*args).output
7
+ end
8
+
9
+ def initialize(options = {})
10
+
11
+ end
12
+
13
+ def valid?
14
+ true
15
+ end
16
+
17
+ def output
18
+
19
+ str = <<-EDITMODE_SCRIPT
20
+ <script>window.chunksProjectIdentifier = '#{Editmode.project_id}'</script>
21
+ <script>window.editmodeENV = '#{ENV["EDITMODE_ENV"] || 'production'}'</script>
22
+ <script src="#{script_url}" async ></script>
23
+ EDITMODE_SCRIPT
24
+
25
+ str.respond_to?(:html_safe) ? str.html_safe : str
26
+
27
+ end
28
+
29
+ def script_url
30
+ ENV["EDITMODE_OVERRIDE_SCRIPT_URL"] || "https://static.editmode.com/editmode@^1.0.0/dist/editmode.js"
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,3 @@
1
+ module Editmode
2
+ VERSION = "1.1.1"
3
+ end
@@ -0,0 +1,26 @@
1
+
2
+ require 'rails/generators/base'
3
+
4
+ module Editmode
5
+ module Generators
6
+ class ConfigGenerator < Rails::Generators::Base
7
+ source_root File.expand_path("../templates", __FILE__)
8
+
9
+ argument :project_id, :desc => "Your Editmode project_id, which can be found here: https://www.editmode.com/projects"
10
+
11
+ def create_config_file
12
+ @project_id = project_id
13
+
14
+ introduction = <<-intro
15
+ Editmode will automatically insert its javascript before the closing '</body>'
16
+ tag on every page.
17
+
18
+ intro
19
+
20
+ print "#{introduction} "
21
+
22
+ template "editmode.rb.erb", "config/initializers/editmode.rb"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ Editmode.setup do |config|
2
+ # Replace TodoProjectId with your Editmode Project ID,
3
+ # visit https://editmode.com/projects
4
+ config.project_id = "<%= @project_id %>"
5
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: editmode
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Tony Ennis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redcarpet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: The Editmode gem allows you to include chunks from the Editmode platform
70
+ in your rails views
71
+ email:
72
+ - ennis.tony@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - Gemfile
78
+ - README.md
79
+ - Rakefile
80
+ - app/controllers/editmode_controller.rb
81
+ - config/routes.rb
82
+ - lib/editmode.rb
83
+ - lib/editmode/action_view_extensions/editmode_helper.rb
84
+ - lib/editmode/auto_include_filter.rb
85
+ - lib/editmode/chunk_value.rb
86
+ - lib/editmode/engine.rb
87
+ - lib/editmode/railtie.rb
88
+ - lib/editmode/script_tag.rb
89
+ - lib/editmode/version.rb
90
+ - lib/generators/editmode/config_generator.rb
91
+ - lib/generators/editmode/templates/editmode.rb.erb
92
+ homepage: https://github.com/tonyennis145/editmode-rails
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.0.8
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Editmode allows you to turn plain text in your rails app into easily inline-editable
115
+ bits of content that can be managed by anyone with no technical knowledge
116
+ test_files: []