active_record_survey 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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +23 -0
  5. data/Gemfile.lock +111 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +38 -0
  8. data/Rakefile +6 -0
  9. data/active_record_survey.gemspec +32 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/lib/active_record_survey/compatibility.rb +27 -0
  13. data/lib/active_record_survey/instance.rb +9 -0
  14. data/lib/active_record_survey/instance_node.rb +26 -0
  15. data/lib/active_record_survey/node/answer/boolean.rb +9 -0
  16. data/lib/active_record_survey/node/answer/scale.rb +5 -0
  17. data/lib/active_record_survey/node/answer/text.rb +5 -0
  18. data/lib/active_record_survey/node/answer.rb +4 -0
  19. data/lib/active_record_survey/node/question.rb +4 -0
  20. data/lib/active_record_survey/node.rb +38 -0
  21. data/lib/active_record_survey/node_map.rb +26 -0
  22. data/lib/active_record_survey/node_validation/maximum_length.rb +4 -0
  23. data/lib/active_record_survey/node_validation/maximum_value.rb +4 -0
  24. data/lib/active_record_survey/node_validation/minimum_length.rb +4 -0
  25. data/lib/active_record_survey/node_validation/minimum_value.rb +4 -0
  26. data/lib/active_record_survey/node_validation.rb +7 -0
  27. data/lib/active_record_survey/survey.rb +33 -0
  28. data/lib/active_record_survey/version.rb +3 -0
  29. data/lib/active_record_survey.rb +30 -0
  30. data/lib/generators/active_record_survey/active_record_generator.rb +22 -0
  31. data/lib/generators/active_record_survey/active_record_survey_generator.rb +6 -0
  32. data/lib/generators/active_record_survey/next_migration_version.rb +14 -0
  33. data/lib/generators/active_record_survey/templates/migration_0.1.0.rb +59 -0
  34. data/spec/active_record_survey/node/answer/boolean_spec.rb +94 -0
  35. data/spec/active_record_survey/survey_spec.rb +111 -0
  36. data/spec/active_record_survey_spec.rb +7 -0
  37. data/spec/spec_helper.rb +41 -0
  38. metadata +176 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 240ce460ae63c7c51f4a360cbe24280df0b5f502
4
+ data.tar.gz: 9420ee37dc83a9cc95e8dbfaa245129671b4443b
5
+ SHA512:
6
+ metadata.gz: 1add51d1fc6436ac5060a9de61af79d0778169dc7432b01939595cb898a9c9b043e7325dccb05c2d3a6b87791713b1228face4ee1b7c9fdf616a88c84b8a5ab9
7
+ data.tar.gz: 8e008c6a8f146e3e705d6ddd789945b10460d759a617a71d5f397f7b4638367ecb555aaff1ccc076ba83df3c157e74784056f504f2525a198d58ee44eb20178b
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.0.0
5
+ - 1.9.3
6
+ script: rspec spec
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+
5
+ group :development, :test do
6
+ activerecord_version = ENV['HAS_DYNAMIC_COLUMNS_ACTIVERECORD_VERSION']
7
+
8
+ if ENV['RAILS_VERSION'] == 'edge' || activerecord_version == "edge"
9
+ gem 'arel', :github => 'rails/arel'
10
+ gem 'activerecord', :github => 'rails/rails'
11
+ elsif activerecord_version && activerecord_version.strip != ""
12
+ gem "activerecord", activerecord_version
13
+ else
14
+ gem 'activerecord', (ENV['RAILS_VERSION'] || ['>= 3.0', '< 5.0'])
15
+ end
16
+
17
+ gem 'coveralls', :require => false
18
+ gem 'rspec', '>= 3'
19
+ gem 'rubocop', '>= 0.25'
20
+ end
21
+
22
+ # Specify your gem's dependencies in active_record_survey.gemspec
23
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,111 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ active_record_survey (0.1.0)
5
+ activerecord (>= 2.0)
6
+ awesome_nested_set (>= 3.0, < 5.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (4.2.4)
12
+ activesupport (= 4.2.4)
13
+ builder (~> 3.1)
14
+ activerecord (4.2.4)
15
+ activemodel (= 4.2.4)
16
+ activesupport (= 4.2.4)
17
+ arel (~> 6.0)
18
+ activesupport (4.2.4)
19
+ i18n (~> 0.7)
20
+ json (~> 1.7, >= 1.7.7)
21
+ minitest (~> 5.1)
22
+ thread_safe (~> 0.3, >= 0.3.4)
23
+ tzinfo (~> 1.1)
24
+ arel (6.0.3)
25
+ ast (2.1.0)
26
+ astrolabe (1.3.1)
27
+ parser (~> 2.2)
28
+ awesome_nested_set (3.0.2)
29
+ activerecord (>= 4.0.0, < 5)
30
+ builder (3.2.2)
31
+ coveralls (0.8.2)
32
+ json (~> 1.8)
33
+ rest-client (>= 1.6.8, < 2)
34
+ simplecov (~> 0.10.0)
35
+ term-ansicolor (~> 1.3)
36
+ thor (~> 0.19.1)
37
+ diff-lcs (1.2.5)
38
+ docile (1.1.5)
39
+ domain_name (0.5.24)
40
+ unf (>= 0.0.5, < 1.0.0)
41
+ factory_girl (4.5.0)
42
+ activesupport (>= 3.0.0)
43
+ http-cookie (1.0.2)
44
+ domain_name (~> 0.5)
45
+ i18n (0.7.0)
46
+ json (1.8.3)
47
+ mime-types (2.6.2)
48
+ minitest (5.8.0)
49
+ netrc (0.10.3)
50
+ parser (2.2.2.6)
51
+ ast (>= 1.1, < 3.0)
52
+ powerpack (0.1.1)
53
+ rainbow (2.0.0)
54
+ rake (10.4.2)
55
+ rest-client (1.8.0)
56
+ http-cookie (>= 1.0.2, < 2.0)
57
+ mime-types (>= 1.16, < 3.0)
58
+ netrc (~> 0.7)
59
+ rspec (3.3.0)
60
+ rspec-core (~> 3.3.0)
61
+ rspec-expectations (~> 3.3.0)
62
+ rspec-mocks (~> 3.3.0)
63
+ rspec-core (3.3.2)
64
+ rspec-support (~> 3.3.0)
65
+ rspec-expectations (3.3.1)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.3.0)
68
+ rspec-mocks (3.3.2)
69
+ diff-lcs (>= 1.2.0, < 2.0)
70
+ rspec-support (~> 3.3.0)
71
+ rspec-support (3.3.0)
72
+ rubocop (0.34.1)
73
+ astrolabe (~> 1.3)
74
+ parser (>= 2.2.2.5, < 3.0)
75
+ powerpack (~> 0.1)
76
+ rainbow (>= 1.99.1, < 3.0)
77
+ ruby-progressbar (~> 1.4)
78
+ ruby-progressbar (1.7.5)
79
+ simplecov (0.10.0)
80
+ docile (~> 1.1.0)
81
+ json (~> 1.8)
82
+ simplecov-html (~> 0.10.0)
83
+ simplecov-html (0.10.0)
84
+ sqlite3 (1.3.10)
85
+ term-ansicolor (1.3.2)
86
+ tins (~> 1.0)
87
+ thor (0.19.1)
88
+ thread_safe (0.3.5)
89
+ tins (1.6.0)
90
+ tzinfo (1.2.2)
91
+ thread_safe (~> 0.1)
92
+ unf (0.1.4)
93
+ unf_ext
94
+ unf_ext (0.0.7.1)
95
+
96
+ PLATFORMS
97
+ ruby
98
+
99
+ DEPENDENCIES
100
+ active_record_survey!
101
+ activerecord (>= 3.0, < 5.0)
102
+ bundler (~> 1.7)
103
+ coveralls
104
+ factory_girl (> 4.0)
105
+ rake
106
+ rspec (>= 3)
107
+ rubocop (>= 0.25)
108
+ sqlite3 (> 0)
109
+
110
+ BUNDLED WITH
111
+ 1.10.6
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Butch Marshall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # ActiveRecordSurvey
2
+
3
+ An attempt at a more versatile data structure for making and taking surveys.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'active_record_survey'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install active_record_survey
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/butchmarshall/active_record_survey.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
38
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_record_survey/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "active_record_survey"
8
+ spec.version = ActiveRecordSurvey::VERSION
9
+ spec.authors = ["Butch Marshall"]
10
+ spec.email = ["butch.a.marshall@gmail.com"]
11
+ spec.summary = "Surveys using activerecord"
12
+ spec.description = "A data structure for adding survey capabilities using activerecord"
13
+ spec.homepage = "https://github.com/butchmarshall/active_record_survey"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activerecord", [">= 2.0"]
22
+ spec.add_dependency "awesome_nested_set", [">= 3.0", "< 5.0"]
23
+ if RUBY_PLATFORM == 'java'
24
+ spec.add_development_dependency "jdbc-sqlite3", "> 0"
25
+ spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "> 0"
26
+ else
27
+ spec.add_development_dependency "sqlite3", "> 0"
28
+ end
29
+ spec.add_development_dependency "factory_girl", "> 4.0"
30
+ spec.add_development_dependency "bundler", "~> 1.7"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "active_record_survey"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ require 'active_record_survey/version'
2
+
3
+ module ActiveRecordSurvey
4
+ module Compatibility
5
+ if ActiveSupport::VERSION::MAJOR >= 4
6
+ require 'active_support/proxy_object'
7
+
8
+ def self.executable_prefix
9
+ 'bin'
10
+ end
11
+
12
+ def self.proxy_object_class
13
+ ActiveSupport::ProxyObject
14
+ end
15
+ else
16
+ require 'active_support/basic_object'
17
+
18
+ def self.executable_prefix
19
+ 'script'
20
+ end
21
+
22
+ def self.proxy_object_class
23
+ ActiveSupport::BasicObject
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveRecordSurvey
2
+ class Instance < ::ActiveRecord::Base
3
+ self.table_name = "active_record_survey_instances"
4
+ belongs_to :survey, :class_name => "ActiveRecordSurvey::Survey", :foreign_key => :active_record_survey_id
5
+ has_many :instance_nodes, :class_name => "ActiveRecordSurvey::InstanceNode", :foreign_key => :active_record_survey_instance_id, autosave: true
6
+
7
+ validates_associated :instance_nodes
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecordSurvey
2
+ class InstanceNode < ::ActiveRecord::Base
3
+ self.table_name = "active_record_survey_instance_nodes"
4
+ belongs_to :instance, :class_name => "ActiveRecordSurvey::Instance", :foreign_key => :active_record_survey_instance_id
5
+ belongs_to :node, :class_name => "ActiveRecordSurvey::Node", :foreign_key => :active_record_survey_node_id
6
+
7
+ validates_presence_of :instance
8
+
9
+ validate do |instance_node|
10
+ # This instance_node has no valid path to the root node
11
+ if !self.node.instance_node_path_to_root?(self)
12
+ instance_node.errors[:base] << "NO_PATH"
13
+ end
14
+
15
+ # Two instance_nodes on the same node for this instance
16
+ if self.instance.instance_nodes.select { |i|
17
+ # Two votes share a parent (this means a question has two answers for this instance)
18
+ (i.node.node_maps.collect { |j| j.parent } & self.node.node_maps.collect { |j| j.parent }).length > 0
19
+ }.length > 1
20
+ instance_node.errors[:base] << "DUPLICATE_PATH"
21
+ end
22
+
23
+ instance_node.errors[:base] << "INVALID" if !self.node.validate_instance_node(self)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveRecordSurvey
2
+ # Boolean answers can have values 0|1
3
+ class Node::Answer::Boolean < Node::Answer
4
+ # Only boolean values
5
+ def validate_instance_node(instance_node)
6
+ !instance_node.value.to_s.match(/^[0|1]$/).nil?
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecordSurvey
2
+ # Can hold a value on a scale (e.g. from 0-10)
3
+ class Node::Answer::Scale < Node::Answer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecordSurvey
2
+ # Text answers are... text answers
3
+ class Node::Answer::Text < Node::Answer
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveRecordSurvey
2
+ class Node::Answer < Node
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveRecordSurvey
2
+ class Node::Question < Node
3
+ end
4
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecordSurvey
2
+ class Node < ::ActiveRecord::Base
3
+ self.table_name = "active_record_survey_nodes"
4
+ has_many :node_maps, :class_name => "ActiveRecordSurvey::NodeMap", :foreign_key => :active_record_survey_node_id
5
+ has_many :node_validations, :class_name => "ActiveRecordSurvey::NodeValidation", :foreign_key => :active_record_survey_node_id
6
+
7
+ # By default all values are accepted
8
+ def validate_instance_node(instance_node)
9
+ true
10
+ end
11
+
12
+ # Whether there is a valid answer path from this node to the root node for the instance
13
+ def instance_node_path_to_root?(instance_node)
14
+ instance_nodes = instance_node.instance.instance_nodes.select { |i| i.node === self }
15
+
16
+ # if ::ActiveRecordSurvey::Node::Answer but no votes, not a valid path
17
+ if self.class.ancestors.include?(::ActiveRecordSurvey::Node::Answer) &&
18
+ (instance_nodes.length === 0)
19
+ return false
20
+ end
21
+
22
+ # Start at each node_map of this node
23
+ # Find the parent node ma
24
+ paths = self.node_maps.collect { |node_map|
25
+ # There is another level to traverse
26
+ if node_map.parent
27
+ node_map.parent.node.instance_node_path_to_root?(instance_node)
28
+ # This is the root node - we made it!
29
+ else
30
+ true
31
+ end
32
+ }
33
+
34
+ # If recursion reports back to have at least one valid path to root
35
+ paths.include?(true)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecordSurvey
2
+ class NodeMap < ::ActiveRecord::Base
3
+ self.table_name = "active_record_survey_node_maps"
4
+ belongs_to :node, :foreign_key => :active_record_survey_node_id
5
+ belongs_to :survey, :class_name => "ActiveRecordSurvey::Survey", :foreign_key => :active_record_survey_id
6
+ acts_as_nested_set :scope => [:active_record_survey_id]
7
+
8
+ after_initialize do |i|
9
+ i.survey.node_maps << self if i.new_record?
10
+ end
11
+
12
+ def as_map(node_maps = nil)
13
+ children = (node_maps.nil?)? self.children : node_maps.select { |i|
14
+ i.parent == self
15
+ }
16
+
17
+ {
18
+ :type => self.node.class.to_s,
19
+ :text => "#{self.node.text}",
20
+ :children => children.collect { |i|
21
+ i.as_map(node_maps)
22
+ }
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveRecordSurvey
2
+ class NodeValidation::MaximumLength < Node
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveRecordSurvey
2
+ class NodeValidation::MaximumValue < Node
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveRecordSurvey
2
+ class NodeValidation::MinimumLength < Node
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveRecordSurvey
2
+ class NodeValidation::MinimumValue < Node
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveRecordSurvey
2
+ # Validations that should be run against a node
3
+ class NodeValidation < ::ActiveRecord::Base
4
+ self.table_name = "active_record_survey_node_validations"
5
+ belongs_to :node, :class_name => "ActiveRecordSurvey::Node", :foreign_key => :active_record_survey_node_id
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module ActiveRecordSurvey
2
+ class Survey < ::ActiveRecord::Base
3
+ self.table_name = "active_record_surveys"
4
+ has_many :node_maps, :class_name => "ActiveRecordSurvey::NodeMap", :foreign_key => :active_record_survey_id
5
+ has_many :nodes, :through => :node_maps
6
+
7
+ def root_node
8
+ self.node_maps.includes(:node).select { |i| i.depth === 0 }.first
9
+ end
10
+
11
+ def as_map
12
+ self.root_node.as_map(self.node_maps.includes(:node))
13
+ end
14
+
15
+ # Build a question with answers for this survey
16
+ def build_question(question, answers = [], parent = nil)
17
+ node_maps = []
18
+ n_question = question.node_maps.build(:node => question, :survey => self)
19
+ node_maps << n_question
20
+
21
+ answers.each { |answer|
22
+ n_answer = answer.node_maps.build(:node => answer, :survey => self)
23
+ n_question.children << n_answer
24
+ node_maps << n_answer
25
+ }
26
+
27
+ # If a parent node is passed, add it
28
+ parent.children << n_question if !parent.nil?
29
+
30
+ node_maps
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveRecordSurvey
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ require "active_support"
2
+ require "active_support/dependencies"
3
+ require "active_record"
4
+
5
+ require "awesome_nested_set"
6
+
7
+ require "active_record_survey/version"
8
+ require "active_record_survey/compatibility"
9
+
10
+ require "active_record_survey/survey"
11
+
12
+ require "active_record_survey/node"
13
+ require "active_record_survey/node/question"
14
+ require "active_record_survey/node/answer"
15
+ require "active_record_survey/node/answer/text"
16
+ require "active_record_survey/node/answer/boolean"
17
+
18
+ require "active_record_survey/node_map"
19
+
20
+ require "active_record_survey/node_validation"
21
+ require "active_record_survey/node_validation/minimum_value"
22
+ require "active_record_survey/node_validation/maximum_value"
23
+ require "active_record_survey/node_validation/maximum_length"
24
+ require "active_record_survey/node_validation/minimum_length"
25
+
26
+ require "active_record_survey/instance"
27
+ require "active_record_survey/instance_node"
28
+
29
+ module ActiveRecordSurvey
30
+ end
@@ -0,0 +1,22 @@
1
+ require "generators/has_dynamic_columns/has_dynamic_columns_generator"
2
+ require "generators/has_dynamic_columns/next_migration_version"
3
+ require "rails/generators/migration"
4
+ require "rails/generators/active_record"
5
+
6
+ # Extend the HasDynamicColumnsGenerator so that it creates an AR migration
7
+ module HasDynamicColumns
8
+ class ActiveRecordGenerator < ::HasDynamicColumnsGenerator
9
+ include Rails::Generators::Migration
10
+ extend NextMigrationVersion
11
+
12
+ source_paths << File.join(File.dirname(__FILE__), "templates")
13
+
14
+ def create_migration_file
15
+ migration_template "migration.rb", "db/migrate/add_has_dynamic_columns.rb"
16
+ end
17
+
18
+ def self.next_migration_number(dirname)
19
+ ::ActiveRecord::Generators::Base.next_migration_number dirname
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ require 'rails/generators/base'
2
+ require 'active_record_survey/compatibility'
3
+
4
+ class ActiveRecordSurveyGenerator < Rails::Generators::Base
5
+ source_paths << File.join(File.dirname(__FILE__), 'templates')
6
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveRecordSurvey
2
+ module NextMigrationVersion
3
+ # while methods have moved around this has been the implementation
4
+ # since ActiveRecord 3.0
5
+ def next_migration_number(dirname)
6
+ next_migration_number = current_migration_number(dirname) + 1
7
+ if ActiveRecord::Base.timestamped_migrations
8
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), format("%.14d", next_migration_number)].max
9
+ else
10
+ format("%.3d", next_migration_number)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,59 @@
1
+ class AddActiveRecordSurvey < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :active_record_surveys do |t|
4
+ t.timestamps null: false
5
+ end
6
+
7
+ create_table :active_record_survey_nodes do |t|
8
+ t.string :type
9
+ t.string :text
10
+
11
+ t.timestamps null: false
12
+ end
13
+
14
+ create_table :active_record_survey_node_validations do |t|
15
+ t.references :active_record_survey_node
16
+ t.string :type
17
+ t.string :value
18
+
19
+ t.timestamps null: false
20
+ end
21
+
22
+ create_table :active_record_survey_node_maps do |t|
23
+ t.references :active_record_survey_node
24
+
25
+ # AwesomeNestedSet fields
26
+ t.integer :parent_id, :null => true, :index => true
27
+ t.integer :lft, :null => false, :index => true
28
+ t.integer :rgt, :null => false, :index => true
29
+ t.integer :depth, :null => false, :default => 0
30
+ t.integer :children_count, :null => false, :default => 0
31
+
32
+ t.references :active_record_survey
33
+
34
+ t.timestamps null: false
35
+ end
36
+
37
+ create_table :active_record_survey_instances do |t|
38
+ t.references :active_record_survey
39
+
40
+ t.timestamps null: false
41
+ end
42
+ create_table :active_record_survey_instance_nodes do |t|
43
+ t.references :active_record_survey_instance
44
+ t.references :active_record_survey_node
45
+ t.string :value
46
+
47
+ t.timestamps null: false
48
+ end
49
+ end
50
+
51
+ def self.down
52
+ drop_table :active_record_surveys
53
+ drop_table :active_record_survey_nodes
54
+ drop_table :active_record_survey_node_validations
55
+ drop_table :active_record_survey_node_maps
56
+ drop_table :active_record_survey_instances
57
+ drop_table :active_record_survey_instance_nodes
58
+ end
59
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecordSurvey::Node::Answer::Boolean, :focus => true do
4
+ describe 'a boolean survey is' do
5
+ before(:all) do
6
+ @survey = ActiveRecordSurvey::Survey.new
7
+
8
+ @q1 = ActiveRecordSurvey::Node::Question.new(:text => "Please select one")
9
+ @q1_a1 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Dog")
10
+ @q1_a2 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Cat")
11
+ @q1_a3 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Mouse")
12
+
13
+ nodes = @survey.build_question(@q1, [@q1_a1])
14
+ nodes = @survey.build_question(@q1_a2, [], nodes[1])
15
+ nodes = @survey.build_question(@q1_a3, [], nodes[0])
16
+
17
+ @survey.save
18
+ end
19
+
20
+ describe 'valid' do
21
+ it 'when 1 is passed' do
22
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
23
+ instance.instance_nodes.build(
24
+ :instance => instance,
25
+ :node => @q1_a1,
26
+ :value => 1
27
+ )
28
+ instance.save
29
+
30
+ expect(instance.valid?).to be(true)
31
+ end
32
+
33
+ it 'when 0 is passed' do
34
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
35
+ instance.instance_nodes.build(
36
+ :instance => instance,
37
+ :node => @q1_a1,
38
+ :value => 0
39
+ )
40
+ instance.save
41
+
42
+ expect(instance.valid?).to be(true)
43
+ end
44
+
45
+ it 'when answered in order' do
46
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
47
+ instance.instance_nodes.build(
48
+ :instance => instance,
49
+ :node => @q1_a1,
50
+ :value => 1
51
+ )
52
+ instance.instance_nodes.build(
53
+ :instance => instance,
54
+ :node => @q1_a2,
55
+ :value => 0
56
+ )
57
+ instance.instance_nodes.build(
58
+ :instance => instance,
59
+ :node => @q1_a3,
60
+ :value => 0
61
+ )
62
+ instance.save
63
+
64
+ expect(instance.valid?).to be(true)
65
+ end
66
+ end
67
+
68
+ describe 'invalid' do
69
+ it 'when non boolean values are passed' do
70
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
71
+ instance.instance_nodes.build(
72
+ :instance => instance,
73
+ :node => @q1_a1,
74
+ :value => 5
75
+ )
76
+ instance.save
77
+
78
+ expect(instance.valid?).to be(false)
79
+ end
80
+
81
+ it 'not answered in order' do
82
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
83
+ instance.instance_nodes.build(
84
+ :instance => instance,
85
+ :node => @q1_a2,
86
+ :value => 1
87
+ )
88
+ instance.save
89
+
90
+ expect(instance.valid?).to be(false)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecordSurvey::Survey do
4
+ describe 'a survey' do
5
+ before(:all) do
6
+ @survey = ActiveRecordSurvey::Survey.new
7
+
8
+ @q1 = ActiveRecordSurvey::Node::Question.new(:text => "Q1")
9
+ @q1_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 A1")
10
+ @q1_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 A2")
11
+ @q1_a3 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 A3")
12
+
13
+ @q2 = ActiveRecordSurvey::Node::Question.new(:text => "Q2")
14
+ @q2_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q2 A1")
15
+ @q2_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q2 A2")
16
+
17
+ @q3 = ActiveRecordSurvey::Node::Question.new(:text => "Q3")
18
+ @q3_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q3 A1")
19
+ @q3_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q3 A2")
20
+
21
+ @q4 = ActiveRecordSurvey::Node::Question.new(:text => "Q4")
22
+ @q4_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q4 A1")
23
+ @q4_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q4 A2")
24
+
25
+ q1_nodes = @survey.build_question(@q1, [@q1_a1, @q1_a2, @q1_a3])
26
+ q2_nodes = @survey.build_question(@q2, [@q2_a1, @q2_a2], q1_nodes[1])
27
+ q4_nodes = @survey.build_question(@q4, [@q4_a1, @q4_a2], q2_nodes[1])
28
+
29
+ q3_nodes = @survey.build_question(@q3, [@q3_a1, @q3_a2], q2_nodes[2])
30
+ q4_nodes = @survey.build_question(@q4, [@q4_a1, @q4_a2], q3_nodes[1])
31
+ q4_nodes = @survey.build_question(@q4, [@q4_a1, @q4_a2], q3_nodes[2])
32
+
33
+ q3_nodes = @survey.build_question(@q3, [@q3_a1, @q3_a2], q1_nodes[2])
34
+ q4_nodes = @survey.build_question(@q4, [@q4_a1, @q4_a2], q3_nodes[1])
35
+ q4_nodes = @survey.build_question(@q4, [@q4_a1, @q4_a2], q3_nodes[2])
36
+
37
+ q4_nodes = @survey.build_question(@q4, [@q4_a1, @q4_a2], q1_nodes[3])
38
+
39
+ @survey.save
40
+ end
41
+
42
+ describe ActiveRecordSurvey::Instance do
43
+ it 'should allow valid instance_nodes to save' do
44
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
45
+ instance.instance_nodes.build(
46
+ :instance => instance,
47
+ :node => @q1_a1
48
+ )
49
+ instance.instance_nodes.build(
50
+ :instance => instance,
51
+ :node => @q2_a2
52
+ )
53
+ instance.instance_nodes.build(
54
+ :instance => instance,
55
+ :node => @q3_a2
56
+ )
57
+ instance.instance_nodes.build(
58
+ :instance => instance,
59
+ :node => @q4_a2
60
+ )
61
+ instance.save
62
+ expect(instance.valid?).to be(true)
63
+ end
64
+
65
+ it 'should prevent invalid instance_nodes from saving' do
66
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
67
+ instance.instance_nodes.build(
68
+ :instance => instance,
69
+ :node => @q1_a1
70
+ )
71
+ instance.instance_nodes.build(
72
+ :instance => instance,
73
+ :node => @q2_a2
74
+ )
75
+ instance.instance_nodes.build(
76
+ :instance => instance,
77
+ :node => @q4_a2
78
+ )
79
+ instance.save
80
+ expect(instance.valid?).to be(false)
81
+ end
82
+
83
+ it 'should allow only allow instance_nodes with one valid path' do
84
+ instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
85
+ instance.instance_nodes.build(
86
+ :instance => instance,
87
+ :node => @q1_a1
88
+ )
89
+ instance.instance_nodes.build(
90
+ :instance => instance,
91
+ :node => @q2_a2
92
+ )
93
+ instance.instance_nodes.build(
94
+ :instance => instance,
95
+ :node => @q3_a2
96
+ )
97
+ instance.instance_nodes.build(
98
+ :instance => instance,
99
+ :node => @q4_a2
100
+ )
101
+ # This vote provides TWO paths
102
+ instance.instance_nodes.build(
103
+ :instance => instance,
104
+ :node => @q1_a3
105
+ )
106
+ instance.save
107
+ expect(instance.valid?).to be(false)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecordSurvey do
4
+ it 'has a version number' do
5
+ expect(ActiveRecordSurvey::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ require 'logger'
2
+ require 'rspec'
3
+ require 'factory_girl'
4
+
5
+ require 'active_record_survey'
6
+
7
+ # Trigger AR to initialize
8
+ ActiveRecord::Base
9
+
10
+ module Rails
11
+ def self.root
12
+ '.'
13
+ end
14
+ end
15
+
16
+ # Add this directory so the ActiveSupport autoloading works
17
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
18
+
19
+ if RUBY_PLATFORM == 'java'
20
+ ActiveRecord::Base.establish_connection :adapter => 'jdbcsqlite3', :database => ':memory:'
21
+ else
22
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
23
+ end
24
+
25
+ #ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base)
26
+ ActiveRecord::Migration.verbose = false
27
+
28
+ require "generators/active_record_survey/templates/migration_0.1.0"
29
+
30
+ ActiveRecord::Schema.define do
31
+ AddActiveRecordSurvey.up
32
+ end
33
+
34
+ RSpec.configure do |config|
35
+ config.include FactoryGirl::Syntax::Methods
36
+ config.after(:each) do
37
+ end
38
+ config.expect_with :rspec do |c|
39
+ c.syntax = :expect
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_record_survey
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Butch Marshall
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: awesome_nested_set
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '5.0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '3.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">"
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">"
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: factory_girl
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">"
66
+ - !ruby/object:Gem::Version
67
+ version: '4.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">"
73
+ - !ruby/object:Gem::Version
74
+ version: '4.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: bundler
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.7'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.7'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '10.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '10.0'
103
+ description: A data structure for adding survey capabilities using activerecord
104
+ email:
105
+ - butch.a.marshall@gmail.com
106
+ executables:
107
+ - console
108
+ - setup
109
+ extensions: []
110
+ extra_rdoc_files: []
111
+ files:
112
+ - ".rspec"
113
+ - ".travis.yml"
114
+ - Gemfile
115
+ - Gemfile.lock
116
+ - LICENSE.txt
117
+ - README.md
118
+ - Rakefile
119
+ - active_record_survey.gemspec
120
+ - bin/console
121
+ - bin/setup
122
+ - lib/active_record_survey.rb
123
+ - lib/active_record_survey/compatibility.rb
124
+ - lib/active_record_survey/instance.rb
125
+ - lib/active_record_survey/instance_node.rb
126
+ - lib/active_record_survey/node.rb
127
+ - lib/active_record_survey/node/answer.rb
128
+ - lib/active_record_survey/node/answer/boolean.rb
129
+ - lib/active_record_survey/node/answer/scale.rb
130
+ - lib/active_record_survey/node/answer/text.rb
131
+ - lib/active_record_survey/node/question.rb
132
+ - lib/active_record_survey/node_map.rb
133
+ - lib/active_record_survey/node_validation.rb
134
+ - lib/active_record_survey/node_validation/maximum_length.rb
135
+ - lib/active_record_survey/node_validation/maximum_value.rb
136
+ - lib/active_record_survey/node_validation/minimum_length.rb
137
+ - lib/active_record_survey/node_validation/minimum_value.rb
138
+ - lib/active_record_survey/survey.rb
139
+ - lib/active_record_survey/version.rb
140
+ - lib/generators/active_record_survey/active_record_generator.rb
141
+ - lib/generators/active_record_survey/active_record_survey_generator.rb
142
+ - lib/generators/active_record_survey/next_migration_version.rb
143
+ - lib/generators/active_record_survey/templates/migration_0.1.0.rb
144
+ - spec/active_record_survey/node/answer/boolean_spec.rb
145
+ - spec/active_record_survey/survey_spec.rb
146
+ - spec/active_record_survey_spec.rb
147
+ - spec/spec_helper.rb
148
+ homepage: https://github.com/butchmarshall/active_record_survey
149
+ licenses:
150
+ - MIT
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.4.8
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: Surveys using activerecord
172
+ test_files:
173
+ - spec/active_record_survey/node/answer/boolean_spec.rb
174
+ - spec/active_record_survey/survey_spec.rb
175
+ - spec/active_record_survey_spec.rb
176
+ - spec/spec_helper.rb