Class: ProductSpecification
Overview
== Schema Information
Table name: product_specifications
Database name: primary
id :integer not null, primary key
description :text
do_not_merge :boolean default(FALSE), not null
formatter :string
grouping :string
has_liquid_blurb :boolean default(FALSE), not null
method :string(255) not null
name :string(255) not null
position :integer not null
print_on_item_label :boolean default(FALSE), not null
propagation :integer default("unrestricted")
sku_regexp :string
template :boolean default(FALSE)
text_blurb :text
token :string(255)
tokenizer_character :string(1)
translations :jsonb
units :string(255)
visibility :integer not null
created_at :datetime not null
updated_at :datetime not null
creator_id :integer
image_id :integer
product_category_id :integer
product_line_id :integer
template_product_specification_id :integer
updater_id :integer
Indexes
idx_name_template (name,template)
idx_pc_id_product_line_id (product_category_id,product_line_id)
idx_propagation_token_template (propagation,token,template)
index_product_specifications_grouping (grouping)
index_product_specifications_on_image_id (image_id)
index_product_specifications_on_position (position)
index_product_specifications_on_product_line_id (product_line_id)
index_product_specifications_on_token (token)
product_specifications_template_product_specification_id_idx (template_product_specification_id)
Foreign Keys
fk_rails_... (image_id => digital_assets.id)
fk_rails_... (product_category_id => product_categories.id)
fk_rails_... (product_line_id => product_lines.id)
fk_rails_... (template_product_specification_id => product_specifications.id) ON DELETE => nullify
Constant Summary
collapse
- TRANSLATION_NAMESPACE =
'ProductSpecification'
- SAFE_METHODS =
%w[
text
base_weight
total_shipping_weight
btu_per_hour
calculate_amps
calculate_kilowatts
calculate_number_of_fixing_strips_at_3_in
calculate_number_of_fixing_strips_at_4_in
calculate_number_of_fixing_strips_at_5_in
calculate_ohms
calculate_ohms_per_ft
calculate_geometric_sqft
calculate_sqft
calculate_watts
calculate_watts_per_sqft
calculate_heated_area_sqft_for_panels
calculate_heated_area_sqm_for_panels
calculate_thermal_power_per_hour
calculate_coverage_for_membrane
country_of_origin_name
coverage_at_3_5_in
coverage_at_3_75_in
coverage_at_3_in
coverage_at_4_in
coverage_at_4_5_in
coverage_at_5_in
harmonization_code
ohms
package_contents_friendly
package_content_html
sensor
sku
upc
watts_per_linear_feet
watts_per_sqft_at_3_in_spacing
watts_per_sqft_at_3_5_in_spacing
watts_per_sqft_at_3_75_in_spacing
watts_per_sqft_at_4_in_spacing
watts_per_sqft_at_4_5_in_spacing
watts_per_sqft_at_5_in_spacing
width_by_length_text
width_by_length_text_2
calculate_size_2d
calculate_size_2d_in
calculate_size_2d_ft
calculate_size_2d_metric_mm
calculate_size_2d_metric_cm
calculate_size_3d
calculate_size_3d_in
calculate_size_3d_ft
calculate_size_3d_metric_mm
calculate_size_3d_metric_cm
].freeze
- NUMERIC_SPECS =
%w[
maximum_current maximum_current_per_relay num_poles
length width cold_lead_length sqft coverage watts ohms voltage amps
].freeze
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
#do_not_compact_translation_container
#creator, #updater
Has and belongs to many
collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#affected_items ⇒ Object
-
#affected_items_ids ⇒ Object
-
#async_refresh_items ⇒ Object
-
#auto_translate(reset_previous_values: false, locales: nil, attributes: nil) ⇒ Object
We're going to auto translate this spec for each locale, we translate name, grouping, unit, and text content TranslationKey is a proxy.
-
#cached_liquid_template(content) ⇒ Object
Returns the parsed Liquid template for the given content string, or nil if it contains no Liquid variables.
-
#dynamic_method? ⇒ Boolean
-
#get_dynamic_spec_data(target, locale) ⇒ Object
-
#get_specification_data(target, options = {}) ⇒ Object
Target could be: item, catalog_item, product_line.
-
#grouping_select_options(locale = nil) ⇒ Object
-
#import_translation_keys(force: false) ⇒ Object
This method will import existing translations or link to existing ones.
-
#name_select_options(filter_grouping: nil) ⇒ Object
-
#refresh_items ⇒ Object
-
#render_priority ⇒ Object
spec value that are static will be first returned, this is so they can be computed and established first in case they are used by future dynamic calculations.
-
#rendering_priority ⇒ Object
-
#set_has_liquid_blurb ⇒ Object
-
#summary ⇒ Object
-
#text_blurb_is_liquid? ⇒ Boolean
-
#text_method? ⇒ Boolean
-
#text_output(locale = Mobility.locale) ⇒ Object
-
#to_ruby_unit(locale = :en) ⇒ Object
-
#to_s ⇒ Object
-
#trim_empty_translations ⇒ Object
-
#unit_symbol ⇒ Object
-
#units_select_options(locale = Mobility.locale) ⇒ Object
#compact_translation_container, skip_compaction_for
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#grouping ⇒ Object
140
|
# File 'app/models/product_specification.rb', line 140
validates :method, :name, :grouping, presence: true
|
#method ⇒ Object
140
|
# File 'app/models/product_specification.rb', line 140
validates :method, :name, :grouping, presence: true
|
#name ⇒ Object
140
|
# File 'app/models/product_specification.rb', line 140
validates :method, :name, :grouping, presence: true
|
#should_auto_translate ⇒ Object
Returns the value of attribute should_auto_translate.
123
124
125
|
# File 'app/models/product_specification.rb', line 123
def should_auto_translate
@should_auto_translate
end
|
#skip_spec_refresh ⇒ Object
Returns the value of attribute skip_spec_refresh.
123
124
125
|
# File 'app/models/product_specification.rb', line 123
def skip_spec_refresh
@skip_spec_refresh
end
|
#text_blurb ⇒ Object
149
|
# File 'app/models/product_specification.rb', line 149
validates :text_blurb, numericality: { if: :spec_requires_numeric? }
|
#text_blurb_lookup ⇒ Object
Returns the value of attribute text_blurb_lookup.
123
124
125
|
# File 'app/models/product_specification.rb', line 123
def text_blurb_lookup
@text_blurb_lookup
end
|
#units ⇒ Object
142
|
# File 'app/models/product_specification.rb', line 142
validates :units, ruby_unit: true
|
Class Method Details
.by_product_category_id ⇒ ActiveRecord::Relation<ProductSpecification>
A relation of ProductSpecifications that are by product category id. Active Record Scope
187
188
189
190
191
192
193
194
195
|
# File 'app/models/product_specification.rb', line 187
scope :by_product_category_id, ->(*pc_id) {
ids = [pc_id].flatten.compact
next none if ids.empty?
paths = ProductCategory.where(id: ids).pluck(:ltree_path_ids).compact
next none if paths.empty?
joins(:product_category).where(ProductCategory[:ltree_path_ids].ltree_descendant(paths))
}
|
.by_product_category_id_ancestry ⇒ ActiveRecord::Relation<ProductSpecification>
A relation of ProductSpecifications that are by product category id ancestry. Active Record Scope
196
197
198
199
|
# File 'app/models/product_specification.rb', line 196
scope :by_product_category_id_ancestry, ->(*pc_id) {
ids = [pc_id].flatten.compact
ids.empty? ? none : where(product_category_id: ProductCategory.self_and_ancestors_ids(ids))
}
|
.by_product_line_id ⇒ ActiveRecord::Relation<ProductSpecification>
A relation of ProductSpecifications that are by product line id. Active Record Scope
174
175
176
177
178
179
180
181
182
|
# File 'app/models/product_specification.rb', line 174
scope :by_product_line_id, ->(*pl_id) {
ids = [pl_id].flatten.compact
next none if ids.empty?
paths = ProductLine.where(id: ids).pluck(:ltree_path_ids).compact
next none if paths.empty?
joins(:product_line).where(ProductLine[:ltree_path_ids].ltree_descendant(paths))
}
|
.by_product_line_id_ancestry ⇒ ActiveRecord::Relation<ProductSpecification>
A relation of ProductSpecifications that are by product line id ancestry. Active Record Scope
183
184
185
186
|
# File 'app/models/product_specification.rb', line 183
scope :by_product_line_id_ancestry, ->(*pl_id) {
ids = [pl_id].flatten.compact
ids.empty? ? none : where(product_line_id: ProductLine.self_and_ancestors_ids(ids))
}
|
.compact_positions ⇒ Object
377
378
379
380
381
|
# File 'app/models/product_specification.rb', line 377
def self.compact_positions
readable_order.each_with_index do |pf, i|
pf.update_column(:position, i)
end
end
|
.convert_unit_for_locale(ruby_unit, target_locale) ⇒ Object
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
# File 'app/models/product_specification.rb', line 217
def self.convert_unit_for_locale(ruby_unit, target_locale)
lang_code, country_code = target_locale.to_s.split('-')
conversion_map = {
'yd' => 'm',
'ft' => 'mm', 'in' => 'mm',
'lbs' => 'kg',
'oz' => 'g',
'degF' => 'degC',
'gal' => 'l',
'quart' => 'l',
'pint' => 'l',
'sqft' => 'm2',
'sqin' => 'm2'
}
target_unit = conversion_map[ruby_unit.units]
return ruby_unit if target_unit.nil? ||
country_code == 'US' ||
(country_code.nil? && lang_code == 'en')
if target_unit == 'mm' && ruby_unit.convert_to(target_unit).scalar.to_i >= 1000
target_unit = 'm' end
if target_unit == 'g' && ruby_unit.convert_to(target_unit).scalar.to_i >= 1000
target_unit = 'kg' end
target_unit = 'ml' if target_unit == 'l' && ruby_unit.convert_to(target_unit).scalar.to_i < 100
ruby_unit.convert_to(target_unit)
end
|
.empty_non_template_specs ⇒ ActiveRecord::Relation<ProductSpecification>
A relation of ProductSpecifications that are empty non template specs. Active Record Scope
173
|
# File 'app/models/product_specification.rb', line 173
scope :empty_non_template_specs, -> { where(text_blurb: nil).where(template: false) }
|
.express(raw_value, native_unit: nil, formatter: nil, locale: nil) ⇒ Object
Formats a value using a formatter and unit
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
|
# File 'app/models/product_specification.rb', line 494
def self.express(raw_value, native_unit: nil, formatter: nil, locale: nil)
return raw_value unless native_unit
if native_unit == 'y' s = "#{raw_value.to_i} "
s << (raw_value.to_i > 1 ? 'years' : 'year')
return s
end
native_value = "#{raw_value} #{native_unit}".squish
native_value_pretty = "#{raw_value}#{UnitHelper.unit_symbol(native_unit)}".squish
return native_value_pretty if formatter.blank? || (locale.present? && locale.to_s.first(2) != 'en')
native_value_unitized = begin
RubyUnits::Unit.new(native_value)
rescue StandardError
nil
end
return native_value unless native_value_unitized
case formatter
when 'FeetAndInches'
inches = native_value_unitized.convert_to('in').scalar
r = UnitConversions.inches_to_feetinches(inches, separator: ' ')
when 'PoundsAndOunces'
s = []
ounces = native_value_unitized.convert_to('oz').scalar
pounds = (ounces / 16).truncate
ounces = (ounces % 16)
if pounds.positive?
pounds = ActionController::Base.helpers.number_with_precision pounds, precision: 0, strip_insignificant_zeros: true
s << "#{pounds} lbs"
end
if ounces.positive?
ounces = ActionController::Base.helpers.number_with_precision ounces, precision: 0, strip_insignificant_zeros: true
s << "#{ounces} oz"
end
r = s.join(', ')
when 'ClosestRational'
fs = []
fs << UnitConversions.closest_rational(raw_value.to_f).to_s
fs << UnitHelper.unit_symbol(native_unit) if native_unit
r = fs.compact.join
else
native_value_unitized.to_s
v = ActionController::Base.helpers.number_with_precision native_value_unitized.scalar.to_f, precision: 4, strip_insignificant_zeros: true
r = "#{v}#{UnitHelper.unit_symbol(native_value_unitized.units)}".squish
end
r
end
|
489
490
491
|
# File 'app/models/product_specification.rb', line 489
def self.formatters_for_select
%w[FeetAndInches PoundsAndOunces ClosestRational]
end
|
.generate_name_from_token_and_grouping(token, grouping = nil) ⇒ Object
745
746
747
748
749
750
751
752
753
|
# File 'app/models/product_specification.rb', line 745
def self.generate_name_from_token_and_grouping(token, grouping = nil)
token = token.to_s
if grouping&.start_with?('X-')
namespace = grouping.split('-').last.parameterize.underscore
token = token.gsub("#{namespace}_", '')
end
token.humanize.capitalize
end
|
.generate_token_from_name_and_grouping(name, grouping = nil) ⇒ Object
734
735
736
737
738
739
740
741
742
743
|
# File 'app/models/product_specification.rb', line 734
def self.generate_token_from_name_and_grouping(name, grouping = nil)
tokenized_name = name.parameterize.underscore
if grouping&.start_with?('X-')
namespace = grouping.split('-').last.parameterize.underscore
tokenized_name = "#{namespace}_#{tokenized_name}"
end
tokenized_name
end
|
.grouping_select_options(locale = nil) ⇒ Object
422
423
424
425
426
427
|
# File 'app/models/product_specification.rb', line 422
def self.grouping_select_options(locale = nil)
locales = [locale, LocaleUtility.root_content_locale(locale)].uniq.compact.presence || Mobility.available_locales
locales.flat_map do |locale|
Mobility.with_locale(locale) { i18n.where.not(grouping: [nil, '']).distinct.pluck(:grouping) }
end.compact.uniq.sort
end
|
.import_translation_keys ⇒ Object
383
384
385
386
387
|
# File 'app/models/product_specification.rb', line 383
def self.import_translation_keys
ProductSpecification.where.not(translations: {}).where(units: nil).find_each do |ps|
ps.import_translation_keys
end
end
|
.locales_for_language(lang_code) ⇒ Object
209
210
211
|
# File 'app/models/product_specification.rb', line 209
def self.locales_for_language(lang_code)
translated_locales.select { |locale| locale.to_s.first(2) == lang_code.to_s }
end
|
.method_select_options ⇒ Object
329
330
331
|
# File 'app/models/product_specification.rb', line 329
def self.method_select_options
where.not(method: [nil, '']).order(:method).distinct.pluck(:method)
end
|
.methods_for_select ⇒ Object
447
448
449
|
# File 'app/models/product_specification.rb', line 447
def self.methods_for_select
SAFE_METHODS
end
|
.name_select_options(grouping: nil) ⇒ Object
408
409
410
411
412
|
# File 'app/models/product_specification.rb', line 408
def self.name_select_options(grouping: nil)
search_scope = ProductSpecification.all.order(:name)
search_scope = search_scope.where(grouping:) if grouping
[['All', nil]] + search_scope.distinct.pluck(:name).map { |name| [name, name] }
end
|
.next_position ⇒ Object
TODO, we have acts as list, don't need this!
373
374
375
|
# File 'app/models/product_specification.rb', line 373
def self.next_position
(maximum(:position) || 0) + 1
end
|
.options_scope(options = {}, _store_id = nil) ⇒ Object
356
357
358
359
360
361
362
|
# File 'app/models/product_specification.rb', line 356
def self.options_scope(options = {}, _store_id = nil)
Rails.logger.debug("ProductLatchable.options_scope", class_name: self.class.to_s)
res = all
crits = (options || {}).reject { |_k, v| v.blank? or v == 'NULL' }
res = res.where(crits) if crits.present?
res
end
|
A relation of ProductSpecifications that are positioned. Active Record Scope
200
|
# File 'app/models/product_specification.rb', line 200
scope :positioned, -> { order(:position) }
|
.product_category_select_options ⇒ Object
368
369
370
|
# File 'app/models/product_specification.rb', line 368
def self.product_category_select_options
[['All', nil]] + ProductCategory.joins(to_s.tableize.to_sym).order(:priority).pluck(:lineage_expanded, :id).uniq
end
|
.product_line_select_options ⇒ Object
364
365
366
|
# File 'app/models/product_specification.rb', line 364
def self.product_line_select_options
[['All', nil]] + ProductLine.joins(to_s.tableize.to_sym).order(:priority).pluck(:lineage_expanded, :id).uniq
end
|
.propagations_for_select ⇒ Object
321
322
323
|
# File 'app/models/product_specification.rb', line 321
def self.propagations_for_select
ProductSpecification.propagations.keys.map { |p| [p.titleize, p] }
end
|
.readable_order ⇒ ActiveRecord::Relation<ProductSpecification>
A relation of ProductSpecifications that are readable order. Active Record Scope
167
168
169
170
171
172
|
# File 'app/models/product_specification.rb', line 167
scope :readable_order, -> {
left_outer_joins(:product_line)
.left_outer_joins(:product_category)
.order(:position, ProductLine[:priority], ProductCategory[:priority], :name, :method)
.readonly(false)
}
|
.token_select_options ⇒ Object
325
326
327
|
# File 'app/models/product_specification.rb', line 325
def self.token_select_options
where.not(token: [nil, '']).order(:token).distinct.pluck(:token)
end
|
.translated_lang_codes ⇒ Object
213
214
215
|
# File 'app/models/product_specification.rb', line 213
def self.translated_lang_codes
translated_locales.map { |l| l.to_s.first(2) }.uniq.sort.map(&:to_sym) - [:en]
end
|
.translated_locales ⇒ Object
204
205
206
207
|
# File 'app/models/product_specification.rb', line 204
def self.translated_locales
[] + EDITABLE_TRANSLATIONS.map(&:to_sym) - [I18n.default_locale]
end
|
.units_select_options(locale = Mobility.locale) ⇒ Object
397
398
399
400
401
|
# File 'app/models/product_specification.rb', line 397
def self.units_select_options(locale = Mobility.locale)
I18n.with_locale(locale) do
UnitHelper.all_units
end
end
|
.visibilities_for_select ⇒ Object
315
316
317
318
319
|
# File 'app/models/product_specification.rb', line 315
def self.visibilities_for_select
[['Internal, for heatwave use only', 'internal'],
['Hidden, exposed to API and Web but recommended not displayed', 'hidden'],
['Open, exposed to API and Web and display recommended', 'open_visibility']]
end
|
Instance Method Details
#affected_items ⇒ Object
614
615
616
617
618
619
620
621
622
623
624
|
# File 'app/models/product_specification.rb', line 614
def affected_items
if items.present?
items
else
item_query = Item.active
item_query = item_query.by_product_category_id(product_category_id) if product_category_id.present?
item_query = item_query.by_product_line_id(product_line_id) if product_line_id.present?
item_query = item_query.where('items.sku ~ ?', sku_regexp) if sku_regexp.present?
item_query
end
end
|
#affected_items_ids ⇒ Object
644
645
646
|
# File 'app/models/product_specification.rb', line 644
def affected_items_ids
affected_items.pluck(:id)
end
|
#async_refresh_items ⇒ Object
652
653
654
655
656
|
# File 'app/models/product_specification.rb', line 652
def async_refresh_items
return if skip_spec_refresh
ProductSpecsWorker.perform_async(id)
end
|
#auto_translate(reset_previous_values: false, locales: nil, attributes: nil) ⇒ Object
We're going to auto translate this spec for each locale, we translate
name, grouping, unit, and text content
TranslationKey is a proxy
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
# File 'app/models/product_specification.rb', line 258
def auto_translate(reset_previous_values: false, locales: nil, attributes: nil)
result = {}
if text_blurb_en.blank?
logger.warn "Auto translate called on Product Specification #{id} with a blank text_blurb_en, skipping"
return result
end
locales = if locales.nil?
self.class.translated_locales
else locales.map(&:to_sym) & self.class.translated_locales
end
ruby_unit = to_ruby_unit
locales.each do |target_locale|
translated_text = nil
if ruby_unit
converted_unit = self.class.convert_unit_for_locale(ruby_unit, target_locale)
translated_units = converted_unit.units
translated_text = (I18n.with_locale(target_locale) { ActionController::Base.helpers.number_with_precision(converted_unit.scalar.to_f, strip_insignificant_zeros: true, precision: 2) } if text_blurb.present?)
else
translated_text = TranslationKey.translate(text_blurb_en, target_locale, namespace: TRANSLATION_NAMESPACE, resource: self, resource_attribute: 'text_blurb') if text_blurb_en&.match?(/[a-zA-Z]{3}/)
translated_text ||= text_blurb_en
translated_units = nil
end
if reset_previous_values
target_locales_for_purge = Mobility.available_locales.select { |l| l.to_s.starts_with?(target_locale.to_s) }
target_locales_for_purge.each do |target_locale_for_purge|
Mobility.with_locale(target_locale_for_purge) do
self.name = nil
self.grouping = nil
self.units = nil
self.text_blurb = nil
end
end
end
Mobility.with_locale(target_locale) do
result[target_locale] = {
units: self.units ||= translated_units,
text_blurb: self.text_blurb ||= translated_text
}
end
end
@should_auto_translate = false
@skip_spec_refresh = true
save && result
end
|
#cached_liquid_template(content) ⇒ Object
Returns the parsed Liquid template for the given content string, or nil if it contains no
Liquid variables. Results are memoized by content so multi-locale renders share a single parse
for identical content, and the ~99.5% of non-liquid specs skip parsing entirely after the
first call.
634
635
636
637
638
639
640
641
642
|
# File 'app/models/product_specification.rb', line 634
def cached_liquid_template(content)
return nil if content.blank?
@cached_liquid_templates ||= {}
return @cached_liquid_templates[content] if @cached_liquid_templates.key?(content)
lt = Liquid::ParseEnvironment.parse(content.to_s)
@cached_liquid_templates[content] = lt.root.nodelist.any? { |node| node.is_a?(Liquid::Variable) } ? lt : nil
end
|
#dynamic_method? ⇒ Boolean
443
444
445
|
# File 'app/models/product_specification.rb', line 443
def dynamic_method?
!text_method?
end
|
#get_dynamic_spec_data(target, locale) ⇒ Object
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
|
# File 'app/models/product_specification.rb', line 552
def get_dynamic_spec_data(target, locale)
target = target.call if target.respond_to?(:call)
if locale != I18n.default_locale
method_localized = :"#{method}_#{LocaleUtility.parameterized_locale(locale)}"
spec_data = target.try(method_localized)
end
if spec_data.blank?
Mobility.with_locale(I18n.default_locale) do
spec_data = target.try(method.to_sym)
end
end
spec_data
end
|
#get_specification_data(target, options = {}) ⇒ Object
Target could be: item, catalog_item, product_line
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
|
# File 'app/models/product_specification.rb', line 570
def get_specification_data(target, options = {})
locale = (options[:locale] || Mobility.locale).to_sym
spec_data = nil
if dynamic_method?
target_chain = case target.class.name
when 'Item'
target.get_self_and_kit_items(spec_only: true).to_a
when 'CatalogItem'
target.item.get_self_and_kit_items(spec_only: true).to_a
else
raise StandardError.new("#{target} is not a valid Item or CatalogItem")
end
target_chain << target.primary_product_line if target.primary_product_line_id.present?
target_chain << product_line if product_line_id && product_line_id != target.primary_product_line_id
target_chain.each do |t|
spec_data = get_dynamic_spec_data(t, locale)
break if spec_data
end
end
spec_data ||= Mobility.with_locale(locale) { text_blurb.presence }
spec_data ||= Mobility.with_locale(I18n.default_locale) { text_blurb.presence }
if spec_data.present? && target.respond_to?(:token_specs_values_for_liquid) && (lt = cached_liquid_template(spec_data))
spec_data = lt.render(target.token_specs_values_for_liquid(include_legacy: false))
end
spec_data = TypeCoercer.coerce(spec_data)
result = OpenStruct.new(raw: spec_data, formatted: spec_data)
return result if options[:skip_units] || units.blank? || spec_data.blank?
values = if tokenizer_character.present?
spec_data.to_s.split(tokenizer_character)
else
[spec_data]
end
localized_units = Mobility.with_locale(locale) { units } || units
result.formatted = values.map { |v| self.class.express(v, native_unit: localized_units, formatter:, locale:) }.join(', ')
result
end
|
#grouping_select_options(locale = nil) ⇒ Object
414
415
416
417
418
419
420
|
# File 'app/models/product_specification.rb', line 414
def grouping_select_options(locale = nil)
r = self.class.grouping_select_options(locale)
Mobility.with_locale(locale) do
r << grouping if grouping.present?
end
r.compact.uniq.sort
end
|
135
|
# File 'app/models/product_specification.rb', line 135
belongs_to :image, optional: true
|
#import_translation_keys(force: false) ⇒ Object
This method will import existing translations or link to existing ones
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
|
# File 'app/models/product_specification.rb', line 334
def import_translation_keys(force: false)
tkrs = []
translations.each do |(locale_string, existing_translations)|
existing_translations.each do |attr, translation|
puts "--- ps: #{id} -- #{attr} -- #{translation}"
next if translation_key_resources.joins(translation_key: :translations).where(resource_attribute: attr).joins(translation_key: :translations).where(TranslationText[:locale].eq(locale_string)).exists?
next unless (text = TranslationKey.newline_normalize(send(:"#{attr}_en"))).present? next unless force || text&.match?(/[a-zA-Z]{3}/)
tk = TranslationKey.where(key: text, namespace: TRANSLATION_NAMESPACE).first_or_create!
tk.translations.where(locale: locale_string.first(2)).first_or_create!(text: translation)
tkrs << tk.translation_key_resources.where(resource: self, resource_attribute: attr).first_or_create!
end
end
tkrs
end
|
#items ⇒ ActiveRecord::Relation<Item>
138
|
# File 'app/models/product_specification.rb', line 138
has_and_belongs_to_many :items, inverse_of: :direct_product_specifications, autosave: false
|
#name_select_options(filter_grouping: nil) ⇒ Object
403
404
405
406
|
# File 'app/models/product_specification.rb', line 403
def name_select_options(filter_grouping: nil)
filter_grouping = grouping if grouping&.start_with?('X-')
self.class.name_select_options(grouping: filter_grouping)
end
|
133
|
# File 'app/models/product_specification.rb', line 133
belongs_to :product_category, optional: true
|
132
|
# File 'app/models/product_specification.rb', line 132
belongs_to :product_line, optional: true
|
#refresh_items ⇒ Object
658
659
660
661
662
663
|
# File 'app/models/product_specification.rb', line 658
def refresh_items
return if skip_spec_refresh
affected_items_ids.each { |iid| ItemAttributeWorker.perform_async(iid) }
affected_items_ids
end
|
#render_priority ⇒ Object
spec value that are static will be first returned, this is so they can be computed and established
first in case they are used by future dynamic calculations. e.g ohms is static and should
be rendered before the calculate_watts method is called which uses it.
454
455
456
|
# File 'app/models/product_specification.rb', line 454
def render_priority
text_method? ? 0 : 1
end
|
#rendering_priority ⇒ Object
433
434
435
436
437
438
439
440
441
|
# File 'app/models/product_specification.rb', line 433
def rendering_priority
if text_method? && !text_blurb_is_liquid?
0
elsif dynamic_method?
1
else
2
end
end
|
#set_has_liquid_blurb ⇒ Object
669
670
671
672
673
674
|
# File 'app/models/product_specification.rb', line 669
def set_has_liquid_blurb
blurb_content = [text_blurb_en.presence, *translations.values.filter_map { |v| v['text_blurb'].presence }].compact
self.has_liquid_blurb = blurb_content.any? do |content|
(lt = Liquid::ParseEnvironment.parse(content)) && lt.root.nodelist.any? { |node| node.is_a?(Liquid::Variable) }
end
end
|
#summary ⇒ Object
648
649
650
|
# File 'app/models/product_specification.rb', line 648
def summary
"#{product_line.slug_ltree} | #{product_category.url} | #{name} | #{method}"
end
|
134
|
# File 'app/models/product_specification.rb', line 134
belongs_to :template_product_specification, class_name: 'ProductSpecification', optional: true
|
#text_blurb_is_liquid? ⇒ Boolean
626
627
628
|
# File 'app/models/product_specification.rb', line 626
def text_blurb_is_liquid?
has_liquid_blurb?
end
|
#text_method? ⇒ Boolean
429
430
431
|
# File 'app/models/product_specification.rb', line 429
def text_method?
method == 'text'
end
|
#text_output(locale = Mobility.locale) ⇒ Object
466
467
468
469
470
|
# File 'app/models/product_specification.rb', line 466
def text_output(locale = Mobility.locale)
Mobility.with_locale(locale) do
"#{text_blurb}#{unit_symbol}".squish.presence
end
end
|
#to_ruby_unit(locale = :en) ⇒ Object
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
|
# File 'app/models/product_specification.rb', line 472
def to_ruby_unit(locale = :en)
native_value_unitized = nil
Mobility.with_locale(locale) do
return unless units.present?
native_value = "#{text_blurb} #{units}".squish
native_value_unitized = begin
RubyUnits::Unit.new(native_value)
rescue StandardError
nil
end
end
native_value_unitized
end
|
#to_s ⇒ Object
458
459
460
|
# File 'app/models/product_specification.rb', line 458
def to_s
"#{name} (#{id})"
end
|
#translation_key_resources ⇒ ActiveRecord::Relation<TranslationKeyResource>
136
|
# File 'app/models/product_specification.rb', line 136
has_many :translation_key_resources, as: :resource, dependent: :destroy
|
#translation_keys ⇒ ActiveRecord::Relation<TranslationKey>
137
|
# File 'app/models/product_specification.rb', line 137
has_many :translation_keys, through: :translation_key_resources
|
#trim_empty_translations ⇒ Object
665
666
667
|
# File 'app/models/product_specification.rb', line 665
def trim_empty_translations
translations.delete_if { |_, value| !value.key?('text_blurb') || value['text_blurb'].blank? }
end
|
#unit_symbol ⇒ Object
462
463
464
|
# File 'app/models/product_specification.rb', line 462
def unit_symbol
UnitHelper.unit_symbol(units)
end
|
#units_select_options(locale = Mobility.locale) ⇒ Object
389
390
391
392
393
394
395
|
# File 'app/models/product_specification.rb', line 389
def units_select_options(locale = Mobility.locale)
I18n.with_locale(locale) do
select_units = self.class.units_select_options(locale)
select_units << [units, units] unless units.blank? || select_units.any? { |v| v.last == units }
select_units.compact.uniq.sort
end
end
|