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.
@@ -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