mycrm 0.0.41

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ require 'mycrm/core_ext/address'
2
+
3
+ module Mycrm
4
+ module CoreExt
5
+ module String
6
+ def to_underscore
7
+ self.to_s.gsub(/::/, '/')
8
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
9
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
10
+ .tr("-", "_")
11
+ .downcase
12
+ end
13
+
14
+ def to_camelized
15
+ return self if self.to_s !~ /_/ && self.to_s =~ /[A-Z]+.*/
16
+ self.to_s.split('_').collect(&:capitalize).join
17
+ end
18
+
19
+ def to_address
20
+ Mycrm::CoreExt::Address.new(self)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,57 @@
1
+ module Mycrm
2
+ # facades
3
+ module Domains
4
+ module Rescuer
5
+ def [](label)
6
+ name = label.downcase.gsub(/\W/, '_')
7
+ raise NameError, "#{label} is not a valid domain" unless constant?(name)
8
+ create_constant(name)
9
+ end
10
+ alias find []
11
+
12
+ private
13
+
14
+ def method_missing(name, *args, &block)
15
+ return super unless constant?(name)
16
+ create_constant(name)
17
+ end
18
+
19
+ def respond_to_missing?(name, include_private = false)
20
+ constant?(name) || super
21
+ end
22
+
23
+ def const_missing(name)
24
+ return super unless constant?(name)
25
+ create_constant(name)
26
+ end
27
+
28
+ def create_constant(name)
29
+ configured_domain[name.to_underscore].tap do |val|
30
+ self.const_set(name.to_camelized, val) unless self.const_defined?(name.to_camelized)
31
+ end
32
+ end
33
+
34
+ def constant?(name)
35
+ configured_domain && !configured_domain[name.to_underscore].nil?
36
+ end
37
+
38
+ def configured_domain
39
+ Mycrm.configuration.domains[self.name.split('::').last.to_underscore]
40
+ end
41
+ end
42
+
43
+ # Uncomment this line to create categories as constants on load.
44
+ #
45
+ # Mycrm.configuration.domains.each { |key, items| create_constant(key) }
46
+
47
+ def self.const_missing(name)
48
+ return super unless Mycrm.configuration.domains.key?(name.to_underscore)
49
+ create_constant(name)
50
+ end
51
+
52
+ def self.create_constant(name)
53
+ domain = Mycrm::Domains.const_set(name.to_camelized, Module.new)
54
+ domain.extend Mycrm::Domains::Rescuer
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,98 @@
1
+ module Mycrm
2
+ # facades
3
+ module Facade
4
+ def self.included(descendant)
5
+ descendant.send(:include, InstanceMethods)
6
+ descendant.send(:extend, ClassMethods)
7
+ end
8
+
9
+ module InstanceMethods
10
+ def self.included(descendant)
11
+ descendant.class_eval do
12
+ attr_accessor :delegates
13
+ end
14
+ end
15
+
16
+ def initialize(attributes = {})
17
+ self.delegates = {}
18
+ self.class.delegate_classes.each do |name, delegate|
19
+ delegates[name] = delegate[:class].new attributes[name]
20
+ end
21
+ end
22
+
23
+ def to_json
24
+ delegates.each_with_object({}) { |(name, instance), r| r[name] = instance.to_json }
25
+ end
26
+
27
+ def run
28
+ {}.tap do |response|
29
+ Mycrm::Connectable.session do
30
+ delegates.each do |name, instance|
31
+ refresh_relation(name)
32
+ response[name] = yield(instance)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def refresh_relation(name)
39
+ self.class.relations(name).to_a.each do |pair|
40
+ delegates[name].send("#{pair.first}=", relative_value(*pair.last.split('.'))) rescue nil
41
+ end
42
+ end
43
+
44
+ def relative_value(delegate_name, field)
45
+ delegates[delegate_name.to_sym].send(field)
46
+ end
47
+
48
+ def method_missing(name, *args, &block)
49
+ return delegates[name] if delegates.key?(name)
50
+ delegate = delegates.values.detect { |d| d.respond_to?(name) }
51
+ delegate ? delegate.send(name, *args) : super
52
+ end
53
+
54
+ def respond_to_missing?(name, include_private = false)
55
+ delegates[name] || delegates.detect { |d| d.respond_to?(name) } || super
56
+ end
57
+ end
58
+
59
+ module ClassMethods
60
+ def self.extended(descendant)
61
+ descendant.singleton_class.class_eval do
62
+ attr_accessor :delegate_classes
63
+ end
64
+ end
65
+
66
+ def delegate(name, clazz, relations = {})
67
+ self.delegate_classes ||= {}
68
+ delegate_classes[name] = { class: clazz, relations: relations }
69
+ end
70
+
71
+ def relations(delegate)
72
+ delegate_classes[delegate][:relations]
73
+ end
74
+
75
+ [:find, :list].each do |method|
76
+ define_method method do |*args|
77
+ run method, *args
78
+ end
79
+ end
80
+
81
+ def run(method = :find, *args)
82
+ attributes = {}.tap do |response|
83
+ Mycrm::Connectable.session do
84
+ self.delegate_classes.each do |name, delegate|
85
+ res = if block_given?
86
+ yield(name, delegate, response)
87
+ elsif delegate[:class].respond_to? :find
88
+ delegate[:class].send(method, *args)
89
+ end
90
+ response[name] = res.is_a?(Array) ? res.map(&:to_json) : res.to_json
91
+ end
92
+ end
93
+ end
94
+ new(attributes)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'facades/lead'
2
+ require_relative 'facades/lead_application'
3
+ require_relative 'facades/organization'
@@ -0,0 +1,24 @@
1
+ require_relative '../facade'
2
+ require_relative '../models/client'
3
+ require_relative '../models/note'
4
+ require_relative '../models/adviser_note'
5
+
6
+ module Mycrm
7
+ module Facades
8
+ class Lead
9
+ include Facade
10
+
11
+ delegate :client, Mycrm::Models::Client
12
+ delegate :note, Mycrm::Models::Note, family_id: 'client.family_id'
13
+ delegate :adviser_note, Mycrm::Models::AdviserNote, family_id: 'client.family_id'
14
+
15
+ def create
16
+ run { |instance| instance.create }.values.all?
17
+ end
18
+
19
+ def create!
20
+ run { |instance| instance.create! }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../facade'
2
+ require_relative '../models/client'
3
+ require_relative '../models/note'
4
+ require_relative '../models/adviser_note'
5
+ require_relative '../models/application'
6
+ require_relative '../models/structure'
7
+
8
+ module Mycrm
9
+ module Facades
10
+ class LeadApplication
11
+ include Facade
12
+
13
+ delegate :client, Mycrm::Models::Client
14
+ delegate :note, Mycrm::Models::Note, family_id: 'client.family_id'
15
+ delegate :adviser_note, Mycrm::Models::AdviserNote, family_id: 'client.family_id'
16
+ delegate :application, Mycrm::Models::Application, client_id: 'client.id'
17
+ delegate :structure, Mycrm::Models::Structure, application_id: 'application.id'
18
+
19
+ def create
20
+ run { |instance| instance.create }.values.all?
21
+ end
22
+
23
+ def create!
24
+ run { |instance| instance.create! }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../facade'
2
+ require_relative '../models/office'
3
+ require_relative '../models/office_address'
4
+
5
+ module Mycrm
6
+ module Facades
7
+ class Organization
8
+ include Facade
9
+
10
+ delegate :office, Mycrm::Models::Office
11
+ delegate :address, Mycrm::Models::OfficeAddress
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,203 @@
1
+ require 'virtus'
2
+ require 'virtus_convert'
3
+ require_relative 'connectable'
4
+
5
+ module Mycrm
6
+ # models
7
+ module Model
8
+ def self.included(descendant)
9
+ descendant.send(:include, Virtus.model)
10
+ descendant.send(:include, InstanceMethods)
11
+ descendant.send(:extend, Connectable)
12
+ descendant.send(:extend, ClassMethods)
13
+
14
+ descendant.class_eval do
15
+ attr_accessor :error
16
+ end
17
+ end
18
+
19
+ # provides methods for the instance
20
+ module InstanceMethods
21
+ def initialize(attributes = nil)
22
+ partitions = (attributes || []).partition{ |field, _| attribute_set[field].nil? }
23
+ super(partitions.last.to_h)
24
+ partitions.first.each { |field, value| send("#{field}=", value) rescue nil }
25
+ end
26
+
27
+ def create!
28
+ mass_assign(self.class.execute(:post, nil, to_json))
29
+ end
30
+
31
+ def update!
32
+ mass_assign(self.class.execute(:put, id, to_json))
33
+ end
34
+
35
+ def delete!
36
+ self.class.execute(:delete, id, nil)
37
+ end
38
+
39
+ def to_json
40
+ VirtusConvert.new(self).to_hash.delete_if(&method(:swoop))
41
+ end
42
+
43
+ def swoop(_, v)
44
+ (v.is_a?(Hash) ? v.delete_if(&method(:swoop)) : v.to_s).empty?
45
+ end
46
+
47
+ def mass_assign(json)
48
+ attribute_set.set(self, json)
49
+ end
50
+
51
+ # safe methods for the principal crud methods
52
+ %w(create update delete).each do |method|
53
+ define_method method do
54
+ begin
55
+ send("#{method}!")
56
+ true
57
+ rescue => e
58
+ self.error = e.message
59
+ false
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ # provides method at class level
66
+ module ClassMethods
67
+ def self.extended(descendant)
68
+ descendant.singleton_class.class_eval do
69
+ attr_accessor :class_endpoint, :alowed_methods, :attribute_to
70
+ end
71
+ end
72
+
73
+ def list(id = nil)
74
+ to_list(execute(:get, id))
75
+ end
76
+
77
+ def list_by(key, value, use_key_as_path = false)
78
+ path = use_key_as_path ? key : nil
79
+ to_list(execute(:get, path, {}, key => value))
80
+ end
81
+
82
+ def find(id)
83
+ new(execute(:get, id))
84
+ end
85
+
86
+ def find_by(key, value, use_key_as_path = false)
87
+ path = use_key_as_path ? key : nil
88
+ new(execute(:get, path, {}, key => value))
89
+ end
90
+
91
+ def execute(action, path, body = {}, query = {})
92
+ raise ApiError, 'Method not allowed' if !alowed_methods.nil? && !alowed_methods.include?(action)
93
+ Mycrm.log(:info, %{
94
+ --------- #{self.name} ---------
95
+ method: #{action}
96
+ path: #{uri(path, body)}
97
+ body: #{body}
98
+ query: #{query}
99
+ -------------------------------})
100
+ send(action, uri(path, body), body, query).tap { |response| Mycrm.log(:info, "response: #{response}") }
101
+ end
102
+
103
+ def uri(relative = nil, parameters = {})
104
+ format([class_endpoint, relative.to_s].compact.join('/').chomp('/'), parameters)
105
+ rescue KeyError => e
106
+ raise ApiError, e
107
+ end
108
+
109
+ def to_list(res)
110
+ raise ApiError, 'The output is not compatible.' unless res.is_a?(Array)
111
+ res.each_with_object([]) { |j, list| list << new(j) }
112
+ end
113
+
114
+ # macro method to setup the endpoint that will be used to connect the API
115
+ def endpoint(endpoint)
116
+ self.class_endpoint = endpoint
117
+ end
118
+
119
+ # macro method to limit the method type
120
+ def only(*methods)
121
+ self.alowed_methods = methods.collect(&:to_sym)
122
+ end
123
+
124
+ def to(mode)
125
+ raise ApiError, "The conversion type '#{mode}' is not compatible." unless [:underscore, :camelized].include?(mode)
126
+ self.attribute_to = mode
127
+ end
128
+
129
+ # macro method to create a new attribute of type Object
130
+ # and relative accessors
131
+ def attribute(name, klazz, options = {})
132
+ if options[:embedded]
133
+ options.merge!(default: {})
134
+ define_embedded_methods(name, klazz)
135
+ end
136
+
137
+ define_formatted_methods name
138
+
139
+ super
140
+
141
+ alias_method(options[:as], name) if options[:as]
142
+ end
143
+
144
+ # Defines accessors for embetted attributes
145
+ #
146
+ # class Phone
147
+ # attribute :home
148
+ # attribute :work
149
+ # attribute :mobile
150
+ # end
151
+ #
152
+ # class Contact
153
+ # attribute :phone, embedded:true
154
+ # end
155
+ #
156
+ # contact = Contact.new
157
+ # contact.phone_home = 'xxx'
158
+ # contact.home_phone = 'xxx'
159
+ #
160
+ # private
161
+ def define_embedded_methods(field, klazz)
162
+ klazz.allowed_writer_methods.each do |att|
163
+ ['%{field}_%{attribute}', '%{attribute}_%{field}'].each do |template|
164
+ define_embedded_method template, field, att.to_s.chomp('=')
165
+ end
166
+ end
167
+ end
168
+
169
+ def define_embedded_method(template, field, attribute)
170
+ accessor = format(template, field: field.to_s, attribute: attribute)
171
+
172
+ define_method "#{accessor}=" do |val|
173
+ instance_variable_get("@#{field}").send("#{attribute}=", val)
174
+ end
175
+
176
+ define_method accessor do
177
+ instance_variable_get("@#{field}").send(attribute)
178
+ end
179
+ end
180
+
181
+ def define_formatted_methods(field)
182
+ return unless attribute_to
183
+ alias_name = field.send("to_#{attribute_to}")
184
+
185
+ define_method "#{alias_name}=" do |val|
186
+ send("#{field}=", val)
187
+ end
188
+
189
+ define_method alias_name do
190
+ send(field)
191
+ end
192
+ end
193
+
194
+ def method_missing(name, *args, &block)
195
+ name =~ /^(find|list)_by_(.*)$/ ? send("#{Regexp.last_match[1]}_by", Regexp.last_match[2], *args) : super
196
+ end
197
+
198
+ def respond_to_missing?(name, include_private = false)
199
+ name !~ /^(find|list)_by_(.*)$/ ? super : true
200
+ end
201
+ end
202
+ end
203
+ end