web-connect 0.1.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.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/Rakefile +3 -0
- data/lib/netfira/web_connect/components/checksum.rb +19 -0
- data/lib/netfira/web_connect/components/guid.rb +11 -0
- data/lib/netfira/web_connect/components/octa_word.rb +73 -0
- data/lib/netfira/web_connect/components.rb +1 -0
- data/lib/netfira/web_connect/configuration.rb +33 -0
- data/lib/netfira/web_connect/db_schema/20140514_support.rb +38 -0
- data/lib/netfira/web_connect/db_schema/20140515_alpha.rb +106 -0
- data/lib/netfira/web_connect/events.rb +14 -0
- data/lib/netfira/web_connect/migration/base_methods.rb +59 -0
- data/lib/netfira/web_connect/migration/refinements.rb +16 -0
- data/lib/netfira/web_connect/migration.rb +101 -0
- data/lib/netfira/web_connect/model/record/digests.rb +24 -0
- data/lib/netfira/web_connect/model/record/events.rb +35 -0
- data/lib/netfira/web_connect/model/record/file_record.rb +79 -0
- data/lib/netfira/web_connect/model/record/materialization.rb +59 -0
- data/lib/netfira/web_connect/model/record/relations.rb +51 -0
- data/lib/netfira/web_connect/model/record/sendable.rb +29 -0
- data/lib/netfira/web_connect/model/record/serializer.rb +56 -0
- data/lib/netfira/web_connect/model/record/translated_string.rb +42 -0
- data/lib/netfira/web_connect/model/record/translation.rb +58 -0
- data/lib/netfira/web_connect/model/record/translations.rb +63 -0
- data/lib/netfira/web_connect/model/record/tree.rb +13 -0
- data/lib/netfira/web_connect/model/record.rb +132 -0
- data/lib/netfira/web_connect/model/relation/events.rb +34 -0
- data/lib/netfira/web_connect/model/relation.rb +67 -0
- data/lib/netfira/web_connect/model/support.rb +13 -0
- data/lib/netfira/web_connect/model.rb +33 -0
- data/lib/netfira/web_connect/models/image.rb +48 -0
- data/lib/netfira/web_connect/models/order.rb +5 -0
- data/lib/netfira/web_connect/models/order_line.rb +13 -0
- data/lib/netfira/web_connect/models/support/setting.rb +31 -0
- data/lib/netfira/web_connect/models/support/shop/settings.rb +42 -0
- data/lib/netfira/web_connect/models/support/shop.rb +22 -0
- data/lib/netfira/web_connect/models/support/table.rb +5 -0
- data/lib/netfira/web_connect/models.rb +73 -0
- data/lib/netfira/web_connect/rack_app/action.rb +66 -0
- data/lib/netfira/web_connect/rack_app/action_helpers/data_types.rb +22 -0
- data/lib/netfira/web_connect/rack_app/action_helpers/env_importer.rb +59 -0
- data/lib/netfira/web_connect/rack_app/action_helpers/env_methods.rb +23 -0
- data/lib/netfira/web_connect/rack_app/actions/version_1/not_supported.rb +11 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/checksums.rb +14 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/commit/records.rb +45 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/commit/relations.rb +48 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/commit.rb +33 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/files.rb +21 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/info.rb +18 -0
- data/lib/netfira/web_connect/rack_app/actions/version_8/settings.rb +79 -0
- data/lib/netfira/web_connect/rack_app/exceptions/http_exception.rb +44 -0
- data/lib/netfira/web_connect/rack_app/exceptions.rb +6 -0
- data/lib/netfira/web_connect/rack_app.rb +56 -0
- data/lib/netfira/web_connect/request_filter.rb +66 -0
- data/lib/netfira/web_connect/schema/table.rb +58 -0
- data/lib/netfira/web_connect/schema.rb +22 -0
- data/lib/netfira/web_connect/version.rb +5 -0
- data/lib/netfira/web_connect.rb +82 -0
- data/lib/netfira.rb +10 -0
- data/lib/web_connect.rb +1 -0
- metadata +246 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Model::Record
|
3
|
+
|
4
|
+
# These methods help relate and unrelate records
|
5
|
+
module Relations
|
6
|
+
|
7
|
+
def relate(record)
|
8
|
+
error_messages = {
|
9
|
+
no_relation: 'You cannot add a %s to a %s',
|
10
|
+
wrong_shop: 'Tried to add a record from shop %s to shop %s'
|
11
|
+
}
|
12
|
+
prepare_relation(record, error_messages) do |relation_class, attrs|
|
13
|
+
relation_class.find_or_create_by attrs
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :<<, :relate
|
18
|
+
|
19
|
+
def unrelate(record)
|
20
|
+
error_messages = {
|
21
|
+
no_relation: 'You cannot remove a %s from a %s',
|
22
|
+
wrong_shop: 'Tried to remove a record of shop %s from shop %s'
|
23
|
+
}
|
24
|
+
prepare_relation(record, error_messages) do |relation_class, attrs|
|
25
|
+
relation_class.where(attrs).destroy_all
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# This method assists #relate and #unrelate
|
32
|
+
def prepare_relation(record, error_messages)
|
33
|
+
|
34
|
+
# The class of the relation between self and record
|
35
|
+
relation_class = Model::Record::Relation.for(self, record)
|
36
|
+
|
37
|
+
# Error for unrelatable classes
|
38
|
+
raise error_messages[:no_relation] % [record.class.name.demodulize, self.class.name.demodulize] unless relation_class
|
39
|
+
|
40
|
+
# Error for shop mismatches
|
41
|
+
raise error_messages[:wrong_shop] % [record.shop_id, shop_id] unless shop_id == record.shop_id
|
42
|
+
|
43
|
+
# The attributes to use for create or delete, e.g. {product_id: 1, category_id: 2}
|
44
|
+
attrs = [self, record].map{ |r| ["#{r.class.name.demodulize.underscore}_id", r.id]}.to_h
|
45
|
+
|
46
|
+
yield relation_class, attrs
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Model::Record
|
3
|
+
|
4
|
+
module Sendable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
after_initialize :create_guid
|
9
|
+
enum delivery_status: [:unsent, :sent, :delivered]
|
10
|
+
end
|
11
|
+
|
12
|
+
def guid
|
13
|
+
@guid ||= Guid.from(self[:guid])
|
14
|
+
end
|
15
|
+
|
16
|
+
def guid=(value)
|
17
|
+
raise 'GUIDs are read-only'
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_guid
|
23
|
+
self[:guid] ||= (@guid = Guid.create).b
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module Netfira::WebConnect
|
4
|
+
class Model::Record
|
5
|
+
|
6
|
+
module Serializer
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
EXCLUDE_FROM_SERIALIZE = %w[id shop_id digest delivery_status created_at updated_at]
|
10
|
+
|
11
|
+
included do
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Returns an array of three arrays of keys, e.g.
|
16
|
+
# [ ['product_id', 'unit_price'], ['description'], [] ]
|
17
|
+
def self.attributes_to_serialize
|
18
|
+
@attributes_to_serialize ||= [
|
19
|
+
attribute_names - EXCLUDE_FROM_SERIALIZE,
|
20
|
+
has_languages? ? self::Translation.translated_attribute_names : [],
|
21
|
+
[] # Custom attributes (not implemented)
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
|
28
|
+
# Map each array of keys from the above method into a sorted hash of key/value pairs
|
29
|
+
data = self.class.attributes_to_serialize.map do |keys|
|
30
|
+
pairs = keys.sort.map do |key|
|
31
|
+
[key.camelize(:lower), cast_for_serialization(__send__ key)]
|
32
|
+
end
|
33
|
+
pairs.reject!{ |v| v[1].nil? }.to_h
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return the class name and the sets above, e.g. Product{...},{...},{...}
|
37
|
+
self.class.name.demodulize << JSON.generate(data)[1..-2]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def cast_for_serialization(value)
|
43
|
+
if TranslatedString === value
|
44
|
+
value = value.sort
|
45
|
+
value = value.empty? ? nil : value.to_h
|
46
|
+
elsif Numeric === value
|
47
|
+
value = (value == value.floor) ? value.to_i : value.to_f
|
48
|
+
elsif OctaWord === value
|
49
|
+
value = value.to_s
|
50
|
+
end
|
51
|
+
value
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Netfira::WebConnect
|
4
|
+
|
5
|
+
# This class represents a translated string on a model, e.g. product.description.
|
6
|
+
# Callbacks are supplied by the Model::Record::Translations mixin
|
7
|
+
class Model::Record::TranslatedString
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@callbacks = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](lang)
|
15
|
+
@callbacks[:get].call lang
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(lang, value)
|
19
|
+
@callbacks[:set].call lang, value
|
20
|
+
end
|
21
|
+
|
22
|
+
def on(action, &block)
|
23
|
+
@callbacks[action] = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
self[nil]
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
@callbacks[:all].call
|
32
|
+
end
|
33
|
+
|
34
|
+
def merge!(values)
|
35
|
+
values.each{ |key, value| self[key] = value }
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
delegate %i[count length size each map sort select reject] => :to_h
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
|
3
|
+
# Subclasses of this model represents rows from translation tables, e.g. nf_products_l10n.
|
4
|
+
# Subclasses are materialized when their parent record models are materialized.
|
5
|
+
class Model::Record::Translation
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def materialize(owner_class)
|
10
|
+
|
11
|
+
# Make a translation model, e.g. Product::Translation
|
12
|
+
klass = Class.new(self)
|
13
|
+
owner_class.const_set :Translation, klass
|
14
|
+
|
15
|
+
# Keep a reference to the owner class
|
16
|
+
klass.instance_variable_set :@owner_class, owner_class
|
17
|
+
|
18
|
+
# Give translations references back to their parent records
|
19
|
+
klass.belongs_to :owner,
|
20
|
+
class_name: owner_class.name,
|
21
|
+
foreign_key: "#{owner_class.name.demodulize.underscore}_id",
|
22
|
+
inverse_of: :translation_models
|
23
|
+
|
24
|
+
# Add a translations relation, e.g. product.translations
|
25
|
+
owner_class.has_many :translation_models,
|
26
|
+
class_name: klass.name,
|
27
|
+
inverse_of: :owner,
|
28
|
+
autosave: true
|
29
|
+
|
30
|
+
# Add translations to the default scope, as they will almost
|
31
|
+
# always be needed for lookups
|
32
|
+
owner_class.default_scope { includes :translation_models }
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def translated_attribute_names
|
37
|
+
@translated_attribute_names ||= attribute_names - ['id', 'language', "#{@owner_class.single_name}_id"]
|
38
|
+
end
|
39
|
+
|
40
|
+
def table_name
|
41
|
+
@table_name ||= if self == Model::Record::Translation
|
42
|
+
Models::Table.table_name
|
43
|
+
else
|
44
|
+
Netfira::WebConnect.db_table_prefix(plural_name).to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def single_name
|
49
|
+
@single_name ||= Netfira::WebConnect.db_table_l10n_suffix @owner_class.single_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def plural_name
|
53
|
+
@plural_name ||= Netfira::WebConnect.db_table_l10n_suffix @owner_class.plural_name
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Model::Record
|
3
|
+
|
4
|
+
# This module is mixed into records that have translations.
|
5
|
+
module Translations
|
6
|
+
|
7
|
+
# Called when getting a translated string, e.g. product.description
|
8
|
+
def get_translated_string(key)
|
9
|
+
translated_string_for key
|
10
|
+
end
|
11
|
+
|
12
|
+
# Called when assigning a translated string, e.g. product.description = 'Stuff'
|
13
|
+
def set_translated_string(key, value)
|
14
|
+
if Hash === value
|
15
|
+
replace_translations_for key, value
|
16
|
+
else
|
17
|
+
translated_string_for(key)[nil] = value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def locale
|
22
|
+
shop.locale
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def translations
|
28
|
+
@translations ||= translation_models.map{ |model| [model.language, model]}.to_h
|
29
|
+
end
|
30
|
+
|
31
|
+
def translated_strings
|
32
|
+
@translated_strings ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def translation_for_language(lang)
|
36
|
+
lang = lang.to_s
|
37
|
+
translations[lang] ||= translation_models.build(language: lang)
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace_translations_for(key, value)
|
41
|
+
translations.each { |lang, model| model[key] = value[lang] }
|
42
|
+
translated_string_for(key).merge! value
|
43
|
+
end
|
44
|
+
|
45
|
+
def translated_string_for(key)
|
46
|
+
translated_strings[key] ||= begin
|
47
|
+
t = Model::Record::TranslatedString.new
|
48
|
+
t.on(:get) { |lang| translation_for_language(lang || locale).__send__ key }
|
49
|
+
t.on(:set) { |lang, value| translation_for_language(lang || locale).__send__ :"#{key}=", value }
|
50
|
+
t.on(:all) { translations.map{ |lang, model| [lang, model[key]] }.reject{ |x| x[1].nil? }.to_h }
|
51
|
+
t
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_translations
|
56
|
+
@translations = nil
|
57
|
+
@translated_strings = nil
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative 'record/relations'
|
2
|
+
require_relative 'record/materialization'
|
3
|
+
require_relative 'record/serializer'
|
4
|
+
require_relative 'record/digests'
|
5
|
+
require_relative 'record/sendable'
|
6
|
+
require_relative 'record/events'
|
7
|
+
|
8
|
+
module Netfira::WebConnect
|
9
|
+
class Model::Record
|
10
|
+
extend Materialization
|
11
|
+
include Relations
|
12
|
+
include Serializer
|
13
|
+
include Digests
|
14
|
+
include Events
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
def table_name
|
19
|
+
@table_name ||= if self == Model::Record
|
20
|
+
Models::Table.table_name
|
21
|
+
else
|
22
|
+
Netfira::WebConnect.db_table_prefix(plural_name).to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_languages?
|
27
|
+
const_defined? :Translation, false
|
28
|
+
end
|
29
|
+
|
30
|
+
def tree?
|
31
|
+
@is_tree ||= attribute_names.include? 'parent_id'
|
32
|
+
end
|
33
|
+
|
34
|
+
def origin_key
|
35
|
+
@origin_key ||= if sendable?
|
36
|
+
:guid
|
37
|
+
elsif writable?
|
38
|
+
nil
|
39
|
+
else
|
40
|
+
(table_props.origin_key || "#{single_name}_id").to_sym
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def writable?
|
45
|
+
@writable ||= table_props.writable
|
46
|
+
end
|
47
|
+
|
48
|
+
def sendable?
|
49
|
+
@sendable ||= table_props.sendable
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_by_origin_id(shop, id)
|
53
|
+
id = id.b if OctaWord === id
|
54
|
+
find_by origin_key => id, shop_id: shop.id
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_or_initialize_by_origin_id(shop, id)
|
58
|
+
find_by_origin_id(shop, id) || new(origin_key => id, shop_id: shop.id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_or_create_by_origin_id(shop, id)
|
62
|
+
record = find_or_initialize_by_origin_id(shop, id)
|
63
|
+
record.save unless record.persisted?
|
64
|
+
record
|
65
|
+
end
|
66
|
+
|
67
|
+
def has_file?
|
68
|
+
@has_file ||= self < FileRecord
|
69
|
+
end
|
70
|
+
|
71
|
+
def schema
|
72
|
+
@schema ||= Netfira::WebConnect.schema[self]
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
after_initialize :init
|
78
|
+
|
79
|
+
%i[has_languages? tree? origin_key writable? sendable? has_file?].each do |method|
|
80
|
+
define_method(method) { self.class.__send__ method }
|
81
|
+
end
|
82
|
+
|
83
|
+
def origin_id
|
84
|
+
__send__ origin_key
|
85
|
+
end
|
86
|
+
|
87
|
+
def as_readonly
|
88
|
+
readonly!
|
89
|
+
yield
|
90
|
+
instance_variable_set :@readonly, false
|
91
|
+
end
|
92
|
+
|
93
|
+
def reload
|
94
|
+
clear_translations if has_languages?
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_with_type_casting(key, value)
|
99
|
+
schema = self.class.schema
|
100
|
+
if schema.localize.include? key
|
101
|
+
set_translated_string key, value
|
102
|
+
else
|
103
|
+
type = schema.columns[key]
|
104
|
+
return false unless type
|
105
|
+
value = value.to_s if type == 'string'
|
106
|
+
write_attribute key, value
|
107
|
+
end
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def self.table_props
|
114
|
+
@table_props
|
115
|
+
end
|
116
|
+
|
117
|
+
def init
|
118
|
+
self.shop_id ||= 0
|
119
|
+
end
|
120
|
+
|
121
|
+
def after_add_relation(model)
|
122
|
+
model.shop_id = shop_id
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
require_relative 'record/file_record'
|
129
|
+
require_relative 'record/translated_string'
|
130
|
+
require_relative 'record/translation'
|
131
|
+
require_relative 'record/translations'
|
132
|
+
require_relative 'record/tree'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Model::Relation
|
3
|
+
module Events
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
after_save :dispatch_create
|
8
|
+
after_destroy :dispatch_destroy
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch_create
|
14
|
+
dispatch_relation_event 'add_%s_to_%s'
|
15
|
+
end
|
16
|
+
|
17
|
+
def dispatch_destroy
|
18
|
+
dispatch_relation_event 'remove_%s_from_%s'
|
19
|
+
end
|
20
|
+
|
21
|
+
def dispatch_relation_event(event_name_template)
|
22
|
+
[records, records.reverse].each do |records|
|
23
|
+
event_name = (event_name_template % records.map{ |record| record.class.single_name }).to_sym
|
24
|
+
records[0].as_readonly do
|
25
|
+
records[1].as_readonly do
|
26
|
+
Netfira::WebConnect.dispatch_event event_name, *records
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'relation/events'
|
2
|
+
|
3
|
+
module Netfira::WebConnect
|
4
|
+
class Model::Relation
|
5
|
+
include Events
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
attr_reader :related_classes
|
10
|
+
|
11
|
+
def materialize(name_a, name_b)
|
12
|
+
klass = Class.new(self)
|
13
|
+
Models.const_set "#{name_a}To#{name_b}", klass
|
14
|
+
klass.related_classes = [name_a, name_b].map{ |n| Models.const_get n.camelize.singularize }
|
15
|
+
end
|
16
|
+
|
17
|
+
def related_classes=(classes)
|
18
|
+
raise 'Related classes have already been set' if related_classes
|
19
|
+
@related_classes = classes
|
20
|
+
classes.each_with_index do |related_class, index|
|
21
|
+
|
22
|
+
# The class on the opposite side of the relation. For example:
|
23
|
+
# related_class => Product
|
24
|
+
# opposite_class => Category
|
25
|
+
opposite_class = related_classes[1 - index]
|
26
|
+
|
27
|
+
# Name of the reference to add to the related class, e.g. Product#category_relations
|
28
|
+
relation_name = :"#{opposite_class.single_name}_relations"
|
29
|
+
|
30
|
+
# The relation class's reference, e.g. CategoryToProduct#product
|
31
|
+
belongs_to related_class.single_name.to_sym
|
32
|
+
|
33
|
+
# The related class's reference to the relation, e.g. Product#category_relations
|
34
|
+
related_class.has_many relation_name,
|
35
|
+
class_name: name,
|
36
|
+
inverse_of: related_class.single_name.to_sym
|
37
|
+
|
38
|
+
# The related class's reference to the other related class,
|
39
|
+
# e.g. Product#categories (at last!)
|
40
|
+
related_class.has_many opposite_class.plural_name.to_sym,
|
41
|
+
through: relation_name,
|
42
|
+
after_add: :after_add_relation
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def table_name
|
48
|
+
@table_name ||= if self == Model::Relation
|
49
|
+
Models::Table.table_name
|
50
|
+
else
|
51
|
+
Netfira::WebConnect.db_table_prefix(related_classes.map(&:plural_name).join '_to_').to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def for(class_a, class_b)
|
56
|
+
name = [class_a, class_b].map{ |c| (c.is_a?(Class) ? c : c.class).name.demodulize }.sort.join 'To'
|
57
|
+
Models.const_get name if Models.const_defined? name
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def records
|
63
|
+
self.class.related_classes.map{ |klass| __send__ klass.single_name.to_sym }
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Model::Support
|
3
|
+
|
4
|
+
def self.table_name
|
5
|
+
@table_name ||= if self == Model::Support
|
6
|
+
Models::Table.table_name
|
7
|
+
else
|
8
|
+
Netfira::WebConnect.db_table_prefix('_' << name.demodulize.underscore.pluralize).to_s
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Model < ActiveRecord::Base
|
3
|
+
class Record < self
|
4
|
+
class FileRecord < self
|
5
|
+
end
|
6
|
+
class Translation < Model
|
7
|
+
end
|
8
|
+
end
|
9
|
+
class Relation < self
|
10
|
+
end
|
11
|
+
class Support < self
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def table_name
|
16
|
+
Models::Table.table_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def single_name
|
20
|
+
@single_name ||= name.demodulize.underscore
|
21
|
+
end
|
22
|
+
|
23
|
+
def plural_name
|
24
|
+
@plural_name ||= single_name.pluralize
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require_relative 'model/record'
|
32
|
+
require_relative 'model/relation'
|
33
|
+
require_relative 'model/support'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'fastimage'
|
3
|
+
|
4
|
+
module Netfira::WebConnect
|
5
|
+
class Models::Image < Model::Record::FileRecord
|
6
|
+
|
7
|
+
def focus_point=(point)
|
8
|
+
self.focus_point_top = point[:top]
|
9
|
+
self.focus_point_left = point[:left]
|
10
|
+
self.focus_point
|
11
|
+
end
|
12
|
+
|
13
|
+
def focus_point
|
14
|
+
@focus_point ||= FocusPoint.new(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def data=(value)
|
18
|
+
super value
|
19
|
+
self.width, self.height = FastImage.size(attachment.staged_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def dimensions
|
23
|
+
[width, height]
|
24
|
+
end
|
25
|
+
|
26
|
+
def dimensions=(value)
|
27
|
+
self.width, self.height = value
|
28
|
+
end
|
29
|
+
|
30
|
+
class FocusPoint
|
31
|
+
|
32
|
+
def initialize(image)
|
33
|
+
@image = image
|
34
|
+
end
|
35
|
+
|
36
|
+
%w[top left].each do |attr|
|
37
|
+
define_method :"#{attr}" do
|
38
|
+
@image[:"focus_point_#{attr}"]
|
39
|
+
end
|
40
|
+
define_method :"#{attr}=" do |value|
|
41
|
+
@image[:"focus_point_#{attr}"] = value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Netfira::WebConnect
|
2
|
+
class Models::Setting < Model::Support
|
3
|
+
|
4
|
+
belongs_to :shop, inverse_of: :setting_models
|
5
|
+
|
6
|
+
def typed_value
|
7
|
+
case content_type
|
8
|
+
when 'application/json' then JSON.parse(value, quirks_mode: true)
|
9
|
+
when 'application/octet-stream' then value.b
|
10
|
+
when /^text\/plain;charset=(.+)$/ then value.force_encoding($1)
|
11
|
+
else value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def typed_value=(value)
|
16
|
+
if String === value
|
17
|
+
if value.encoding.name == 'ASCII-8BIT'
|
18
|
+
self.value = value.b
|
19
|
+
self.content_type = 'application/octet-stream'
|
20
|
+
else
|
21
|
+
self.value = value.force_encoding('UTF-8')
|
22
|
+
self.content_type = 'text/plain;charset=UTF-8'
|
23
|
+
end
|
24
|
+
else
|
25
|
+
self.value = JSON.generate(value, quirks_mode: true)
|
26
|
+
self.content_type = 'application/json'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|