active_record_survey 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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