Skip to content

Commit c3ebcb2

Browse files
committed
Refactor LevelsHelper#blockly_options
Split out cacheable level-specific portion into LevelsHelper#blockly_level_options.
1 parent ec3bc86 commit c3ebcb2

File tree

6 files changed

+110
-80
lines changed

6 files changed

+110
-80
lines changed

dashboard/app/controllers/levels_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def index
2121
# GET /levels/1
2222
# GET /levels/1.json
2323
def show
24-
set_videos_and_blocks_and_callouts
24+
set_videos_and_callouts
2525
@full_width = true
2626
end
2727

dashboard/app/controllers/script_levels_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def present_level
107107
@game = @level.game
108108
@stage = @script_level.stage
109109

110-
set_videos_and_blocks_and_callouts
110+
set_videos_and_callouts
111111

112112
load_level_source
113113

dashboard/app/helpers/levels_helper.rb

Lines changed: 102 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,9 @@ def url_from_path(path)
2020
"#{root_url.chomp('/')}#{path}"
2121
end
2222

23-
def set_videos_and_blocks_and_callouts
23+
def set_videos_and_callouts
2424
@autoplay_video_info = select_and_track_autoplay_video
2525
@callouts = select_and_remember_callouts(params[:show_callouts])
26-
27-
if @level.is_a? Blockly
28-
@toolbox_blocks ||=
29-
@level.try(:project_template_level).try(:toolbox_blocks) ||
30-
@level.toolbox_blocks
31-
32-
@start_blocks ||=
33-
@level.try(:project_template_level).try(:start_blocks) ||
34-
@level.start_blocks
35-
end
3626
end
3727

3828
def select_and_track_autoplay_video
@@ -137,23 +127,25 @@ def boolean_string_false
137127
"false"
138128
end
139129

140-
# Code for generating the blockly options hash
141-
def blockly_options(local_assigns={})
130+
# Generate a level-specific blockly options hash
131+
def blockly_level_options(level)
142132
# Use values from properties json when available (use String keys instead of Symbols for consistency)
143-
level = @level.properties.dup || {}
133+
level_prop = level.properties.dup || {}
134+
135+
extra_options = level.embed == 'true' ? {embed: true, hide_source: true, no_padding: true, show_finish: true} : {}
144136

145137
# Set some specific values
146-
level['puzzle_number'] = @script_level ? @script_level.position : 1
147-
level['stage_total'] = @script_level ? @script_level.stage_total : 1
148-
if @level.is_a?(Maze) && @level.step_mode
149-
@level.step_mode = blockly_value(@level.step_mode)
150-
level['step'] = @level.step_mode == 1 || @level.step_mode == 2
151-
level['stepOnly'] = @level.step_mode == 2
138+
139+
if level.is_a? Blockly
140+
level_prop['start_blocks'] = level.try(:project_template_level).try(:start_blocks) || level.start_blocks
141+
level_prop['toolbox_blocks'] = level.try(:project_template_level).try(:toolbox_blocks) || level.toolbox_blocks
152142
end
153143

154-
# Pass blockly the edit mode: "<start|toolbox|required>_blocks"
155-
level['edit_blocks'] = params[:type]
156-
level['edit_blocks_success'] = t('builder.success')
144+
if level.is_a?(Maze) && level.step_mode
145+
step_mode = blockly_value(level.step_mode)
146+
level_prop['step'] = step_mode == 1 || step_mode == 2
147+
level_prop['stepOnly'] = step_mode == 2
148+
end
157149

158150
# Map Dashboard-style names to Blockly-style names in level object.
159151
# Dashboard underscore_names mapped to Blockly lowerCamelCase, or explicit 'Dashboard:Blockly'
@@ -220,83 +212,119 @@ def blockly_options(local_assigns={})
220212
is_project_level
221213
failure_message_override
222214
).map{ |x| x.include?(':') ? x.split(':') : [x,x.camelize(:lower)]}]
223-
.each do |dashboard, blockly|
224-
# Select first valid value from 1. local_assigns, 2. property of @level object, 3. named instance variable, 4. properties json
215+
.each do |dashboard, blockly|
216+
# Select value from extra options, or properties json
225217
# Don't override existing valid (non-nil/empty) values
226-
property = local_assigns[dashboard.to_sym].presence ||
227-
@level[dashboard].presence ||
228-
instance_variable_get("@#{dashboard}").presence ||
229-
level[dashboard].presence
230-
value = blockly_value(level[blockly] || property)
231-
level[blockly] = value unless value.nil? # make sure we convert false
218+
property = extra_options[dashboard].presence ||
219+
level_prop[dashboard].presence
220+
value = blockly_value(level_prop[blockly] || property)
221+
level_prop[blockly] = value unless value.nil? # make sure we convert false
232222
end
233223

