klastera 1.2.4 → 1.3.2

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: 5cc4bc5d29598040f68599863b41983f415778050bc988fb0c35b67e7d9e264b
4
- data.tar.gz: d96dd17be603a6683084bc34c9c040c51e4cb9422af7a2956c97860f812299ae
3
+ metadata.gz: 4fe09e51ca6d275a070d368cd49d916ff2ad2b02f2c06e2afcec708bda66ef88
4
+ data.tar.gz: 03d542c381f4eaefa9079d8ca47c33c787658faa0cce87127225f52a069bbd46
5
5
  SHA512:
6
- metadata.gz: ad8633404c203750947c1566d4c1d883bd7c33c1eae58982bb0489d62e769255caad49d75277244ad4b50f79fd95ef361c37408078d5ff657a7da7324d3bf75a
7
- data.tar.gz: a4c6e14fafdd2eaa1c7664901a1590800d89006a257412b04b483c313fcdc61a1797340028ffe0e9c0bd9a15d38c3783d823bba4d665cd194dd8790d2369e971
6
+ metadata.gz: 88d80a8309499a0b61d96a44d3a297fcb87fe9352d46021a7e415a66bb1e2e4776400888396478deacb9108c861bfe69755477e23f9d43ed4273b2ce98206bf5
7
+ data.tar.gz: 836a8472f3a71fda307b77c4c7c69e038bb9c01f2d364b6fb98817cc096960dca2a4ee7d8eb8735b83fbb1a67bd31e4f168f45871fa79362d6e600ac10aed307
@@ -8,4 +8,39 @@
8
8
  background: none;
9
9
  }
10
10
  }
