shoperb-theme-editor 0.8.0

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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +40 -0
  3. data/LICENSE.md +21 -0
  4. data/README.md +124 -0
  5. data/bin/shoperb +313 -0
  6. data/lib/shoperb_theme_editor/api/server.rb +10 -0
  7. data/lib/shoperb_theme_editor/api/views/callback.erb +53 -0
  8. data/lib/shoperb_theme_editor/api.rb +264 -0
  9. data/lib/shoperb_theme_editor/build/json.rb +167 -0
  10. data/lib/shoperb_theme_editor/build/liquid.rb +38 -0
  11. data/lib/shoperb_theme_editor/build/section.rb +51 -0
  12. data/lib/shoperb_theme_editor/build/settings.rb +224 -0
  13. data/lib/shoperb_theme_editor/build.rb +68 -0
  14. data/lib/shoperb_theme_editor/configuration.rb +98 -0
  15. data/lib/shoperb_theme_editor/error.rb +35 -0
  16. data/lib/shoperb_theme_editor/ext/array.rb +17 -0
  17. data/lib/shoperb_theme_editor/ext/nil_class.rb +7 -0
  18. data/lib/shoperb_theme_editor/ext/sequel.rb +82 -0
  19. data/lib/shoperb_theme_editor/ext.rb +2 -0
  20. data/lib/shoperb_theme_editor/init.rb +87 -0
  21. data/lib/shoperb_theme_editor/logger.rb +58 -0
  22. data/lib/shoperb_theme_editor/mounter/models/address.rb +65 -0
  23. data/lib/shoperb_theme_editor/mounter/models/attribute.rb +11 -0
  24. data/lib/shoperb_theme_editor/mounter/models/attribute_key.rb +17 -0
  25. data/lib/shoperb_theme_editor/mounter/models/base.rb +196 -0
  26. data/lib/shoperb_theme_editor/mounter/models/blog_category.rb +47 -0
  27. data/lib/shoperb_theme_editor/mounter/models/blog_post.rb +45 -0
  28. data/lib/shoperb_theme_editor/mounter/models/brand.rb +11 -0
  29. data/lib/shoperb_theme_editor/mounter/models/cart.rb +35 -0
  30. data/lib/shoperb_theme_editor/mounter/models/cart_item.rb +71 -0
  31. data/lib/shoperb_theme_editor/mounter/models/category.rb +99 -0
  32. data/lib/shoperb_theme_editor/mounter/models/collection.rb +40 -0
  33. data/lib/shoperb_theme_editor/mounter/models/country.rb +18 -0
  34. data/lib/shoperb_theme_editor/mounter/models/currency.rb +17 -0
  35. data/lib/shoperb_theme_editor/mounter/models/custom_field.rb +22 -0
  36. data/lib/shoperb_theme_editor/mounter/models/customer.rb +77 -0
  37. data/lib/shoperb_theme_editor/mounter/models/customer_customer_group.rb +12 -0
  38. data/lib/shoperb_theme_editor/mounter/models/customer_group.rb +13 -0
  39. data/lib/shoperb_theme_editor/mounter/models/customer_subscription.rb +40 -0
  40. data/lib/shoperb_theme_editor/mounter/models/customer_subscription_plan.rb +32 -0
  41. data/lib/shoperb_theme_editor/mounter/models/discount.rb +40 -0
  42. data/lib/shoperb_theme_editor/mounter/models/discount_variant.rb +15 -0
  43. data/lib/shoperb_theme_editor/mounter/models/image.rb +51 -0
  44. data/lib/shoperb_theme_editor/mounter/models/language.rb +17 -0
  45. data/lib/shoperb_theme_editor/mounter/models/link.rb +61 -0
  46. data/lib/shoperb_theme_editor/mounter/models/media_file.rb +19 -0
  47. data/lib/shoperb_theme_editor/mounter/models/menu.rb +21 -0
  48. data/lib/shoperb_theme_editor/mounter/models/meta.rb +10 -0
  49. data/lib/shoperb_theme_editor/mounter/models/news_item.rb +11 -0
  50. data/lib/shoperb_theme_editor/mounter/models/order.rb +133 -0
  51. data/lib/shoperb_theme_editor/mounter/models/order_item.rb +137 -0
  52. data/lib/shoperb_theme_editor/mounter/models/order_item_attribute.rb +17 -0
  53. data/lib/shoperb_theme_editor/mounter/models/order_return.rb +42 -0
  54. data/lib/shoperb_theme_editor/mounter/models/order_return_item.rb +29 -0
  55. data/lib/shoperb_theme_editor/mounter/models/order_return_item_entity.rb +25 -0
  56. data/lib/shoperb_theme_editor/mounter/models/order_return_parcel.rb +20 -0
  57. data/lib/shoperb_theme_editor/mounter/models/page.rb +26 -0
  58. data/lib/shoperb_theme_editor/mounter/models/payment_card.rb +21 -0
  59. data/lib/shoperb_theme_editor/mounter/models/payment_method.rb +67 -0
  60. data/lib/shoperb_theme_editor/mounter/models/payment_provider.rb +23 -0
  61. data/lib/shoperb_theme_editor/mounter/models/product.rb +144 -0
  62. data/lib/shoperb_theme_editor/mounter/models/product_attribute.rb +32 -0
  63. data/lib/shoperb_theme_editor/mounter/models/product_search.rb +53 -0
  64. data/lib/shoperb_theme_editor/mounter/models/product_type.rb +21 -0
  65. data/lib/shoperb_theme_editor/mounter/models/review.rb +38 -0
  66. data/lib/shoperb_theme_editor/mounter/models/search.rb +11 -0
  67. data/lib/shoperb_theme_editor/mounter/models/shipping_method.rb +39 -0
  68. data/lib/shoperb_theme_editor/mounter/models/shop.rb +58 -0
  69. data/lib/shoperb_theme_editor/mounter/models/state.rb +17 -0
  70. data/lib/shoperb_theme_editor/mounter/models/theme.rb +89 -0
  71. data/lib/shoperb_theme_editor/mounter/models/variant.rb +96 -0
  72. data/lib/shoperb_theme_editor/mounter/models/variant_attribute.rb +46 -0
  73. data/lib/shoperb_theme_editor/mounter/models/vendor.rb +38 -0
  74. data/lib/shoperb_theme_editor/mounter/server/assets.rb +35 -0
  75. data/lib/shoperb_theme_editor/mounter/server/defaults.rb +44 -0
  76. data/lib/shoperb_theme_editor/mounter/server/exception_handler.rb +22 -0
  77. data/lib/shoperb_theme_editor/mounter/server/partials/_shoperb_footer.liquid +0 -0
  78. data/lib/shoperb_theme_editor/mounter/server/partials/_shoperb_header.liquid +0 -0
  79. data/lib/shoperb_theme_editor/mounter/server/partials/_shoperb_stylesheets.liquid +3 -0
  80. data/lib/shoperb_theme_editor/mounter/server/renderer.rb +166 -0
  81. data/lib/shoperb_theme_editor/mounter/server/routes/cart.rb +127 -0
  82. data/lib/shoperb_theme_editor/mounter/server/routes/dummy.rb +34 -0
  83. data/lib/shoperb_theme_editor/mounter/server/routes/locale.rb +31 -0
  84. data/lib/shoperb_theme_editor/mounter/server/routes/pages.rb +33 -0
  85. data/lib/shoperb_theme_editor/mounter/server/routes/search.rb +18 -0
  86. data/lib/shoperb_theme_editor/mounter/server/routes.rb +366 -0
  87. data/lib/shoperb_theme_editor/mounter/server/routes_helper.rb +278 -0
  88. data/lib/shoperb_theme_editor/mounter/server.rb +66 -0
  89. data/lib/shoperb_theme_editor/mounter.rb +30 -0
  90. data/lib/shoperb_theme_editor/os.rb +13 -0
  91. data/lib/shoperb_theme_editor/package.rb +81 -0
  92. data/lib/shoperb_theme_editor/sync/images.rb +69 -0
  93. data/lib/shoperb_theme_editor/sync/pagination.rb +52 -0
  94. data/lib/shoperb_theme_editor/sync.rb +229 -0
  95. data/lib/shoperb_theme_editor/translations.rb +22 -0
  96. data/lib/shoperb_theme_editor/utils.rb +50 -0
  97. data/lib/shoperb_theme_editor.rb +159 -0
  98. data/shoperb_theme_editor.gemspec +60 -0
  99. metadata +510 -0