234-
level['images'] = JSON.parse(level['images']) if level['images'].present?
224+
level_prop['images'] = JSON.parse(level_prop['images']) if level_prop['images'].present?
235225

236226
# Blockly requires startDirection as an integer not a string
237-
level['startDirection'] = level['startDirection'].to_i if level['startDirection'].present?
238-
level['sliderSpeed'] = level['sliderSpeed'].to_f if level['sliderSpeed']
239-
level['scale'] = {'stepSpeed' => @level.properties['step_speed'].to_i } if @level.properties['step_speed'].present?
227+
level_prop['startDirection'] = level_prop['startDirection'].to_i if level_prop['startDirection'].present?
228+
level_prop['sliderSpeed'] = level_prop['sliderSpeed'].to_f if level_prop['sliderSpeed']
229+
level_prop['scale'] = {'stepSpeed' => level_prop['step_speed'].to_i } if level_prop['step_speed'].present?
240230

241231
# Blockly requires these fields to be objects not strings
242232
%w(map initialDirt finalDirt goal soft_buttons).each do |x|
243-
level[x] = JSON.parse(level[x]) if level[x].is_a? String
233+
level_prop[x] = JSON.parse(level_prop[x]) if level_prop[x].is_a? String
244234
end
245235

246236
# Blockly expects fn_successCondition and fn_failureCondition to be inside a 'goals' object
247-
if level['fn_successCondition'] || level['fn_failureCondition']
248-
level['goal'] = {fn_successCondition: level['fn_successCondition'], fn_failureCondition: level['fn_failureCondition']}
249-
level.delete('fn_successCondition')
250-
level.delete('fn_failureCondition')
237+
if level_prop['fn_successCondition'] || level_prop['fn_failureCondition']
238+
level_prop['goal'] = {fn_successCondition: level_prop['fn_successCondition'], fn_failureCondition: level_prop['fn_failureCondition']}
239+
level_prop.delete('fn_successCondition')
240+
level_prop.delete('fn_failureCondition')
241+
end
242+
243+
app_options = {}
244+
245+
app_options[:levelGameName] = level.game.name if level.game
246+
app_options[:skinId] = level.skin if level.is_a?(Blockly)
247+
248+
# Set some values that Blockly expects on the root of its options string
249+
app_options.merge!({
250+
baseUrl: "#{ActionController::Base.asset_host}/blockly/",
251+
app: level.game.try(:app),
252+
levelId: level.level_num,
253+
level: level_prop,
254+
cacheBust: blockly_cache_bust,
255+
droplet: level.game.try(:uses_droplet?),
256+
pretty: Rails.configuration.pretty_apps ? '' : '.min',
257+
})
258+
259+
# Move these values up to the root
260+
%w(hideSource share noPadding embed).each do |key|
261+
app_options[key.to_sym] = level_prop[key]
262+
level_prop.delete key
251263
end
252264

