activr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,161 @@
1
+ #
2
+ # A timeline route describes how an activity is routed to a timeline
3
+ #
4
+ class Activr::Timeline::Route
5
+
6
+ # @return [Class] timeline class
7
+ attr_reader :timeline_class
8
+
9
+ # @return [Class] activity class
10
+ attr_reader :activity_class
11
+
12
+ # @return [Hash] route settings
13
+ attr_reader :settings
14
+
15
+
16
+ class << self
17
+
18
+ # Get route kind
19
+ #
20
+ # @param routing_kind [String] Routing kind
21
+ # @param activity_kind [String] Activity kind
22
+ # @return [String] Route kind
23
+ def kind_for_routing_and_activity(routing_kind, activity_kind)
24
+ "#{routing_kind}_#{activity_kind}"
25
+ end
26
+
27
+ end # class << self
28
+
29
+
30
+ # @param timeline_class [Class] Timeline class
31
+ # @param activity_class [Class] Activity class
32
+ # @param settings [Hash] Route settings
33
+ # @option settings [Symbol] :using Predefined routing kind
34
+ # @option settings [Symbol] :to Routing path
35
+ # @option settings [Symbol] :kind Manually specified routing kind
36
+ def initialize(timeline_class, activity_class, settings)
37
+ @timeline_class = timeline_class
38
+ @activity_class = activity_class
39
+ @settings = settings
40
+ end
41
+
42
+ # Get route kind
43
+ #
44
+ # @return [String] Route kind
45
+ def kind
46
+ @kind ||= self.class.kind_for_routing_and_activity(self.routing_kind, self.activity_class.kind)
47
+ end
48
+
49
+ # Get routing kind
50
+ #
51
+ # @return [String] Routing kind
52
+ def routing_kind
53
+ @routing_kind ||= (self.settings[:kind] && self.settings[:kind].to_s) || begin
54
+ if self.settings[:using] && self.settings[:to]
55
+ raise "Several routing kinds specified for #{self.activity_class}: #{self.settings.inspect}"
56
+ end
57
+
58
+ if self.settings[:using].blank? && self.settings[:to].blank?
59
+ raise "Missing routing for #{self.activity_class}: #{self.settings.inspect}"
60
+ end
61
+
62
+ result = self.settings[:using]
63
+ if result.blank?
64
+ result = if self.settings[:to].is_a?(Symbol)
65
+ self.settings[:to]
66
+ else
67
+ self.settings[:to].to_s.underscore.gsub('.', '_').to_sym
68
+ end
69
+ end
70
+
71
+ result.to_s
72
+ end
73
+ end
74
+
75
+ # Resolve recipients for given activity
76
+ #
77
+ # @param activity [Activity] Activity to resolve
78
+ # @return [Array<Object>] Array of recipients instances and/or ids
79
+ def resolve(activity)
80
+ recipients = if self.settings[:using]
81
+ self.resolve_using_method(self.settings[:using], activity)
82
+ elsif self.settings[:to]
83
+ self.resolve_to_path(self.settings[:to], activity)
84
+ else
85
+ raise "Don't know how to resolve recipients: #{self.settings}"
86
+ end
87
+
88
+ recipients = [ recipients ] unless recipients.is_a?(Array)
89
+ recipients.compact!
90
+
91
+ # check recipients
92
+ bad_recipient = recipients.find{ |recipient| !self.timeline_class.valid_recipient?(recipient) }
93
+ if bad_recipient
94
+ raise "Invalid recipient resolved by route #{self.inspect}: #{bad_recipient.inspect}"
95
+ end
96
+
97
+ recipients
98
+ end
99
+
100
+ # Resolve route using method call
101
+ #
102
+ # @api private
103
+ #
104
+ # @param meth [Symbol] Method to call on timeline class
105
+ # @param activity [Activity] Activity to resolve
106
+ # @return [Array<Object>] Array of recipients instances and/or ids
107
+ def resolve_using_method(meth, activity)
108
+ # send method
109
+ self.apply_meth(self.timeline_class, meth, activity)
110
+ end
111
+
112
+ # Resolve route using path
113
+ #
114
+ # @api private
115
+ #
116
+ # @param path [String] Path on activity
117
+ # @param activity [Activity] Activity to resolve
118
+ # @return [Array<Object>] Array of recipients instances and/or ids
119
+ def resolve_to_path(path, activity)
120
+ receivers = [ activity ]
121
+
122
+ meth_ary = path.to_s.split('.')
123
+ meth_ary.map(&:to_sym).each do |meth|
124
+ receivers = receivers.map do |receiver|
125
+ if !receiver.respond_to?(meth)
126
+ raise "Can't resolve routing path: receiver does not respond to method '#{meth}': #{receiver.inspect}"
127
+ end
128
+
129
+ # send method
130
+ self.apply_meth(receiver, meth, activity)
131
+ end.flatten.compact
132
+ end
133
+
134
+ receivers
135
+ end
136
+
137
+
138
+ #
139
+ # Private
140
+ #
141
+
142
+ # Apply a method on receiver, with given activity as parameter if receiver arity permits it
143
+ #
144
+ # @api private
145
+ #
146
+ # @param receiver [Object] Receiver
147
+ # @param meth [Symbol] Method to call
148
+ # @param activity [Activity] Activity to provide in method call
149
+ # @return [Object] Result of method call on receiver
150
+ def apply_meth(receiver, meth, activity)
151
+ case receiver.method(meth).arity
152
+ when 2
153
+ receiver.__send__(meth, activity, self.timeline_class)
154
+ when 1
155
+ receiver.__send__(meth, activity)
156
+ else
157
+ receiver.__send__(meth)
158
+ end
159
+ end
160
+
161
+ end # class Activr::Timeline::Route
@@ -0,0 +1,74 @@
1
+ module Activr
2
+
3
+ # Utilities methods
4
+ class Utils
5
+
6
+ class << self
7
+
8
+ # Returns kind for given class
9
+ #
10
+ # @api private
11
+ #
12
+ # @param klass [Class] Class
13
+ # @param suffix [String] Expected suffix
14
+ # @return [String] Kind
15
+ def kind_for_class(klass, suffix = nil)
16
+ class_name = klass.to_s.split('::').last.underscore
17
+ if suffix && (match_data = class_name.match(/(.+)_#{suffix}$/))
18
+ match_data[1]
19
+ else
20
+ class_name
21
+ end
22
+ end
23
+
24
+ # Returns class for given kind
25
+ #
26
+ # @api private
27
+ #
28
+ # @param kind [String] Kind
29
+ # @param suffix [String] Suffix
30
+ # @return [Class] Class
31
+ def class_for_kind(kind, suffix = nil)
32
+ str = suffix ? "#{kind}_#{suffix}" : kind
33
+ str.camelize.constantize
34
+ end
35
+
36
+
37
+ #
38
+ # Mustache rendering
39
+ #
40
+
41
+ # Get a compiled mustache template
42
+ #
43
+ # @api private
44
+ #
45
+ # @param tpl [String] Template
46
+ # @return [String] Compiled template
47
+ def compiled_mustache_template(tpl)
48
+ @compiled_mustache_templates ||= {}
49
+ @compiled_mustache_templates[tpl] ||= begin
50
+ view = Mustache.new
51
+ view.raise_on_context_miss = true
52
+ view.template = tpl # will compile and store template once for all
53
+ view
54
+ end
55
+ end
56
+
57
+ # Render a mustache template
58
+ #
59
+ # @api private
60
+ #
61
+ # @param text [String] Template to render
62
+ # @param bindings [Hash] Template bindings
63
+ # @return [String] Rendered template
64
+ def render_mustache(text, bindings)
65
+ tpl = self.compiled_mustache_template(text)
66
+ tpl.raise_on_context_miss = true
67
+ tpl.render(bindings)
68
+ end
69
+
70
+ end # class << self
71
+
72
+ end # class Utils
73
+
74
+ end # module Activr
@@ -0,0 +1,6 @@
1
+ module Activr
2
+
3
+ # Activr version
4
+ VERSION = '1.0.0'
5
+
6
+ end
@@ -0,0 +1,91 @@
1
+ module Activr
2
+ # Rails generators
3
+ module Generators
4
+
5
+ # Generates an {Activity} subclass in your Rails application
6
+ class ActivityGenerator < ::Rails::Generators::NamedBase
7
+ # Default entity humanization method
8
+ DEFAULT_ENTITY_FIELD = :name
9
+
10
+ source_root File.expand_path("../templates", __FILE__)
11
+
12
+ check_class_collision :suffix => "Activity"
13
+
14
+ argument :entities, :type => :array, :default => [], :banner => "entity[:class] entity[:class]"
15
+ class_option :full, type: :boolean, :desc => "Generate full class"
16
+
17
+ desc "Creates an Activity class"
18
+
19
+ # Create the activity class file
20
+ #
21
+ # @api private
22
+ def create_activity_file
23
+ template "activity.rb", "#{Activr.config.app_path}/activities/#{file_name}_activity.rb"
24
+ end
25
+
26
+ # Compute entities infos
27
+ #
28
+ # @api private
29
+ def entities_infos
30
+ entities.map do |str|
31
+ ary = str.split(':')
32
+
33
+ name = ary[0].underscore
34
+
35
+ klass, set_klass = if ary[1]
36
+ [ ary[1].camelize, true ]
37
+ else
38
+ [ ary[0].camelize, false ]
39
+ end
40
+
41
+ human_meth = _resolve_entity_field(klass)
42
+
43
+ result = {
44
+ :name => name,
45
+ :humanize => human_meth,
46
+ }
47
+
48
+ result[:class] = klass if set_klass
49
+
50
+ result
51
+ end
52
+ end
53
+
54
+ # Generates a default humanization template
55
+ #
56
+ # @api private
57
+ def humanization
58
+ result = ""
59
+
60
+ actor = entities_infos.find{ |entity| entity[:name] == 'actor' }
61
+ if actor
62
+ result += "{{actor}} "
63
+ end
64
+
65
+ result += name.underscore.gsub('_', ' ')
66
+
67
+ entities_infos.each do |entity|
68
+ if entity[:name] != 'actor'
69
+ result += " {{{#{entity[:name]}}}}"
70
+ end
71
+ end
72
+
73
+ result
74
+ end
75
+
76
+ private
77
+
78
+ # @api private
79
+ def _resolve_entity_field(entity_klass)
80
+ klass = entity_klass.constantize
81
+
82
+ field = [ :fullname, :name, :title ].find do |meth_name|
83
+ klass.method_defined?(meth_name)
84
+ end
85
+
86
+ field || DEFAULT_ENTITY_FIELD
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ class <%= class_name %>Activity < Activr::Activity
4
+ <% if entities_infos.blank? %>
5
+ # entity :actor, :class => User, :humanize => :fullname
6
+ # entity :buddy, :class => User, :humanize => :fullname
7
+
8
+ # humanize "{{{actor}}} is now following {{{buddy}}}"
9
+ <% else %>
10
+ <% entities_infos.each do |entity| -%>
11
+ entity :<%= entity[:name] %><% if entity[:class] %>, :class => <%= entity[:class] %><% end %>, :humanize => :<%= entity[:humanize] %>
12
+ <% end -%>
13
+
14
+ humanize "<%= humanization %>"
15
+ <% end %><% if options[:full] %>
16
+ #
17
+ # callbacks
18
+ #
19
+
20
+ # activity is stored in main collection
21
+ # before_store :check_stuff
22
+ # after_store :notify_stuff
23
+
24
+ # activity is routed to all timelines
25
+ # before_route :check_stuff
26
+ # after_route :notify_stuff
27
+ <% end %>
28
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ class <%= class_name %>Timeline < Activr::Timeline
4
+
5
+ recipient <%= recipient_class %>
6
+
7
+
8
+ #
9
+ # Routes
10
+ #
11
+
12
+ # route FollowBuddyActivity, :to => 'buddy', :humanize => "{{{actor}}} is now following you"
13
+
14
+
15
+ #
16
+ # Callbacks
17
+ #
18
+
19
+ # def self.should_route_activity?(activity)
20
+ # # return `false` to cancel activity routing
21
+ # true
22
+ # end
23
+ #
24
+ # def should_handle_activity?(activity, route)
25
+ # # return `false` to skip routed activity
26
+ # true
27
+ # end
28
+ #
29
+ # def should_store_timeline_entry?(timeline_entry)
30
+ # # return `false` to cancel timeline entry storing
31
+ # true
32
+ # end
33
+ #
34
+ # def will_store_timeline_entry(timeline_entry)
35
+ # # this is your last chance to modify timeline entry before it is stored
36
+ # end
37
+ #
38
+ # def did_store_timeline_entry(timeline_entry)
39
+ # # the timeline entry was stored, you can now do some post-processing
40
+ # end
41
+
42
+ end
@@ -0,0 +1,24 @@
1
+ module Activr
2
+ # Rails generators
3
+ module Generators
4
+
5
+ # Generates a {Timeline} subclass in your Rails application
6
+ class TimelineGenerator < ::Rails::Generators::NamedBase
7
+ source_root File.expand_path("../templates", __FILE__)
8
+
9
+ check_class_collision :suffix => "Timeline"
10
+
11
+ desc "Creates a Timeline class"
12
+ # class_option :recipient, :required => true, :type => :string, :desc => "Recipient class"
13
+ argument :recipient_class, :type => :string
14
+
15
+ # Create the timeline class file
16
+ #
17
+ # @api private
18
+ def create_timeline_files
19
+ template "timeline.rb", "#{Activr.config.app_path}/timelines/#{file_name}_timeline.rb"
20
+ end
21
+ end
22
+
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,197 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activr
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aymerick JEHANNE
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mustache
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fwissr
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activemodel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mongoid
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: database_cleaner
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: delorean
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: |2
140
+ Activity Streams system by Fotonauts.
141
+ email:
142
+ - aymerick@fotonauts.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - LICENSE
148
+ - Rakefile
149
+ - README.md
150
+ - lib/activr/activity.rb
151
+ - lib/activr/async/resque.rb
152
+ - lib/activr/async.rb
153
+ - lib/activr/configuration.rb
154
+ - lib/activr/dispatcher.rb
155
+ - lib/activr/entity/model_mixin.rb
156
+ - lib/activr/entity.rb
157
+ - lib/activr/rails_ctx.rb
158
+ - lib/activr/railtie.rb
159
+ - lib/activr/railties/activr.rake
160
+ - lib/activr/registry.rb
161
+ - lib/activr/storage/mongo_driver.rb
162
+ - lib/activr/storage.rb
163
+ - lib/activr/timeline/entry.rb
164
+ - lib/activr/timeline/route.rb
165
+ - lib/activr/timeline.rb
166
+ - lib/activr/utils.rb
167
+ - lib/activr/version.rb
168
+ - lib/activr.rb
169
+ - lib/generators/activr/activity_generator.rb
170
+ - lib/generators/activr/templates/activity.rb
171
+ - lib/generators/activr/templates/timeline.rb
172
+ - lib/generators/activr/timeline_generator.rb
173
+ homepage: https://github.com/fotonauts/activr
174
+ licenses: []
175
+ metadata: {}
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - '>='
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubyforge_project:
192
+ rubygems_version: 2.1.11
193
+ signing_key:
194
+ specification_version: 4
195
+ summary: Activr
196
+ test_files: []
197
+ has_rdoc: