footprint 1.0.0.pre → 1.0.0.rc2
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 -1
- data/.rspec +1 -1
- data/README.md +120 -9
- data/Rakefile +20 -0
- data/footprint.gemspec +8 -4
- data/lib/footprint.rb +12 -4
- data/lib/footprint/impression.rb +31 -0
- data/lib/footprint/leave_a_track.rb +49 -0
- data/lib/footprint/version.rb +1 -1
- data/lib/generators/footprint/document/document_generator.rb +26 -0
- data/spec/factories.rb +55 -0
- data/spec/footprint/impression_spec.rb +60 -0
- data/spec/footprint/track_spec.rb +75 -0
- data/spec/footprint_spec.rb +11 -0
- data/spec/generators/rspec/document/document_generator_spec.rb +19 -0
- data/spec/schema/documents/leopard_footprint.rb +18 -0
- data/spec/schema/documents/yeti_footprint.rb +19 -0
- data/spec/schema/migrations/001_create_yetis.rb +20 -0
- data/spec/schema/migrations/002_create_leopards.rb +19 -0
- data/spec/schema/models/leopard.rb +3 -0
- data/spec/schema/models/yeti.rb +4 -0
- data/spec/spec_helper.rb +78 -0
- metadata +87 -10
- data/spec/lib/footprint_spec.rb +0 -7
data/.gitignore
CHANGED
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
--color
|
2
|
-
--format
|
2
|
+
--format documentation
|
data/README.md
CHANGED
@@ -1,24 +1,122 @@
|
|
1
1
|
# Footprint
|
2
2
|
|
3
|
-
|
3
|
+
**Footprint** is a **MongoDB-backed** versioning system for those **Big Applications** which have a need for storing
|
4
|
+
|
5
|
+
* Huge amounts of Version data for auditing purposes
|
6
|
+
* All states of a record for analysis by BI tools
|
7
|
+
|
8
|
+
But struggle because they cannot
|
9
|
+
|
10
|
+
* Use a simple database system due to performance constraints
|
11
|
+
* Shift to a NoSQL DB completely because of transaction requirements
|
12
|
+
|
13
|
+
Footprint integrates seamlessly into your ActiveRecord-driven Rails Application, effectively converting it into a Hybrid application, storing only version data (or **Impressions**, as they are called) in a MongoDB database.
|
4
14
|
|
5
15
|
## Installation
|
6
16
|
|
7
|
-
Add
|
17
|
+
Add Footprint to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "footprint", "1.0.0.rc2"
|
21
|
+
```
|
22
|
+
|
23
|
+
Generate mongoid.yml if you don't have one in your project already
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ rails g mongoid:config
|
27
|
+
```
|
28
|
+
|
29
|
+
Modify your config/application.rb to use ActiveRecord generators by default
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
config.generators do |g|
|
33
|
+
g.orm :active_record
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
Add a special lookup for Mongoid gem: (will be removed when Mongoid 3.1.0 is released)
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem "mongoid", :git => 'git@github.com:mongoid/mongoid.git'
|
41
|
+
```
|
42
|
+
|
43
|
+
## Setting Up Documents for your ActiveRecord models
|
44
|
+
|
45
|
+
Generate a document from an existing ActiveRecord Model
|
46
|
+
|
47
|
+
```bash
|
48
|
+
$ rails g footprint:document User
|
49
|
+
```
|
50
|
+
|
51
|
+
Add `leave_a_track` to your Model class
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class User < ActiveRecord::Base
|
55
|
+
leave_a_track
|
56
|
+
attr_accessible :name, :dob
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
## Handling Impressions
|
8
61
|
|
9
|
-
|
62
|
+
### Querying
|
10
63
|
|
11
|
-
|
64
|
+
Impressions (a.k.a versions) of a record are accessible through `impressions` attribute
|
12
65
|
|
13
|
-
|
66
|
+
```ruby
|
67
|
+
u = User.find(1)
|
68
|
+
u.impressions
|
69
|
+
```
|
14
70
|
|
15
|
-
|
71
|
+
### Collecting values
|
16
72
|
|
17
|
-
|
73
|
+
Collecting specific values in Impressions too is very similar to normal AR model behavior
|
18
74
|
|
19
|
-
|
75
|
+
```ruby
|
76
|
+
u = User.find(1)
|
77
|
+
u.impressons.collect { |imp| imp.name }
|
78
|
+
```
|
20
79
|
|
21
|
-
|
80
|
+
### Reviving a copy of the parent
|
81
|
+
|
82
|
+
Converting an Impression to an ActiveRecord model, to mirror the parent can be done via `as_parent` method
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
User.create("name" => "Subhash", "join_date" => 2.days.ago) # User:1
|
86
|
+
u = User.find(1)
|
87
|
+
u.impressions.count # 1
|
88
|
+
u.name = "Subhash Bhushan"
|
89
|
+
u.save
|
90
|
+
u.impressions.count # 2
|
91
|
+
User copy_of_user_1 = u.impressions.last.as_parent
|
92
|
+
```
|
93
|
+
|
94
|
+
### Sorting
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
p.impressions.asc(:updated_at)
|
98
|
+
p.impressions.order_by(:updated_at.asc)
|
99
|
+
p.impressions.desc(:updated_at)
|
100
|
+
```
|
101
|
+
|
102
|
+
For a complete list of sorting options: [Mongoid Sorting](http://mongoid.org/en/origin/docs/options.html#sorting)
|
103
|
+
|
104
|
+
### Pagination
|
105
|
+
|
106
|
+
`Kaminari` is the preferred way to paginate mongoid documents. `will_paginate_mongoid` gem is an option for those who already use `will_paginate`.
|
107
|
+
* [will_paginate_mongoid](https://github.com/lucasas/will_paginate_mongoid)
|
108
|
+
* [kaminari](https://github.com/amatsuda/kaminari) ([Example](http://code.dblock.org/mongoid-202-dropped-pagination-kaminari))
|
109
|
+
|
110
|
+
## Setting up MongoDB
|
111
|
+
|
112
|
+
Getting a MongoDB instance up and running is pretty simple.
|
113
|
+
Visit [Installation Guides](http://docs.mongodb.org/manual/installation/) to set up one for your OS.
|
114
|
+
|
115
|
+
The tutorial [Getting Started with MongoDB Development](http://docs.mongodb.org/manual/tutorial/getting-started/) explains basics of connecting to a MongoDB instance and querying on collections
|
116
|
+
|
117
|
+
## Known Issues
|
118
|
+
|
119
|
+
* If you get a message that "ActiveRecord model 'Project' was not found" while generating a document, do `rake db:migrate` and then try generating the document again.
|
22
120
|
|
23
121
|
## Contributing
|
24
122
|
|
@@ -27,3 +125,16 @@ TODO: Write usage instructions here
|
|
27
125
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
126
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
127
|
5. Create new Pull Request
|
128
|
+
|
129
|
+
## Inspirations
|
130
|
+
|
131
|
+
* [Paper Trail](https://github.com/airblade/paper_trail)
|
132
|
+
* [Mongoid](mongoid.org/en/mongoid/index.html)
|
133
|
+
|
134
|
+
## Resources
|
135
|
+
|
136
|
+
* MongoDB
|
137
|
+
* [Why MongoDB](https://speakerdeck.com/jnunemaker/why-mongodb-is-awesome)
|
138
|
+
* [Installation](http://docs.mongodb.org/manual/installation/)
|
139
|
+
* [Hybrid Applications](http://www.slideshare.net/spf13/hybrid-mongodb-and-rdbms-applications)
|
140
|
+
* [Versioning Strategies](http://blog.jondh.me.uk/2011/11/relational-database-versioning-strategies/)
|
data/Rakefile
CHANGED
@@ -1 +1,21 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require 'active_record'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
namespace :db do
|
6
|
+
desc "Migrate the database through scripts in spec/schema/migrations"
|
7
|
+
task :prepare => :environment do
|
8
|
+
ActiveRecord::Migration.verbose = true
|
9
|
+
|
10
|
+
MIGRATIONS = File.join(File.dirname(__FILE__), "spec/schema/migrations")
|
11
|
+
ActiveRecord::Migrator.migrate(MIGRATIONS)
|
12
|
+
end
|
13
|
+
|
14
|
+
task :environment do
|
15
|
+
Dir.mkdir("db") unless File.exist?("db")
|
16
|
+
File.delete('db/fpdb') if File.exist?('db/fpdb')
|
17
|
+
|
18
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: "db/fpdb"
|
19
|
+
ActiveRecord::Base.logger = Logger.new(File.open('db/database.log', 'a'))
|
20
|
+
end
|
21
|
+
end
|
data/footprint.gemspec
CHANGED
@@ -8,17 +8,21 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Footprint::VERSION
|
9
9
|
gem.authors = ["Subhash Bhushan"]
|
10
10
|
gem.email = ["subhash.bhushan@stratalabs.in"]
|
11
|
-
gem.description = %q{Big Data Versioning for those seriously Big
|
12
|
-
gem.summary = %q{Big Data Versioning for those seriously Big
|
11
|
+
gem.description = %q{Big Data Versioning for those seriously Big Applications}
|
12
|
+
gem.summary = %q{Big Data Versioning for those seriously Big Applications}
|
13
13
|
gem.homepage = ""
|
14
|
+
gem.license = "MIT"
|
14
15
|
|
15
16
|
gem.add_development_dependency "rspec", "~> 2.12"
|
16
17
|
gem.add_development_dependency "guard-rspec"
|
17
18
|
gem.add_development_dependency "growl", "~> 1.0.3"
|
18
19
|
gem.add_development_dependency "rb-fsevent", "~> 0.9.1"
|
19
|
-
gem.add_development_dependency "
|
20
|
+
gem.add_development_dependency "factory_girl"
|
21
|
+
gem.add_development_dependency "sqlite3"
|
22
|
+
gem.add_development_dependency "ammeter"
|
20
23
|
|
21
|
-
gem.
|
24
|
+
gem.add_dependency "mongoid", "~> 3.0"
|
25
|
+
gem.add_dependency "activerecord", "~> 3.2.0"
|
22
26
|
|
23
27
|
gem.files = `git ls-files`.split($/)
|
24
28
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/lib/footprint.rb
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
require "footprint/version"
|
2
|
+
require "footprint/impression"
|
3
|
+
require "footprint/leave_a_track"
|
4
|
+
|
5
|
+
require "generators/footprint/document/document_generator"
|
6
|
+
|
7
|
+
require "active_support/inflector"
|
2
8
|
|
3
9
|
module Footprint
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load(:active_record) do
|
14
|
+
include Footprint::Model
|
15
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
|
3
|
+
module Footprint
|
4
|
+
class Impression
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
def self.leave(model, phase)
|
8
|
+
self.create attributes_for_impression(model).merge(mandatory_attributes(model)).merge(:phase => phase)
|
9
|
+
end
|
10
|
+
|
11
|
+
def as_parent
|
12
|
+
parent = Object.const_get(parent_type).new (self.attributes).except("_id", "_type", "parent_id", "parent_type", "phase", "created_at", "updated_at")
|
13
|
+
|
14
|
+
#id,created_at and updated_at is attr-protected, so no mass-assignment
|
15
|
+
parent.id = self.parent_id
|
16
|
+
parent.created_at = self.created_at
|
17
|
+
parent.updated_at = self.updated_at
|
18
|
+
parent
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.mandatory_attributes(model)
|
24
|
+
Hash.new.merge(:parent_id => model.id, :parent_type => model.class.name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.attributes_for_impression(model)
|
28
|
+
model.attributes.except(:id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Footprint
|
2
|
+
module Model
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send :extend, ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def leave_a_track
|
11
|
+
send :include, InstanceMethods
|
12
|
+
|
13
|
+
class_attribute :impressions_association_name
|
14
|
+
self.impressions_association_name = self.name + "Footprint"
|
15
|
+
|
16
|
+
class_attribute :impressions_class
|
17
|
+
self.impressions_class = Object.const_get(self.impressions_association_name)
|
18
|
+
|
19
|
+
class_attribute :leaving_a_track
|
20
|
+
self.leaving_a_track = true
|
21
|
+
|
22
|
+
after_create :impression_of_create
|
23
|
+
after_update :impression_of_update
|
24
|
+
after_destroy :impression_of_destroy
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
def impressions
|
31
|
+
impressions_class.where(parent_id: self.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def impression_of_create
|
37
|
+
impressions_class.leave self, "create"
|
38
|
+
end
|
39
|
+
|
40
|
+
def impression_of_update
|
41
|
+
impressions_class.leave(self, "update") if changed?
|
42
|
+
end
|
43
|
+
|
44
|
+
def impression_of_destroy
|
45
|
+
impressions_class.leave self, "destroy"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/footprint/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Footprint
|
4
|
+
module Generators
|
5
|
+
class DocumentGenerator < Rails::Generators::Base
|
6
|
+
argument :model_name, :type => :string, :desc => "Active Record model to generate Document from"
|
7
|
+
|
8
|
+
def run_mongoid_generator
|
9
|
+
begin
|
10
|
+
#TODO Provide option to prevent generation of test unit files (--skip-test-unit)
|
11
|
+
attributes = Object.const_get(model_name).columns_hash.except("id").values.collect { |c| c.name + ":" + type_class(c.sql_type) }.join(" ") + " phase:string parent_id:integer parent_type:string"
|
12
|
+
generate "mongoid:model", "#{model_name.camelize}Footprint #{attributes} --parent Footprint::Impression --collection=#{model_name.downcase.pluralize}"
|
13
|
+
rescue Exception => e
|
14
|
+
Logger.new(STDOUT).error("ActiveRecord model \"#{model_name.to_s}\" was not found")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def type_class(sql_type)
|
21
|
+
return "string" if sql_type.to_s == "varchar(255)"
|
22
|
+
return sql_type.downcase
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/factories.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :yeti do
|
3
|
+
sequence(:name) { |n| "Yeti#{n}" }
|
4
|
+
notes "Yet to Start Tracking"
|
5
|
+
region "Himalayas"
|
6
|
+
|
7
|
+
trait :male do
|
8
|
+
gender "Male"
|
9
|
+
end
|
10
|
+
trait :female do
|
11
|
+
gender "Female"
|
12
|
+
end
|
13
|
+
|
14
|
+
trait :dangerous do
|
15
|
+
name "Ivan"
|
16
|
+
aggression 9
|
17
|
+
mass 9
|
18
|
+
strength 10
|
19
|
+
height 325
|
20
|
+
weight 1400
|
21
|
+
region "Himalayas"
|
22
|
+
notes "Stay away and take evasive measures - Extremely dangerous"
|
23
|
+
end
|
24
|
+
trait :tameable do
|
25
|
+
name "Johanne"
|
26
|
+
aggression 1
|
27
|
+
mass 8
|
28
|
+
strength 6
|
29
|
+
height 310
|
30
|
+
weight 1100
|
31
|
+
region "Amazon"
|
32
|
+
notes "Gentle and harmless"
|
33
|
+
end
|
34
|
+
trait :huge do
|
35
|
+
name "Duncan"
|
36
|
+
aggression 5
|
37
|
+
mass 10
|
38
|
+
strength 9
|
39
|
+
height 375
|
40
|
+
weight 1700
|
41
|
+
region "Andes"
|
42
|
+
notes "Unknown behavior - Be wary of proximity"
|
43
|
+
end
|
44
|
+
|
45
|
+
factory :male_yeti, traits: [:male]
|
46
|
+
factory :female_yeti, traits: [:female]
|
47
|
+
|
48
|
+
factory :male_dangerous_yeti, traits: [:male, :dangerous]
|
49
|
+
factory :female_tameable_yeti, traits: [:female, :tameable]
|
50
|
+
factory :male_huge_yeti, traits: [:male, :huge]
|
51
|
+
factory :female_dangerous_yeti, traits: [:female, :dangerous, name: "Victoria"]
|
52
|
+
factory :male_tameable_yeti, traits: [:male, :tameable, name: "Beethoven"]
|
53
|
+
factory :female_huge_yeti, traits: [:female, :huge, name: "Hidimbi"]
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Footprint::Impression do
|
4
|
+
before(:each) do
|
5
|
+
@yeti = FactoryGirl.create(:yeti)
|
6
|
+
end
|
7
|
+
|
8
|
+
context "is linked" do
|
9
|
+
it "to parent" do
|
10
|
+
expect(@yeti.impressions.first.parent_id).to eq(@yeti.id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "shares attributes of parent" do
|
15
|
+
it "like name" do
|
16
|
+
expect(@yeti.impressions.first.name).to eq(@yeti.name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "records mandatory criteria" do
|
21
|
+
it "like parent_id" do
|
22
|
+
expect(@yeti.impressions.first.parent_id).to eq(@yeti.id)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "like parent_type" do
|
26
|
+
expect(@yeti.impressions.first.parent_type).to eq(@yeti.class.name)
|
27
|
+
end
|
28
|
+
|
29
|
+
context "like phase" do
|
30
|
+
it "of birth" do
|
31
|
+
expect(@yeti.impressions.first.phase).to eq("create")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "of growth" do
|
35
|
+
@yeti.name = "Changed"
|
36
|
+
@yeti.save
|
37
|
+
expect(@yeti.impressions.last.phase).to eq("update")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "of death" do
|
41
|
+
@yeti.destroy
|
42
|
+
expect(@yeti.impressions.last.phase).to eq("destroy")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "returns" do
|
48
|
+
it "an impression in general cases" do
|
49
|
+
expect(@yeti.impressions.first.class).to be_instance_of(YetiFootprint.class)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "the parent type when asked" do
|
53
|
+
expect(@yeti.impressions.first.as_parent).to be_instance_of(Yeti)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns its parent when asked" do
|
57
|
+
expect(@yeti.impressions.first.as_parent.id).to eq(@yeti.impressions.first.parent_id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Footprint::Model do
|
4
|
+
before(:each) do
|
5
|
+
@yeti = FactoryGirl.build(:yeti)
|
6
|
+
end
|
7
|
+
|
8
|
+
context "during its lifetime" do
|
9
|
+
it "can be conceived" do
|
10
|
+
expect( @yeti ).to be_valid
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can be born" do
|
14
|
+
expect{ @yeti.save }.to change{Yeti.count}.by(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can grow" do
|
18
|
+
@yeti.save
|
19
|
+
expect{ @yeti.update_attributes(:height => 100) }.not_to change{Yeti.count}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can die" do
|
23
|
+
@yeti.save
|
24
|
+
expect { @yeti.destroy }.to change{Yeti.count}.by(-1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "with tracking enabled" do
|
29
|
+
it "can be monitored" do
|
30
|
+
expect( Yeti.new ).to be_leaving_a_track
|
31
|
+
end
|
32
|
+
|
33
|
+
context "while alive" do
|
34
|
+
context "will leave an impression" do
|
35
|
+
it "when born" do
|
36
|
+
expect { @yeti.save }.to change{YetiFootprint.count}.by(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "when growing" do
|
40
|
+
@yeti.save
|
41
|
+
@yeti.name = @yeti.name + "Changed"
|
42
|
+
expect { @yeti.save }.to change{YetiFootprint.count}.by(1)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "when dead" do
|
46
|
+
@yeti.save
|
47
|
+
expect { @yeti.destroy }.to change{YetiFootprint.count}.by(1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when saved multiple times" do
|
52
|
+
it "will store multiple impressions" do
|
53
|
+
expect {
|
54
|
+
@yeti.save
|
55
|
+
@yeti.name = @yeti.name + "Changed"
|
56
|
+
@yeti.save
|
57
|
+
}.to change{YetiFootprint.count}.by(2)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "will not leave an impression" do
|
62
|
+
it "if there is no growth" do
|
63
|
+
@yeti.save
|
64
|
+
expect { @yeti.save }.to_not change{YetiFootprint.count}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with no tracking" do
|
71
|
+
it "does not respond to leaving_a_track" do
|
72
|
+
expect(Leopard.new).to_not respond_to(:leaving_a_track)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# Generators are not automatically loaded by Rails
|
4
|
+
require 'generators/footprint/document/document_generator'
|
5
|
+
|
6
|
+
describe Footprint::Generators::DocumentGenerator do
|
7
|
+
destination File.expand_path("../../../../../tmp", __FILE__)
|
8
|
+
before { prepare_destination }
|
9
|
+
|
10
|
+
describe "with valid ActiveRecord model" do
|
11
|
+
describe "generates a document" do
|
12
|
+
# run_generator %w(Yeti)
|
13
|
+
# subject { file('app/models/yeti_footprint.rb') }
|
14
|
+
#it { should exist }
|
15
|
+
# TODO Fix problem of 'scope/rails' not found in spec
|
16
|
+
it "needs a fix problem 'script/rails' not found"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class LeopardFootprint
|
2
|
+
include Mongoid::Document
|
3
|
+
store_in collection: "leopards"
|
4
|
+
field :id, type: Integer
|
5
|
+
field :name, type: String
|
6
|
+
field :region, type: String
|
7
|
+
field :height, type: Integer
|
8
|
+
field :weight, type: Integer
|
9
|
+
field :strength, type: Integer
|
10
|
+
field :speed, type: Integer
|
11
|
+
field :aggression, type: Integer
|
12
|
+
field :notes, type: String
|
13
|
+
field :created_at, type: Time
|
14
|
+
field :updated_at, type: Time
|
15
|
+
field :phase, type: String
|
16
|
+
field :parent_id, type: Integer
|
17
|
+
field :parent_type, type: String
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class YetiFootprint < Footprint::Impression
|
2
|
+
include Mongoid::Document
|
3
|
+
store_in collection: "yetis"
|
4
|
+
field :id, type: Integer
|
5
|
+
field :name, type: String
|
6
|
+
field :gender, type: String
|
7
|
+
field :region, type: String
|
8
|
+
field :height, type: Integer
|
9
|
+
field :weight, type: Integer
|
10
|
+
field :strength, type: Integer
|
11
|
+
field :aggression, type: Integer
|
12
|
+
field :mass, type: Integer
|
13
|
+
field :notes, type: String
|
14
|
+
field :created_at, type: Time
|
15
|
+
field :updated_at, type: Time
|
16
|
+
field :phase, type: String
|
17
|
+
field :parent_id, type: Integer
|
18
|
+
field :parent_type, type: String
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateYetis < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :yetis, :force => true do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :gender
|
6
|
+
t.string :region
|
7
|
+
t.integer :height
|
8
|
+
t.integer :weight
|
9
|
+
t.integer :strength
|
10
|
+
t.integer :aggression
|
11
|
+
t.integer :mass
|
12
|
+
t.string :notes
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down
|
18
|
+
drop_table :yetis
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateLeopards < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :leopards, :force => true do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :region
|
6
|
+
t.integer :height
|
7
|
+
t.integer :weight
|
8
|
+
t.integer :strength
|
9
|
+
t.integer :speed
|
10
|
+
t.integer :aggression
|
11
|
+
t.string :notes
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :leopards
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,68 @@
|
|
1
|
+
#Required for ammeter - start
|
2
|
+
require 'rails/all'
|
3
|
+
|
4
|
+
module Footprint
|
5
|
+
class Application < ::Rails::Application
|
6
|
+
end
|
7
|
+
end
|
8
|
+
#Required for ammeter - end
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
11
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
12
|
+
|
13
|
+
MODELS = File.join(File.dirname(__FILE__), "schema/models")
|
14
|
+
DOCUMENTS = File.join(File.dirname(__FILE__), "schema/documents")
|
15
|
+
$LOAD_PATH.unshift(MODELS)
|
16
|
+
$LOAD_PATH.unshift(DOCUMENTS)
|
17
|
+
|
1
18
|
require 'footprint'
|
19
|
+
require 'active_record'
|
20
|
+
require 'ammeter/init'
|
21
|
+
require 'factory_girl'
|
22
|
+
require "mongoid"
|
23
|
+
|
24
|
+
# Autoload every model for the test suite that sits in spec/schema/models.
|
25
|
+
Dir[ File.join(MODELS, "*.rb") ].sort.each do |file|
|
26
|
+
name = File.basename(file, ".rb")
|
27
|
+
autoload name.camelize.to_sym, name
|
28
|
+
end
|
29
|
+
|
30
|
+
# Autoload every document for the test suite that sits in spec/schema/documents.
|
31
|
+
Dir[ File.join(DOCUMENTS, "*.rb") ].sort.each do |file|
|
32
|
+
name = File.basename(file, ".rb")
|
33
|
+
autoload name.camelize.to_sym, name
|
34
|
+
end
|
35
|
+
|
36
|
+
# To load FactoryGirl in a non-rails environment
|
37
|
+
FactoryGirl.find_definitions
|
38
|
+
|
39
|
+
# Mongoid Configuration - start
|
40
|
+
# These environment variables can be set if wanting to test against a database
|
41
|
+
# that is not on the local machine.
|
42
|
+
ENV["MONGOID_SPEC_HOST"] ||= "localhost"
|
43
|
+
ENV["MONGOID_SPEC_PORT"] ||= "27017"
|
44
|
+
|
45
|
+
# These are used when creating any connection in the test suite.
|
46
|
+
HOST = ENV["MONGOID_SPEC_HOST"]
|
47
|
+
PORT = ENV["MONGOID_SPEC_PORT"].to_i
|
48
|
+
|
49
|
+
# Moped.logger.level = Logger::DEBUG
|
50
|
+
# Mongoid.logger.level = Logger::DEBUG
|
51
|
+
|
52
|
+
# When testing locally we use the database named mongoid_test. However when
|
53
|
+
# tests are running in parallel on Travis we need to use different database
|
54
|
+
# names for each process running since we do not have transactions and want a
|
55
|
+
# clean slate before each spec run.
|
56
|
+
def database_id
|
57
|
+
"mongoid_test"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set the database that the spec suite connects to.
|
61
|
+
Mongoid.configure do |config|
|
62
|
+
config.allow_dynamic_fields = false
|
63
|
+
config.connect_to(database_id, consistency: :strong)
|
64
|
+
end
|
65
|
+
# Mongoid Configuration - end
|
2
66
|
|
3
67
|
RSpec.configure do |config|
|
4
68
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
@@ -10,4 +74,18 @@ RSpec.configure do |config|
|
|
10
74
|
# the seed, which is printed after each run.
|
11
75
|
# --seed 1234
|
12
76
|
config.order = 'random'
|
77
|
+
|
78
|
+
config.expect_with :rspec do |c|
|
79
|
+
c.syntax = :expect
|
80
|
+
end
|
81
|
+
|
82
|
+
#Initialize database connection
|
83
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: "db/fpdb"
|
84
|
+
|
85
|
+
#Initialize Mongoid
|
86
|
+
# Drop all collections and clear the identity map before each spec.
|
87
|
+
config.before(:each) do
|
88
|
+
Mongoid.purge!
|
89
|
+
Mongoid::IdentityMap.clear
|
90
|
+
end
|
13
91
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: footprint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -76,7 +76,39 @@ dependencies:
|
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: 0.9.1
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
|
-
name:
|
79
|
+
name: factory_girl
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: sqlite3
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: ammeter
|
80
112
|
requirement: !ruby/object:Gem::Requirement
|
81
113
|
none: false
|
82
114
|
requirements:
|
@@ -98,7 +130,23 @@ dependencies:
|
|
98
130
|
requirements:
|
99
131
|
- - ~>
|
100
132
|
- !ruby/object:Gem::Version
|
101
|
-
version: 3.0
|
133
|
+
version: '3.0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '3.0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: activerecord
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 3.2.0
|
102
150
|
type: :runtime
|
103
151
|
prerelease: false
|
104
152
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,8 +154,8 @@ dependencies:
|
|
106
154
|
requirements:
|
107
155
|
- - ~>
|
108
156
|
- !ruby/object:Gem::Version
|
109
|
-
version: 3.
|
110
|
-
description: Big Data Versioning for those seriously Big
|
157
|
+
version: 3.2.0
|
158
|
+
description: Big Data Versioning for those seriously Big Applications
|
111
159
|
email:
|
112
160
|
- subhash.bhushan@stratalabs.in
|
113
161
|
executables: []
|
@@ -123,11 +171,28 @@ files:
|
|
123
171
|
- Rakefile
|
124
172
|
- footprint.gemspec
|
125
173
|
- lib/footprint.rb
|
174
|
+
- lib/footprint/impression.rb
|
175
|
+
- lib/footprint/leave_a_track.rb
|
126
176
|
- lib/footprint/version.rb
|
127
|
-
-
|
177
|
+
- lib/generators/footprint/.DS_Store
|
178
|
+
- lib/generators/footprint/document/document_generator.rb
|
179
|
+
- spec/factories.rb
|
180
|
+
- spec/footprint/impression_spec.rb
|
181
|
+
- spec/footprint/track_spec.rb
|
182
|
+
- spec/footprint_spec.rb
|
183
|
+
- spec/generators/.DS_Store
|
184
|
+
- spec/generators/rspec/.DS_Store
|
185
|
+
- spec/generators/rspec/document/document_generator_spec.rb
|
186
|
+
- spec/schema/documents/leopard_footprint.rb
|
187
|
+
- spec/schema/documents/yeti_footprint.rb
|
188
|
+
- spec/schema/migrations/001_create_yetis.rb
|
189
|
+
- spec/schema/migrations/002_create_leopards.rb
|
190
|
+
- spec/schema/models/leopard.rb
|
191
|
+
- spec/schema/models/yeti.rb
|
128
192
|
- spec/spec_helper.rb
|
129
193
|
homepage: ''
|
130
|
-
licenses:
|
194
|
+
licenses:
|
195
|
+
- MIT
|
131
196
|
post_install_message:
|
132
197
|
rdoc_options: []
|
133
198
|
require_paths:
|
@@ -149,7 +214,19 @@ rubyforge_project:
|
|
149
214
|
rubygems_version: 1.8.24
|
150
215
|
signing_key:
|
151
216
|
specification_version: 3
|
152
|
-
summary: Big Data Versioning for those seriously Big
|
217
|
+
summary: Big Data Versioning for those seriously Big Applications
|
153
218
|
test_files:
|
154
|
-
- spec/
|
219
|
+
- spec/factories.rb
|
220
|
+
- spec/footprint/impression_spec.rb
|
221
|
+
- spec/footprint/track_spec.rb
|
222
|
+
- spec/footprint_spec.rb
|
223
|
+
- spec/generators/.DS_Store
|
224
|
+
- spec/generators/rspec/.DS_Store
|
225
|
+
- spec/generators/rspec/document/document_generator_spec.rb
|
226
|
+
- spec/schema/documents/leopard_footprint.rb
|
227
|
+
- spec/schema/documents/yeti_footprint.rb
|
228
|
+
- spec/schema/migrations/001_create_yetis.rb
|
229
|
+
- spec/schema/migrations/002_create_leopards.rb
|
230
|
+
- spec/schema/models/leopard.rb
|
231
|
+
- spec/schema/models/yeti.rb
|
155
232
|
- spec/spec_helper.rb
|