11
- }
11
+ }
12
+
13
+ #cluster-remote-table {
14
+ .cluster-id { width: 70px; }
15
+ .cluster-order { width: 70px; }
16
+ .cluster-color { width: 70px; }
17
+ .odd .colorless { color: #F9F9F9 }
18
+ .even .colorless { color: #FFF }
19
+ tr:hover .colorless { color: #444; opacity: 0.2;}
20
+ }
21
+
22
+ #cluster-remote-form {
23
+ h4.blank-modal-title {
24
+ margin: 0;
25
+ line-height: 28px;
26
+ letter-spacing: 0.05rem;
27
+ font-size: 21px;
28
+ font-weight: 400;
29
+ color: #666;
30
+ }
31
+ }
32
+
33
+
34
+ ul.klastera-option-help {
35
+ padding-left: 20px;
36
+ list-style-type: disc;
37
+ }
38
+
39
+ ul.klastera-option-help li {
40
+ margin-bottom: 10px;
41
+ padding-bottom: 5px;
42
+ border-bottom: 1px solid #DDD;
43
+ }
44
+
45
+ ul.klastera-option-help li b { color: #8A8A8A; font-weight: 400; }
46
+ ul.klastera-option-help li b:first-child { color: #2E5F9B; font-weight: bold; }
@@ -33,13 +33,11 @@ module Klastera
33
33
 
34
34
  def destroy
35
35
  new_cluster_id = params.require(:transfer).permit(:new_cluster_id).values.first rescue nil
36
- @transfer = Transfer.new(
37
- current_cluster: @cluster,
38
- new_cluster_id: new_cluster_id
39
- )
36
+ @transfer = Transfer.new( current_cluster: @cluster, new_cluster_id: new_cluster_id )
40
37
  if @transfer.valid?
41
- @transfer.to!(::Cluster.related_entities.map{|re|re.constantize})
42
- @cluster.destroy
38
+ if @transfer.apply!
39
+ @cluster.destroy
40
+ end
43
41
  set_clusters
44
42
  end
45
43
  end
@@ -11,6 +11,8 @@ 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
15
+ has_many :cluster_entities, dependent: :destroy
14
16
 
15
17
  scope :of, -> (organization,except_ids=[]) {
16
18
  _scope = where(organization: organization)
@@ -41,7 +43,7 @@ module Klastera::Concerns::Cluster
41
43
  end
42
44
 
43
45
  def has_related_entities_using_it?
44
- ::Cluster.total_records_assign_to(self) > 0
46
+ self.cluster_entities.size > 0
45
47
  end
46
48
 
47
49
  def can_transfer_and_destroy?
@@ -59,24 +61,5 @@ module Klastera::Concerns::Cluster
59
61
  end
60
62
 
61
63
  module ClassMethods
62
- def related_entities(attr_needed: :class_name, macro: :has_many)
63
- ::Cluster.reflections.map do |association_name, reflection|
64
- reflection.send(attr_needed) if reflection.macro == macro
65
- end.compact
66
- end
67
-
68
- def total_records_assign_to(cluster_instance)
69
- related_entities(attr_needed: :name).inject(0) do |total, association_name|
70
- entity = cluster_instance.send(association_name)
71
- if entity.respond_to?(:cluster_id)
72
- total+=entity.where(cluster_id: cluster_instance.id).count
73
- end
74
- total
75
- end
76
- end
77
-
78
- def modes_as_strings
79
- ::Cluster::MODES.map{|m|m.to_s}
80
- end
81
64
  end
82
65
  end
@@ -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
- includes(: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'))
@@ -4,7 +4,7 @@ module Klastera::Concerns::Organization
4
4
 
5
5
  has_many :clusters
6
6
 
7
- validates :use_cluster_as, inclusion: { in: ::Cluster.modes_as_strings , message: I18n.t('klastera.clusters.wrong_option') }, if: proc{ use_cluster_as.present? }
7
+ validates :use_cluster_as, inclusion: { in: ::Cluster::MODES.map{|m|m.to_s}, message: I18n.t('klastera.clusters.wrong_option') }, if: proc{ use_cluster_as.present? }
8
8
 
9
9
  # Return a symbol version of use_cluster_as value
10
10
  def cluster_mode
@@ -7,20 +7,14 @@ module Klastera::Concerns::Transfer
7
7
  include ActiveModel::Model
8
8
  include ActiveModel::Validations::Callbacks
9
9
 
10
- attr_accessor :current_cluster, :new_cluster_id, :entities_transfered
10
+ attr_accessor :current_cluster, :new_cluster_id
11
11
 
12
12
  validates :current_cluster, presence: true
13
13
  validates :new_cluster_id, presence: true, if: proc { self.required_transfer? }
14
14
 
15
15
  validate do
16
16
  new_cluster = ::Cluster.find(self.new_cluster_id.to_i) rescue nil
17
- #
18
- # In my time, to_a? didnt work
19
- # current_cluster.class returned:
20
- # Cluster(id: integer, name: string, nid: text, organization_id: integer, created_at: datetime, updated_at: datetime, color: string)
21
- #
22
- # If you see this, please fixe it. Thanks
23
- #
17
+
24
18
  if current_cluster.class.name != 'Cluster' || current_cluster.try(:is_the_last_record_in_required_suborganization_mode?)
25
19
  errors.add(:current_cluster, I18n.t('klastera.messages.current_cluster.cant_transfer'))
26
20
  elsif self.required_transfer? && new_cluster_id.present? && new_cluster.nil?
@@ -33,22 +27,19 @@ module Klastera::Concerns::Transfer
33
27
  end
34
28
  end
35
29
 
36
- ##
37
- #
38
- #
39
- def to!(related_entities)
40
- self.entities_transfered = 0
41
- related_entities.each do |entity|
42
- if entity.is_a?(Class) && entity.respond_to?(:cluster_id)
43
- self.entities_transfered += entity.where(
44
- cluster_id: self.current_cluster.id
45
- ).update_all(cluster_id: self.new_cluster_id)
46
- end
47
- end
48
- end
49
-
50
30
  def required_transfer?
51
31
  self.current_cluster.required_transfer? && self.current_cluster.has_related_entities_using_it?
52
32
  end
33
+
34
+ ##
35
+ # A returned boolean is expected. It should always be true even nothing is
36
+ # transfered, and it only will return false if creation fails.
37
+ #
38
+ def apply!
39
+ Klastera::ClusterEntity.create(current_cluster.cluster_entities.map{ |relation|
40
+ next if self.new_cluster_id.blank?
41
+ { cluster_id: self.new_cluster_id, entity_id: relation.entity_id, entity_type: relation.entity_type }
42
+ }.compact)
43
+ end
53
44
  end
54
45
  end
@@ -3,16 +3,16 @@
3
3
 
4
4
  <div class="<%=classes_for_remote_modal_body()%>">
5
5
  <div class="row">
6
- <div class="col-xs-12">
6
+ <div class="col-xs-6">
7
7
  <%= f.input :name, as: :string, label: t('klastera.cluster_name') %>
8
8
  </div>
9
- <div class="col-xs-12">
9
+ <div class="col-xs-6">
10
10
  <%= f.input :nid, as: :string, label: t('klastera.cluster_nid') %>
11
11
  </div>
12
- <div class="col-xs-12">
12
+ <div class="col-xs-6">
13
13
  <%= f.input :color, as: :string, label: t('klastera.cluster_color'), input_html: { class: 'color-picker'} %>
14
14
  </div>
15
- <div class="col-xs-12">
15
+ <div class="col-xs-6">
16
16
  <%= f.input :order, as: :string, label: t('klastera.cluster_order') %>
17
17
  </div>
18
18
  </div>
@@ -2,8 +2,8 @@
2
2
  <%= simple_form_for( @transfer, url: cluster_path(@cluster), method: :delete, remote: true, html: { autocomplete: :off, class: 'destroy-form slim-form-field' } ) do |f| %>
3
3
  <%=render 'layouts/remote_form/header', o: @cluster, title: title, fa: :qrcode %>
4
4
  <div class="<%=classes_for_remote_modal_body()%>">
5
- <h4 class="text-center">Esta acción es irreversible.<br/>¿Está seguro?</h4>
6
- <% if ::Cluster.total_records_assign_to(@cluster) > 0 %>
5
+ <h4 class="blank-modal-title text-center">Esta acción es irreversible<br/>¿Está seguro?</h4>
6
+ <% if @cluster.cluster_entities.size > 0 %>
7
7
  <br />
8
8
  <div class="row">
9
9
  <% if can_transfer_and_destroy %>
@@ -1,4 +1,4 @@
1
- <table class="table table-striped table-hover table-condensed table-bordered datatable">
1
+ <table class="table table-striped table-hover table-condensed table-bordered datatable table">
2
2
  <thead>
3
3
  <tr>
4
4
  <th class="text-center cluster-id"><%=t('klastera.cluster_id')%></th>
@@ -6,7 +6,7 @@
6
6
  <th class="text-center cluster-color"><%=t('klastera.cluster_color')%></th>
7
7
  <th class="text-center cluster-name"><%=t('klastera.cluster_name')%></th>
8
8
  <th class="text-center cluster-nid"><%=t('klastera.cluster_nid')%></th>
9
- <th class="cogs-actions four-icons"><span class="fa fa-cogs"></span></th>
9
+ <th class="cogs-actions two-icons"><span class="fa fa-cogs"></span></th>
10
10
  </tr>
11
11
  </thead>
12
12
  <tbody>
@@ -19,7 +19,7 @@
19
19
  </td>
20
20
  <td class="text-center cluster-name"><%= cluster.name %></td>
21
21
  <td class="text-center cluster-nid"><%= cluster.nid %></td>
22
- <td class="cogs-actions four-icons">
22
+ <td class="cogs-actions two-icons">
23
23
  <%= link_to edit_cluster_path(cluster), class:'btn btn-primary btn-xs', title: t('shared.actions.edit'), "data-toggle": "modal", "data-target": "#remote-modal-block" do %>
24
24
  <span class="fa fa-pencil-square-o"></span>
25
25
  <% end %>
@@ -30,12 +30,4 @@
30
30
  </tr>
31
31
  <% end %>
32
32
  </tbody>
33
- </table>
34
- <style type="text/css">
35
- .cluster-id { width: 70px; }
36
- .cluster-order { width: 70px; }
37
- .cluster-color { width: 70px; }
38
- .odd .colorless { color: #F9F9F9 }
39
- .even .colorless { color: #FFF }
40
- tr:hover .colorless { color: #F5F5F5 }
41
- </style>
33
+ </table>
@@ -18,4 +18,4 @@
18
18
 
19
19
  </div>
20
20
 
21
- <%= render 'layouts/remote_form/modal'%>
21
+ <%= render 'layouts/remote_form/modal', width: 'md' %>
@@ -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>
@@ -19,20 +19,4 @@
19
19
  <script type="text/javascript">
20
20
  var popoverTemplate = '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
21
21
  $('.klastera-cluster-option').popover({ template: popoverTemplate, html: true, trigger: 'click', });
22
- </script>
23
-
24
- <style type="text/css">
25
- ul.klastera-option-help {
26
- padding-left: 20px;
27
- list-style-type: disc;
28
- }
29
-
30
- ul.klastera-option-help li {
31
- margin-bottom: 10px;
32
- padding-bottom: 5px;
33
- border-bottom: 1px solid #DDD;
34
- }
35
-
36
- ul.klastera-option-help li b { color: #8A8A8A; font-weight: 400; }
37
- ul.klastera-option-help li b:first-child { color: #2E5F9B; font-weight: bold; }
38
- </style>
22
+ </script>
@@ -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
11
- user_clusters_string_list
10
+ cluster_user cluster_organization user_has_more_than_one_cluster cluster_scope cluster_of_my_own
11
+ user_clusters_string_list set_collection_before_group_by_entity __cluster_scope
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,94 +38,109 @@ module Klastera
35
38
  cluster_entities_attributes
36
39
  end
37
40
 
38
- #
39
- # I like to wrap methods
40
- #
41
- def cluster_scope_filtered!(scope,cluster_id,user,organization,includes=[])
42
- self.filter_clusterized_collection_with!(
43
- organization,
44
- cluster_id,
45
- self.cluster_scope!(user,organization,scope,includes)
46
- )
47
- end
48
-
49
- #
50
- # In order to this works, active_record_collection argument
51
- # should be passed through cluster_scope! method before.
52
- #
53
- def filter_clusterized_collection_with!(cluster_organization,cluster_id,active_record_collection)
54
- if cluster_organization.is_in_cluster_mode?
55
- if cluster_id.present?
56
- cluster_array = [cluster_id]
57
- # Based on force/use/show definition we don't really need this validation.
58
- # A force cluster organization won't return an entity out of a cluster, but
59
- # we don't know if the clusterized entities are fully definition-compliant.
60
- cluster_array << nil if cluster_organization.optional_suborganization_mode?
61
- active_record_collection = active_record_collection.joins(:cluster_entities).where("cluster_entities.cluster_id": cluster_array)
62
- end
63
- # you may use a block only with clusterizable data
64
- yield(active_record_collection) if block_given?
65
- end
66
- active_record_collection
67
- end
68
-
69
- #
70
- #
71
- #
72
- def set_collection_before_group_by_entity!(cluster_organization,active_record_collection,model_class,entity_params)
41
+ ##
42
+ #
43
+ #
44
+ def set_collection_before_group_by_entity!(active_record_collection,entity_params,organization)
73
45
  entity_params_keys = [:entity_name,:entity_attribute,:entity_id,:entity_id_attribute,:unamed]
46
+ entity_params[:entity_id] ||= nil #Ensures the entity_id attribute presence even if there is no filter
74
47
  entity_params = entity_params.slice(*entity_params_keys).values
75
- if model_class.try(:reflections).try(:keys).try(:include?,entity_params[0])
48
+ model_class = active_record_collection.model.base_class
49
+ model_relations = model_class.reflections.keys
50
+ if model_relations.include?(entity_params[0])
76
51
  entity_params << "#{entity_params[0]}_id".to_sym
77
52
  if entity_params[0] == 'cluster'
78
- entity_params << :unclustered if cluster_organization.is_in_cluster_mode?
79
- active_record_collection = Klastera.filter_clusterized_collection_with!(
80
- cluster_organization,
53
+ entity_params << I18n.t("klastera.#{UNCLUSTERED_ENTITY}") if organization.is_in_cluster_mode?
54
+ active_record_collection = Klastera.filter_clusterized!(
55
+ active_record_collection,
81
56
  entity_params[2],
82
- active_record_collection
57
+ organization
83
58
  )
84
59
  end
85
- yield(
86
- active_record_collection,
87
- entity_params_keys.zip(entity_params).to_h)
60
+ yield( active_record_collection, entity_params_keys.zip(entity_params).to_h )
88
61
  end
89
62
  end
90
63
 
91
- ##
92
- # Returns a ::Cluster::ActiveRecord_Relation from a given scope
93
- #
94
- def clusters_from!(user,organization,scope,includes=[])
95
- cluster_scope!(user,organization,scope,includes).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
96
80
  end
97
81
 
98
82
  ##
99
- # Returns a scope filtered by clusters or its
100
- # 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
101
84
  #
102
- def cluster_scope!(user,organization,scope,includes=[])
85
+ def cluster_scope!(scope,user,organization,cluster_id=nil)
103
86
  scope_klass = scope_class(scope).where(organization_id: organization)
104
- session_clusters(user,organization) do |clusters|
105
- if organization.is_in_cluster_mode?
106
- scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*")
107
- scope_klass = scope_klass.includes(includes) if includes.present?
108
- cluster_ids = clusters.map(&:id)
109
- if organization.required_suborganization_mode?
110
- scope_klass = scope_klass.joins(:cluster_entities).where( cluster_entities: { cluster_id: cluster_ids } )
111
- else
112
- or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
113
- scope_klass = scope_klass.joins("
114
- LEFT OUTER JOIN cluster_entities
115
- ON entity_id = #{scope.table_name}.id
116
- AND entity_type = '#{scope}'
117
- ").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
118
- end
119
- # Provisional fix to avoid SQL clashes due to DISTINCT ON clause
120
- scope_klass = scope_class(scope).where(organization_id: organization).where(id: scope_klass.map(&:id))
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
121
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}")
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)
122
112
  end
123
113
  scope_klass
124
114
  end
125
115
 
116
+
117
+ # Optimized version of cluster_scope!
118
+ #
119
+ def same_cluster_scope!(scope,user,organization,cluster_id=nil)
120
+ scope_klass = scope_class(scope)
121
+ if organization.is_in_cluster_mode?
122
+ scope_klass = scope_klass.eager_load(:organization,cluster_entities: :cluster)
123
+
124
+ if cluster_id.present?
125
+ cluster_ids = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
126
+ else
127
+ clusters = cluster_of!(user,organization)
128
+ cluster_ids = clusters.map(&:id).compact.sort
129
+ end
130
+
131
+ scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*, clusters.*")
132
+
133
+ if organization.required_suborganization_mode?# || cluster_id # If cluster_id is passed NULL clusters are expected, rigth?
134
+ scope_klass = scope_klass.where( cluster_entities: { cluster_id: cluster_ids } )
135
+ else
136
+ or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
137
+ scope_klass = scope_klass.where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
138
+ end
139
+ end
140
+
141
+ scope_klass.where(organization_id: organization)
142
+ end
143
+
126
144
  ##
127
145
  # TODO:
128
146
  # Implement a validation to ensure that
@@ -135,19 +153,19 @@ module Klastera
135
153
  end
136
154
 
137
155
  ##
138
- # Returns a cluster array if organization is using the cluster mode
139
- #
156
+ # Returns a Cluster::ActiveRecord_Relation if the organization is using the cluster mode.
140
157
  # Use this only to get current_user/organization clusters
141
- # understanding this wont be useful out of a cluster mode context.
142
158
  #
143
- ##
144
- def session_clusters(user,organization)
145
- clusters = organization.is_in_cluster_mode? ? ::ClusterUser.clusters_from(user,organization) : []
146
- if block_given?
147
- clusters = clusters.reject{|c|c.id.blank?}
148
- yield(clusters)
159
+ def cluster_of!(user,organization)
160
+ # A cluster user with role Root or Admin retrieve every cluster of its organizations
161
+ and_this_user = user.can_admin_clusters? ? nil : user
162
+ # We weill return a cluster active record collection that can be filtered
163
+ active_record_collection = ::ClusterUser.clusters_of(organization,and_this_user)
164
+ # Add a empty cluster instance to handle models without a cluster assignation.
165
+ if organization.optional_mode? # For use and show modes only
166
+ active_record_collection << ::Cluster.new({nid: UNCLUSTERED_ENTITY, name: I18n.t("klastera.#{UNCLUSTERED_ENTITY}")})
149
167
  end
150
- clusters
168
+ active_record_collection
151
169
  end
152
170
 
153
171
  ##
@@ -165,12 +183,11 @@ module Klastera
165
183
  end
166
184
 
167
185
  ##
168
- # cluster_clusters needs the logged user and its organization
169
- # that why, we perfomed this logic here
186
+ # cluster_of! needs a user and a organization. that why we perfomed this logic here
170
187
  #
171
188
  def user_clusters_string_list!(user,organization,cluster_entities,separator,attribute=:name)
172
- @_session_clusters ||= self.session_clusters(user,organization)
173
- self.entity_clusters_string_list!(cluster_entities, separator, attribute, @_session_clusters.map(&:id))
189
+ @clusters_session ||= self.cluster_of!(user,organization)
190
+ self.entity_clusters_string_list!(cluster_entities, separator, attribute, @clusters_session.map(&:id))
174
191
  end
175
192
  end
176
193
 
@@ -183,8 +200,8 @@ module Klastera
183
200
  end
184
201
 
185
202
  def user_has_more_than_one_cluster
186
- @cluster_clusters ||= cluster_clusters
187
- @cluster_clusters.size > 1
203
+ @clusters_session ||= cluster_of_my_own
204
+ @clusters_session.size > 1
188
205
  end
189
206
 
190
207
  def set_the_lonely_cluster
@@ -192,7 +209,7 @@ module Klastera
192
209
  parameters = params.require( form_model ) rescue nil
193
210
  lonely_cluster = parameters.blank? ? false : parameters.permit( :lonely_cluster ).present?
194
211
  if lonely_cluster
195
- params[form_model][:cluster_id] = cluster_clusters.first.try(:id)
212
+ params[form_model][:cluster_id] = cluster_of_my_own.first.try(:id)
196
213
  end
197
214
  end
198
215
 
@@ -210,26 +227,20 @@ module Klastera
210
227
  [ :cluster_id ].concat( ::ClusterFilter.attributes )
211
228
  end
212
229
 
213
- def cluster_scope(scope,includes=[])
214
- Klastera.cluster_scope!(cluster_user,cluster_organization,scope,includes)
230
+ def filter_clusterized(active_record_collection,cluster_id)
231
+ Klastera.filter_clusterized!(active_record_collection, cluster_id, cluster_organization)
215
232
  end
216
233
 
217
- def cluster_clusters
218
- Klastera.session_clusters(cluster_user,cluster_organization)
234
+ def cluster_scope(scope,cluster_id=nil)
235
+ Klastera.cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
219
236
  end
220
237
 
221
- def cluster_scope_filtered(scope,cluster_id,includes=[])
222
- Klastera.cluster_scope_filtered!(
223
- scope,
224
- cluster_id,
225
- cluster_user,
226
- cluster_organization,
227
- includes
228
- )
238
+ def same_cluster_scope(scope,cluster_id=nil)
239
+ Klastera.same_cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
229
240
  end
230
241
 
231
- def clusters_from(scope,includes=[])
232
- Klastera.clusters_from!(cluster_user,cluster_organization,scope,includes)
242
+ def cluster_of_my_own
243
+ Klastera.cluster_of!(cluster_user, cluster_organization)
233
244
  end
234
245
 
235
246
  def user_clusters_string_list(object_entity,separator,attribute=:name)
@@ -242,6 +253,10 @@ module Klastera
242
253
  )
243
254
  end
244
255
 
256
+ def set_collection_before_group_by_entity(active_record_collection,entity_params,&block)
257
+ Klastera.set_collection_before_group_by_entity!(active_record_collection, params, cluster_organization, &block)
258
+ end
259
+
245
260
  included do
246
261
  Klastera::KLSTR_HELPERS.each do |action|
247
262
  if respond_to?(:helper_method)
@@ -1,3 +1,3 @@
1
1
  module Klastera
2
- VERSION = "1.2.4"
3
- end
2
+ VERSION = "1.3.2"
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
4
+ version: 1.3.2
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-06 00:00:00.000000000 Z
11
+ date: 2020-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails