skimming 0.0.2 → 1.0.0
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 +4 -4
- data/README.md +14 -27
- data/lib/generators/skimming_migration_generator.rb +25 -23
- data/lib/models/skimming/filter.rb +11 -0
- data/lib/models/skimming/item.rb +15 -0
- data/lib/models/skimming/rule.rb +7 -0
- data/lib/skimming.rb +3 -3
- data/lib/skimming/model.rb +9 -3
- data/lib/skimming/skimmer.rb +12 -24
- data/skimming.gemspec +2 -2
- metadata +10 -10
- data/lib/models/skimming/collection_filter.rb +0 -11
- data/lib/skimming/configuration.rb +0 -18
- data/lib/skimming/parasite_model.rb +0 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26418f0331bb4b3d54054941f86629eec8f48959ef4df9dba5349f9cf61bc0eb
|
|
4
|
+
data.tar.gz: 0c7555240ff2343ee5348bad95be19a5b1ba326b4297598d0cca0c342fc4b141
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db658918f56316e975ec52d41776afe0f8d9693d907fb72f58b92fe3cbc99319865befe108730a0fe281dca770c4d974ac39863a71e8ffed61b0fa436df7f861
|
|
7
|
+
data.tar.gz: 0133cb9935aed599bdd3eec639276b5e1f5f7e98a76c8aa4682ce1783847ae646195fa6259f2d0d83ea6e5ba1a65a03e096fbc4bbe8751488b1964107dc633fc
|
data/README.md
CHANGED
|
@@ -15,12 +15,6 @@ In each model you want to have the feature, just put the following.
|
|
|
15
15
|
|
|
16
16
|
include Skimming::Model
|
|
17
17
|
|
|
18
|
-
Create `config/skimming.yml` file in your project and compile it as follows for each of the models you included the code above into (let's say for instance you included Skimming::Model into Role model).
|
|
19
|
-
|
|
20
|
-
associations:
|
|
21
|
-
roles:
|
|
22
|
-
class_name: 'Role'
|
|
23
|
-
|
|
24
18
|
Once this config file is created, you can launch the following command.
|
|
25
19
|
|
|
26
20
|
rails g skimming_migration
|
|
@@ -31,43 +25,36 @@ The command above will generate a migration file in your project. Now you can mi
|
|
|
31
25
|
|
|
32
26
|
### Use skimming filters of another model
|
|
33
27
|
|
|
34
|
-
If you want, you can also allow a related model (for example User) to use Role's skimming rules. In that case the User model should
|
|
28
|
+
If you want, you can also allow a related model (for example User) to use Role's skimming rules. In that case the in the User model you should do as follows.
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
skim_throught :roles
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
## Usage
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
roles:
|
|
42
|
-
class_name: 'Role'
|
|
43
|
-
options:
|
|
44
|
-
user:
|
|
45
|
-
skim_through:
|
|
46
|
-
- 'roles'
|
|
34
|
+
Create one `Skimming::Item` for each class you want your skimmable models to skim. The name of the item has to be PascalCase.
|
|
47
35
|
|
|
48
|
-
|
|
36
|
+
item = Skimming::Item.create(name: 'Order')
|
|
37
|
+
|
|
38
|
+
Create one `Skimming::Rule` for each condition you want to evaluate when you decide if the skimmable should keep the items of a certain collection.
|
|
49
39
|
|
|
50
|
-
|
|
40
|
+
rule = Skimming::Rule.create(statement: '@order.created_at < 1.month.ago')
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
role.create_collection_filter(object_name: 'pet', rule: '@pet.nice?')
|
|
54
|
-
role.create_collection_filter(object_name: 'room', rule: '@room.clean?')
|
|
42
|
+
This, for example, hides all orders older than 1 month from the collection.
|
|
55
43
|
|
|
56
|
-
|
|
44
|
+
Create one `Skimming::Filter` for each item your model needs to skim and assign the rule.
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
role.create_filter(item: item, rule: rule)
|
|
59
47
|
|
|
60
|
-
|
|
48
|
+
If now you call `role.skim orders_collection` only orders newer than 1 month ago will be returned.
|
|
61
49
|
|
|
62
|
-
|
|
63
|
-
user.skim rooms_collection, through: [:roles, :whatever]
|
|
50
|
+
Also user can do that if it has the role assigned and you have specified `skim_through :roles` in User model.
|
|
64
51
|
|
|
65
52
|
### Rules
|
|
66
53
|
|
|
67
54
|
You can store in filters rules whatever conditions you like in plain ruby and the string will be evaulated. Inside the rules, object have to be called as instance variables. These instance variables indeed need to be present and can be declared in a few ways.
|
|
68
55
|
|
|
69
56
|
1. The model name of the instance you called `skim` from, like the user, will be defined automatically based on model name, so if you call `user.skim rooms_collection` you will have `@user` variable defined.
|
|
70
|
-
2. Based on the skimmed collection, instance variables are defined. In the case above, `@
|
|
57
|
+
2. Based on the skimmed collection, instance variables are defined. In the case above, `@order` variable will be present for each of the orders to be evaluated in. The collection name is calculated based on the class of the first object in the collection and will raise an error if is not the same for all collection elements. You can override this calculation specifying the `item_name` of the collection (the skimming will look for collection_filters with that `item_name`). This can be necessary if you are filtering throught different classes instances having STI.
|
|
71
58
|
3. You can pass as third argument an hash of objects like this `user.skim rooms_collection, time: Time.zone.now, whatever: @you_want` and you will have `@time` and `@whatever` variables defined for rule evaluation.
|
|
72
59
|
|
|
73
60
|
WARNING: since the strings get evaluated, i recommend not to allow anyone except project developers to create collection_filters, in order to prevent malicious code from being executed.
|
|
@@ -3,39 +3,41 @@ require 'rails/generators'
|
|
|
3
3
|
class SkimmingMigrationGenerator < Rails::Generators::Base
|
|
4
4
|
|
|
5
5
|
def create_migration_file
|
|
6
|
-
create_file "db/migrate/#{Time.zone.now.strftime("%Y%m%d%H%M%S")}
|
|
6
|
+
create_file "db/migrate/#{Time.zone.now.strftime("%Y%m%d%H%M%S")}_skimming_migration_1_0_0.rb", migration_data
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def migration_data
|
|
12
12
|
<<MIGRATION
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
t.timestamps
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
#{generate_join_tables_creation_data}
|
|
13
|
+
class SkimmingMigration100 < ActiveRecord::Migration[5.2]
|
|
14
|
+
def change
|
|
15
|
+
unless table_exists? :items
|
|
16
|
+
create_table :items do |t|
|
|
17
|
+
t.string :name
|
|
18
|
+
|
|
19
|
+
t.timestamps
|
|
25
20
|
end
|
|
26
21
|
end
|
|
27
|
-
end
|
|
28
|
-
MIGRATION
|
|
29
|
-
end
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
unless table_exists? :filters
|
|
24
|
+
create_table :filters do |t|
|
|
25
|
+
t.bigint :item_id
|
|
26
|
+
t.bigint :skimmable_id
|
|
27
|
+
t.string :skimmable_type
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
end
|
|
29
|
+
t.timestamps
|
|
30
|
+
end
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
create_table :rules do |t|
|
|
33
|
+
t.bigint :filter_id
|
|
34
|
+
t.string :statement
|
|
35
|
+
|
|
36
|
+
t.timestamps
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
MIGRATION
|
|
40
42
|
end
|
|
41
43
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Skimming
|
|
2
|
+
class Filter < ActiveRecord::Base
|
|
3
|
+
belongs_to :skimmable, polymorphic: true
|
|
4
|
+
belongs_to :item, class_name: 'Skimming::Item', inverse_of: :filters, foreign_key: :item_id
|
|
5
|
+
has_many :rules
|
|
6
|
+
|
|
7
|
+
validates_presence_of :rules
|
|
8
|
+
|
|
9
|
+
scope :for_item, -> (item_name) { joins(:item).where(items: { name: item_name }) }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Skimming
|
|
2
|
+
class Item < ActiveRecord::Base
|
|
3
|
+
has_many :filters, class_name: 'Skimming::Filter', inverse_of: :item, foreign_key: :item_id
|
|
4
|
+
|
|
5
|
+
validates :name, presence: true, uniqueness: true
|
|
6
|
+
|
|
7
|
+
attr_readonly :name
|
|
8
|
+
|
|
9
|
+
before_validation :classify_name
|
|
10
|
+
|
|
11
|
+
def classify_name
|
|
12
|
+
self.name = self.name.classify
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/skimming.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require 'skimming/configuration'
|
|
2
1
|
require 'skimming/model'
|
|
3
|
-
require 'skimming/parasite_model'
|
|
4
2
|
require 'skimming/skimmer'
|
|
5
|
-
require 'models/skimming/
|
|
3
|
+
require 'models/skimming/filter'
|
|
4
|
+
require 'models/skimming/item'
|
|
5
|
+
require 'models/skimming/rule'
|
|
6
6
|
require 'generators/skimming_migration_generator'
|
data/lib/skimming/model.rb
CHANGED
|
@@ -3,11 +3,17 @@ module Skimming
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
included do
|
|
6
|
-
|
|
6
|
+
has_many :filters, class_name: 'Skimming::Filter', as: :skimmable
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def skim_through(association)
|
|
11
|
+
has_many :filters, through: association, class_name: 'Skimming::Filter', source: :filters
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def skim(collection, item_name: nil, **skimming_instances)
|
|
16
|
+
Skimming::Skimmer.new(self, collection, item_name, skimming_instances).skim
|
|
11
17
|
end
|
|
12
18
|
end
|
|
13
19
|
end
|
data/lib/skimming/skimmer.rb
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
module Skimming
|
|
2
2
|
class Skimmer
|
|
3
|
-
attr_reader :subject, :collection, :
|
|
3
|
+
attr_reader :subject, :collection, :item_name, :skimming_instances
|
|
4
4
|
|
|
5
|
-
def initialize(subject, collection,
|
|
5
|
+
def initialize(subject, collection, item_name, skimming_instances)
|
|
6
6
|
@subject = subject
|
|
7
7
|
@collection = collection
|
|
8
|
-
@
|
|
8
|
+
@item_name = calculate_item_name
|
|
9
9
|
@skimming_instances = skimming_instances
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def skim
|
|
13
13
|
set_instance_variables
|
|
14
14
|
|
|
15
|
-
filters_rules = subject.
|
|
15
|
+
filters_rules = subject.filters.for_item(item_name).map(&:rules).flatten.map(&:statement)
|
|
16
16
|
|
|
17
17
|
return collection if filters_rules.empty?
|
|
18
18
|
|
|
19
|
-
skimming_result = collection.select do |
|
|
20
|
-
instance_variable_set("@#{
|
|
19
|
+
skimming_result = collection.select do |collection_item|
|
|
20
|
+
instance_variable_set("@#{item_name.downcase}", collection_item)
|
|
21
21
|
|
|
22
22
|
filters_rules.all? { |rule| eval rule }
|
|
23
23
|
end
|
|
@@ -25,28 +25,16 @@ module Skimming
|
|
|
25
25
|
skimming_result
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
set_instance_variables
|
|
30
|
-
|
|
31
|
-
skimming_associations = Skimming.configuration.options[subject.class.name.underscore][:skim_through] if skimming_associations.blank?
|
|
32
|
-
skimming_associations = [skimming_associations] unless skimming_associations.respond_to? :each
|
|
33
|
-
skimming_result = []
|
|
34
|
-
|
|
35
|
-
skimming_associations.each do |association_name|
|
|
36
|
-
subject.send(association_name.to_sym).each do |skimming_object|
|
|
37
|
-
skimming_result += skimming_object.skim(collection, subject.class.name.downcase.to_sym => subject)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
28
|
+
private
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
def calculate_item_name
|
|
31
|
+
return item_name.to_s.classify if item_name
|
|
43
32
|
|
|
44
|
-
|
|
33
|
+
items_classes = collection.map(&:class).uniq
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
raise 'Invalid collection: contains objects with different classes' unless collection.map(&:class).uniq.count == 1
|
|
35
|
+
raise "Invalid collection: contains items with different classes (#{items.classes.join(', ')}). Use same class items or specify their item_name." unless items_classes.count == 1
|
|
48
36
|
|
|
49
|
-
|
|
37
|
+
items_classes.first.name.demodulize
|
|
50
38
|
end
|
|
51
39
|
|
|
52
40
|
def set_instance_variables
|
data/skimming.gemspec
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = 'skimming'
|
|
3
|
-
s.version = '0.0
|
|
4
|
-
s.date = '2020-
|
|
3
|
+
s.version = '1.0.0'
|
|
4
|
+
s.date = '2020-09-14'
|
|
5
5
|
s.summary = "Collections skimming"
|
|
6
6
|
s.description = "Filter your collections with database-configurable rules"
|
|
7
7
|
s.authors = ["Valerio Bellaveglia"]
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: skimming
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Valerio Bellaveglia
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-09-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Filter your collections with database-configurable rules
|
|
14
|
-
email:
|
|
14
|
+
email:
|
|
15
15
|
executables: []
|
|
16
16
|
extensions: []
|
|
17
17
|
extra_rdoc_files: []
|
|
@@ -19,18 +19,18 @@ files:
|
|
|
19
19
|
- ".gitignore"
|
|
20
20
|
- README.md
|
|
21
21
|
- lib/generators/skimming_migration_generator.rb
|
|
22
|
-
- lib/models/skimming/
|
|
22
|
+
- lib/models/skimming/filter.rb
|
|
23
|
+
- lib/models/skimming/item.rb
|
|
24
|
+
- lib/models/skimming/rule.rb
|
|
23
25
|
- lib/skimming.rb
|
|
24
|
-
- lib/skimming/configuration.rb
|
|
25
26
|
- lib/skimming/model.rb
|
|
26
|
-
- lib/skimming/parasite_model.rb
|
|
27
27
|
- lib/skimming/skimmer.rb
|
|
28
28
|
- skimming.gemspec
|
|
29
29
|
homepage: https://github.com/ValerioBellaveglia/Skimming
|
|
30
30
|
licenses:
|
|
31
31
|
- MIT
|
|
32
32
|
metadata: {}
|
|
33
|
-
post_install_message:
|
|
33
|
+
post_install_message:
|
|
34
34
|
rdoc_options: []
|
|
35
35
|
require_paths:
|
|
36
36
|
- lib
|
|
@@ -45,8 +45,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '0'
|
|
47
47
|
requirements: []
|
|
48
|
-
rubygems_version: 3.
|
|
49
|
-
signing_key:
|
|
48
|
+
rubygems_version: 3.1.2
|
|
49
|
+
signing_key:
|
|
50
50
|
specification_version: 4
|
|
51
51
|
summary: Collections skimming
|
|
52
52
|
test_files: []
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
module Skimming
|
|
2
|
-
class CollectionFilter < ActiveRecord::Base
|
|
3
|
-
Skimming.configuration.associations.each do |association_name, association_options|
|
|
4
|
-
has_and_belongs_to_many association_name.to_sym, class_name: association_options[:class_name], inverse_of: :collection_filters, foreign_key: "#{association_name}_id".to_sym
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
validates_presence_of :object_name, :rule
|
|
8
|
-
|
|
9
|
-
scope :for_object, -> (object_name) { where(object_name: object_name) }
|
|
10
|
-
end
|
|
11
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module Skimming
|
|
2
|
-
def self.configuration
|
|
3
|
-
@configuration ||= Configuration.new(configuration_hash)
|
|
4
|
-
end
|
|
5
|
-
|
|
6
|
-
def self.configuration_hash
|
|
7
|
-
@configuration_hash ||= HashWithIndifferentAccess.new(YAML.load_file('config/skimming.yml'))
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
class Configuration
|
|
11
|
-
attr_accessor :associations, :options
|
|
12
|
-
|
|
13
|
-
def initialize(configuration_hash)
|
|
14
|
-
@associations = configuration_hash[:associations]
|
|
15
|
-
@options = configuration_hash[:options]
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module Skimming
|
|
2
|
-
module ParasiteModel
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
|
|
5
|
-
included do
|
|
6
|
-
Skimming.configuration.options[name.demodulize.underscore][:skim_through].each do |association_name|
|
|
7
|
-
has_many :collection_filters, through: association_name.to_sym, class_name: 'Skimming::CollectionFilter'
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def skim(collection, through: nil, object_name: nil, **skimming_instances)
|
|
12
|
-
Skimming::Skimmer.new(self, collection, object_name, skimming_instances).skim_through through
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|