patriarch 0.0.4 → 0.1.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.
- 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
|
|