klastera 1.2.4 → 1.3.2

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