stratify-base 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 +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +52 -0
- data/LICENSE +20 -0
- data/README.md +21 -0
- data/Rakefile +10 -0
- data/lib/stratify/activity.rb +33 -0
- data/lib/stratify/archiver.rb +45 -0
- data/lib/stratify/collector.rb +100 -0
- data/lib/stratify/collector_coordinator.rb +13 -0
- data/lib/stratify/field_definition.rb +15 -0
- data/lib/stratify/logger.rb +14 -0
- data/lib/stratify/mongoid_extension.rb +38 -0
- data/lib/stratify/renderable.rb +41 -0
- data/lib/stratify/version.rb +5 -0
- data/lib/stratify-base.rb +4 -0
- data/spec/mongoid.yml +3 -0
- data/spec/prototypes/bacon.rb +46 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/stratify/activity_spec.rb +55 -0
- data/spec/stratify/archiver_spec.rb +85 -0
- data/spec/stratify/collector_coordinator_spec.rb +41 -0
- data/spec/stratify/collector_spec.rb +125 -0
- data/spec/stratify/field_definition_spec.rb +29 -0
- data/spec/stratify/integration_spec.rb +31 -0
- data/spec/stratify/mongoid_extension_spec.rb +80 -0
- data/spec/stratify/renderable_spec.rb +53 -0
- data/stratify-base.gemspec +29 -0
- metadata +158 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm use ruby-1.9.2-p180@stratify
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
stratify-base (0.1.0)
|
|
5
|
+
bson_ext (~> 1.3.1)
|
|
6
|
+
mongoid (~> 2.0.2)
|
|
7
|
+
tilt (~> 1.3.2)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: http://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
activemodel (3.1.0.rc1)
|
|
13
|
+
activesupport (= 3.1.0.rc1)
|
|
14
|
+
bcrypt-ruby (~> 2.1.4)
|
|
15
|
+
builder (~> 3.0.0)
|
|
16
|
+
i18n (~> 0.6.0beta1)
|
|
17
|
+
activesupport (3.1.0.rc1)
|
|
18
|
+
multi_json (~> 1.0)
|
|
19
|
+
bcrypt-ruby (2.1.4)
|
|
20
|
+
bson (1.3.1)
|
|
21
|
+
bson_ext (1.3.1)
|
|
22
|
+
builder (3.0.0)
|
|
23
|
+
database_cleaner (0.6.7)
|
|
24
|
+
diff-lcs (1.1.2)
|
|
25
|
+
i18n (0.6.0)
|
|
26
|
+
mocha (0.9.12)
|
|
27
|
+
mongo (1.3.1)
|
|
28
|
+
bson (>= 1.3.1)
|
|
29
|
+
mongoid (2.0.2)
|
|
30
|
+
activemodel (~> 3.0)
|
|
31
|
+
mongo (~> 1.3)
|
|
32
|
+
tzinfo (~> 0.3.22)
|
|
33
|
+
multi_json (1.0.3)
|
|
34
|
+
rspec (2.6.0)
|
|
35
|
+
rspec-core (~> 2.6.0)
|
|
36
|
+
rspec-expectations (~> 2.6.0)
|
|
37
|
+
rspec-mocks (~> 2.6.0)
|
|
38
|
+
rspec-core (2.6.3)
|
|
39
|
+
rspec-expectations (2.6.0)
|
|
40
|
+
diff-lcs (~> 1.1.2)
|
|
41
|
+
rspec-mocks (2.6.0)
|
|
42
|
+
tilt (1.3.2)
|
|
43
|
+
tzinfo (0.3.27)
|
|
44
|
+
|
|
45
|
+
PLATFORMS
|
|
46
|
+
ruby
|
|
47
|
+
|
|
48
|
+
DEPENDENCIES
|
|
49
|
+
database_cleaner (~> 0.6.7)
|
|
50
|
+
mocha (~> 0.9.12)
|
|
51
|
+
rspec (~> 2.6.0)
|
|
52
|
+
stratify-base!
|
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2011 Jason Rudolph (http://jasonrudolph.com)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# stratify-base
|
|
2
|
+
|
|
3
|
+
## Dependencies
|
|
4
|
+
|
|
5
|
+
Stratify is developed and tested with the following dependencies.
|
|
6
|
+
|
|
7
|
+
* Ruby 1.9.2 (See `.rvmrc` for the specific version.)
|
|
8
|
+
* MongoDB 1.8
|
|
9
|
+
|
|
10
|
+
## Development
|
|
11
|
+
|
|
12
|
+
To get set up for development on stratify-base, clone the repo, and ...
|
|
13
|
+
|
|
14
|
+
cd stratify/stratify-base
|
|
15
|
+
gem install bundler
|
|
16
|
+
bundle
|
|
17
|
+
rake
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
Copyright 2011 Jason Rudolph ([jasonrudolph.com](http://jasonrudolph.com)). Released under the MIT license. See the LICENSE file for further details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'mongoid'
|
|
2
|
+
require 'stratify/mongoid_extension'
|
|
3
|
+
require 'stratify/renderable'
|
|
4
|
+
|
|
5
|
+
module Stratify
|
|
6
|
+
class Activity
|
|
7
|
+
include Mongoid::Document
|
|
8
|
+
include Mongoid::Paranoia
|
|
9
|
+
include MongoidExtension::NaturalKey
|
|
10
|
+
include Renderable
|
|
11
|
+
|
|
12
|
+
store_in :activities
|
|
13
|
+
|
|
14
|
+
field :source, :type => String
|
|
15
|
+
field :created_at, :type => DateTime
|
|
16
|
+
|
|
17
|
+
validates_presence_of :created_at
|
|
18
|
+
|
|
19
|
+
def permalink
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def created_on
|
|
24
|
+
return nil unless created_at
|
|
25
|
+
created_at.to_date
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def duplicate?
|
|
29
|
+
duplicate_activities = self.class.where(natural_key_hash)
|
|
30
|
+
duplicate_activities.exists? || duplicate_activities.deleted.exists?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'stratify/logger'
|
|
2
|
+
|
|
3
|
+
module Stratify
|
|
4
|
+
class Archiver
|
|
5
|
+
attr_reader :collector
|
|
6
|
+
|
|
7
|
+
def initialize(collector)
|
|
8
|
+
@collector = collector
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run
|
|
12
|
+
collect_activities
|
|
13
|
+
ensure
|
|
14
|
+
record_collection_statistics
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
def collect_activities
|
|
20
|
+
collector.activities.each do |activity|
|
|
21
|
+
document_activity_source(activity)
|
|
22
|
+
persist_activity(activity)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def document_activity_source(activity)
|
|
27
|
+
activity.source = collector.source
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def persist_activity(activity)
|
|
31
|
+
# Since we run the collectors frequently, it is very common to encounter
|
|
32
|
+
# objects that we have already imported. If this activity is a duplicate
|
|
33
|
+
# of an existing object, then we skip importing this activity.
|
|
34
|
+
return if activity.duplicate?
|
|
35
|
+
|
|
36
|
+
unless activity.save
|
|
37
|
+
Stratify.logger.error("Failed to persist activity: #{activity}.\nValidation errors: #{activity.errors}")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def record_collection_statistics
|
|
42
|
+
collector.update_attribute :last_ran_at, Time.now
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require 'mongoid'
|
|
2
|
+
require 'stratify/archiver'
|
|
3
|
+
require 'stratify/field_definition'
|
|
4
|
+
|
|
5
|
+
module Stratify
|
|
6
|
+
class Collector
|
|
7
|
+
include Mongoid::Document
|
|
8
|
+
|
|
9
|
+
store_in :collectors
|
|
10
|
+
|
|
11
|
+
field :last_ran_at, :type => DateTime
|
|
12
|
+
|
|
13
|
+
def self.configuration_fields(fields_hash = nil)
|
|
14
|
+
@configuration_fields = initialize_configuration_fields(fields_hash) unless fields_hash.nil?
|
|
15
|
+
@configuration_fields || []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def configuration_fields
|
|
19
|
+
self.class.configuration_fields
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.configuration_instructions(instructions = nil)
|
|
23
|
+
@configuration_instructions = instructions unless instructions.nil?
|
|
24
|
+
@configuration_instructions
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def configuration_instructions
|
|
28
|
+
self.class.configuration_instructions
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def configuration_summary
|
|
32
|
+
return nil if configuration_fields.empty?
|
|
33
|
+
read_attribute(configuration_fields.first.name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run
|
|
37
|
+
Archiver.new(self).run
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# To be implemented by subclasses
|
|
41
|
+
#
|
|
42
|
+
# Returns a collection of activities to be saved.
|
|
43
|
+
def activities
|
|
44
|
+
raise NotImplementedError
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Define where this collector's activities originate from (e.g., "Twitter", "iTunes")
|
|
48
|
+
def self.source(src = nil)
|
|
49
|
+
@source = src unless src.nil?
|
|
50
|
+
@source
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def source
|
|
54
|
+
self.class.source
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.sources
|
|
58
|
+
unsorted_sources = Collector.collector_classes.map(&:source)
|
|
59
|
+
unsorted_sources.sort_by {|source| source.downcase}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class << self
|
|
63
|
+
attr_reader :collector_classes
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@collector_classes = []
|
|
67
|
+
|
|
68
|
+
def self.inherited(subclass)
|
|
69
|
+
super
|
|
70
|
+
Collector.collector_classes << subclass
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.collector_class_for(source)
|
|
74
|
+
Collector.collector_classes.find {|clazz| clazz.source == source}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def self.initialize_configuration_fields(fields_hash)
|
|
80
|
+
field_definitions = objectify_fields(fields_hash)
|
|
81
|
+
apply_fields_to_class(field_definitions)
|
|
82
|
+
field_definitions
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.objectify_fields(fields_hash)
|
|
86
|
+
fields_hash.map do |f|
|
|
87
|
+
name = f[0]
|
|
88
|
+
attributes = f[1]
|
|
89
|
+
Stratify::FieldDefinition.new(name, attributes)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.apply_fields_to_class(field_definitions)
|
|
94
|
+
field_definitions.each do |field|
|
|
95
|
+
field field.name
|
|
96
|
+
validates_presence_of field.name
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Stratify
|
|
2
|
+
class CollectorCoordinator
|
|
3
|
+
def self.run_all
|
|
4
|
+
Stratify::Collector.all.each do |collector|
|
|
5
|
+
begin
|
|
6
|
+
collector.run
|
|
7
|
+
rescue => e
|
|
8
|
+
Stratify.logger.error("Error running collector. Collector => #{collector}. Error => #{e}")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Stratify
|
|
2
|
+
class FieldDefinition
|
|
3
|
+
attr_reader :name, :type, :label
|
|
4
|
+
|
|
5
|
+
def initialize(name, attribute_hash)
|
|
6
|
+
@name = name
|
|
7
|
+
@type = attribute_hash[:type]
|
|
8
|
+
@label = attribute_hash[:label]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def label
|
|
12
|
+
@label || @name.to_s.titleize
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Stratify
|
|
2
|
+
module MongoidExtension
|
|
3
|
+
module NaturalKey
|
|
4
|
+
module ClassMethods
|
|
5
|
+
attr_reader :natural_key_fields
|
|
6
|
+
|
|
7
|
+
def natural_key(*fields)
|
|
8
|
+
@natural_key_fields = fields.dup
|
|
9
|
+
validates_uniqueness_of_natural_key
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def validates_uniqueness_of_natural_key
|
|
13
|
+
first, *rest = *natural_key_fields
|
|
14
|
+
if rest.empty?
|
|
15
|
+
validates_uniqueness_of first
|
|
16
|
+
else
|
|
17
|
+
validates_uniqueness_of first, :scope => rest
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module InstanceMethods
|
|
23
|
+
def natural_key_hash
|
|
24
|
+
{}.tap do |hash|
|
|
25
|
+
self.class.natural_key_fields.each do |field|
|
|
26
|
+
hash[field] = self.send(field)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.included(receiver)
|
|
33
|
+
receiver.extend ClassMethods
|
|
34
|
+
receiver.send :include, InstanceMethods
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "tilt"
|
|
2
|
+
|
|
3
|
+
module Stratify
|
|
4
|
+
module Renderable
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend ClassMethods
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def template(data = nil)
|
|
11
|
+
@template = data unless data.nil?
|
|
12
|
+
@template
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def template_format(format = nil)
|
|
16
|
+
@template_format = format unless format.nil?
|
|
17
|
+
@template_format || default_template_format
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def default_template_format
|
|
21
|
+
:erb
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_html
|
|
26
|
+
template_handler = Tilt[self.class.template_format].new { self.class.template.strip }
|
|
27
|
+
template_handler.render(presenter)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the object that will be passed to the template for rendering.
|
|
31
|
+
#
|
|
32
|
+
# The default implementation returns 'self' (i.e., the renderable object).
|
|
33
|
+
#
|
|
34
|
+
# Subclasses may optionally override this method to provide an object
|
|
35
|
+
# better suited for use in rendering (e.g., an object implementing the
|
|
36
|
+
# presenter pattern).
|
|
37
|
+
def presenter
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/spec/mongoid.yml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# This file defines an example Stratify collector and activity for testing
|
|
2
|
+
# purposes.
|
|
3
|
+
#
|
|
4
|
+
# Stratify::Bacon::Activity and Stratify::Bacon::Collector represent a
|
|
5
|
+
# (fictitious, unfortunately) service for tracking your bacon-related
|
|
6
|
+
# achievements. This also happens to be an *amazing* idea for a service.
|
|
7
|
+
# You know you want to track your bacon-related achievements. You just know it.
|
|
8
|
+
|
|
9
|
+
# Stratify::Bacon::Activity implements all *mandatory* functionality required
|
|
10
|
+
# for it to be a valid activity.
|
|
11
|
+
module Stratify
|
|
12
|
+
module Bacon
|
|
13
|
+
class Activity < Stratify::Activity
|
|
14
|
+
field :slices, :type => Integer
|
|
15
|
+
|
|
16
|
+
natural_key :created_at
|
|
17
|
+
|
|
18
|
+
template "Enjoyed <%= slices %> slices of delicious bacon"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Stratify::Bacon::Collector implements all *mandatory* functionality required
|
|
24
|
+
# for it to be a valid collector.
|
|
25
|
+
module Stratify
|
|
26
|
+
module Bacon
|
|
27
|
+
class Collector < Stratify::Collector
|
|
28
|
+
source "Baconation"
|
|
29
|
+
|
|
30
|
+
configuration_fields :username => {:type => :string},
|
|
31
|
+
:password => {:type => :password}
|
|
32
|
+
|
|
33
|
+
# Return a fixed set of Stratify::Bacon::Activity objects. (A real
|
|
34
|
+
# collector would tend to return new data as time goes by. For testing
|
|
35
|
+
# purposes, it's convenient to have a fixed set of data returned by the
|
|
36
|
+
# collector.)
|
|
37
|
+
def activities
|
|
38
|
+
[
|
|
39
|
+
Stratify::Bacon::Activity.new(:slices => 3, :created_at => Time.new(2011, 4, 3, 7, 2)),
|
|
40
|
+
Stratify::Bacon::Activity.new(:slices => 5, :created_at => Time.new(2011, 4, 7, 8, 17)),
|
|
41
|
+
Stratify::Bacon::Activity.new(:slices => 4, :created_at => Time.new(2011, 4, 9, 6, 24)),
|
|
42
|
+
]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require 'stratify-base'
|
|
5
|
+
|
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
|
7
|
+
# in ./support/ and its subdirectories.
|
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
|
9
|
+
|
|
10
|
+
require 'database_cleaner'
|
|
11
|
+
|
|
12
|
+
require 'prototypes/bacon'
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |config|
|
|
15
|
+
config.color_enabled = true
|
|
16
|
+
config.formatter = :progress
|
|
17
|
+
config.mock_with :mocha
|
|
18
|
+
|
|
19
|
+
# Configure RSpec to run focused specs, and also respect the alias 'fit' for focused specs
|
|
20
|
+
config.filter_run :focused => true
|
|
21
|
+
config.run_all_when_everything_filtered = true
|
|
22
|
+
config.alias_example_to :fit, :focused => true
|
|
23
|
+
|
|
24
|
+
# Configure RSpec to truncate the database before any spec tagged with ":database => true"
|
|
25
|
+
DatabaseCleaner.strategy = :truncation
|
|
26
|
+
DatabaseCleaner.orm = "mongoid"
|
|
27
|
+
config.before(:each, :database => true) do
|
|
28
|
+
DatabaseCleaner.clean
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
config.before(:suite) do
|
|
32
|
+
silence_mongoid_logging
|
|
33
|
+
initialize_mongoid_configuration
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def silence_mongoid_logging
|
|
38
|
+
Mongoid.logger = Logger.new("/dev/null")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def silence_stratify_logging
|
|
42
|
+
Stratify.stubs(:logger).returns(Logger.new("/dev/null")) # Silence the logger so as not to clutter the test output
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def initialize_mongoid_configuration
|
|
46
|
+
mongoid_config = YAML::load_file(File.join(File.dirname(__FILE__), "mongoid.yml"))
|
|
47
|
+
Mongoid.from_hash(mongoid_config)
|
|
48
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Stratify::Activity do
|
|
4
|
+
|
|
5
|
+
describe "#created_on" do
|
|
6
|
+
it "returns the date portion of the created_at value" do
|
|
7
|
+
activity = Stratify::Activity.new(:created_at => DateTime.parse("2010-11-27 6:35 PM"))
|
|
8
|
+
activity.created_on.should == Date.parse("2010-11-27")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns nil if the created_at value is nil" do
|
|
12
|
+
activity = Stratify::Activity.new(:created_at => nil)
|
|
13
|
+
activity.created_on.should be_nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "#delete", :database => true do
|
|
18
|
+
it "soft-deletes the activity" do
|
|
19
|
+
creation_time = Time.now
|
|
20
|
+
activity = Stratify::Bacon::Activity.create!(:slices => 42, :created_at => creation_time)
|
|
21
|
+
activity.delete
|
|
22
|
+
|
|
23
|
+
Stratify::Bacon::Activity.where(:slices => 42, :created_at => creation_time).should_not exist
|
|
24
|
+
Stratify::Bacon::Activity.deleted.where(:slices => 42, :created_at => creation_time).should exist
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "#duplicate?", :database => true do
|
|
29
|
+
class ActivityForTestingDuplicates < Stratify::Activity
|
|
30
|
+
field :foo
|
|
31
|
+
field :bar
|
|
32
|
+
|
|
33
|
+
natural_key :foo, :bar
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "returns false if no record exists with the same natural key" do
|
|
37
|
+
ActivityForTestingDuplicates.create!(:foo => "a", :bar => "b", :created_at => Time.now)
|
|
38
|
+
ActivityForTestingDuplicates.new(:foo => "1", :bar => "2").should_not be_duplicate
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "returns true if a saved record already exists with the same natural key" do
|
|
42
|
+
ActivityForTestingDuplicates.create!(:foo => "a", :bar => "b", :created_at => Time.now)
|
|
43
|
+
ActivityForTestingDuplicates.new(:foo => "a", :bar => "b").should be_duplicate
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "returns true if a soft-deleted record already exists with the same natural key" do
|
|
47
|
+
original = ActivityForTestingDuplicates.new(:foo => "a", :bar => "b", :created_at => Time.now)
|
|
48
|
+
original.save!
|
|
49
|
+
original.delete
|
|
50
|
+
|
|
51
|
+
ActivityForTestingDuplicates.new(:foo => "a", :bar => "b").should be_duplicate
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Stratify::Archiver do
|
|
4
|
+
|
|
5
|
+
describe ".run" do
|
|
6
|
+
context "when the collection succeeds" do
|
|
7
|
+
it "updates the last_ran_at timestamp for the collector" do
|
|
8
|
+
stub_current_timestamp = Time.parse "2011-04-30 14:11:02"
|
|
9
|
+
Time.stubs(:now).returns stub_current_timestamp
|
|
10
|
+
|
|
11
|
+
collector = Stratify::Collector.create
|
|
12
|
+
archiver = Stratify::Archiver.new(collector)
|
|
13
|
+
archiver.stubs(:collect_activities)
|
|
14
|
+
archiver.run
|
|
15
|
+
collector.last_ran_at.should == stub_current_timestamp
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "when an exception occurs during collection" do
|
|
20
|
+
it "propagates the exception to the caller" do
|
|
21
|
+
archiver = Stratify::Archiver.new(collector = stub)
|
|
22
|
+
archiver.stubs(:collect_activities).raises("some gnarly error")
|
|
23
|
+
lambda { archiver.run }.should raise_error
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "updates the last_ran_at timestamp for the collector" do
|
|
27
|
+
stub_current_timestamp = Time.parse "2011-04-30 14:11:02"
|
|
28
|
+
Time.stubs(:now).returns stub_current_timestamp
|
|
29
|
+
|
|
30
|
+
collector = Stratify::Collector.create
|
|
31
|
+
archiver = Stratify::Archiver.new(collector)
|
|
32
|
+
archiver.stubs(:collect_activities).raises("some gnarly error")
|
|
33
|
+
archiver.run rescue nil
|
|
34
|
+
collector.last_ran_at.should == stub_current_timestamp
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe ".collect_activities" do
|
|
40
|
+
it "documents the source of each activity" do
|
|
41
|
+
example_collector_class = Class.new(Stratify::Collector)
|
|
42
|
+
example_collector_class.source("some-example-source")
|
|
43
|
+
collector = example_collector_class.new
|
|
44
|
+
|
|
45
|
+
activity = Stratify::Activity.new
|
|
46
|
+
collector.stubs(:activities).returns [activity]
|
|
47
|
+
|
|
48
|
+
archiver = Stratify::Archiver.new(collector)
|
|
49
|
+
archiver.stubs(:persist_activity)
|
|
50
|
+
archiver.send(:collect_activities)
|
|
51
|
+
activity.source.should == "some-example-source"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "persists each activity" do
|
|
55
|
+
collector = Stratify::Collector.new
|
|
56
|
+
activity = Stratify::Activity.new
|
|
57
|
+
collector.stubs(:activities).returns [activity]
|
|
58
|
+
|
|
59
|
+
archiver = Stratify::Archiver.new(collector)
|
|
60
|
+
archiver.expects(:persist_activity).with(activity)
|
|
61
|
+
archiver.send(:collect_activities)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "persisting activities" do
|
|
66
|
+
it "logs when an error is encountered attempting to persist an activity" do
|
|
67
|
+
activity = stub('activity', :duplicate? => false, :save => false, :errors => {})
|
|
68
|
+
|
|
69
|
+
archiver = Stratify::Archiver.new(collector = stub)
|
|
70
|
+
|
|
71
|
+
Stratify.logger.expects(:error).with() { |value| value.starts_with? "Failed to persist activity" }
|
|
72
|
+
archiver.send(:persist_activity, activity)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "does not log an error when activity is persisted successfully" do
|
|
76
|
+
activity = stub('activity', :duplicate? => false, :save => true)
|
|
77
|
+
|
|
78
|
+
archiver = Stratify::Archiver.new(collector = stub)
|
|
79
|
+
|
|
80
|
+
Stratify.logger.expects(:error).never
|
|
81
|
+
archiver.send(:persist_activity, activity)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Stratify::CollectorCoordinator do
|
|
4
|
+
|
|
5
|
+
describe ".run_all" do
|
|
6
|
+
it "runs all collectors" do
|
|
7
|
+
collector_1 = mock(:run)
|
|
8
|
+
collector_2 = mock(:run)
|
|
9
|
+
|
|
10
|
+
Stratify::Collector.stubs(:all).returns([collector_1, collector_2])
|
|
11
|
+
|
|
12
|
+
Stratify::CollectorCoordinator.run_all
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "when an exception occurs in a collector" do
|
|
16
|
+
it "logs the exception" do
|
|
17
|
+
collector = stub
|
|
18
|
+
collector.stubs(:run).raises("some gnarly error")
|
|
19
|
+
Stratify::Collector.stubs(:all).returns([collector])
|
|
20
|
+
|
|
21
|
+
Stratify.logger.expects(:error).with() { |value| value.include? "some gnarly error" }
|
|
22
|
+
Stratify::CollectorCoordinator.run_all
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "runs the remaining collectors" do
|
|
26
|
+
silence_stratify_logging # Silence the logger so as not to clutter the test output
|
|
27
|
+
|
|
28
|
+
troublesome_collector = stub
|
|
29
|
+
troublesome_collector.stubs(:run).raises("some gnarly error")
|
|
30
|
+
|
|
31
|
+
other_collector = mock
|
|
32
|
+
other_collector.expects(:run).returns(nil)
|
|
33
|
+
|
|
34
|
+
Stratify::Collector.stubs(:all).returns([troublesome_collector, other_collector])
|
|
35
|
+
|
|
36
|
+
Stratify::CollectorCoordinator.run_all
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class TestCollector < Stratify::Collector
|
|
4
|
+
configuration_fields :username => {:type => :string},
|
|
5
|
+
:password => {:type => :password}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def anonymous_collector_subclass
|
|
9
|
+
Class.new(Stratify::Collector)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe Stratify::Collector do
|
|
13
|
+
|
|
14
|
+
describe ".collector_class_for" do
|
|
15
|
+
it "returns the collector subclass associated with the given source" do
|
|
16
|
+
example_collector_subclass = anonymous_collector_subclass
|
|
17
|
+
example_collector_subclass.source("Twitter")
|
|
18
|
+
Stratify::Collector.collector_class_for("Twitter").should == example_collector_subclass
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "returns nil when no collector subclass exists for the given source" do
|
|
22
|
+
Stratify::Collector.collector_class_for("lol").should be_nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe ".sources" do
|
|
27
|
+
before { Stratify::Collector.collector_classes.clear }
|
|
28
|
+
|
|
29
|
+
it "returns the list of sources for all collectors" do
|
|
30
|
+
anonymous_collector_subclass.source("FourSquare")
|
|
31
|
+
anonymous_collector_subclass.source("Twitter")
|
|
32
|
+
Stratify::Collector.sources.should =~ ["FourSquare", "Twitter"]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "returns the sources in case-insensitive alphabetical order" do
|
|
36
|
+
anonymous_collector_subclass.source("Twitter")
|
|
37
|
+
anonymous_collector_subclass.source("iTunes")
|
|
38
|
+
anonymous_collector_subclass.source("Instapaper")
|
|
39
|
+
anonymous_collector_subclass.source("Pandora")
|
|
40
|
+
Stratify::Collector.sources.should == %w(Instapaper iTunes Pandora Twitter)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe ".configuration_fields" do
|
|
45
|
+
it "tracks the fields used to configure the collector" do
|
|
46
|
+
fields = TestCollector.configuration_fields
|
|
47
|
+
fields.find {|f| f.name == :username && f.type == :string}.should be
|
|
48
|
+
fields.find {|f| f.name == :password && f.type == :password}.should be
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "registers the given fields with Mongoid" do
|
|
52
|
+
TestCollector.fields.should include("username", "password")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "influences validation" do
|
|
56
|
+
it "adds an error when a collector instance fails to provide a value for each field" do
|
|
57
|
+
collector = TestCollector.new
|
|
58
|
+
collector.should_not be_valid
|
|
59
|
+
collector.errors[:username].should include("can't be blank")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "adds NO errors when a collector instance provides a value for each field" do
|
|
63
|
+
collector = TestCollector.new(:username => "foo")
|
|
64
|
+
collector.valid?
|
|
65
|
+
collector.errors[:username].should_not include("can't be blank")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe ".configuration_instructions (class method)" do
|
|
71
|
+
it "returns the configuration instructions for a specific Collector subclass" do
|
|
72
|
+
collector_subclass = anonymous_collector_subclass
|
|
73
|
+
collector_subclass.configuration_instructions("Provide your username and ...")
|
|
74
|
+
collector_subclass.configuration_instructions.should == "Provide your username and ..."
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "returns nil if a Collector subclass does not specify any configuration instructions" do
|
|
78
|
+
anonymous_collector_subclass.configuration_instructions.should == nil
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "#configuration_instructions (instance method)" do
|
|
83
|
+
it "returns the configuration instructions defined in the class" do
|
|
84
|
+
collector_subclass = anonymous_collector_subclass
|
|
85
|
+
collector_subclass.configuration_instructions("Provide your username and ...")
|
|
86
|
+
collector = collector_subclass.new
|
|
87
|
+
collector.configuration_instructions.should == "Provide your username and ..."
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe "#configuration_summary" do
|
|
92
|
+
context "when the collector's class has one configuration field" do
|
|
93
|
+
it "returns the value of the field" do
|
|
94
|
+
collector_subclass = Class.new(Stratify::Collector) do
|
|
95
|
+
configuration_fields :username => {:type => :string}
|
|
96
|
+
end
|
|
97
|
+
collector = collector_subclass.new
|
|
98
|
+
collector.username = "johndoe"
|
|
99
|
+
collector.configuration_summary.should == "johndoe"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
context "when the collector's class has multiple configuration fields" do
|
|
104
|
+
it "returns the value of the first field" do
|
|
105
|
+
collector_subclass = Class.new(Stratify::Collector) do
|
|
106
|
+
configuration_fields :username => {:type => :string},
|
|
107
|
+
:tag => {:type => :string}
|
|
108
|
+
end
|
|
109
|
+
collector = collector_subclass.new
|
|
110
|
+
collector.username = "johndoe"
|
|
111
|
+
collector.tag = "programming"
|
|
112
|
+
collector.configuration_summary.should == "johndoe"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context "when the collector's class has no configuration fields" do
|
|
117
|
+
it "returns nil" do
|
|
118
|
+
collector_subclass = Class.new(Stratify::Collector)
|
|
119
|
+
collector = collector_subclass.new
|
|
120
|
+
collector.configuration_summary.should == nil
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Stratify::FieldDefinition do
|
|
4
|
+
describe "#name" do
|
|
5
|
+
it "returns the name of the field" do
|
|
6
|
+
field = Stratify::FieldDefinition.new(:username, :type => :string)
|
|
7
|
+
field.name.should == :username
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "#type" do
|
|
12
|
+
it "returns the type of the field" do
|
|
13
|
+
field = Stratify::FieldDefinition.new(:username, :type => :string)
|
|
14
|
+
field.type.should == :string
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#label" do
|
|
19
|
+
it "returns the label for the field when an explicit label is specified" do
|
|
20
|
+
field = Stratify::FieldDefinition.new(:username, :type => :string, :label => "User ID")
|
|
21
|
+
field.label.should == "User ID"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns the titleized name for the field when no explicit label is specified" do
|
|
25
|
+
field = Stratify::FieldDefinition.new(:username, :type => :string)
|
|
26
|
+
field.label.should == "Username"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "collecting activities", :database => true do
|
|
4
|
+
it "runs the collector and stores activities" do
|
|
5
|
+
collector = Stratify::Bacon::Collector.create!(:username => "johndoe", :password => "secret")
|
|
6
|
+
|
|
7
|
+
expect { collector.run }.to change(Stratify::Bacon::Activity, :count).by(3)
|
|
8
|
+
|
|
9
|
+
Stratify::Bacon::Activity.where(:slices => 3, :created_at => Time.new(2011, 4, 3, 7, 2)).should exist
|
|
10
|
+
Stratify::Bacon::Activity.where(:slices => 5, :created_at => Time.new(2011, 4, 7, 8, 17)).should exist
|
|
11
|
+
Stratify::Bacon::Activity.where(:slices => 4, :created_at => Time.new(2011, 4, 9, 6, 24)).should exist
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "does not import duplicate activities" do
|
|
15
|
+
collector = Stratify::Bacon::Collector.new(:username => "johndoe", :password => "secret")
|
|
16
|
+
expect { collector.run }.to change(Stratify::Bacon::Activity, :count).by(3)
|
|
17
|
+
expect { collector.run }.to change(Stratify::Bacon::Activity, :count).by(0)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "does not re-import soft-deleted activities" do
|
|
21
|
+
collector = Stratify::Bacon::Collector.new(:username => "johndoe", :password => "secret")
|
|
22
|
+
|
|
23
|
+
collector.run
|
|
24
|
+
activity = Stratify::Bacon::Activity.where(:created_at => Time.new(2011, 4, 3, 7, 2)).first
|
|
25
|
+
activity.should be
|
|
26
|
+
activity.delete
|
|
27
|
+
|
|
28
|
+
collector.run
|
|
29
|
+
Stratify::Bacon::Activity.where(:created_at => Time.new(2011, 4, 3, 7, 2)).should_not exist
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
def class_with_natural_key_mixin(&blk)
|
|
4
|
+
Class.new do
|
|
5
|
+
include Mongoid::Document
|
|
6
|
+
include Stratify::MongoidExtension::NaturalKey
|
|
7
|
+
yield(self) if block_given?
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe Stratify::MongoidExtension::NaturalKey do
|
|
12
|
+
describe ".natural_key" do
|
|
13
|
+
context "given a single field as the key" do
|
|
14
|
+
it "stores the field in an array of key fields" do
|
|
15
|
+
klass = class_with_natural_key_mixin do |k|
|
|
16
|
+
k.natural_key :status_id
|
|
17
|
+
end
|
|
18
|
+
klass.natural_key_fields.should == [:status_id]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "given a multi-field key" do
|
|
23
|
+
it "stores the fields in an array of key fields" do
|
|
24
|
+
klass = class_with_natural_key_mixin do |k|
|
|
25
|
+
k.natural_key :url, :created_at
|
|
26
|
+
end
|
|
27
|
+
klass.natural_key_fields.should == [:url, :created_at]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "declares a uniqueness validator for the natural key" do
|
|
32
|
+
klass = class_with_natural_key_mixin
|
|
33
|
+
klass.expects(:validates_uniqueness_of_natural_key)
|
|
34
|
+
klass.natural_key :foo
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe ".validates_uniqueness_of_natural_key" do
|
|
39
|
+
context "given a single field as the key" do
|
|
40
|
+
it "declares that no two documents can have the same value for that field" do
|
|
41
|
+
klass = class_with_natural_key_mixin
|
|
42
|
+
klass.stubs(:natural_key_fields).returns [:status_id]
|
|
43
|
+
klass.expects(:validates_uniqueness_of).with(:status_id)
|
|
44
|
+
klass.validates_uniqueness_of_natural_key
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "given a two-field key" do
|
|
49
|
+
it "declares that no two documents can have the same combination of values for these two fields" do
|
|
50
|
+
klass = class_with_natural_key_mixin
|
|
51
|
+
klass.stubs(:natural_key_fields).returns [:url, :created_at]
|
|
52
|
+
klass.expects(:validates_uniqueness_of).with(:url, :scope => [:created_at])
|
|
53
|
+
klass.validates_uniqueness_of_natural_key
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "given a key with more than two fields" do
|
|
58
|
+
it "declares that no two documents can have the same combination of values for these fields" do
|
|
59
|
+
klass = class_with_natural_key_mixin
|
|
60
|
+
klass.stubs(:natural_key_fields).returns [:foo, :bar, :baz]
|
|
61
|
+
klass.expects(:validates_uniqueness_of).with(:foo, :scope => [:bar, :baz])
|
|
62
|
+
klass.validates_uniqueness_of_natural_key
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "#natural_key_hash" do
|
|
68
|
+
it "returns the field name and value for the object's natural key" do
|
|
69
|
+
klass = class_with_natural_key_mixin do |k|
|
|
70
|
+
k.field :foo
|
|
71
|
+
k.field :bar
|
|
72
|
+
k.field :baz
|
|
73
|
+
k.natural_key :foo, :bar
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
model = klass.new(:foo => 1, :bar => 2, :baz => 3)
|
|
77
|
+
model.natural_key_hash.should == {:foo => 1, :bar => 2}
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
def anonymous_class_with_renderable_mixin
|
|
4
|
+
Class.new do
|
|
5
|
+
include Stratify::Renderable
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe Stratify::Renderable do
|
|
10
|
+
describe ".template" do
|
|
11
|
+
it "returns the template specified for use in rendering the object" do
|
|
12
|
+
renderable_class = anonymous_class_with_renderable_mixin
|
|
13
|
+
renderable_class.template "some custom template"
|
|
14
|
+
renderable_class.template.should == "some custom template"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe ".template_format" do
|
|
19
|
+
it "returns ':erb' when no explicit format has been set" do
|
|
20
|
+
renderable_class = anonymous_class_with_renderable_mixin
|
|
21
|
+
renderable_class.template_format.should == :erb
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns a custom template format when one has been set" do
|
|
25
|
+
renderable_class = anonymous_class_with_renderable_mixin
|
|
26
|
+
renderable_class.template_format :haml
|
|
27
|
+
renderable_class.template_format.should == :haml
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "#presenter" do
|
|
32
|
+
it "returns the object" do
|
|
33
|
+
renderable_object = anonymous_class_with_renderable_mixin.new
|
|
34
|
+
renderable_object.presenter.should == renderable_object
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "#to_html" do
|
|
39
|
+
it "returns the result of rendering the template with the presenter" do
|
|
40
|
+
renderable_class = anonymous_class_with_renderable_mixin
|
|
41
|
+
renderable_class.template "Hello, <%= name %>"
|
|
42
|
+
|
|
43
|
+
renderable_object = renderable_class.new
|
|
44
|
+
class << renderable_object
|
|
45
|
+
def name
|
|
46
|
+
"Kilgore"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
renderable_object.to_html.should == "Hello, Kilgore"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "stratify/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "stratify-base"
|
|
7
|
+
s.version = Stratify::Base::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["Jason Rudolph"]
|
|
10
|
+
s.email = ["jason@jasonrudolph.com"]
|
|
11
|
+
s.homepage = "http://github.com/jasonrudolph/stratify/"
|
|
12
|
+
s.summary = "Core collector and activity componentry for Stratify"
|
|
13
|
+
s.description = "Provides the infrastructure to support the development and use of Stratify collectors and activities"
|
|
14
|
+
|
|
15
|
+
s.rubyforge_project = "stratify-base"
|
|
16
|
+
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
|
+
s.require_paths = ["lib"]
|
|
21
|
+
|
|
22
|
+
s.add_runtime_dependency "bson_ext", "~> 1.3.1"
|
|
23
|
+
s.add_runtime_dependency "mongoid", "~> 2.0.2"
|
|
24
|
+
s.add_runtime_dependency "tilt", "~> 1.3.2"
|
|
25
|
+
|
|
26
|
+
s.add_development_dependency "rspec", "~> 2.6.0"
|
|
27
|
+
s.add_development_dependency "mocha", "~> 0.9.12"
|
|
28
|
+
s.add_development_dependency "database_cleaner", "~> 0.6.7"
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: stratify-base
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jason Rudolph
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-06-26 00:00:00 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: bson_ext
|
|
17
|
+
prerelease: false
|
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
19
|
+
none: false
|
|
20
|
+
requirements:
|
|
21
|
+
- - ~>
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 1.3.1
|
|
24
|
+
type: :runtime
|
|
25
|
+
version_requirements: *id001
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: mongoid
|
|
28
|
+
prerelease: false
|
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
30
|
+
none: false
|
|
31
|
+
requirements:
|
|
32
|
+
- - ~>
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: 2.0.2
|
|
35
|
+
type: :runtime
|
|
36
|
+
version_requirements: *id002
|
|
37
|
+
- !ruby/object:Gem::Dependency
|
|
38
|
+
name: tilt
|
|
39
|
+
prerelease: false
|
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ~>
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 1.3.2
|
|
46
|
+
type: :runtime
|
|
47
|
+
version_requirements: *id003
|
|
48
|
+
- !ruby/object:Gem::Dependency
|
|
49
|
+
name: rspec
|
|
50
|
+
prerelease: false
|
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
52
|
+
none: false
|
|
53
|
+
requirements:
|
|
54
|
+
- - ~>
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 2.6.0
|
|
57
|
+
type: :development
|
|
58
|
+
version_requirements: *id004
|
|
59
|
+
- !ruby/object:Gem::Dependency
|
|
60
|
+
name: mocha
|
|
61
|
+
prerelease: false
|
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
63
|
+
none: false
|
|
64
|
+
requirements:
|
|
65
|
+
- - ~>
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 0.9.12
|
|
68
|
+
type: :development
|
|
69
|
+
version_requirements: *id005
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: database_cleaner
|
|
72
|
+
prerelease: false
|
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
74
|
+
none: false
|
|
75
|
+
requirements:
|
|
76
|
+
- - ~>
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: 0.6.7
|
|
79
|
+
type: :development
|
|
80
|
+
version_requirements: *id006
|
|
81
|
+
description: Provides the infrastructure to support the development and use of Stratify collectors and activities
|
|
82
|
+
email:
|
|
83
|
+
- jason@jasonrudolph.com
|
|
84
|
+
executables: []
|
|
85
|
+
|
|
86
|
+
extensions: []
|
|
87
|
+
|
|
88
|
+
extra_rdoc_files: []
|
|
89
|
+
|
|
90
|
+
files:
|
|
91
|
+
- .gitignore
|
|
92
|
+
- .rvmrc
|
|
93
|
+
- Gemfile
|
|
94
|
+
- Gemfile.lock
|
|
95
|
+
- LICENSE
|
|
96
|
+
- README.md
|
|
97
|
+
- Rakefile
|
|
98
|
+
- lib/stratify-base.rb
|
|
99
|
+
- lib/stratify/activity.rb
|
|
100
|
+
- lib/stratify/archiver.rb
|
|
101
|
+
- lib/stratify/collector.rb
|
|
102
|
+
- lib/stratify/collector_coordinator.rb
|
|
103
|
+
- lib/stratify/field_definition.rb
|
|
104
|
+
- lib/stratify/logger.rb
|
|
105
|
+
- lib/stratify/mongoid_extension.rb
|
|
106
|
+
- lib/stratify/renderable.rb
|
|
107
|
+
- lib/stratify/version.rb
|
|
108
|
+
- spec/mongoid.yml
|
|
109
|
+
- spec/prototypes/bacon.rb
|
|
110
|
+
- spec/spec_helper.rb
|
|
111
|
+
- spec/stratify/activity_spec.rb
|
|
112
|
+
- spec/stratify/archiver_spec.rb
|
|
113
|
+
- spec/stratify/collector_coordinator_spec.rb
|
|
114
|
+
- spec/stratify/collector_spec.rb
|
|
115
|
+
- spec/stratify/field_definition_spec.rb
|
|
116
|
+
- spec/stratify/integration_spec.rb
|
|
117
|
+
- spec/stratify/mongoid_extension_spec.rb
|
|
118
|
+
- spec/stratify/renderable_spec.rb
|
|
119
|
+
- stratify-base.gemspec
|
|
120
|
+
homepage: http://github.com/jasonrudolph/stratify/
|
|
121
|
+
licenses: []
|
|
122
|
+
|
|
123
|
+
post_install_message:
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
none: false
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: "0"
|
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
|
+
none: false
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: "0"
|
|
140
|
+
requirements: []
|
|
141
|
+
|
|
142
|
+
rubyforge_project: stratify-base
|
|
143
|
+
rubygems_version: 1.8.5
|
|
144
|
+
signing_key:
|
|
145
|
+
specification_version: 3
|
|
146
|
+
summary: Core collector and activity componentry for Stratify
|
|
147
|
+
test_files:
|
|
148
|
+
- spec/mongoid.yml
|
|
149
|
+
- spec/prototypes/bacon.rb
|
|
150
|
+
- spec/spec_helper.rb
|
|
151
|
+
- spec/stratify/activity_spec.rb
|
|
152
|
+
- spec/stratify/archiver_spec.rb
|
|
153
|
+
- spec/stratify/collector_coordinator_spec.rb
|
|
154
|
+
- spec/stratify/collector_spec.rb
|
|
155
|
+
- spec/stratify/field_definition_spec.rb
|
|
156
|
+
- spec/stratify/integration_spec.rb
|
|
157
|
+
- spec/stratify/mongoid_extension_spec.rb
|
|
158
|
+
- spec/stratify/renderable_spec.rb
|