klastera 1.2.4.2 → 1.3

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