253-
#Fetch localized strings
254-
if @level.level_num_custom?
255-
loc_val = data_t("instructions", "#{@level.name}_instruction")
265+
app_options
266+
end
267+
268+
# Code for generating the blockly options hash
269+
def blockly_options
270+
271+
## Level-dependent options
272+
l = @level
273+
app_options = Rails.cache.fetch("#{l.cache_key}/blockly_level_options") do
274+
blockly_level_options(l)
275+
end
276+
level_options = app_options[:level]
277+
278+
## Locale-dependent option
279+
# Fetch localized strings
280+
if l.level_num_custom?
281+
loc_val = data_t("instructions", "#{l.name}_instruction")
256282
unless I18n.locale.to_s == 'en-us' || loc_val.nil?
257-
level['instructions'] = loc_val
283+
level_options['instructions'] = loc_val
258284
end
259285
else
260286
%w(instructions).each do |label|
261-
val = [@level.game.app, @level.game.name].map { |name|
262-
data_t("level.#{label}", "#{name}_#{@level.level_num}")
287+
val = [l.game.app, l.game.name].map { |name|
288+
data_t("level.#{label}", "#{name}_#{l.level_num}")
263289
}.compact.first
264-
level[label] ||= val unless val.nil?
290+
level_options[label] ||= val unless val.nil?
265291
end
266292
end
267293

268-
# Set some values that Blockly expects on the root of its options string
269-
app_options = {
270-
baseUrl: "#{ActionController::Base.asset_host}/blockly/",
271-
app: @game.try(:app),
272-
levelId: @level.level_num,
273-
level: level,
274-
callouts: @callouts,
275-
cacheBust: blockly_cache_bust,
276-
autoplayVideo: @autoplay_video_info,
277-
report: {
278-
fallback_response: @fallback_response,
279-
callback: @callback,
280-
},
281-
droplet: @game.try(:uses_droplet?),
282-
pretty: Rails.configuration.pretty_apps ? '' : '.min',
283-
applabUserId: @applab_user_id,
284-
}
285-
app_options[:scriptId] = @script.id if @script
286-
app_options[:levelGameName] = @level.game.name if @level.game
287-
app_options[:skinId] = @level.skin if @level.is_a?(Blockly)
294+
## Script-dependent option
295+
script = @script
296+
app_options[:scriptId] = script.id if script
297+
298+
## ScriptLevel-dependent option
299+
script_level = @script_level
300+
level_options['puzzle_number'] = script_level ? script_level.position : 1
301+
level_options['stage_total'] = script_level ? script_level.stage_total : 1
302+
303+
## LevelSource-dependent options
288304
app_options[:level_source_id] = @level_source.id if @level_source
289-
app_options[:sendToPhone] = request.location.try(:country_code) == 'US' ||
290-
(!Rails.env.production? && request.location.try(:country_code) == 'RD') if request
291305
app_options[:send_to_phone_url] = @phone_share_url if @phone_share_url
292-
app_options[:disableSocialShare] = true if (@current_user && @current_user.under_13?) || @embed
293306

294-
# Move these values up to the root
295-
%w(hideSource share noPadding embed).each do |key|
296-
app_options[key.to_sym] = level[key]
297-
level.delete key
307+
## Edit blocks-dependent options
308+
if @edit_blocks
309+
# Pass blockly the edit mode: "<start|toolbox|required>_blocks"
310+
level_options['edit_blocks'] = @edit_blocks
311+
level_options['edit_blocks_success'] = t('builder.success')
298312
end
299313

314+
## User/session-dependent options
315+
app_options[:callouts] = @callouts
316+
app_options[:autoplayVideo] = @autoplay_video_info
317+
app_options[:disableSocialShare] = true if (@current_user && @current_user.under_13?) || @embed
318+
app_options[:applabUserId] = @applab_user_id
319+
app_options[:report] = {
320+
fallback_response: @fallback_response,
321+
callback: @callback,
322+
}
323+
324+
## Request-dependent option
325+
app_options[:sendToPhone] = request.location.try(:country_code) == 'US' ||
326+
(!Rails.env.production? && request.location.try(:country_code) == 'RD') if request
327+
300328
app_options
301329
end
302330

dashboard/app/views/levels/_blockly.html.haml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
:javascript
1515
//<![CDATA[
1616
var script_path = "#{@script_level && build_script_level_path(@script_level)}";
17-
var appOptions = #{blockly_options(local_assigns).to_json};
17+
var appOptions = #{blockly_options.to_json};
1818
appOptions.locale = '#{js_locale}';
1919
//]]>
2020

dashboard/app/views/levels/show.html.haml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#title
2121
%h4{style: 'margin-left: auto; margin-right: auto; max-width: 800px;'}!= @level.properties['instructions']
2222
- # Pass special parameters to Blockly partial for embedded level type.
23-
= render partial: 'levels/blockly', locals: {embed: true, hide_source: true, no_padding: true, show_finish: true}
23+
= render partial: 'levels/blockly'
2424
- elsif @level.is_a?(DSLDefined)
2525
= render partial: "levels/#{@level.class.to_s.underscore}"
2626
%div{style: 'width: 400px'}= render partial: 'levels/reference_area'

dashboard/test/controllers/script_levels_controller_test.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ class ScriptLevelsControllerTest < ActionController::TestCase
4848

4949
assert_response :success
5050
# start blocks comes from project_level not real_level
51-
assert_equal '<xml/>', assigns(:start_blocks)
51+
app_options = blockly_level_options(assigns(:level))
52+
assert_equal '<xml/>', app_options[:level]['start_blocks']
5253
end
5354

5455
test 'project template level sets toolbox blocks' do
@@ -66,7 +67,8 @@ class ScriptLevelsControllerTest < ActionController::TestCase
6667

6768
assert_response :success
6869
# toolbox blocks comes from project_level not real_level
69-
assert_equal '<xml><toolbox/></xml>', assigns(:toolbox_blocks)
70+
app_options = blockly_level_options(assigns(:level))
71+
assert_equal '<xml><toolbox/></xml>', app_options[:level]['toolbox_blocks']
7072
end
7173

7274
test 'should show video in twenty hour script level' do

0 commit comments

Comments
 (0)