activr 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.
@@ -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: