klastera 1.2.4.2 → 1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8780a6928f5249deb34be42cac1f52a226113865b9f29ddb12e1a36f018141ed
4
- data.tar.gz: 1d99b709cea49a08533192ad8a1df621d78ea4d4ee59e1c266f06e952e6d8063
3
+ metadata.gz: 999d5c067d080b90f56c82a36cbeb286e08000bf09af922845bdd67df023d16c
4
+ data.tar.gz: e4e105b33d55b228cc4d515e0167586b7b962218860d78593a29f6a71b436712
5
5
  SHA512:
6
- metadata.gz: 62c72441e00d9edb6c17525453cde71d864389c9342dbe54df3538609814aef0cdcbc3f7c49f49134d526e2da1f99ff1cc258b750cd024a7031afd3a80977d2d
7
- data.tar.gz: 806978a3fae34b0a2ddda331ce37b90de2e66f9c77bd707859b721ac7c83980f706b5eb432a00875db8b26b4c8901b11307ef62ab063952d013eef30eca69772
6
+ metadata.gz: 6902637aaf11219cff0b3ed2f9dcdf1f86c74d2e6c43211d683c559177da39498220d5fca6b987418b936a89c6b08dde5d49a0014d0b86d1fe3c258612912f92
7
+ data.tar.gz: 1ec8aad0c6793c426c01969bcdb9f04deed692b06499b0726ee047f13932fda3fe5046067432604947cf85e68662269569d61c482971b8855595a92dc87d1d29
@@ -11,6 +11,7 @@ module Klastera::Concerns::Cluster
11
11
  MODES = [ :required_suborganization, :optional_suborganization, :optional_filter ].freeze
12
12
 
13
13
  belongs_to :organization, class_name: Klastera.organization_class
14
+ has_many :cluster_users
14
15
 
15
16
  scope :of, -> (organization,except_ids=[]) {
16
17
  _scope = where(organization: organization)
@@ -4,11 +4,8 @@ module Klastera::Concerns::ClusterFilter
4
4
  included do
5
5
  include ActiveModel::Model
6
6
  include ActiveModel::Validations::Callbacks
7
- attr_accessor :cluster_id
8
7
 
9
- def kcluster_id
10
- self.cluster_id == 'without_cluster' ? nil : self.cluster_id
11
- end
8
+ attr_accessor :cluster_id
12
9
  end
13
10
 
14
11
  module ClassMethods
@@ -3,55 +3,39 @@ module Klastera::Concerns::ClusterUser
3
3
 
4
4
  included do
5
5
  self.table_name = 'cluster_users'
6
-
7
6
  belongs_to :user
8
7
  belongs_to :cluster
9
-
10
8
  validates :cluster_id, presence: true
11
-
12
- scope :of, -> (user,organization) {
13
- Rails.logger.warn("DON'T USE THIS SCOPE DIRECTLY: Use ::ClusterUser.clusters_from class method instead!")
14
- includes(:cluster).where(user_id: user, "clusters.organization_id": organization)
15
- }
16
9
  end
17
10
 
18
11
  module ClassMethods
12
+ ##
13
+ # Return a Cluster::ActiveRecord_Relation of organization (and) user
14
+ #
15
+ def clusters_of(organization,and_user=nil)
16
+ and_user_id = and_user.present? ? { users: { id: and_user } } : {}
17
+ ::Cluster.eager_load(cluster_users: :user).where({ organization_id: organization }.merge(and_user_id) )
18
+ end
19
19
 
20
20
  ##
21
- # Returns a hash with the users with cluster relation
22
- #
23
- def user_clusters_from(organization)
24
- users_hash = {}
25
- organization_cluster_ids = ::Cluster.of(organization).map(&:id)
26
- ::ClusterUser.includes(:user).where(cluster_id: organization_cluster_ids).each do |cluster_user|
27
- user_id = cluster_user.user_id || :uncluster
28
- users_hash[user_id] ||= []
29
- users_hash[user_id] << cluster_user.cluster_id
30
- end
31
- users_hash
21
+ # Return a User::ActiveRecord_Relation of organization (and) user
22
+ #
23
+ def users_of(organization)
24
+ ::User.eager_load(:cluster_users).where(users: { organization_id: organization } )
32
25
  end
33
26
 
34
27
  ##
35
- # Returns a Cluster::ActiveRecord_Relation
28
+ # Return a hash of users and its clusters
36
29
  #
37
- ##
38
- def clusters_from(user,organization)
39
- clusters = []
40
- unless user.can_admin_clusters?
41
- # We could return cu.clusters but you may quering this result and a array can't handle order, where and etc.
42
- # So we take only the cluster_ids and perfome a new search in order to get a Cluster::ActiveRecord_Relation
43
- # like the else block.
44
- # TODO: resolve it without quering twice
45
- clusters_id = ::ClusterUser.of(user,organization).map(&:cluster_id)
46
- clusters = ::Cluster.where(id: clusters_id)
47
- # Add a empty cluster instance to handle models without a cluster assignation. Only for use and show modes
48
- if organization.optional_mode?
49
- clusters << ::Cluster.new({nid: :without_cluster, name: I18n.t('klastera.without_cluster')})
50
- end
51
- else
52
- clusters = ::Cluster.of(organization)
30
+ def users_hash_of(organization)
31
+ users = {}
32
+ rows = self.users_of(organization).pluck("users.id AS user_id","cluster_users.cluster_id").uniq
33
+ rows.each do |row|
34
+ user_id = row.first
35
+ users[user_id] ||= []
36
+ users[user_id] << row.last
53
37
  end
54
- clusters
38
+ users
55
39
  end
56
40
  end
57
41
  end
@@ -11,10 +11,6 @@ module Klastera::Concerns::Clusterizable
11
11
  validate :at_least_one_cluster_entity, if: proc { self.organization.required_suborganization_mode? }
12
12
  validate :uniqueness_of_cluster_entity_record
13
13
 
14
- scope :related_clusters, ->() {
15
- joins(:cluster_entities).where("cluster_entities.entity_id = #{self.table_name}.id")
16
- }
17
-
18
14
  def at_least_one_cluster_entity
19
15
  if cluster_entities.length == 0 || cluster_entities.reject{|cluster_entity| cluster_entity._destroy == true}.empty?
20
16
  return errors.add(:cluster_entities, I18n.t('klastera.messages.at_least_one_cluster_entity'))
@@ -1,6 +1,6 @@
1
1
  <tr class="nested-fields">
2
2
  <td class="field">
3
- <%= f.select :cluster_id, @cluster_clusters.map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
3
+ <%= f.select :cluster_id, @clusters_session.map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
4
4
  </td>
5
5
  <td class="action vertical-align-middle text-center" width="44">
6
6
  <%= link_to_remove_association f, class: 'btn btn-danger btn-xs text-danger' do %>
@@ -7,7 +7,7 @@
7
7
  <%=yield(f) if block_given? %>
8
8
 
9
9
  <% if cluster_organization.is_in_cluster_mode? %>
10
- <% cluster_collection = cluster_clusters.map{|c|[c.name,(c.id||c.nid)]} %>
10
+ <% cluster_collection = cluster_of_my_own.map{|c|[c.name,(c.id||c.nid)]} %>
11
11
  <% if cluster_collection.size > 1 %>
12
12
  <div class="inline-label-control-block">
13
13
  <%= f.input :cluster_id, collection: cluster_collection, prompt: t('klastera.clusters.all'), label: false, wrapper: false %>
@@ -4,7 +4,7 @@
4
4
  %>
5
5
  <% if cluster_organization.is_in_cluster_mode? || other_visibility_reason %>
6
6
  <%
7
- cluster_collection = @cluster_clusters || cluster_clusters
7
+ cluster_collection = @clusters_session || cluster_of_my_own
8
8
  label = t('klastera.cluster.title')
9
9
  %>
10
10
  <% if f.nil? %>
@@ -1,5 +1,5 @@
1
1
  <% if cluster_organization.is_in_cluster_mode? %>
2
- <% @cluster_clusters = cluster_clusters %>
2
+ <% @clusters_session = cluster_of_my_own %>
3
3
  <div class="col-xs-12">
4
4
  <% if hide_title||=false %>
5
5
  <div class="form-group file required <%=f.object.class.name.parameterize%>_cluster_entity<%=' has-error' if f.object.errors.has_key?(:cluster_entities)%>">
@@ -27,7 +27,7 @@
27
27
  <table id="cluster-entities" class="table table-striped">
28
28
  <tbody class="cluster-entity-rows">
29
29
  <%= f.fields_for :cluster_entities do |cluster_entity|%>
30
- <% next unless @cluster_clusters.map(&:id).include?(cluster_entity.try(:object).try(:cluster_id)) %>
30
+ <% next unless @clusters_session.map(&:id).include?(cluster_entity.try(:object).try(:cluster_id)) %>
31
31
  <%= render 'layouts/klastera/cluster_entity_fields', f: cluster_entity %>
32
32
  <% end %>
33
33
  </body>
@@ -5,14 +5,17 @@ module Klastera
5
5
 
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ UNCLUSTERED_ENTITY = 'without_cluster'
8
9
  KLSTR_HELPERS = %i[
9
- cluster_user cluster_organization user_has_more_than_one_cluster
10
- cluster_scope cluster_clusters cluster_scope_filtered clusters_from
10
+ cluster_user cluster_organization user_has_more_than_one_cluster cluster_scope cluster_of_my_own
11
11
  user_clusters_string_list set_collection_before_group_by_entity
12
12
  ]
13
13
 
14
14
  class << self
15
15
 
16
+ ##
17
+ #
18
+ #
16
19
  def set_cluster_entities_attributes!(entity,array_cluster_ids)
17
20
  cluster_entities_attributes = {}
18
21
  entity_cluster_entities = entity.try(:cluster_entities) || []
@@ -35,57 +38,10 @@ module Klastera
35
38
  cluster_entities_attributes
36
39
  end
37
40
 
38
- #
39
- # In cases you don't have the active_record_collection object to filter, this method helps you
40
- # calling cluster_scope! before and pass the collction to filter_clusterized_collection_with!,
41
- # but at the end of the day, I just like to wrap methods.
42
- #
43
- def cluster_scope_filtered!(scope,cluster_id,user,organization)
44
- self.filter_clusterized_collection_with!(
45
- cluster_id,
46
- self.cluster_scope!(scope,user,organization),
47
- organization
48
- )
49
- end
50
-
51
- #
52
- # In order to this works, active_record_collection argument
53
- # should be passed through cluster_scope! method before.
54
- #
55
- def filter_clusterized_collection_with!(cluster_id,active_record_collection,cluster_organization)
56
- if cluster_organization.is_in_cluster_mode?
57
-
58
- # IMPORTANT
59
- # The next block was commented on because cluster_scope! method is not returning
60
- # the cluster_entities within the scope anymore and it doesn't make any sense trying
61
- # to filter based on force/use/show logic. Nevertheless, someday we will need it again.
62
-
63
- # # If cluster_id is nil we will try to filter including unclustered entities active_record_collection wsas
64
- # unless cluster_id.present?
65
- # # Based on force/use/show definition we don't really need this validation. A force cluster organization won't return an entity that
66
- # # doesn't belong to a cluster. Nevertheless we don't know if active_record_collection argument is fully definition-compliant.
67
- # if cluster_organization.optional_suborganization_mode?
68
- # cluster_array << nil
69
- # end
70
- # end
71
-
72
- if cluster_id.present?
73
- cluster_array = [cluster_id] unless cluster_id.is_a?(Array)
74
- # The ActiveRecordCollection argument should have previously eager-loaded the cluster entities, thus the join statement is unnecessary.
75
- active_record_collection = active_record_collection.joins(:cluster_entities).where("cluster_entities.cluster_id": cluster_array)
76
- end
77
-
78
- # You should use a block with clusterable data only
79
- yield(active_record_collection) if block_given?
80
- end
81
- active_record_collection
82
- end
83
-
84
41
  ##
85
42
  #
86
43
  #
87
- ##
88
- def set_collection_before_group_by_entity!(active_record_collection,entity_params,cluster_organization)
44
+ def set_collection_before_group_by_entity!(active_record_collection,entity_params,organization)
89
45
  entity_params_keys = [:entity_name,:entity_attribute,:entity_id,:entity_id_attribute,:unamed]
90
46
  entity_params[:entity_id] ||= nil #Ensures the entity_id attribute presence even if there is no filter
91
47
  entity_params = entity_params.slice(*entity_params_keys).values
@@ -94,47 +50,65 @@ module Klastera
94
50
  if model_relations.include?(entity_params[0])
95
51
  entity_params << "#{entity_params[0]}_id".to_sym
96
52
  if entity_params[0] == 'cluster'
97
- entity_params << :unclustered if cluster_organization.is_in_cluster_mode?
98
- active_record_collection = Klastera.filter_clusterized_collection_with!(
99
- entity_params[2],
53
+ entity_params << :unclustered if organization.is_in_cluster_mode?
54
+ active_record_collection = Klastera.filter_clusterized!(
100
55
  active_record_collection,
101
- cluster_organization
56
+ entity_params[2],
57
+ organization
102
58
  )
103
59
  end
104
60
  yield( active_record_collection, entity_params_keys.zip(entity_params).to_h )
105
61
  end
106
62
  end
107
63
 
108
- ##
109
- # Returns a ::Cluster::ActiveRecord_Relation from a given scope
110
- #
111
- def clusters_from!(scope,user,organization)
112
- cluster_scope!(scope,user,organization).related_clusters
64
+ #
65
+ # In order to this work, the active_record_collection argument should be a cluster_scope! return.
66
+ #
67
+ def filter_clusterized!(active_record_collection,cluster_id,organization)
68
+ if organization.is_in_cluster_mode? && cluster_id.present?
69
+ # Ensures that an array of ids is used in the statement
70
+ cluster_array = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
71
+ # If we receive a UNCLUSTERED_ENTITY request, it means that we should filter by entities without clusters.
72
+ # The optional_mode?(show/use) condition add to this method an ambivalent use,
73
+ # where you can filter a unique value or multiple including NULL ones.
74
+ cluster_array << nil if cluster_array.delete(UNCLUSTERED_ENTITY) || organization.optional_mode?
75
+ active_record_collection = active_record_collection.where(cluster_entities: { cluster_id: cluster_array.uniq })
76
+ # You should use a block with clusterable data only
77
+ yield(active_record_collection) if block_given?
78
+ end
79
+ active_record_collection
113
80
  end
114
81
 
115
82
  ##
116
- # Returns a scope filtered by clusters or its
117
- # organization if the cluster mode is not active.
83
+ # If the cluster mode is not active, this returns a scope filtered by clusters of its organization/users/cluster
118
84
  #
119
- def cluster_scope!(scope,user,organization)
85
+ def cluster_scope!(scope,user,organization,cluster_id=nil)
120
86
  scope_klass = scope_class(scope).where(organization_id: organization)
121
- session_clusters(user,organization) do |clusters|
122
- if organization.is_in_cluster_mode?
123
- scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*")
124
- cluster_ids = clusters.map(&:id)
125
- if organization.required_suborganization_mode?
126
- scope_klass = scope_klass.joins(:cluster_entities).where( cluster_entities: { cluster_id: cluster_ids } )
127
- else
128
- or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
129
- scope_klass = scope_klass.joins("
130
- LEFT OUTER JOIN cluster_entities
131
- ON entity_id = #{scope.table_name}.id
132
- AND entity_type = '#{scope}'
133
- ").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
134
- end
135
- # Provisional fix to avoid SQL clashes due to DISTINCT ON clause
136
- scope_klass = scope_class(scope).eager_load(:cluster_entities).where(id: scope_klass.map(&:id), organization_id: organization)
87
+ if organization.is_in_cluster_mode?
88
+
89
+ if cluster_id.present?
90
+ # Ensures that an array of id is used in the statement
91
+ cluster_ids = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
92
+ else
93
+ clusters = cluster_of!(user,organization)
94
+ cluster_ids = clusters.map(&:id).compact
95
+ end
96
+
97
+ scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*")
98
+
99
+ if organization.required_suborganization_mode?
100
+ scope_klass = scope_klass.joins(:cluster_entities).where( cluster_entities: { cluster_id: cluster_ids } )
101
+ else
102
+ or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
103
+ scope_klass = scope_klass.joins("
104
+ LEFT OUTER JOIN cluster_entities
105
+ ON entity_id = #{scope.table_name}.id
106
+ AND entity_type = '#{scope}'
107
+ ").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
137
108
  end
109
+
110
+ # Provisional fix to avoid unresolved SQL clashes with the main application due to DISTINCT ON clause
111
+ scope_klass = scope_class(scope).eager_load(:cluster_entities).where(id: scope_klass.map(&:id), organization_id: organization)
138
112
  end
139
113
  scope_klass
140
114
  end
@@ -151,19 +125,19 @@ module Klastera
151
125
  end
152
126
 
153
127
  ##
154
- # Returns a cluster array if organization is using the cluster mode
155
- #
128
+ # Returns a Cluster::ActiveRecord_Relation if the organization is using the cluster mode.
156
129
  # Use this only to get current_user/organization clusters
157
- # understanding this wont be useful out of a cluster mode context.
158
130
  #
159
- ##
160
- def session_clusters(user,organization)
161
- clusters = organization.is_in_cluster_mode? ? ::ClusterUser.clusters_from(user,organization) : []
162
- if block_given?
163
- clusters = clusters.reject{|c|c.id.blank?}
164
- yield(clusters)
131
+ def cluster_of!(user,organization)
132
+ # A cluster user with role Root or Admin retrieve every cluster of its organizations
133
+ and_this_user = user.can_admin_clusters? ? nil : user
134
+ # We weill return a cluster active record collection that can be filtered
135
+ active_record_collection = ::ClusterUser.clusters_of(organization,and_this_user)
136
+ # Add a empty cluster instance to handle models without a cluster assignation.
137
+ if organization.optional_mode? # For use and show modes only
138
+ active_record_collection << ::Cluster.new({nid: UNCLUSTERED_ENTITY, name: I18n.t("klastera.#{UNCLUSTERED_ENTITY}")})
165
139
  end
166
- clusters
140
+ active_record_collection
167
141
  end
168
142
 
169
143
  ##
@@ -181,12 +155,11 @@ module Klastera
181
155
  end
182
156
 
183
157
  ##
184
- # cluster_clusters needs the logged user and its organization
185
- # that why, we perfomed this logic here
158
+ # cluster_of! needs a user and a organization. that why we perfomed this logic here
186
159
  #
187
160
  def user_clusters_string_list!(user,organization,cluster_entities,separator,attribute=:name)
188
- @_session_clusters ||= self.session_clusters(user,organization)
189
- self.entity_clusters_string_list!(cluster_entities, separator, attribute, @_session_clusters.map(&:id))
161
+ @clusters_session ||= self.cluster_of!(user,organization)
162
+ self.entity_clusters_string_list!(cluster_entities, separator, attribute, @clusters_session.map(&:id))
190
163
  end
191
164
  end
192
165
 
@@ -199,8 +172,8 @@ module Klastera
199
172
  end
200
173
 
201
174
  def user_has_more_than_one_cluster
202
- @cluster_clusters ||= cluster_clusters
203
- @cluster_clusters.size > 1
175
+ @clusters_session ||= cluster_of_my_own
176
+ @clusters_session.size > 1
204
177
  end
205
178
 
206
179
  def set_the_lonely_cluster
@@ -208,7 +181,7 @@ module Klastera
208
181
  parameters = params.require( form_model ) rescue nil
209
182
  lonely_cluster = parameters.blank? ? false : parameters.permit( :lonely_cluster ).present?
210
183
  if lonely_cluster
211
- params[form_model][:cluster_id] = cluster_clusters.first.try(:id)
184
+ params[form_model][:cluster_id] = cluster_of_my_own.first.try(:id)
212
185
  end
213
186
  end
214
187
 
@@ -226,29 +199,16 @@ module Klastera
226
199
  [ :cluster_id ].concat( ::ClusterFilter.attributes )
227
200
  end
228
201
 
229
- def filter_clusterized_collection_with(cluster_id,active_record_collection)
230
- Klastera.filter_clusterized_collection_with!(cluster_id,active_record_collection,cluster_organization)
231
- end
232
-
233
- def cluster_scope(scope)
234
- Klastera.cluster_scope!(scope,cluster_user,cluster_organization)
235
- end
236
-
237
- def cluster_clusters
238
- Klastera.session_clusters(cluster_user,cluster_organization)
202
+ def filter_clusterized(active_record_collection,cluster_id)
203
+ Klastera.filter_clusterized!(active_record_collection, cluster_id, cluster_organization)
239
204
  end
240
205
 
241
- def cluster_scope_filtered(scope,cluster_id)
242
- Klastera.cluster_scope_filtered!(
243
- scope,
244
- cluster_id,
245
- cluster_user,
246
- cluster_organization
247
- )
206
+ def cluster_scope(scope,cluster_id=nil)
207
+ Klastera.cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
248
208
  end
249
209
 
250
- def clusters_from(scope)
251
- Klastera.clusters_from!(scope,cluster_user,cluster_organization)
210
+ def cluster_of_my_own
211
+ Klastera.cluster_of!(cluster_user, cluster_organization)
252
212
  end
253
213
 
254
214
  def user_clusters_string_list(object_entity,separator,attribute=:name)
@@ -262,7 +222,7 @@ module Klastera
262
222
  end
263
223
 
264
224
  def set_collection_before_group_by_entity(active_record_collection,entity_params,&block)
265
- Klastera.set_collection_before_group_by_entity!(active_record_collection,params,cluster_organization,&block)
225
+ Klastera.set_collection_before_group_by_entity!(active_record_collection, params, cluster_organization, &block)
266
226
  end
267
227
 
268
228
  included do
@@ -1,3 +1,3 @@
1
1
  module Klastera
2
- VERSION = "1.2.4.2"
3
- end
2
+ VERSION = "1.3"
3
+ end
@@ -13,6 +13,11 @@ namespace :klastera do
13
13
  klass = args.entity.constantize
14
14
  ActiveRecord::Base.transaction do
15
15
  klass.where.not(cluster_id: nil).each do |entity|
16
+ if entity.cluster.blank?
17
+ puts "Cluster ID #{entity.cluster_id} was not found!"
18
+ puts "skip..."
19
+ next
20
+ end
16
21
  Klastera::ClusterEntity.create(entity: entity, cluster: entity.cluster)
17
22
  end
18
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klastera
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4.2
4
+ version: '1.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gino Barahona
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-15 00:00:00.000000000 Z
11
+ date: 2020-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails