effective_developer 0.6.7 → 0.6.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/effective/form_upgrader.rb +32 -8
- data/lib/effective_developer/engine.rb +0 -3
- data/lib/effective_developer/version.rb +1 -1
- data/lib/tasks/hatchbox.rake +11 -0
- data/lib/tasks/pg_pull.rake +5 -6
- data/lib/tasks/sidekiq_clear.rake +22 -0
- metadata +4 -8
- data/app/jobs/effective/asset_replacer_job.rb +0 -17
- data/app/jobs/effective/snippet_replacer_job.rb +0 -17
- data/app/models/effective/asset_replacer.rb +0 -156
- data/app/models/effective/snippet_replacer.rb +0 -67
- data/lib/tasks/replace_effective_assets.rake +0 -5
- data/lib/tasks/replace_effective_snippets.rake +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce8857439e77912b69f4d782989c25125a30adfa38bdfa098a0ee099d45b5158
|
4
|
+
data.tar.gz: 8c81459bbbc23a9e582cb3e18789bf8923e35744e1d851b6cc282d01d32019c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fb7415f3045e979db76b27025696b4c891624813accec70e9832c65dce0d0a639f2b82ff7f39ce1b18ebb4ad233d7ae09df12a2de37adbecdcc5319aa284aab
|
7
|
+
data.tar.gz: 68b07f9aeb57b8c0c0a3b4ec1211bdb645920c3f78ac733fdaedfb16e008dba4efe3642a3697c2ae6d34c26f3f2920b50540441b0ff332f96721c7d359e1df61
|
@@ -38,6 +38,8 @@ module Effective
|
|
38
38
|
private
|
39
39
|
|
40
40
|
SIMPLE_FORM_FOR_REGEX = /simple_form_for( |\()(\[:[^,]+, ?[^,]+\])?(([^,]+),)?.+do \|(\w+)\|/
|
41
|
+
SIMPLE_FORM_FOR_REGEX2 = /simple_form_for\((\w+).+?\) do \|(\w+)\|/
|
42
|
+
|
41
43
|
SIMPLE_FORM_INPUT_ATTRIBUTE = /\.input( |\():(\w+)/
|
42
44
|
SIMPLE_FORM_INPUT_AS_ONE = /(as: :(\w+))/
|
43
45
|
SIMPLE_FORM_INPUT_AS_TWO = /(:as => :(\w+))/
|
@@ -53,17 +55,38 @@ module Effective
|
|
53
55
|
# Replace simple_form_for
|
54
56
|
writer.all { |line| line.include?('simple_form_for') }.each do |line|
|
55
57
|
content = writer.lines[line]
|
58
|
+
|
59
|
+
# Try first time
|
56
60
|
matched = content.match(SIMPLE_FORM_FOR_REGEX)
|
57
|
-
raise("unable to match simple_form_for from:\n#{content}") unless matched.present?
|
58
61
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
if matched.present?
|
63
|
+
original = matched[0]
|
64
|
+
model = matched[2] || matched[4]
|
65
|
+
letter = matched[5]
|
62
66
|
|
63
|
-
|
67
|
+
if original && model && letter
|
68
|
+
content.sub!(original, "effective_form_with(model: #{model}) do |#{letter}|")
|
69
|
+
writer.replace(line, content)
|
70
|
+
next
|
71
|
+
end
|
72
|
+
end
|
64
73
|
|
65
|
-
|
66
|
-
|
74
|
+
# Try second time
|
75
|
+
matched = content.match(SIMPLE_FORM_FOR_REGEX2)
|
76
|
+
|
77
|
+
if matched.present?
|
78
|
+
original = matched[0]
|
79
|
+
model = matched[1]
|
80
|
+
letter = matched[2]
|
81
|
+
|
82
|
+
if original && model && letter
|
83
|
+
content.sub!(original, "effective_form_with(model: #{model}) do |#{letter}|")
|
84
|
+
writer.replace(line, content)
|
85
|
+
next
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
puts("unable to determine simple_form_for subject from:\n#{content}")
|
67
90
|
end
|
68
91
|
|
69
92
|
# Try to figure out klass again if its missing from filename
|
@@ -189,7 +212,8 @@ module Effective
|
|
189
212
|
when 'text' then 'text_area'
|
190
213
|
when 'url' then 'url_field'
|
191
214
|
else
|
192
|
-
|
215
|
+
puts("unknown input type #{input_type} (for attribute :#{attribute})")
|
216
|
+
'text_field'
|
193
217
|
end
|
194
218
|
end
|
195
219
|
|
@@ -2,9 +2,6 @@ module EffectiveDeveloper
|
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
engine_name 'effective_developer'
|
4
4
|
|
5
|
-
config.autoload_paths += Dir["#{config.root}/models/jobs/"]
|
6
|
-
config.eager_load_paths += Dir["#{config.root}/models/jobs/"]
|
7
|
-
|
8
5
|
# Set up our default configuration options.
|
9
6
|
initializer 'effective_developer.defaults', before: :load_config_initializers do |app|
|
10
7
|
# Set up our defaults, as per our initializer template
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# bundle exec rake hb:ssh
|
2
|
+
# bundle exec rake hb:staging
|
3
|
+
namespace :hb do
|
4
|
+
task :ssh do
|
5
|
+
system("ssh -t deploy@#{ENV.fetch('HATCHBOX_IP')} \"cd ~/arta/current ; bash --login\"")
|
6
|
+
end
|
7
|
+
|
8
|
+
task :staging do
|
9
|
+
system("ssh -t deploy@#{ENV.fetch('HATCHBOX_IP')} \"cd ~/arta-staging/current ; bash --login\"")
|
10
|
+
end
|
11
|
+
end
|
data/lib/tasks/pg_pull.rake
CHANGED
@@ -18,8 +18,8 @@ namespace :pg do
|
|
18
18
|
args.with_defaults(defaults.compact.merge(env_keys.compact).merge(keywords))
|
19
19
|
|
20
20
|
# Validate Config
|
21
|
-
config = ActiveRecord::Base.configurations[Rails.env]
|
22
21
|
configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
22
|
+
config = configs.first
|
23
23
|
|
24
24
|
if configs.length > 1 && args.database.blank?
|
25
25
|
puts "Multiple database configs exist for #{Rails.env} environment."
|
@@ -102,8 +102,8 @@ namespace :pg do
|
|
102
102
|
end
|
103
103
|
|
104
104
|
# Validate Config
|
105
|
-
config = ActiveRecord::Base.configurations[Rails.env]
|
106
105
|
configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
106
|
+
config = configs.first
|
107
107
|
|
108
108
|
if configs.length > 1 && args.database.blank?
|
109
109
|
puts "Multiple database configs exist for #{Rails.env} environment."
|
@@ -151,8 +151,8 @@ namespace :pg do
|
|
151
151
|
{ username: uri.user, password: uri.password, host: uri.host, port: (uri.port || 5432), database: uri.path.sub('/', '') }
|
152
152
|
else
|
153
153
|
# Validate Config
|
154
|
-
config = ActiveRecord::Base.configurations[Rails.env]
|
155
154
|
configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
155
|
+
config = configs.first
|
156
156
|
|
157
157
|
if configs.length > 1 && args.database.blank?
|
158
158
|
puts "Multiple database configs exist for #{Rails.env} environment."
|
@@ -189,11 +189,10 @@ namespace :pg do
|
|
189
189
|
desc 'Clones the production (--remote heroku by default) database to staging (--remote staging by default)'
|
190
190
|
task :clone, [:source_remote, :target_remote] => :environment do |t, args|
|
191
191
|
args.with_defaults(:source_remote => 'heroku', :target_remote => 'staging')
|
192
|
-
db = ActiveRecord::Base.configurations[Rails.env]
|
193
192
|
|
194
193
|
puts "=== Cloning remote '#{args.source_remote}' to '#{args.target_remote}'"
|
195
194
|
|
196
|
-
Bundler.
|
195
|
+
Bundler.with_unbundled_env do
|
197
196
|
unless system("heroku pg:backups:capture --remote #{args.source_remote}")
|
198
197
|
abort "Error capturing heroku backup"
|
199
198
|
end
|
@@ -240,7 +239,7 @@ namespace :pg do
|
|
240
239
|
puts "=== Cloning local table '#{args.table}' to remote #{args.remote} database"
|
241
240
|
|
242
241
|
# Dump my local database table
|
243
|
-
db = ActiveRecord::Base.configurations
|
242
|
+
db = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
|
244
243
|
tmpfile = "tmp/#{args.table}.sql"
|
245
244
|
|
246
245
|
unless system("pg_dump --data-only --table=#{args.table} -h localhost -U '#{db['username']}' '#{db['database']}' > #{tmpfile}")
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# bundle exec rake sidekiq:start
|
2
|
+
# bundle exec rake sidekiq:clear
|
3
|
+
|
4
|
+
namespace :sidekiq do
|
5
|
+
desc 'Starts the sidekiq background job server'
|
6
|
+
task :start do
|
7
|
+
system('bundle exec sidekiq -C config/sidekiq.yml')
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Clears all in progress sidekiq jobs'
|
11
|
+
task clear: :environment do
|
12
|
+
unless Rails.env.development?
|
13
|
+
puts "Cannot run in non-development mode"; exit
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'sidekiq/api'
|
17
|
+
Sidekiq::Queue.all.each { |q| q.clear }
|
18
|
+
|
19
|
+
puts "All sidekiq queues cleared"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_developer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -48,16 +48,12 @@ files:
|
|
48
48
|
- MIT-LICENSE
|
49
49
|
- README.md
|
50
50
|
- Rakefile
|
51
|
-
- app/jobs/effective/asset_replacer_job.rb
|
52
|
-
- app/jobs/effective/snippet_replacer_job.rb
|
53
51
|
- app/models/effective/annotator.rb
|
54
|
-
- app/models/effective/asset_replacer.rb
|
55
52
|
- app/models/effective/code_writer.rb
|
56
53
|
- app/models/effective/csv_importer.rb
|
57
54
|
- app/models/effective/form_upgrader.rb
|
58
55
|
- app/models/effective/live_generator.rb
|
59
56
|
- app/models/effective/profiler.rb
|
60
|
-
- app/models/effective/snippet_replacer.rb
|
61
57
|
- config/effective_developer.rb
|
62
58
|
- lib/effective_developer.rb
|
63
59
|
- lib/effective_developer/engine.rb
|
@@ -103,11 +99,11 @@ files:
|
|
103
99
|
- lib/scaffolds/views/_resource.html.haml
|
104
100
|
- lib/tasks/annotate.rake
|
105
101
|
- lib/tasks/effective_csv_importer.rake
|
102
|
+
- lib/tasks/hatchbox.rake
|
106
103
|
- lib/tasks/pg_pull.rake
|
107
104
|
- lib/tasks/rename_class.rake
|
108
|
-
- lib/tasks/replace_effective_assets.rake
|
109
|
-
- lib/tasks/replace_effective_snippets.rake
|
110
105
|
- lib/tasks/reset_pk_sequence.rake
|
106
|
+
- lib/tasks/sidekiq_clear.rake
|
111
107
|
- lib/tasks/upgrade_forms.rake
|
112
108
|
- lib/tasks/validate.rake
|
113
109
|
homepage: https://github.com/code-and-effect/effective_developer
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Effective
|
2
|
-
class AssetReplacerJob < ::ApplicationJob
|
3
|
-
|
4
|
-
queue_as :default
|
5
|
-
|
6
|
-
if defined?(Sidekiq)
|
7
|
-
# The retry setting works. But none of these waits seem to. Still getting exponential backoff.
|
8
|
-
sidekiq_options retry: 2, retry_in: 10, wait: 10
|
9
|
-
sidekiq_retry_in { |count, exception| 10 }
|
10
|
-
end
|
11
|
-
|
12
|
-
def perform(attachment, box)
|
13
|
-
Effective::AssetReplacer.new.replace_attachment!(attachment, box)
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Effective
|
2
|
-
class SnippetReplacerJob < ::ApplicationJob
|
3
|
-
|
4
|
-
queue_as :default
|
5
|
-
|
6
|
-
if defined?(Sidekiq)
|
7
|
-
# The retry setting works. But none of these waits seem to. Still getting exponential backoff.
|
8
|
-
sidekiq_options retry: 2, retry_in: 10, wait: 10
|
9
|
-
sidekiq_retry_in { |count, exception| 10 }
|
10
|
-
end
|
11
|
-
|
12
|
-
def perform(region)
|
13
|
-
Effective::SnippetReplacer.new.replace_region!(region)
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,156 +0,0 @@
|
|
1
|
-
# Looks at the Effective::Asset and Effective::Attachments and converts to ActiveStorage
|
2
|
-
#
|
3
|
-
# 1. Put in the has_attached_files to each model to be upgraded
|
4
|
-
# 2. Remove the acts_as_asset_box
|
5
|
-
|
6
|
-
require 'timeout'
|
7
|
-
|
8
|
-
module Effective
|
9
|
-
class AssetReplacer
|
10
|
-
include ActiveStorage::Blob::Analyzable
|
11
|
-
|
12
|
-
BATCH_SIZE = 500
|
13
|
-
TIMEOUT = 120
|
14
|
-
|
15
|
-
def replace!(skip_existing: false)
|
16
|
-
verify!
|
17
|
-
|
18
|
-
attachments = attachments_to_process().to_a
|
19
|
-
|
20
|
-
while(true)
|
21
|
-
wait_for_active_job!
|
22
|
-
|
23
|
-
puts "\nEnqueuing #{attachments.length} attachments with ids #{attachments.first.id} to #{attachments.last.id}"
|
24
|
-
|
25
|
-
attachments.each do |attachment|
|
26
|
-
asset = attachment.asset
|
27
|
-
attachable = attachment.attachable
|
28
|
-
next if asset.blank? || attachable.blank?
|
29
|
-
|
30
|
-
box = attachment.box.singularize
|
31
|
-
boxes = attachment.box
|
32
|
-
|
33
|
-
one = attachable.respond_to?(box) && attachable.send(box).kind_of?(ActiveStorage::Attached::One)
|
34
|
-
many = attachable.respond_to?(boxes) && attachable.send(boxes).kind_of?(ActiveStorage::Attached::Many)
|
35
|
-
box = (one ? box : boxes)
|
36
|
-
|
37
|
-
if skip_existing
|
38
|
-
existing = Array(attachable.send(box))
|
39
|
-
|
40
|
-
if existing.any? { |obj| obj.respond_to?(:filename) && obj.filename.to_s == asset.file_name }
|
41
|
-
puts("Skipping existing #{attachable.class.name} #{attachable.id} #{box} #{asset.file_name}.")
|
42
|
-
next
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
Effective::AssetReplacerJob.perform_later(attachment, box)
|
47
|
-
end
|
48
|
-
|
49
|
-
attachments = attachments_to_process().where.not(id: attachments.map(&:id))
|
50
|
-
break if attachments.to_a.blank?
|
51
|
-
|
52
|
-
GC.start
|
53
|
-
end
|
54
|
-
|
55
|
-
puts "\nAll Done. Have a great day."
|
56
|
-
true
|
57
|
-
end
|
58
|
-
|
59
|
-
# This is called on the background by the AssetReplacerJob
|
60
|
-
def replace_attachment!(attachment, box)
|
61
|
-
raise('expected an Effective::Attachment') unless attachment.kind_of?(Effective::Attachment)
|
62
|
-
puts("Processing attachment ##{attachment.id}")
|
63
|
-
|
64
|
-
asset = attachment.asset
|
65
|
-
attachable = attachment.attachable
|
66
|
-
|
67
|
-
attachable.replacing_asset = true if attachable.respond_to?(:replacing_asset=)
|
68
|
-
|
69
|
-
Timeout.timeout(TIMEOUT) do
|
70
|
-
attachable.send(box).attach(
|
71
|
-
io: URI.open(asset.url),
|
72
|
-
filename: asset.file_name,
|
73
|
-
content_type: asset.content_type.presence,
|
74
|
-
identify: (asset.content_type.blank?)
|
75
|
-
)
|
76
|
-
|
77
|
-
attachment.update_column(:replaced, true)
|
78
|
-
end
|
79
|
-
|
80
|
-
true
|
81
|
-
end
|
82
|
-
|
83
|
-
def verify!
|
84
|
-
raise('expected effective assets') unless defined?(Effective::Asset)
|
85
|
-
raise('expected active storage') unless defined?(ActiveStorage)
|
86
|
-
|
87
|
-
unless Effective::Attachment.new.respond_to?(:replaced?)
|
88
|
-
raise('please add a replaced boolean to Effective::Attachment. add_column :attachments, :replaced, :boolean')
|
89
|
-
end
|
90
|
-
|
91
|
-
(Effective::Attachment.all.pluck(:attachable_type, :box).uniq).each do |name, boxes|
|
92
|
-
next if name.blank? || boxes.blank?
|
93
|
-
|
94
|
-
box = boxes.singularize
|
95
|
-
|
96
|
-
klass = name.safe_constantize
|
97
|
-
raise("invalid class #{klass}") unless klass.present?
|
98
|
-
|
99
|
-
instance = klass.new
|
100
|
-
|
101
|
-
if instance.respond_to?(:effective_assets)
|
102
|
-
raise("please remove acts_as_asset_box() from #{klass.name}")
|
103
|
-
end
|
104
|
-
|
105
|
-
unless instance.respond_to?(box) || instance.respond_to?(boxes)
|
106
|
-
raise("expected #{klass.name} to has_one_attached :#{box} or has_many_attached :#{boxes}")
|
107
|
-
end
|
108
|
-
|
109
|
-
one = instance.respond_to?(box) && instance.send(box).kind_of?(ActiveStorage::Attached::One)
|
110
|
-
many = instance.respond_to?(boxes) && instance.send(boxes).kind_of?(ActiveStorage::Attached::Many)
|
111
|
-
|
112
|
-
unless one.present? || many.present?
|
113
|
-
raise("expected #{klass.name} to has_one_attached :#{box} or has_many_attached :#{boxes}")
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
puts 'All attachment classes verified.'
|
118
|
-
|
119
|
-
true
|
120
|
-
end
|
121
|
-
|
122
|
-
def reset!
|
123
|
-
Effective::Attachment.update_all(replaced: false)
|
124
|
-
end
|
125
|
-
|
126
|
-
private
|
127
|
-
|
128
|
-
def wait_for_active_job!
|
129
|
-
while(true)
|
130
|
-
if(jobs = enqueued_jobs_count) > (BATCH_SIZE / 10)
|
131
|
-
print '.'; sleep(3)
|
132
|
-
else
|
133
|
-
break
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# The last BATCH_SIZE attachments
|
139
|
-
def attachments_to_process
|
140
|
-
Effective::Attachment.all
|
141
|
-
.includes(:asset)
|
142
|
-
.where(replaced: [nil, false])
|
143
|
-
.reorder(id: :desc)
|
144
|
-
.limit(BATCH_SIZE)
|
145
|
-
end
|
146
|
-
|
147
|
-
def enqueued_jobs_count
|
148
|
-
if Rails.application.config.active_job.queue_adapter == :sidekiq
|
149
|
-
Sidekiq::Stats.new.enqueued.to_i
|
150
|
-
else
|
151
|
-
ActiveJob::Base.queue_adapter.enqueued_jobs.count
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
end
|
156
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# Replaces the [snippet_x] in all effective regions with static content
|
2
|
-
|
3
|
-
module Effective
|
4
|
-
class SnippetReplacer
|
5
|
-
include ActiveStorage::Blob::Analyzable
|
6
|
-
include ActionView::Helpers::UrlHelper
|
7
|
-
include ActionView::Helpers::AssetTagHelper
|
8
|
-
|
9
|
-
def replace!
|
10
|
-
raise('expected effective regions') unless defined?(Effective::Region)
|
11
|
-
raise('expected effective assets') unless defined?(Effective::Asset)
|
12
|
-
raise('expected active storage') unless defined?(ActiveStorage)
|
13
|
-
|
14
|
-
Effective::Region.with_snippets.find_each do |region|
|
15
|
-
Effective::SnippetReplacerJob.perform_later(region)
|
16
|
-
end
|
17
|
-
|
18
|
-
puts 'All Done. Background jobs are running. Have a great day.'
|
19
|
-
true
|
20
|
-
end
|
21
|
-
|
22
|
-
def replace_region!(region)
|
23
|
-
region.snippet_objects.each do |snippet|
|
24
|
-
print('.')
|
25
|
-
|
26
|
-
begin
|
27
|
-
case snippet.class.name
|
28
|
-
when 'Effective::Snippets::EffectiveAsset'
|
29
|
-
replace_effective_asset(region, snippet)
|
30
|
-
else
|
31
|
-
raise("unsupported snippet: #{snippet.class.name}")
|
32
|
-
end
|
33
|
-
rescue => e
|
34
|
-
puts "\nError: #{e}\n"
|
35
|
-
remove_snippet(region, snippet)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
region.save!
|
40
|
-
end
|
41
|
-
|
42
|
-
def replace_effective_asset(region, snippet)
|
43
|
-
asset = snippet.asset
|
44
|
-
raise("Effective:Asset id=#{snippet.asset_id || 'none'} does not exist") unless asset.present?
|
45
|
-
|
46
|
-
blob = ActiveStorage::Blob.create_and_upload!(io: URI.open(asset.url), filename: asset.file_name)
|
47
|
-
url = Rails.application.routes.url_helpers.rails_blob_url(blob, only_path: true)
|
48
|
-
|
49
|
-
content = if asset.image?
|
50
|
-
image_tag(url, class: snippet.html_class, alt: snippet.link_title)
|
51
|
-
else
|
52
|
-
link_to(snippet.link_title, url, class: snippet.html_class, title: snippet.link_title)
|
53
|
-
end
|
54
|
-
|
55
|
-
region.content.sub!("[#{snippet.id}]", content.to_s)
|
56
|
-
region.snippets.delete(snippet.id)
|
57
|
-
|
58
|
-
true
|
59
|
-
end
|
60
|
-
|
61
|
-
def remove_snippet(region, snippet)
|
62
|
-
region.content.sub!("[#{snippet.id}]", '')
|
63
|
-
region.snippets.delete(snippet.id)
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
end
|