mycrm 0.0.41
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/MIT-LICENSE +20 -0
- data/README.md +112 -0
- data/Rakefile +16 -0
- data/lib/mycrm.rb +48 -0
- data/lib/mycrm/attributes.rb +5 -0
- data/lib/mycrm/attributes/address.rb +13 -0
- data/lib/mycrm/attributes/date.rb +13 -0
- data/lib/mycrm/attributes/deposit.rb +12 -0
- data/lib/mycrm/attributes/name.rb +18 -0
- data/lib/mycrm/attributes/organization.rb +13 -0
- data/lib/mycrm/connectable.rb +107 -0
- data/lib/mycrm/core_ext.rb +7 -0
- data/lib/mycrm/core_ext/address.rb +26 -0
- data/lib/mycrm/core_ext/hash.rb +28 -0
- data/lib/mycrm/core_ext/string.rb +24 -0
- data/lib/mycrm/domains.rb +57 -0
- data/lib/mycrm/facade.rb +98 -0
- data/lib/mycrm/facades.rb +3 -0
- data/lib/mycrm/facades/lead.rb +24 -0
- data/lib/mycrm/facades/lead_application.rb +28 -0
- data/lib/mycrm/facades/organization.rb +14 -0
- data/lib/mycrm/model.rb +203 -0
- data/lib/mycrm/models.rb +9 -0
- data/lib/mycrm/models/adviser.rb +62 -0
- data/lib/mycrm/models/adviser_compliance_detail.rb +26 -0
- data/lib/mycrm/models/adviser_note.rb +23 -0
- data/lib/mycrm/models/application.rb +38 -0
- data/lib/mycrm/models/client.rb +40 -0
- data/lib/mycrm/models/lender.rb +16 -0
- data/lib/mycrm/models/note.rb +27 -0
- data/lib/mycrm/models/office.rb +48 -0
- data/lib/mycrm/models/office_address.rb +37 -0
- data/lib/mycrm/models/structure.rb +22 -0
- data/lib/mycrm/resources/domains.yml +73 -0
- data/lib/mycrm/version.rb +3 -0
- metadata +221 -0
@@ -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
|
data/lib/mycrm/facade.rb
ADDED
@@ -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,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
|
data/lib/mycrm/model.rb
ADDED
@@ -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
|