patriarch 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +43 -3
- data/lib/generators/patriarch/behaviour_generator.rb +117 -0
- data/lib/generators/patriarch/install_generator.rb +27 -10
- data/lib/generators/patriarch/templates/after_manager_service.rb +2 -1
- data/lib/generators/patriarch/templates/authorization_service.rb +1 -1
- data/lib/generators/patriarch/templates/before_manager_service.rb +2 -1
- data/lib/generators/patriarch/templates/before_service.rb +1 -1
- data/lib/generators/patriarch/templates/empty_behaviours_declaration.rb +2 -0
- data/lib/generators/patriarch/templates/empty_services_declaration.rb +2 -0
- data/lib/generators/patriarch/templates/manager_service-tripartite.rb +36 -0
- data/lib/generators/patriarch/templates/manager_service.rb +15 -8
- data/lib/generators/patriarch/templates/service-tripartite.rb +5 -0
- data/lib/generators/patriarch/templates/service.rb +1 -1
- data/lib/generators/patriarch/templates/tools_methods.rb +2 -1
- data/lib/generators/patriarch/templates/undo_service-tripartite.rb +5 -0
- data/lib/patriarch/behaviours.rb +170 -13
- data/lib/patriarch/dao_services/bipartite_relationship_builder_service.rb +0 -4
- data/lib/patriarch/dao_services/redis_mapper_service.rb +45 -8
- data/lib/patriarch/dao_services/retriever_service.rb +26 -4
- data/lib/patriarch/dao_services/tripartite_relationship_builder_service.rb +51 -0
- data/lib/patriarch/manager_service.rb +0 -9
- data/lib/patriarch/tool_services/redis_cleaner_service.rb +64 -0
- data/lib/patriarch/tool_services/redis_extractor_service.rb +33 -4
- data/lib/patriarch/transaction.rb +9 -4
- data/lib/patriarch/transaction_services/transaction_manager_service.rb +9 -0
- data/lib/patriarch/transaction_step.rb +10 -2
- data/lib/patriarch/version.rb +1 -1
- data/lib/patriarch.rb +11 -5
- data/patriarch.gemspec +2 -4
- data/spec/lib/patriarch/behaviours_spec.rb +233 -0
- data/spec/lib/patriarch/{bipartite_relationship_builder_spec.rb → dao_services/bipartite_relationship_builder_spec.rb} +19 -23
- data/spec/lib/patriarch/dao_services/tripartite_relationship_builder_spec.rb +49 -0
- data/spec/lib/patriarch/redis_mapper_service_spec.rb +46 -14
- data/spec/lib/patriarch/retriever_service_spec.rb +27 -8
- data/spec/lib/patriarch/tool_services/redis_cleaner_service_spec.rb +48 -0
- data/spec/lib/patriarch/transaction_spec.rb +13 -0
- data/spec/lib/patriarch/transaction_step_spec.rb +29 -0
- data/spec/spec_helper.rb +45 -11
- metadata +30 -53
- data/lib/generators/patriarch/patriarch_generator.rb +0 -114
- data/lib/patriarch/dao_services/.DAOinstancier.rb.swp +0 -0
- data/lib/patriarch/tool_services/redis_cleaner.rb +0 -47
- data/spec/lib/patriarch/add_behaviour_spec.rb +0 -59
- data/spec/lib/patriarch/like_service_spec.rb +0 -19
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
# Patriarch
|
2
2
|
|
3
|
-
|
3
|
+
N-N tables are often a pain to deal with in SQL especially when you want to
|
4
|
+
- have a lot of different N-N relations
|
5
|
+
- have a fast API with a lot of calls
|
6
|
+
|
7
|
+
A solution is to use Redis to store the results of the joins in a simple way. For instance
|
8
|
+
a user likes many items → Let's store the items ids liked into a redis_key
|
9
|
+
an item is liked by many users → reverse logic
|
10
|
+
|
11
|
+
|
12
|
+
The complexity also increases as you add callbacks. Let's say you provide users with the
|
13
|
+
behaviour "like" and "subscribe". You may want to trigger "subscribe" right after "like" was performed
|
14
|
+
and keep track of all this in one transaction.
|
15
|
+
|
16
|
+
|
17
|
+
Patriarch allows you to handle all of this in a matter of seconds and
|
18
|
+
- gathers all of the redis calls in one transaction object that stores a redis queue ready to be processed
|
19
|
+
- rollbacks SQL destroy transaction if redis database dropped while processing the instruction queue to clean data relative
|
20
|
+
to SQL rows destroyed. Reduces possibilities of painful database incoherences
|
21
|
+
|
22
|
+
# What Patriarch is not
|
23
|
+
|
24
|
+
Patriarch is not a replacement for SQL
|
4
25
|
|
5
26
|
## Installation
|
6
27
|
|
@@ -10,7 +31,7 @@ Add this line to your application's Gemfile:
|
|
10
31
|
|
11
32
|
And then execute:
|
12
33
|
|
13
|
-
$ bundle
|
34
|
+
$ bundle update
|
14
35
|
|
15
36
|
Or install it yourself as:
|
16
37
|
|
@@ -18,7 +39,26 @@ Or install it yourself as:
|
|
18
39
|
|
19
40
|
## Usage
|
20
41
|
|
21
|
-
|
42
|
+
First include it into your model:
|
43
|
+
|
44
|
+
class User < ActiveRecord::Base
|
45
|
+
include Patriarch::Behaviours
|
46
|
+
end
|
47
|
+
|
48
|
+
### Initializing files for a behaviour
|
49
|
+
|
50
|
+
Just type in:
|
51
|
+
|
52
|
+
rails generate patriach:behaviour BEHAVIOUR
|
53
|
+
|
54
|
+
### Bipartite relations
|
55
|
+
|
56
|
+
Add a behaviour in a simple way and write this just below the include:
|
57
|
+
|
58
|
+
add_behaviour :on => [Model1,Model2] # add active_behaviour performed on instances of model1, model2
|
59
|
+
add_behaviour :by => [Model3] # add passive_behaviour performed on us by instances of model3
|
60
|
+
|
61
|
+
It works like belongs_to and has_many helpers of active record, you need to include declarations in both models
|
22
62
|
|
23
63
|
## Contributing
|
24
64
|
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "redis/objects/sorted_sets"
|
3
|
+
|
4
|
+
module Patriarch
|
5
|
+
module Generators
|
6
|
+
class BehaviourGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
desc "Generate files needed to implement the BEHAVIOUR you specified. Don't forget to add declarations into models"
|
10
|
+
|
11
|
+
argument :behaviour, :type => :string
|
12
|
+
argument :behaviour_type, :type => :string, :optional => true, :default => "bipartite"
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
def fail_if_bad_syntax
|
17
|
+
if behaviour_type != "bipartite" && behaviour_type != "tripartite"
|
18
|
+
# Pleasedon't, qualify an exception here
|
19
|
+
raise Exception, "bad syntax behaviour_type must be bipartite or tripartite"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def init_directories_for_behaviour
|
24
|
+
empty_directory "lib/patriarch/services/#{behaviour.underscore.downcase}"
|
25
|
+
empty_directory "lib/patriarch/services/#{Patriarch.undo(behaviour.underscore.downcase)}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def customize_basic_files
|
29
|
+
template "empty_behaviour_module.rb", "lib/patriarch/behaviours/#{behaviour.underscore.downcase}.rb"
|
30
|
+
template "empty_service_module.rb", "lib/patriarch/services/#{behaviour.underscore.downcase}.rb"
|
31
|
+
template "tools_methods.rb", "lib/patriarch/behaviours/#{behaviour.underscore.downcase}/tools_methods.rb"
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_autoload
|
35
|
+
# Autoload declaration insertion for services
|
36
|
+
insert_into_file "lib/patriarch/services.rb", :after => " module Services\n" do
|
37
|
+
" autoload :#{class_name}, 'patriarch/services/#{behaviour.underscore.downcase}.rb'\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
insert_into_file "lib/patriarch/services.rb", :after => " module Services\n" do
|
41
|
+
" autoload :#{undo_class_name}, 'patriarch/services/#{behaviour.underscore.downcase}.rb'\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Autoload declaration insertion for behaviours (no undo needed ...)
|
45
|
+
insert_into_file "lib/patriarch/behaviours.rb", :after => " module Behaviours\n" do
|
46
|
+
" autoload :#{class_name}, 'patriarch/behaviours/#{behaviour.underscore.downcase}.rb'\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
# load File.expand_path('lib/patriarch/behaviours.rb', __FILE__)
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_services
|
53
|
+
create_services(behaviour,behaviour_type)
|
54
|
+
# implémenter un switch ici, plus zoli ...
|
55
|
+
self.class.send(:define_method,:class_name) do
|
56
|
+
Patriarch.undo(behaviour).classify
|
57
|
+
end
|
58
|
+
create_undo_services(behaviour,behaviour_type)
|
59
|
+
self.class.send(:define_method,:class_name) do
|
60
|
+
behaviour.classify
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def class_name
|
66
|
+
behaviour.classify
|
67
|
+
end
|
68
|
+
|
69
|
+
def undo_class_name
|
70
|
+
Patriarch.undo(behaviour).classify
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_services(behaviour,behaviour_type)
|
74
|
+
behaviour_str = behaviour.underscore.downcase
|
75
|
+
template "authorization_service.rb", "lib/patriarch/services/#{behaviour_str}/authorization_service.rb"
|
76
|
+
|
77
|
+
template "before_manager_service.rb", "lib/patriarch/services/#{behaviour_str}/before_manager_service.rb"
|
78
|
+
template "before_service.rb", "lib/patriarch/services/#{behaviour_str}/before_service.rb"
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
if behaviour_type == "tripartite"
|
83
|
+
template "service-tripartite.rb", "lib/patriarch/services/#{behaviour_str}/service.rb"
|
84
|
+
template "manager_service-tripartite.rb", "lib/patriarch/services/#{behaviour_str}/manager_service.rb"
|
85
|
+
elsif behaviour_type == "bipartite"
|
86
|
+
template "service.rb", "lib/patriarch/services/#{behaviour_str}/service.rb"
|
87
|
+
template "manager_service.rb", "lib/patriarch/services/#{behaviour_str}/manager_service.rb"
|
88
|
+
end
|
89
|
+
|
90
|
+
template "after_manager_service.rb", "lib/patriarch/services/#{behaviour_str}/after_manager_service.rb"
|
91
|
+
template "after_service.rb", "lib/patriarch/services/#{behaviour_str}/after_service.rb"
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_undo_services(behaviour,behaviour_type)
|
95
|
+
undo_behaviour_str = Patriarch.undo(behaviour).underscore.downcase
|
96
|
+
|
97
|
+
template "authorization_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/authorization_service.rb"
|
98
|
+
|
99
|
+
template "before_manager_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/before_manager_service.rb"
|
100
|
+
template "before_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/before_service.rb"
|
101
|
+
|
102
|
+
# Please don't go another generator if necessary or do other functions
|
103
|
+
if behaviour_type == "tripartite"
|
104
|
+
template "undo_service-tripartite.rb", "lib/patriarch/services/#{undo_behaviour_str}/service.rb"
|
105
|
+
template "manager_service-tripartite.rb", "lib/patriarch/services/#{undo_behaviour_str}/manager_service.rb"
|
106
|
+
elsif behaviour_type == "bipartite"
|
107
|
+
template "undo_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/service.rb"
|
108
|
+
template "manager_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/manager_service.rb"
|
109
|
+
end
|
110
|
+
|
111
|
+
template "after_manager_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/after_manager_service.rb"
|
112
|
+
template "after_service.rb", "lib/patriarch/services/#{undo_behaviour_str}/after_service.rb"
|
113
|
+
end
|
114
|
+
|
115
|
+
end # BehaviourGenerator
|
116
|
+
end # Generators
|
117
|
+
end # Patriarch
|
@@ -1,11 +1,28 @@
|
|
1
1
|
require "rails/generators"
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
2
|
+
module Patriarch
|
3
|
+
module Generators
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
desc "Installs the gem"
|
8
|
+
|
9
|
+
def init_directories
|
10
|
+
copy_file "empty_behaviours_declaration.rb", "lib/patriarch/behaviours.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy_compulsory_files
|
14
|
+
copy_file "empty_behaviours_declaration.rb", "lib/patriarch/behaviours.rb"
|
15
|
+
copy_file "empty_services_declaration.rb", "lib/patriarch/services.rb"
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_initializer
|
19
|
+
create_file "config/initializers/patriarch.rb" do
|
20
|
+
"require 'patriarch'\n\n"+
|
21
|
+
"load File.expand_path('../../../lib/patriarch/behaviours.rb', __FILE__)\n" +
|
22
|
+
"load File.expand_path('../../../lib/patriarch/services.rb', __FILE__)"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end # InstallGenerator
|
27
|
+
end # Generators
|
28
|
+
end # Patriarch
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class Patriarch::Services::<%= class_name %>::AfterManagerService < Patriarch::ManagerService
|
2
2
|
def resolve(transac)
|
3
|
-
# Manages what happens after the service call
|
3
|
+
# Manages what happens after the service call, it allows you to be independant
|
4
|
+
# from active_models callback if you wish to do so
|
4
5
|
Patriarch::Services::<%= class_name %>::AfterService.instance.call(transac) if true
|
5
6
|
end
|
6
7
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class Patriarch::Services::<%= class_name %>::BeforeManagerService < Patriarch::ManagerService
|
2
2
|
def resolve(transac)
|
3
|
-
# Manages what happens after the service call
|
3
|
+
# Manages what happens after the service call, it allows you to be independant
|
4
|
+
# from active_models callback if you wish to do so
|
4
5
|
Patriarch::Services::<%= class_name %>::BeforeService.instance.call(transac) if true
|
5
6
|
end
|
6
7
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Patriarch::Services::<%= class_name %>::BeforeService < Patriarch::Service
|
2
2
|
def call(transac,options={})
|
3
|
-
# Add after service logic here and return the corresponding
|
3
|
+
# Add after service logic here and return the boolean corresponding to success (or not)
|
4
4
|
true
|
5
5
|
end
|
6
6
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Patriarch::Services::<%= class_name %>::ManagerService < Patriarch::ManagerService
|
2
|
+
# Return true or false
|
3
|
+
def resolve(actor, target, medium, options={})
|
4
|
+
|
5
|
+
# Flag initialized only if first add_step
|
6
|
+
# It allows you to insert other behaviours into the callbacks without quitting the transaction
|
7
|
+
# A like of an user on an item can trigger a subscribe to an item for instance
|
8
|
+
# It means it will rollback if follow is not performed correctly
|
9
|
+
|
10
|
+
if options[:transac]
|
11
|
+
transac_first_step = false
|
12
|
+
else
|
13
|
+
transac_first_step = true
|
14
|
+
end
|
15
|
+
|
16
|
+
transac = options[:transac] || Patriarch::TransactionServices::TransactionManagerService.instance.new_transaction(:<%= class_name.underscore.to_sym %>)
|
17
|
+
transac.add_step(:<%= class_name.underscore.to_sym %>,actor,target,medium)
|
18
|
+
|
19
|
+
callbacks_were_completed = false
|
20
|
+
|
21
|
+
# Before logic not implemented yet here, to come in further releases
|
22
|
+
if Patriarch::Services::<%= class_name %>::AuthorizationService.instance.grant?(transac)
|
23
|
+
Patriarch::Services::<%= class_name %>::Service.instance.call(transac)
|
24
|
+
if Patriarch::Services::<%= class_name %>::AfterManagerService.instance.resolve(transac)
|
25
|
+
callbacks_were_completed = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# In some cases we want to interrupt the transaction before it executes and just play with
|
30
|
+
# the transaction object
|
31
|
+
return transac if options[:stop_execution]
|
32
|
+
|
33
|
+
# If everything went smoothly
|
34
|
+
transac.execute if (transac_first_step && callbacks_were_completed)
|
35
|
+
end
|
36
|
+
end
|
@@ -1,29 +1,36 @@
|
|
1
1
|
class Patriarch::Services::<%= class_name %>::ManagerService < Patriarch::ManagerService
|
2
2
|
# Return true or false
|
3
|
-
def resolve(actor, target,
|
3
|
+
def resolve(actor, target, options={})
|
4
4
|
|
5
|
-
#
|
6
|
-
|
5
|
+
# Flag initialized only if first add_step
|
6
|
+
# It allows you to insert other behaviours into the callbacks without quitting the transaction
|
7
|
+
# A like of an user on an item can trigger a subscribe to an item for instance
|
8
|
+
# It means it will rollback if follow is not performed correctly
|
9
|
+
|
10
|
+
if options[:transac]
|
7
11
|
transac_first_step = false
|
8
12
|
else
|
9
13
|
transac_first_step = true
|
10
14
|
end
|
11
15
|
|
12
|
-
transac
|
13
|
-
# Do not listen Loïc anymore ... DO NOT MOVE, LikeAuthorizationService needs us to build
|
14
|
-
# a transaction step
|
16
|
+
transac = options[:transac] || Patriarch::TransactionServices::TransactionManagerService.instance.new_transaction(:<%= class_name.underscore.to_sym %>)
|
15
17
|
transac.add_step(:<%= class_name.underscore.to_sym %>,actor,target)
|
16
18
|
|
17
19
|
callbacks_were_completed = false
|
18
20
|
|
21
|
+
# Before logic not implemented yet here, to come in further releases
|
19
22
|
if Patriarch::Services::<%= class_name %>::AuthorizationService.instance.grant?(transac)
|
20
|
-
|
23
|
+
Patriarch::Services::<%= class_name %>::Service.instance.call(transac)
|
21
24
|
if Patriarch::Services::<%= class_name %>::AfterManagerService.instance.resolve(transac)
|
22
25
|
callbacks_were_completed = true
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
#
|
29
|
+
# In some cases we want to interrupt the transaction before it executes and just play with
|
30
|
+
# the transaction object
|
31
|
+
return transac if options[:stop_execution]
|
32
|
+
|
33
|
+
# If everything went smoothly
|
27
34
|
transac.execute if (transac_first_step && callbacks_were_completed)
|
28
35
|
end
|
29
36
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Patriarch::Services::<%= class_name %>::Service < Patriarch::Service
|
2
2
|
def call(transaction_item)
|
3
|
-
|
3
|
+
Patriarch::DAOServices::BipartiteRelationshipBuilderService.instance.create(transaction_item)
|
4
4
|
end
|
5
5
|
end
|
data/lib/patriarch/behaviours.rb
CHANGED
@@ -8,7 +8,7 @@ module Patriarch
|
|
8
8
|
|
9
9
|
class << self
|
10
10
|
|
11
|
-
def
|
11
|
+
def complete_custom_active_module_bipartite(module_to_complete,relation_type,acted_on_model_list)
|
12
12
|
module_to_complete.class_eval do
|
13
13
|
|
14
14
|
acted_on_model_list.each do |acted_on_model|
|
@@ -33,7 +33,7 @@ module Patriarch
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def complete_custom_passive_module_bipartite(module_to_complete,relation_type,targetted_by_model_list)
|
37
37
|
module_to_complete.class_eval do
|
38
38
|
|
39
39
|
targetted_by_model_list.each do |targetted_by_model|
|
@@ -54,15 +54,80 @@ module Patriarch
|
|
54
54
|
define_method(raw_tool_method_name) do |options={}|
|
55
55
|
Patriarch::ToolServices::RedisExtractorService.instance.get_ids_from_sorted_set(self,redis_key,options)
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
end
|
59
|
+
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
|
64
|
+
def complete_custom_active_module_tripartite(module_to_complete,relation_type,acted_on_model_list,via_model_list)
|
65
|
+
module_to_complete.class_eval do
|
66
|
+
|
67
|
+
acted_on_model_list.each do |acted_on_model|
|
68
|
+
via_model_list.each do |via_model|
|
69
|
+
|
70
|
+
# Compute names based on conventions
|
71
|
+
classic_tool_method_name = "#{acted_on_model.to_s.tableize}_i_#{relation_type.to_s}_via_#{via_model.to_s.tableize}"
|
72
|
+
raw_tool_method_name = classic_tool_method_name + "_ids"
|
73
|
+
redis_key = "patriarch_" + classic_tool_method_name
|
74
|
+
|
75
|
+
# Define methods with the pattern : items_i_like that returns models and items_i_like_ids that return
|
76
|
+
# Redis key has the same radical as the method in class by convention (but has a patriarch_ prefix to avoid collisions with other gems)
|
77
|
+
define_method(classic_tool_method_name) do |options={}|
|
78
|
+
Patriarch::ToolServices::RedisExtractorService.instance.
|
79
|
+
get_models_from_ids(self,acted_on_model.to_s.classify.constantize,raw_tool_method_name,options.merge({:tripartite => true}))
|
80
|
+
end
|
81
|
+
|
82
|
+
define_method(raw_tool_method_name) do |options={}|
|
83
|
+
Patriarch::ToolServices::RedisExtractorService.instance.get_ids_from_sorted_set(self,redis_key,options.merge({:tripartite => true, :protagonist_type => :actor}))
|
84
|
+
# triplet is by convention [actor,target,medium]
|
85
|
+
# TODO Marshallize true items instead of some crappy convention ...
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def complete_custom_passive_module_tripartite(module_to_complete,relation_type,targetted_by_model_list,via_model_list)
|
95
|
+
module_to_complete.class_eval do
|
96
|
+
|
97
|
+
targetted_by_model_list.each do |targetted_by_model|
|
98
|
+
via_model_list.each do |via_model|
|
99
|
+
# Compute names based on conventions
|
100
|
+
progressive_present_relation_type = (Verbs::Conjugator.conjugate relation_type.to_sym, :aspect => :progressive).split(/ /).last
|
101
|
+
classic_tool_method_name = "#{targetted_by_model.to_s.tableize}_#{progressive_present_relation_type}_me_via_#{via_model.to_s.tableize}"
|
102
|
+
raw_tool_method_name = classic_tool_method_name + "_ids"
|
103
|
+
redis_key = "patriarch_" + classic_tool_method_name
|
104
|
+
|
105
|
+
# Define methods with the pattern : items_i_like that returns models and items_i_like_ids that return
|
106
|
+
# Redis key has the same radical as the method in class by convention (but has a patriarch_ prefix to avoid collisions with other gems)
|
107
|
+
define_method(classic_tool_method_name) do |options={}|
|
108
|
+
Patriarch::ToolServices::RedisExtractorService.instance.
|
109
|
+
get_models_from_ids(self,targetted_by_model.to_s.classify.constantize,raw_tool_method_name,options.merge({:tripartite => true}))
|
110
|
+
end
|
111
|
+
|
112
|
+
define_method(raw_tool_method_name) do |options={}|
|
113
|
+
Patriarch::ToolServices::RedisExtractorService.instance.get_ids_from_sorted_set(self,redis_key,options.merge({:tripartite => true , :protagonist_type => :target}))
|
114
|
+
|
115
|
+
# triplet is by convention [actor,target,medium]
|
116
|
+
# TODO Marshallize true items instead of some crappy convention ...
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
62
124
|
def included(klass)
|
63
125
|
klass.extend ClassMethods
|
64
126
|
klass.extend ActiveModel::Callbacks
|
65
127
|
class << klass;
|
128
|
+
# not sure if should add an alias like behaviours here
|
129
|
+
# might override other behaviour methods from other namespaces
|
130
|
+
# but patriarch is damn heavy here
|
66
131
|
attr_accessor :patriarch_behaviours
|
67
132
|
end
|
68
133
|
end
|
@@ -71,17 +136,97 @@ module Patriarch
|
|
71
136
|
module ClassMethods
|
72
137
|
|
73
138
|
def add_behaviour(behaviour,options)
|
139
|
+
if options[:medium_between] || options[:via]
|
140
|
+
add_tripartite_behaviour(behaviour,options)
|
141
|
+
elsif options[:on] || options[:by]
|
142
|
+
add_bipartite_behaviour(behaviour,options)
|
143
|
+
else
|
144
|
+
raise AddBehaviourSyntaxError, "syntax is wrong, declaration does not suplly enough arguments declared with right options"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_tripartite_behaviour(behaviour,options)
|
149
|
+
behaviour = behaviour.to_s.underscore
|
150
|
+
|
151
|
+
# Xor on options :medium_between and :via, disparate cases ...
|
152
|
+
unless options[:medium_between].nil? ^ options[:via].nil?
|
153
|
+
raise AddBehaviourSyntaxError, "you must not define a behaviour as active (using on) and passive at the same time (using by)"
|
154
|
+
end
|
155
|
+
|
156
|
+
if options[:via]
|
157
|
+
unless options[:by].nil? ^ options[:on].nil?
|
158
|
+
raise AddBehaviourSyntaxError, "you must not define a behaviour as active (using on) and passive at the same time (using by)"
|
159
|
+
end
|
160
|
+
else
|
161
|
+
medium_between_option = options[:medium_between]
|
162
|
+
unless medium_between_option.is_a?(Array) && medium_between_option.size == 2
|
163
|
+
raise AddBehaviourSyntaxError, "syntax with medium medium_between requires that you provide an array of two symbols/strings"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
methods_mod = Module.new do; end
|
169
|
+
methods_mod.const_set(:Behaviour,behaviour)
|
170
|
+
|
171
|
+
# Target on Actor cases
|
172
|
+
if options[:via]
|
173
|
+
# Actor case
|
174
|
+
if options[:on]
|
175
|
+
methods_mod.instance_eval do
|
176
|
+
# Defines the hook thing ...
|
177
|
+
# TODO
|
178
|
+
def self.included(klass)
|
179
|
+
klass.extend ActiveModel::Callbacks
|
180
|
+
klass.send(:define_model_callbacks, self.const_get(:Behaviour).to_sym, Patriarch.undo(self.const_get(:Behaviour)).to_sym)
|
181
|
+
end
|
182
|
+
|
183
|
+
# behave
|
184
|
+
define_method(methods_mod.const_get(:Behaviour).to_sym) do |entity,via_entity,options={}|
|
185
|
+
run_callbacks methods_mod.const_get(:Behaviour).to_sym do
|
186
|
+
"Patriarch::Services::#{methods_mod.const_get(:Behaviour).classify}::ManagerService".constantize.instance.resolve(self,entity,via_entity,options)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# undo_behave
|
191
|
+
define_method(Patriarch.undo(methods_mod.const_get(:Behaviour)).to_sym) do |entity,via_entity,options={}|
|
192
|
+
run_callbacks Patriarch.undo(methods_mod.const_get(:Behaviour)).to_sym do
|
193
|
+
"Patriarch::Services::#{(Patriarch.undo(methods_mod.const_get(:Behaviour))).classify}::ManagerService".constantize.instance.resolve(self,entity,via_entity,options)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
acted_on_model_list = Array(options[:on])
|
199
|
+
via_model_list = Array(options[:via])
|
200
|
+
Patriarch::Behaviours.complete_custom_active_module_tripartite(methods_mod,behaviour,acted_on_model_list,via_model_list)
|
201
|
+
#Target case
|
202
|
+
elsif options[:by]
|
203
|
+
targetted_by_model_list = Array(options[:by])
|
204
|
+
via_model_list = Array(options[:via])
|
205
|
+
Patriarch::Behaviours.complete_custom_passive_module_tripartite(methods_mod,behaviour,targetted_by_model_list,via_model_list)
|
206
|
+
end
|
207
|
+
# Medium case
|
208
|
+
elsif options[:medium_between]
|
209
|
+
# Define tool_methods_here ...
|
210
|
+
end
|
211
|
+
|
212
|
+
# Finally ...
|
213
|
+
self.send(:include,methods_mod)
|
214
|
+
# include in which we can overwite the methods of the previous custom module included there ...
|
215
|
+
self.send(:include,"Patriarch::Behaviours::#{behaviour.classify}::ToolsMethods".constantize)
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
def add_bipartite_behaviour(behaviour,options)
|
74
220
|
# add_behaviour :like, :by => bla, :on => [:item,:user] || :community, :as => :aimer, :reverse_as => :haïr ...
|
75
221
|
|
76
|
-
behaviour = behaviour.to_s.
|
222
|
+
behaviour = behaviour.to_s.underscore
|
77
223
|
|
78
|
-
#
|
224
|
+
# Xor on options :on and :by, is what is needed, raise an exception otherwise
|
79
225
|
unless options[:by].nil? ^ options[:on].nil?
|
80
226
|
raise AddBehaviourSyntaxError, "you must not define a behaviour as active (using on) and passive at the same time (using by)"
|
81
227
|
end
|
82
228
|
|
83
|
-
methods_mod = Module.new do;
|
84
|
-
end
|
229
|
+
methods_mod = Module.new do; end
|
85
230
|
methods_mod.const_set(:Behaviour,behaviour)
|
86
231
|
|
87
232
|
if options[:on]
|
@@ -98,17 +243,19 @@ module Patriarch
|
|
98
243
|
# like
|
99
244
|
define_method(methods_mod.const_get(:Behaviour).to_sym) do |entity,options={}|
|
100
245
|
run_callbacks methods_mod.const_get(:Behaviour).to_sym do
|
101
|
-
"Patriarch::Services::#{methods_mod.const_get(:Behaviour).classify}::ManagerService".constantize.instance.resolve(self,entity)
|
246
|
+
"Patriarch::Services::#{methods_mod.const_get(:Behaviour).classify}::ManagerService".constantize.instance.resolve(self,entity,options)
|
102
247
|
end
|
103
248
|
end
|
104
249
|
|
105
250
|
# undo_like
|
106
251
|
define_method(Patriarch.undo(methods_mod.const_get(:Behaviour)).to_sym) do |entity,options={}|
|
107
252
|
run_callbacks Patriarch.undo(methods_mod.const_get(:Behaviour)).to_sym do
|
108
|
-
"Patriarch::Services::#{(Patriarch.undo(methods_mod.const_get(:Behaviour))).classify}::ManagerService".constantize.instance.resolve(self,entity)
|
253
|
+
"Patriarch::Services::#{(Patriarch.undo(methods_mod.const_get(:Behaviour))).classify}::ManagerService".constantize.instance.resolve(self,entity,options)
|
109
254
|
end
|
110
255
|
end
|
111
256
|
|
257
|
+
# Not used / not tested, added this method "en passant"
|
258
|
+
# FIX requirements with users
|
112
259
|
# likes?
|
113
260
|
define_method((Verbs::Conjugator.conjugate methods_mod.const_get(:Behaviour).to_sym, :person => :third).split(/ /).last) do |entity|
|
114
261
|
self.send("#{entity.class.name.tableize}_i_#{methods_mod.const_get(:Behaviour)}_ids").include? entity.id
|
@@ -129,10 +276,10 @@ module Patriarch
|
|
129
276
|
|
130
277
|
if options[:on]
|
131
278
|
acted_on_model_list = Array(options[:on])
|
132
|
-
Patriarch::Behaviours.
|
279
|
+
Patriarch::Behaviours.complete_custom_active_module_bipartite(methods_mod,behaviour,acted_on_model_list)
|
133
280
|
else
|
134
281
|
targetted_by_model_list = Array(options[:by])
|
135
|
-
Patriarch::Behaviours.
|
282
|
+
Patriarch::Behaviours.complete_custom_passive_module_bipartite(methods_mod,behaviour,targetted_by_model_list)
|
136
283
|
end
|
137
284
|
|
138
285
|
# Finally ...
|
@@ -141,8 +288,18 @@ module Patriarch
|
|
141
288
|
self.send(:include,"Patriarch::Behaviours::#{behaviour.classify}::ToolsMethods".constantize)
|
142
289
|
|
143
290
|
# register the behaviour we just added
|
144
|
-
|
145
|
-
self.patriarch_behaviours
|
291
|
+
# TODO disjonction bi/tripart
|
292
|
+
self.patriarch_behaviours ||= { }
|
293
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym] ||= { :on => [], :by =>[] }
|
294
|
+
if options[:on]
|
295
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym][:on] << options[:on]
|
296
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym][:on].uniq!
|
297
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym][:on].flatten!
|
298
|
+
elsif options[:by]
|
299
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym][:by] << options[:by]
|
300
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym][:by].uniq!
|
301
|
+
self.patriarch_behaviours[behaviour.underscore.to_sym][:by].flatten!
|
302
|
+
end
|
146
303
|
end
|
147
304
|
end
|
148
305
|
|