@@ -0,0 +1,264 @@
1
+ OAuth2::Response.register_parser(:zip, ["application/zip"]) do |body|
2
+ Shoperb::Theme::Editor::Utils.mk_tempfile body, "theme-", ".zip"
3
+ end
4
+
5
+ OAuth2::Response.register_parser(:json, ["application/json"]) do |body|
6
+ JSON.parse(body)
7
+ end
8
+
9
+ module Shoperb module Theme module Editor
10
+ EMAIL_SPLITTER = "---\n"
11
+ module Api
12
+ extend self
13
+ mattr_accessor :auth_code, :client, :token
14
+
15
+ def pull handle=nil, *args
16
+ prepare
17
+ response = request Pathname.new(["api", "v1", "themes", handle, "download"].compact.join("/")).cleanpath.to_s, method: :get, notify: -> { "Downloading" } do |faraday|
18
+ faraday.options.timeout = 120
19
+ faraday.headers['Current-Shop'] = Editor["oauth-site"]
20
+ end
21
+ Package.unzip response.parsed
22
+ end
23
+
24
+ def get_emails(params: "", notify: )
25
+ total = 2
26
+ page = 1
27
+ per = 1
28
+ while page*per < total do
29
+ response = request Pathname.new(["api", "v1", "emails"].join("/")).cleanpath.to_s + "?" + params, method: :get, notify: ->{ notify } do |faraday|
30
+ faraday.options.timeout = 120
31
+ faraday.headers['Current-Shop'] = Editor["oauth-site"]
32
+ end
33
+ pagination = JSON.parse(response.headers["X-Pagination"])
34
+ page = pagination["page"]
35
+ per = pagination["per"]
36
+ total = pagination["total"]
37
+ `mkdir -p emails`
38
+ JSON.parse(response.body).each do |email|
39
+ yield(email) if block_given?
40
+ end
41
+ end
42
+ end
43
+
44
+ def pull_emails handle=nil, *args
45
+ prepare
46
+ old_emails = {}
47
+ get_emails(params: "version:1", notify: "Downloading old") do |email|
48
+ old_emails[email["key"]] = email
49
+ end
50
+
51
+ get_emails(params: "version:2", notify: "Downloading") do |email|
52
+ email = old_emails[email["key"]] if old_emails[email["key"]]
53
+
54
+ content = EMAIL_SPLITTER + "subject: #{email["subject"]}\n" + EMAIL_SPLITTER + email["content_html"].to_s
55
+ File.write("emails/#{email["key"]}.html.liquid", content)
56
+ File.write("emails/#{email["key"]}.text.liquid", email["content_text"])
57
+ end
58
+ end
59
+
60
+ def push **args
61
+ prepare
62
+ file = Package.zip
63
+
64
+ theme = Faraday::UploadIO.new(file, "application/zip")
65
+ request Pathname.new("api/v1/themes/#{Editor.handle}/upload").cleanpath.to_s, method: :patch, notify: -> { "Uploading #{Editor.handle}" }, body: { zip: theme, reset: args[:reset] } do |faraday|
66
+ faraday.options.timeout = 120
67
+ faraday.headers['Current-Shop'] = Editor["oauth-site"]
68
+ end
69
+ ensure
70
+ Utils.rm_tempfile file
71
+ end
72
+
73
+ def push_emails **args
74
+ prepare
75
+ html_end = ".html.liquid"
76
+ Dir["emails/*"].each do |file|
77
+ next unless file.end_with?(html_end)
78
+ key = file.sub(html_end,"").sub("emails/","") # like: "order.confirmation"
79
+ ext_id = nil
80
+
81
+ # fetch old one to delete and existing one to update
82
+ get_emails(params: "where=key:#{key}", notify: "Getting info for #{key}") do |email|
83
+ if email["version"] == 1 || email["version"] == 0
84
+ # send to delete
85
+ response = request "api/v1/emails/#{email["id"]}?version=1", method: :delete, notify: -> { "Deleting old" } do |faraday|
86
+ faraday.headers['Current-Shop'] = Editor["oauth-site"]
87
+ end
88
+ else
89
+ ext_id = email["id"]
90
+ end
91
+ end
92
+
93
+ # update existing one
94
+ if ext_id # patch
95
+ html = File.read("emails/#{key}.html.liquid").split("---\n")
96
+ json = {
97
+ id: ext_id,
98
+ subject: html[1].sub("subject: ","").strip,
99
+ content_text: File.read("emails/#{key}.text.liquid"),
100
+ content_html: html[2],
101
+ translations: {}
102
+ }
103
+ # needed while we have translations and didn't remove them
104
+ %w(en ru et lv).each do |locale|
105
+ json[:translations]["#{locale}.subject"] = json[:subject]
106
+ json[:translations]["#{locale}.content_html"] = json[:content_html]
107
+ json[:translations]["#{locale}.content_text"] = json[:content_text]
108
+ end
109
+ response = request "api/v1/emails/#{ext_id}?version=2", method: :patch, notify: -> { "Updating #{key}" }, body: {email: json} do |faraday|
110
+ faraday.headers['Current-Shop'] = Editor["oauth-site"]
111
+ end
112
+ else # create
113
+ raise "Email #{key} was removed from DB. Contact helpdesk."
114
+ end
115
+ end
116
+ end
117
+
118
+ def zip
119
+ zip_name = "#{Editor.handle}.zip"
120
+ file = Package.zip
121
+ Logger.notify "Writing #{zip_name}" do
122
+ Utils.write_file(zip_name) { file.read }
123
+ end
124
+ end
125
+
126
+ def sync resource = nil, opts={}
127
+ prepare
128
+
129
+ if resource
130
+ Sync.send(resource)
131
+ Mounter::Model::Base.save
132
+ return
133
+ end
134
+
135
+ product_ids = opts[:"only-products"].present? ? opts[:"only-products"].tr(",", "|") : nil
136
+ customer_ids = opts[:"only-customers"].present? ? opts[:"only-customers"].tr(",", "|") : nil
137
+ Sync.products product_ids
138
+ Sync.shop
139
+ Sync.images unless opts[:"skip-images"]
140
+ Sync.media_files
141
+ Sync.collections
142
+ Sync.vendors
143
+ Sync.pages
144
+ Sync.menus
145
+ Sync.countries
146
+ Sync.states
147
+ Sync.addresses customer_ids
148
+ Sync.links
149
+ Sync.blog_posts
150
+ Sync.settings_data
151
+ Sync.customers customer_ids
152
+ Sync.customer_groups
153
+ Sync.reviews
154
+ Sync.discounts
155
+ Sync.custom_fields
156
+ Sync.subcriptions
157
+ Mounter::Model::Base.save
158
+ end
159
+
160
+ def oauth_client
161
+ return @client if defined?(@client)
162
+
163
+ @client = OAuth2::Client.new(
164
+ Editor["oauth-client-id"],
165
+ Editor["oauth-client-secret"],
166
+ site: "#{Editor["server"]["protocol"]}://#{Editor["server"]["url"]}",
167
+ token_url: "/api/v1/oauth/token",
168
+ authorize_url: "oauth/authorize?domain=#{Editor["oauth-site"]}"
169
+ ) do |faraday|
170
+ faraday.request :multipart
171
+ faraday.request :url_encoded
172
+ faraday.adapter :net_http
173
+ end
174
+ end
175
+
176
+ def prepare
177
+ oauth_client
178
+ Logger.notify "Asking for permission" do
179
+ start_server(authorize_url)
180
+ end unless have_token?
181
+
182
+ `rm #{Sequel::Model.db.opts[:database]}`
183
+ Sequel::Model.db= Sequel.sqlite(Sequel::Model.db.opts[:database])
184
+ end
185
+
186
+ def request url, notify: false, method:, **options, &block
187
+ response = get_response(url, notify: notify, method: method, **options, &block)
188
+
189
+ if /\/admin\/authenticate$/ =~ response.response.env.url.to_s
190
+ Editor.reset("oauth-cache")
191
+ get_response(url, notify: notify, method: method, **options, &block)
192
+ else
193
+ response
194
+ end
195
+ end
196
+
197
+ def get_response url, notify: false, method:, **options, &block
198
+ if notify
199
+ atoken = access_token
200
+ Logger.notify notify[] do
201
+ atoken.send(method, url, **options, &block)
202
+ end
203
+ else
204
+ access_token.send(method, url, **options, &block)
205
+ end
206
+ end
207
+
208
+ def get_token
209
+ cache = Editor["oauth-cache"]
210
+ OAuth2::AccessToken.new(oauth_client, cache["access_token"], refresh_token: cache["refresh_token"])
211
+ end
212
+
213
+ def authorize_url
214
+ oauth_client.auth_code.authorize_url(redirect_uri: Editor["oauth-redirect-uri"], scope: "admin", response_type: "code_and_token")
215
+ end
216
+
217
+ def get_authented_token(code)
218
+ oauth_client.auth_code.get_token(code, client_id: oauth_client.id, client_secret: oauth_client.secret, redirect_uri: Editor["oauth-redirect-uri"], scope: "admin", headers: { 'Current-Shop' => Editor["oauth-site"] })
219
+ end
220
+
221
+ def access_token
222
+ if have_token?
223
+ get_token
224
+ else
225
+ prepare
226
+ get_token
227
+ end
228
+ rescue OAuth2::Error => oauth_error
229
+ handle_oauth_error oauth_error
230
+ end
231
+
232
+ def have_token?
233
+ (cache = Editor["oauth-cache"]) && cache["access_token"] && valid_expired_token?
234
+ end
235
+
236
+ def valid_expired_token?
237
+ !Editor["oauth-cache"]["expires_at"] || Time.at(Editor["oauth-cache"]["expires_at"].to_i) > Time.now
238
+ end
239
+
240
+ def start_server url
241
+ thread = Thread.new do
242
+ Rack::Handler::WEBrick.run Server.new,
243
+ Port: Editor["port"],
244
+ StartCallback: -> { Launchy.open url },
245
+ AccessLog: [],
246
+ Logger: WEBrick::Log::new(Os["/dev/null"], 7)
247
+ end
248
+ thread.abort_on_exception = true
249
+ sleep(0.5) while !have_token?
250
+ thread.kill
251
+ end
252
+
253
+ def handle_oauth_error exception
254
+ case exception.code
255
+ when "invalid_client"
256
+ Editor.reset("oauth-client-id", "oauth-client-secret", "oauth-cache")
257
+ end
258
+ raise exception
259
+ end
260
+
261
+ Editor.autoload_all self, "api"
262
+
263
+ end
264
+ end end end
@@ -0,0 +1,167 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+
4
+ module Shoperb
5
+ module Theme
6
+ module Editor
7
+ module Build
8
+ class Json
9
+ attr_reader :json_content
10
+
11
+ def initialize(section_handle)
12
+ @section_handle = section_handle
13
+ @json_content = {}
14
+ end
15
+
16
+ def create_or_update_json_file
17
+ config_dir = File.join(Dir.pwd, "config/sections")
18
+ FileUtils.mkdir_p(config_dir)
19
+ json_file_path = File.join(config_dir, "#{@section_handle}.json")
20
+
21
+ if File.exist?(json_file_path)
22
+ @json_content = JSON.parse(File.read(json_file_path))
23
+ else
24
+ section_name = format_section_name(@section_handle)
25
+ @json_content = {
26
+ "name" => section_name,
27
+ "generic" => true,
28
+ "settings" => [],
29
+ "blocks" => [],
30
+ "default" => {
31
+ "blocks" => default_blocks
32
+ }
33
+ }
34
+ end
35
+
36
+ File.open(json_file_path, 'w') do |file|
37
+ file.write(JSON.pretty_generate(@json_content))
38
+ end
39
+ end
40
+
41
+ def add_blocks_to_json_file(max_blocks)
42
+ json_file_path = File.join(Dir.pwd, "config/sections/#{@section_handle}.json")
43
+ return unless File.exist?(json_file_path)
44
+
45
+ @json_content["blocks"] ||= []
46
+ @json_content["max_blocks"] = max_blocks
47
+
48
+ File.open(json_file_path, 'w') do |file|
49
+ file.write(JSON.pretty_generate(@json_content))
50
+ end
51
+ end
52
+
53
+ def add_block_to_json_file(block_type, block_name)
54
+ json_file_path = File.join(Dir.pwd, "config/sections/#{@section_handle}.json")
55
+ return unless File.exist?(json_file_path)
56
+
57
+ block = {
58
+ "type" => block_type,
59
+ "name" => block_name,
60
+ "settings" => []
61
+ }
62
+
63
+ @json_content["blocks"] << block
64
+
65
+ add_default_blocks(block_type)
66
+
67
+ File.open(json_file_path, 'w') do |file|
68
+ file.write(JSON.pretty_generate(@json_content))
69
+ end
70
+ end
71
+
72
+ def add_config(handle, type, *args)
73
+ json_file_path = File.join(Dir.pwd, "config/sections/#{@section_handle}.json")
74
+ return unless File.exist?(json_file_path)
75
+
76
+ @json_content["settings"] ||= []
77
+ config_item = build_config_item(handle, type, *args)
78
+ @json_content["settings"] << config_item
79
+
80
+ File.open(json_file_path, 'w') do |file|
81
+ file.write(JSON.pretty_generate(@json_content))
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def format_section_name(handle)
88
+ handle.split('_').map(&:capitalize).join(' ')
89
+ end
90
+
91
+ def build_config_item(handle, type, *args)
92
+ case type
93
+ when 'text', 'richtext'
94
+ {
95
+ "type" => type,
96
+ "handle" => handle,
97
+ "default" => args[0] || ""
98
+ }
99
+ when 'select', 'radio'
100
+ {
101
+ "type" => type,
102
+ "handle" => handle,
103
+ "default" => args[0] || "",
104
+ "options" => build_select_options(args[1..-1])
105
+ }
106
+ when 'range'
107
+ {
108
+ "type" => "range",
109
+ "handle" => handle,
110
+ "min" => args[0],
111
+ "max" => args[1],
112
+ "step" => args[2]
113
+ }
114
+ when 'checkbox'
115
+ {
116
+ "type" => "checkbox",
117
+ "handle" => handle,
118
+ "default" => args[0]
119
+ }
120
+ when 'subcategory'
121
+ {
122
+ "type" => "subcategory",
123
+ "handle" => handle,
124
+ "settings" => args[0]
125
+ }
126
+ else
127
+ {
128
+ "type" => type,
129
+ "handle" => handle,
130
+ "default" => args[0] || ""
131
+ }
132
+ end
133
+ end
134
+
135
+ def build_select_options(options)
136
+ options.each_slice(2).map do |value, handle|
137
+ {
138
+ "value" => value,
139
+ "handle" => handle
140
+ }
141
+ end
142
+ end
143
+
144
+ def default_blocks
145
+ Array.new(3) { { "type" => "collection" } }
146
+ end
147
+
148
+ def add_default_blocks(block_type)
149
+ @json_content["default"] ||= {}
150
+ @json_content["default"]["blocks"] ||= []
151
+
152
+ default_blocks = @json_content["default"]["blocks"]
153
+
154
+ if default_blocks.size < 3
155
+ default_blocks << { "type" => block_type }
156
+ else
157
+ default_blocks.shift
158
+ default_blocks << { "type" => block_type }
159
+ end
160
+
161
+ @json_content["default"]["blocks"] = default_blocks
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,38 @@
1
+ require 'fileutils'
2
+
3
+ module Shoperb
4
+ module Theme
5
+ module Editor
6
+ module Build
7
+ class Liquid
8
+ def initialize(section_handle)
9
+ @section_handle = section_handle
10
+ end
11
+
12
+ def create_or_update_liquid_file
13
+ sections_dir = File.join(Dir.pwd, "sections")
14
+ FileUtils.mkdir_p(sections_dir)
15
+ liquid_file_path = File.join(sections_dir, "#{@section_handle}.liquid")
16
+
17
+ unless File.exist?(liquid_file_path)
18
+ liquid_content = <<-LIQUID
19
+ {% capture section_id %}{{section.id}}{% endcapture %}
20
+ {% cache "#{@section_handle}_section", section.updated_at, section_id %}
21
+ <section data-section-id="{{ section.id }}" data-type="#{@section_handle}" class="{{ section.id }} #{@section_handle}_section">
22
+
23
+ ### YOUR CODE HERE ###
24
+
25
+ </section>
26
+ {% endcache %}
27
+ LIQUID
28
+
29
+ File.open(liquid_file_path, 'w') do |file|
30
+ file.write(liquid_content)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ module Shoperb
2
+ module Theme
3
+ module Editor
4
+ module Build
5
+ class Section
6
+ attr_reader :section_handle, :section_name
7
+
8
+ def initialize(section_handle, section_name = nil)
9
+ @section_handle = section_handle
10
+ @section_name = section_name || section_handle.split('_').map(&:capitalize).join(' ')
11
+ end
12
+
13
+ def create_or_update_json_file
14
+ sections_dir = File.join(Dir.pwd, "config/sections")
15
+ FileUtils.mkdir_p(sections_dir)
16
+ json_file_path = File.join(sections_dir, "#{section_handle}.json")
17
+
18
+ json_content = if File.exist?(json_file_path)
19
+ JSON.parse(File.read(json_file_path))
20
+ else
21
+ { "name" => section_name, "settings" => [] }
22
+ end
23
+
24
+ File.open(json_file_path, 'w') do |file|
25
+ file.write(JSON.pretty_generate(json_content))
26
+ end
27
+ end
28
+
29
+ def create_or_update_liquid_file
30
+ sections_dir = File.join(Dir.pwd, "sections")
31
+ FileUtils.mkdir_p(sections_dir)
32
+ liquid_file_path = File.join(sections_dir, "#{section_handle}.liquid")
33
+
34
+ liquid_content = <<-LIQUID
35
+ {% capture section_id %}{{section.id}}{% endcapture %}
36
+ {% cache "#{@section_handle}_section", section.updated_at, section_id %}
37
+ <section data-section-id="{{ section.id }}" data-type="#{@section_handle}" class="{{ section.id }} #{@section_handle}_section">
38
+ ### YOUR CODE HERE ###
39
+ </section>
40
+ {% endcache %}
41
+ LIQUID
42
+
43
+ File.open(liquid_file_path, 'w') do |file|
44
+ file.write(liquid_content)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end