quirkey-static_model 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,26 @@
1
+ == 0.2.0 2008-10-27
2
+
3
+ * 2 major enhancements
4
+
5
+ * StaticModel generator
6
+ * 'class_attributes' and ability to set default for record attributes
7
+ * Real documentation on the website: http://quirkey.rubyforge.org/static_model
8
+
9
+ == 0.1.5 2008-09-18
10
+
11
+ * 1 minor enhancement
12
+
13
+ * better handling of empty YAML files (ienders)
14
+
15
+ == 0.1.0 2008-08-27
16
+
17
+ * 1 minor enchancement
18
+
19
+ * fixed the dynamic finders to match AR's behavior AKA find_by === find_first_by
20
+
21
+ == 0.1.0 2008-07-09
22
+
23
+ * 1 major enhancement:
24
+ * Initial release
25
+ - Basic functionality
26
+ - Full test suite using shoulda
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Aaron Quint, Quirkey NYC, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,17 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.rdoc
5
+ generators/static_model/USAGE
6
+ generators/static_model/static_model_generator.rb
7
+ generators/static_model/templates/model.rb.erb
8
+ generators/static_model/templates/models.yml.erb
9
+ lib/static_model.rb
10
+ lib/static_model/active_record.rb
11
+ lib/static_model/associations.rb
12
+ lib/static_model/base.rb
13
+ lib/static_model/comparable.rb
14
+ lib/static_model/errors.rb
15
+ lib/static_model/rails.rb
16
+ lib/static_model/scope.rb
17
+ lib/static_model/version.rb
data/README.rdoc ADDED
@@ -0,0 +1,53 @@
1
+ = static_model
2
+
3
+ http://quirkey.rubyforge.org/static_model/
4
+
5
+ == DESCRIPTION:
6
+
7
+ ActiveRecord like functionalities for reading from YAML with a simple class implementation
8
+
9
+ == SYNOPSIS:
10
+
11
+ Use like ActiveRecord::Base, except no database, just a YAML file. The YAML should contain an array of records.
12
+
13
+ For usage and setup check out the project's site:
14
+
15
+ http://quirkey.rubyforge.org/static_model/
16
+
17
+ == REQUIREMENTS:
18
+
19
+ YAML
20
+ activesupport >= 2.1.0
21
+
22
+ == INSTALL:
23
+
24
+ sudo gem install static_model
25
+
26
+ The source is at github:
27
+
28
+ http://github.com/quirkey/static_model/
29
+
30
+ == LICENSE:
31
+
32
+ (The MIT License)
33
+
34
+ Copyright (c) 2008 Aaron Quint, Quirkey NYC, LLC
35
+
36
+ Permission is hereby granted, free of charge, to any person obtaining
37
+ a copy of this software and associated documentation files (the
38
+ 'Software'), to deal in the Software without restriction, including
39
+ without limitation the rights to use, copy, modify, merge, publish,
40
+ distribute, sublicense, and/or sell copies of the Software, and to
41
+ permit persons to whom the Software is furnished to do so, subject to
42
+ the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be
45
+ included in all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
48
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
49
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
50
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
51
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
52
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
53
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ Description:
2
+
3
+ Generates an empty StaticModel class/model
4
+
5
+ Usage:
6
+
7
+ ./script/generate static_model ModelName --data=config/data/ --lib=app/models/
8
+
@@ -0,0 +1,57 @@
1
+ class StaticModelGenerator < RubiGen::Base
2
+
3
+ default_options :author => nil
4
+
5
+ attr_reader :name, :lib_dir, :data_dir
6
+
7
+ def initialize(runtime_args, runtime_options = {})
8
+ super
9
+ usage if args.empty?
10
+ @name = args.shift
11
+ extract_options
12
+ end
13
+
14
+ def manifest
15
+ record do |m|
16
+ # Ensure appropriate folder(s) exists
17
+ m.directory data_dir
18
+ m.directory lib_dir
19
+ m.template 'model.rb.erb', File.join(lib_dir, name.underscore + ".rb")
20
+ m.template 'models.yml.erb', File.join(data_dir, table_name + ".yml")
21
+ end
22
+ end
23
+
24
+ protected
25
+ def banner
26
+ <<-EOS
27
+ Creates a ...
28
+
29
+ USAGE: #{$0} #{spec.name} ModelName
30
+ EOS
31
+ end
32
+
33
+ def class_name
34
+ @name.classify
35
+ end
36
+
37
+ def table_name
38
+ @name.tableize
39
+ end
40
+
41
+ def add_options!(opts)
42
+ # opts.separator ''
43
+ # opts.separator 'Options:'
44
+ # For each option below, place the default
45
+ # at the top of the file next to "default_options"
46
+ # opts.on("-a", "--author=\"Your Name\"", String,
47
+ # "Some comment about this option",
48
+ # "Default: none") { |options[:author]| }
49
+ opts.on("-l", "--lib=your/path/", String, "Put the model.rb in a specific lib dir (default = app/models/)") {|o| options[:lib_dir] = o }
50
+ opts.on("-d", "--data=your/path/", String, "Put the models.yml data file in a specific data dir (default = config/data/)") {|o| options[:data_dir] = o }
51
+ end
52
+
53
+ def extract_options
54
+ @lib_dir = options[:lib_dir] || File.join('app', 'models')
55
+ @data_dir = options[:data_dir] || File.join('config', 'data')
56
+ end
57
+ end
@@ -0,0 +1,4 @@
1
+ class <%= class_name %> < StaticModel::Base
2
+ set_data_file File.join('<%= data_dir %>', '<%= table_name %>.yml')
3
+
4
+ end
@@ -0,0 +1,6 @@
1
+ ---
2
+ records:
3
+ - id: 1
4
+ name: One
5
+ - id: 2
6
+ name: Two
@@ -0,0 +1,11 @@
1
+ module StaticModel
2
+ module ActiveRecord
3
+ # Stubbing out ActiveRecord methods so that associations work
4
+
5
+ def new_record?
6
+ false
7
+ end
8
+
9
+
10
+ end
11
+ end
@@ -0,0 +1,82 @@
1
+ module StaticModel
2
+ module Associations
3
+
4
+ def self.included(klass)
5
+ klass.extend MacroMethods
6
+ end
7
+
8
+ module MacroMethods
9
+
10
+ def associations
11
+ @associations ||= {}
12
+ end
13
+
14
+ def has_many(association_name, options = {})
15
+ self.associations[association_name.to_sym] = HasManyAssociation.new(self, association_name.to_sym, {:foreign_key => "#{self.to_s.foreign_key}"}.merge(options))
16
+ end
17
+
18
+ def belongs_to(association_name, options = {})
19
+ self.associations[association_name.to_sym] = BelongsToAssociation.new(self, association_name.to_sym, {:foreign_key => "#{association_name.to_s.foreign_key}"}.merge(options))
20
+ end
21
+
22
+ end
23
+
24
+
25
+ class Association
26
+ attr_reader :klass, :name, :options, :foreign_key
27
+
28
+ def initialize(klass, name, options = {})
29
+ @klass = klass
30
+ @name = name
31
+ @options = options
32
+ @reflection_klass_name = @options[:class_name] || @name.to_s.classify
33
+ @foreign_key = @options[:foreign_key]
34
+ define_association_methods
35
+ end
36
+
37
+ def reflection_klass
38
+ Object.const_get(@reflection_klass_name)
39
+ rescue
40
+ eval <<-EOT
41
+ class #{@reflection_klass_name}; end;
42
+ #{@reflection_klass_name}
43
+ EOT
44
+ end
45
+
46
+ def define_association_methods
47
+ raise 'Should only use descendants of Association'
48
+ end
49
+ end
50
+
51
+ class HasManyAssociation < Association
52
+
53
+ def define_association_methods
54
+ if reflection_klass.respond_to?(:scoped)
55
+ klass.module_eval <<-EOT
56
+ def #{name}
57
+ #{reflection_klass}.scoped(:conditions => {:#{foreign_key} => id})
58
+ end
59
+ EOT
60
+ else
61
+ klass.module_eval <<-EOT
62
+ def #{name}
63
+ #{reflection_klass}.send(:find_all_by_#{foreign_key}, id)
64
+ end
65
+ EOT
66
+ end
67
+ end
68
+ end
69
+
70
+ class BelongsToAssociation < Association
71
+
72
+ def define_association_methods
73
+ klass.module_eval <<-EOT
74
+ def #{name}
75
+ #{reflection_klass}.send(:find, #{foreign_key})
76
+ end
77
+ EOT
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,169 @@
1
+ module StaticModel
2
+ class Base
3
+ include StaticModel::Associations
4
+ include StaticModel::ActiveRecord
5
+ include StaticModel::Comparable
6
+
7
+ @@load_path = File.join('config', 'data')
8
+
9
+ attr_reader :id
10
+
11
+
12
+ def initialize(attribute_hash = {})
13
+ raise(StaticModel::BadOptions, "Initializing a model is done with a Hash {} given #{attribute_hash.inspect}") unless attribute_hash.is_a?(Hash)
14
+ @id = attribute_hash.delete('id') || attribute_hash.delete(:id) || (self.class.count + 1)
15
+ self.attributes = attribute_hash
16
+ end
17
+
18
+ def to_s
19
+ self.inspect
20
+ end
21
+
22
+ def attributes
23
+ @attributes ||= HashWithIndifferentAccess.new
24
+ end
25
+
26
+ def attributes=(attribute_hash)
27
+ attribute_hash.each {|k,v| set_attribute(k,v) }
28
+ end
29
+
30
+ def attribute_names
31
+ (attributes.keys | self.class.class_attributes.keys).collect {|k| k.to_s }
32
+ end
33
+
34
+ class << self
35
+
36
+ def find(what, *args, &block)
37
+ case what
38
+ when Symbol
39
+ send("find_#{what}")
40
+ when Integer
41
+ find_by_id(what)
42
+ end
43
+ end
44
+
45
+ def find_by_id(id)
46
+ record = records.detect {|r| r.id == id }
47
+ raise(StaticModel::RecordNotFound, "Could not find record with id = #{id}") unless record
48
+ record
49
+ end
50
+
51
+ def [](id)
52
+ find_by_id(id)
53
+ end
54
+
55
+ def find_all
56
+ records
57
+ end
58
+ alias_method :all, :find_all
59
+
60
+ def find_first
61
+ records[0]
62
+ end
63
+ alias_method :first, :find_first
64
+
65
+ def find_all_by(attribute, value)
66
+ records.find_all {|r| r.send(attribute) == value }
67
+ end
68
+
69
+ def find_first_by(attribute, value)
70
+ records.find {|r| r.send(attribute) == value }
71
+ end
72
+ alias_method :find_by, :find_first_by
73
+
74
+ def load(reload = false)
75
+ return if loaded? && !reload
76
+ raise(StaticModel::DataFileNotFound, "You must set a data file to load from") unless File.readable?(data_file)
77
+ data = YAML::load_file(data_file)
78
+ records = []
79
+ if data.is_a?(Hash) && data.has_key?('records')
80
+ records = data.delete('records')
81
+ @class_attributes = HashWithIndifferentAccess.new(data)
82
+ elsif data.is_a?(Array)
83
+ records = data
84
+ end
85
+ @records = records && !records.empty? ? records.dup.collect {|r| new(r) } : []
86
+ @loaded = true
87
+ # rescue
88
+ # raise(StaticModel::BadDataFile, "The data file you specified '#{data_file}' was not in a readable format.")
89
+ end
90
+
91
+ def loaded?
92
+ @loaded ||= false
93
+ end
94
+
95
+ def data_file
96
+ @data_file ||= default_data_file_path
97
+ end
98
+
99
+ def set_data_file(file_path)
100
+ raise(StaticModel::DataFileNotFound, "Could not find data file #{file_path}") unless File.readable?(file_path)
101
+ @data_file = file_path
102
+ # force reload
103
+ @loaded = false
104
+ @records = nil
105
+ end
106
+
107
+ def count
108
+ records.length
109
+ end
110
+
111
+ def class_attributes
112
+ load
113
+ @class_attributes ||= {}
114
+ end
115
+
116
+ def class_attribute(name)
117
+ class_attributes[name]
118
+ end
119
+
120
+ def records
121
+ load
122
+ @records
123
+ end
124
+
125
+ protected
126
+ def default_data_file_path
127
+ File.join(@@load_path, "#{self.to_s.tableize}.yml")
128
+ end
129
+
130
+ private
131
+ def method_missing(meth, *args)
132
+ meth_name = meth.to_s
133
+ if meth_name =~ /^find_(all_by|first_by|by)_(.+)/
134
+ attribute_name = meth_name.gsub(/^find_(all_by|first_by|by)_/, '')
135
+ finder = meth_name.gsub(/_#{attribute_name}/, '')
136
+ return self.send(finder, attribute_name, *args)
137
+ elsif class_attributes.has_key? meth_name
138
+ return class_attributes[meth_name]
139
+ end
140
+ super
141
+ end
142
+ end
143
+
144
+ protected
145
+ def set_attribute(name, value)
146
+ self.attributes[name] = value
147
+ end
148
+
149
+ def get_attribute(name)
150
+ attributes.has_key?(name) ? attributes[name] : self.class.class_attribute(name)
151
+ end
152
+
153
+ private
154
+ def method_missing(meth, *args)
155
+ attribute_name = meth.to_s.gsub(/=$/,'')
156
+ if attribute_names.include?(attribute_name)
157
+ if meth.to_s =~ /=$/
158
+ # set
159
+ return set_attribute(attribute_name, args[0])
160
+ else
161
+ # get
162
+ return get_attribute(attribute_name)
163
+ end
164
+ end
165
+ super
166
+ end
167
+
168
+ end
169
+ end
@@ -0,0 +1,14 @@
1
+ module StaticModel
2
+ module Comparable
3
+ include ::Comparable
4
+
5
+ def <=>(other)
6
+ if self.class == other.class
7
+ self.id <=> other.id
8
+ else
9
+ -1
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module StaticModel
2
+ class Error < RuntimeError; end;
3
+ class RecordNotFound < Error; end;
4
+ class DataFileNotFound < Error; end;
5
+ class BadDataFile < Error; end;
6
+ class BadOptions < Error; end;
7
+ end
@@ -0,0 +1,7 @@
1
+ if defined?(Rails)
2
+ module StaticModel
3
+ class Base
4
+ @@load_path = File.join(Rails.root,'config', 'data')
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module StaticModel
2
+ module Scope
3
+
4
+ private
5
+ class << self
6
+ def with_scope
7
+
8
+ end
9
+
10
+ def scoped_conditions
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module StaticModel #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'yaml'
5
+
6
+ require 'active_support/inflector'
7
+ require 'active_support/core_ext/hash'
8
+ require 'active_support/core_ext/string'
9
+
10
+
11
+ require 'static_model/errors'
12
+ require 'static_model/associations'
13
+ require 'static_model/active_record'
14
+ require 'static_model/comparable'
15
+ require 'static_model/base'
16
+ require 'static_model/rails'
@@ -0,0 +1,29 @@
1
+ begin
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+ rescue LoadError
4
+ require 'test/unit'
5
+ end
6
+ require 'fileutils'
7
+
8
+ # Must set before requiring generator libs.
9
+ TMP_ROOT = File.dirname(__FILE__) + "/tmp" unless defined?(TMP_ROOT)
10
+ PROJECT_NAME = "myproject" unless defined?(PROJECT_NAME)
11
+ app_root = File.join(TMP_ROOT, PROJECT_NAME)
12
+ if defined?(APP_ROOT)
13
+ APP_ROOT.replace(app_root)
14
+ else
15
+ APP_ROOT = app_root
16
+ end
17
+ if defined?(RAILS_ROOT)
18
+ RAILS_ROOT.replace(app_root)
19
+ else
20
+ RAILS_ROOT = app_root
21
+ end
22
+
23
+ begin
24
+ require 'rubigen'
25
+ rescue LoadError
26
+ require 'rubygems'
27
+ require 'rubigen'
28
+ end
29
+ require 'rubigen/helpers/generator_test_helper'
@@ -0,0 +1,80 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+
6
+ require File.dirname(__FILE__) + '/../lib/static_model'
7
+
8
+ class Book < StaticModel::Base
9
+ set_data_file File.join(File.dirname(__FILE__), 'data', 'books.yml')
10
+ end
11
+
12
+ unless defined?(ActiveRecord::Base)
13
+ module ActiveRecord
14
+ class Base
15
+ def self.scoped(*args)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class Article < ActiveRecord::Base
22
+ end
23
+
24
+ class Publisher < StaticModel::Base
25
+ set_data_file File.join(File.dirname(__FILE__), 'data', 'publishers.yml')
26
+
27
+ has_many :authors
28
+ end
29
+
30
+ class Author < StaticModel::Base
31
+ set_data_file File.join(File.dirname(__FILE__), 'data','authors.yml')
32
+ has_many :books
33
+ has_many :articles
34
+ belongs_to :publisher
35
+ end
36
+
37
+ class Page < StaticModel::Base
38
+ set_data_file File.join(File.dirname(__FILE__), 'data', 'pages.yml')
39
+
40
+ end
41
+
42
+
43
+
44
+
45
+ class Test::Unit::TestCase
46
+
47
+ def assert_all(collection)
48
+ collection.each do |one|
49
+ assert yield(one), "#{one} is not true"
50
+ end
51
+ end
52
+
53
+
54
+ def assert_any(collection, &block)
55
+ has = collection.any? do |one|
56
+ yield(one)
57
+ end
58
+ assert has
59
+ end
60
+
61
+ def assert_ordered(array_of_ordered_items, message = nil, &block)
62
+ raise "Parameter must be an Array" unless array_of_ordered_items.is_a?(Array)
63
+ message ||= "Items were not in the correct order"
64
+ i = 0
65
+ # puts array_of_ordered_items.length
66
+ while i < (array_of_ordered_items.length - 1)
67
+ # puts "j"
68
+ a, b = array_of_ordered_items[i], array_of_ordered_items[i+1]
69
+ comparison = yield(a,b)
70
+ # raise "#{comparison}"
71
+ assert(comparison, message + " - #{a}, #{b}")
72
+ i += 1
73
+ end
74
+ end
75
+
76
+ def assert_set_of(klass, set)
77
+ assert set.respond_to?(:each), "#{set} is not a set (does not include Enumerable)"
78
+ assert_all(set) {|a| a.is_a?(klass) }
79
+ end
80
+ end
@@ -0,0 +1,350 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestStaticModel < Test::Unit::TestCase
4
+
5
+ context "StaticModel" do
6
+ context "A class that inherits from Base" do
7
+ context "an instance" do
8
+
9
+ setup do
10
+ @book = Book.new(book_params)
11
+ end
12
+
13
+ context "initialization" do
14
+
15
+ should "set attributes with a hash" do
16
+ assert @book.id
17
+ end
18
+
19
+ should "raise error if passed anything but hash" do
20
+ assert_raise(StaticModel::BadOptions) do
21
+ Book.new("im bad")
22
+ end
23
+ end
24
+ end
25
+
26
+ context "attributes" do
27
+ should "get attributes by methods" do
28
+ assert_equal book_params[:title], @book.title
29
+ assert_equal book_params[:genre], @book.genre
30
+ end
31
+
32
+ should "set attributes by methods" do
33
+ @book.title = 'New Title'
34
+ assert_equal 'New Title', @book.title
35
+ end
36
+ end
37
+
38
+ context "to_s" do
39
+ should "inspect" do
40
+ assert_equal @book.inspect, @book.to_s
41
+ end
42
+ end
43
+
44
+ context "comparing" do
45
+
46
+ should "be equal to an instance of the same class with same id" do
47
+ assert_equal @book, Book.new(book_params)
48
+ end
49
+
50
+ should "not be equal to an instance with the same class with different ids" do
51
+ assert_not_equal Book[1], @book
52
+ end
53
+
54
+ should "not be equal to an instance with different classes and the same ids" do
55
+ assert_not_equal Book[1], Author[1]
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ context "on the class" do
62
+ context "set data file" do
63
+ context "After the class is defined" do
64
+
65
+ setup do
66
+ @book = Book.find(1)
67
+ @author = Author.find(1)
68
+ @original_data_file = Book.data_file
69
+ @data_file = File.join(File.dirname(__FILE__), 'data', 'authors.yml')
70
+ Book.set_data_file @data_file
71
+ @new_book = Book.find(1)
72
+ end
73
+
74
+ should "set the @data_file" do
75
+ assert_equal @data_file, Book.data_file
76
+ end
77
+
78
+ should "reload with next find" do
79
+ assert @author.attributes, @new_book.attributes
80
+ end
81
+
82
+ teardown do
83
+ Book.set_data_file @original_data_file
84
+ end
85
+ end
86
+ end
87
+
88
+ context "find" do
89
+
90
+ context "with an integer" do
91
+ setup do
92
+ @book = Book.find(1)
93
+ end
94
+
95
+ should "set loaded?" do
96
+ assert Book.loaded?
97
+ end
98
+
99
+ should "load by id" do
100
+ assert_equal 1, @book.id
101
+ end
102
+
103
+ should "return instance of klass" do
104
+ assert @book.is_a?(Book)
105
+ end
106
+
107
+ should "raise error if cant find record with id" do
108
+ assert_raise(StaticModel::RecordNotFound) do
109
+ @book = Book.find(1000)
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ context "[]" do
116
+ should "be an alias for find by id" do
117
+ assert_equal Book.find(1), Book[1]
118
+ end
119
+ end
120
+
121
+ context "find(:all)" do
122
+ should "be an alias for find_all" do
123
+ assert_equal Book.find_all, Book.find(:all)
124
+ end
125
+ end
126
+ context "find(:first)" do
127
+ should "be an alias for find_first" do
128
+ assert_equal Book.find_first, Book.find(:first)
129
+ end
130
+ end
131
+
132
+ context "all" do
133
+ should "be an alias for find_all" do
134
+ assert_equal Book.all, Book.find(:all)
135
+ end
136
+ end
137
+ context "first" do
138
+ should "be an alias for find_first" do
139
+ assert_equal Book.first, Book.find(:first)
140
+ end
141
+ end
142
+
143
+ context "find_first" do
144
+ setup do
145
+ @book = Book.find_first
146
+ end
147
+
148
+ should "return the first instance from all records" do
149
+ assert_equal Book.find_all.first, @book
150
+ end
151
+
152
+ should "return instance of klass" do
153
+ assert @book.is_a?(Book)
154
+ end
155
+ end
156
+
157
+ context "find_all" do
158
+ setup do
159
+ @books = Book.find_all
160
+ end
161
+
162
+ should "return an array" do
163
+ assert @books.is_a?(Array)
164
+ end
165
+
166
+ should "return all records" do
167
+ assert_equal 4, @books.length
168
+ end
169
+
170
+ should "return set of klass instances" do
171
+ @books.each do |book|
172
+ assert book.is_a?(Book)
173
+ end
174
+ end
175
+
176
+ context "with an empty data file" do
177
+ setup do
178
+ @original_data_file = Book.data_file
179
+ @data_file = File.join(File.dirname(__FILE__), 'data', 'empty.yml')
180
+ Book.set_data_file @data_file
181
+ end
182
+
183
+ should "return an empty array" do
184
+ assert_equal [], Book.find_all
185
+ end
186
+
187
+ teardown do
188
+ Book.set_data_file @original_data_file
189
+ end
190
+
191
+ end
192
+ end
193
+
194
+ context "find_first_by" do
195
+ setup do
196
+ @author = 'Michael Pollan'
197
+ @book = Book.find_first_by(:author,@author)
198
+ end
199
+
200
+ should "return an instance of klass" do
201
+ assert @book.is_a?(Book)
202
+ end
203
+
204
+ should "return record matching search" do
205
+ assert @author, @book.author
206
+ end
207
+
208
+ context "when there is no match" do
209
+ should "return nil" do
210
+ assert_nil Book.find_first_by(:author,'Aaron Quint')
211
+ end
212
+ end
213
+ end
214
+
215
+ context "find_by" do
216
+ setup do
217
+ @author = 'Michael Pollan'
218
+ @book = Book.find_by(:author,@author)
219
+ end
220
+
221
+ should "return an instance of klass" do
222
+ assert @book.is_a?(Book)
223
+ end
224
+
225
+ should "return record matching search" do
226
+ assert @author, @book.author
227
+ end
228
+
229
+ context "when there is no match" do
230
+ should "return nil" do
231
+ assert_nil Book.find_by(:author,'Aaron Quint')
232
+ end
233
+ end
234
+
235
+ end
236
+
237
+ context "find_all_by" do
238
+ setup do
239
+ @author = 'Michael Pollan'
240
+ @books = Book.find_all_by(:author,@author)
241
+ end
242
+
243
+ should "return an array" do
244
+ assert @books.is_a?(Array)
245
+ end
246
+
247
+ should "return all records that match search" do
248
+ @books.each {|b| assert_equal @author, b.author}
249
+ end
250
+
251
+ should "return set of klass instances" do
252
+ @books.each {|b| assert b.is_a?(Book) }
253
+ end
254
+
255
+ context "when there is no match" do
256
+ should "return an empty array" do
257
+ assert_equal [], Book.find_all_by(:author,'Aaron Quint')
258
+ end
259
+ end
260
+ context "when there is only one match" do
261
+ should "return an array" do
262
+ assert_equal [Book.find(3)], Book.find_all_by(:author,'Piers Anthony')
263
+ end
264
+ end
265
+ end
266
+
267
+ context "dynamic finders" do
268
+ setup do
269
+ @book = Book.first
270
+ end
271
+
272
+ context "find_by_*attribute*" do
273
+ should "be equivalent to find_first_by(attribute,)" do
274
+ assert_equal @book, Book.find_first_by(:genre, 'Non-Fiction')
275
+ assert_equal Book.find_first_by(:genre, 'Non-Fiction'), Book.find_by_genre('Non-Fiction')
276
+ end
277
+ end
278
+
279
+ context "find_first_by_*attribute*" do
280
+ should "be equivalent to find_first_by(attribute,)" do
281
+ assert_equal @book, Book.find_first_by(:genre, 'Non-Fiction')
282
+ assert_equal Book.find_first_by(:genre, 'Non-Fiction'), Book.find_first_by_genre('Non-Fiction')
283
+ end
284
+ end
285
+
286
+ context "find_all_by_*attribute*" do
287
+ should "be equivalent to find_all_by(attribute,)" do
288
+ assert_equal Book.find_all_by(:genre, 'Non-Fiction'), Book.find_all_by_genre('Non-Fiction')
289
+ end
290
+ end
291
+ end
292
+
293
+ context "count" do
294
+ should "return the count of all records" do
295
+ assert_equal Book.all.length, Book.count
296
+ end
297
+ end
298
+
299
+ context "with a class with yaml class vars" do
300
+ setup do
301
+ @pages = Page.all
302
+ end
303
+
304
+ should "load :records into @records" do
305
+ assert_set_of Page, @pages
306
+ assert Page.loaded?
307
+ end
308
+
309
+ should "give access to top level attributes as class methods" do
310
+ assert_equal 'http://www.quirkey.com', Page.url
311
+ assert_equal 'The Best Ever', Page.title
312
+ end
313
+
314
+ should "return a hash for class attribute" do
315
+ assert Page.settings.is_a?(Hash)
316
+ assert_equal 'test', Page.settings['username']
317
+ end
318
+
319
+ should "have class attributes appear as record accessor defaults if none exist" do
320
+ assert_equal 'http://www.quirkey.com', Page[1].url
321
+ end
322
+
323
+ should "not overwrite record specific methods" do
324
+ assert_equal 'http://github.com', Page[2].url
325
+ end
326
+
327
+ context "class_attributes" do
328
+ setup do
329
+ @attributes = Page.class_attributes
330
+ end
331
+
332
+ should "return a hash of all top level settings" do
333
+ assert @attributes.is_a?(Hash)
334
+ assert_equal 3, @attributes.length
335
+ assert_equal 'http://www.quirkey.com', @attributes['url']
336
+ end
337
+ end
338
+ end
339
+
340
+ end
341
+ end
342
+ end
343
+
344
+ protected
345
+ def book_params
346
+ {:id => 15, :title => 'Lord of the Rings', :author => 'J.R. Tolkien', :genre => 'Fantasy'}
347
+ end
348
+
349
+
350
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestStaticModelAssociations < Test::Unit::TestCase
4
+
5
+ context "a static model class" do
6
+
7
+ context "with a has many association" do
8
+ context "to another static model" do
9
+ # Author has many books
10
+ setup do
11
+ @author = Author.find(1)
12
+ end
13
+
14
+ should "respond to association name" do
15
+ assert @author.books
16
+ end
17
+
18
+ should "return an array of association instances if association is a StaticModel" do
19
+ assert_set_of Book, @author.books
20
+ end
21
+
22
+ should "find books by foreign_key" do
23
+ assert_equal Book.find_all_by_author_id(@author.id), @author.books
24
+ end
25
+
26
+ should "add association to associations" do
27
+ assert Author.associations.has_key?(:books)
28
+ end
29
+
30
+ should "have HasManyAssociation in associations" do
31
+ assert Author.associations[:books].is_a?(StaticModel::Associations::HasManyAssociation)
32
+ end
33
+ end
34
+
35
+ context "to an active record model" do
36
+ setup do
37
+ @author = Author.find(1)
38
+ Article.expects(:scoped).returns([Article.new, Article.new])
39
+ end
40
+
41
+ should "respond to association name" do
42
+ assert @author.articles
43
+ end
44
+
45
+ should "return an array of association instances if association is a StaticModel" do
46
+ assert_set_of Article, @author.articles
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+
53
+ context "with a belongs to association" do
54
+ context "to another static model" do
55
+ # Author belongs to publisher
56
+ setup do
57
+ @author = Author.find(1)
58
+ end
59
+
60
+ should "respond to association name" do
61
+ assert @author.publisher
62
+ end
63
+
64
+ should "return a single instance" do
65
+ assert @author.publisher.is_a?(Publisher)
66
+ end
67
+
68
+ should "find publisher by foreign key" do
69
+ assert_equal Publisher.find(1), @author.publisher
70
+ end
71
+
72
+ should "add association to associations" do
73
+ assert Author.associations.has_key?(:publisher)
74
+ end
75
+
76
+ should "have BelongsTo association in associations" do
77
+ assert Author.associations[:publisher].is_a?(StaticModel::Associations::BelongsToAssociation)
78
+ end
79
+
80
+ context "when foreign key is nil" do
81
+ setup do
82
+ @author = Author.find(2)
83
+ end
84
+
85
+ should "respond to association name and return nil" do
86
+ assert_nil @author.publisher
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,48 @@
1
+ require File.join(File.dirname(__FILE__), "test_generator_helper.rb")
2
+
3
+
4
+ class TestStaticModelGenerator < Test::Unit::TestCase
5
+ include RubiGen::GeneratorTestHelper
6
+
7
+ def setup
8
+ bare_setup
9
+ end
10
+
11
+ def teardown
12
+ bare_teardown
13
+ end
14
+
15
+ # Some generator-related assertions:
16
+ # assert_generated_file(name, &block) # block passed the file contents
17
+ # assert_directory_exists(name)
18
+ # assert_generated_class(name, &block)
19
+ # assert_generated_module(name, &block)
20
+ # assert_generated_test_for(name, &block)
21
+ # The assert_generated_(class|module|test_for) &block is passed the body of the class/module within the file
22
+ # assert_has_method(body, *methods) # check that the body has a list of methods (methods with parentheses not supported yet)
23
+ #
24
+ # Other helper methods are:
25
+ # app_root_files - put this in teardown to show files generated by the test method (e.g. p app_root_files)
26
+ # bare_setup - place this in setup method to create the APP_ROOT folder for each test
27
+ # bare_teardown - place this in teardown method to destroy the TMP_ROOT or APP_ROOT folder after each test
28
+
29
+ def test_generator_with_name_as_a_class_name
30
+ name = "StaticPage"
31
+ run_generator('static_model', [name], sources)
32
+ assert_directory_exists 'config/data'
33
+ assert_generated_file 'config/data/static_pages.yml'
34
+ assert_generated_file 'app/models/static_page.rb' do |content|
35
+ assert_match(/class StaticPage/, content)
36
+ end
37
+ end
38
+
39
+ private
40
+ def sources
41
+ [RubiGen::PathSource.new(:test, File.join(File.dirname(__FILE__),"..", generator_path))
42
+ ]
43
+ end
44
+
45
+ def generator_path
46
+ "generators"
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestStaticModelScope < Test::Unit::TestCase
4
+
5
+ # context "a static model class" do
6
+ # context "A class with scope" do
7
+ #
8
+ # end
9
+ # end
10
+
11
+ def test_truth
12
+ assert true
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quirkey-static_model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Quint
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-28 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.1.0
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: hoe
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.8.0
32
+ version:
33
+ description: ActiveRecord like functionalities for reading from YAML with a simple class implementation
34
+ email:
35
+ - aaron@quirkey.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - History.txt
42
+ - License.txt
43
+ - Manifest.txt
44
+ - README.rdoc
45
+ files:
46
+ - History.txt
47
+ - License.txt
48
+ - Manifest.txt
49
+ - README.rdoc
50
+ - generators/static_model/USAGE
51
+ - generators/static_model/static_model_generator.rb
52
+ - generators/static_model/templates/model.rb.erb
53
+ - generators/static_model/templates/models.yml.erb
54
+ - lib/static_model.rb
55
+ - lib/static_model/active_record.rb
56
+ - lib/static_model/associations.rb
57
+ - lib/static_model/base.rb
58
+ - lib/static_model/comparable.rb
59
+ - lib/static_model/errors.rb
60
+ - lib/static_model/rails.rb
61
+ - lib/static_model/scope.rb
62
+ - lib/static_model/version.rb
63
+ - test/test_generator_helper.rb
64
+ - test/test_helper.rb
65
+ - test/test_static_model.rb
66
+ - test/test_static_model_associations.rb
67
+ - test/test_static_model_generator.rb
68
+ - test/test_static_model_scope.rb
69
+ has_rdoc: true
70
+ homepage: http://quirkey.rubyforge.org
71
+ post_install_message: ""
72
+ rdoc_options:
73
+ - --main
74
+ - README.rdoc
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project: quirkey
92
+ rubygems_version: 1.2.0
93
+ signing_key:
94
+ specification_version: 2
95
+ summary: ActiveRecord like functionalities for reading from YAML with a simple class implementation
96
+ test_files:
97
+ - test/test_generator_helper.rb
98
+ - test/test_helper.rb
99
+ - test/test_static_model.rb
100
+ - test/test_static_model_associations.rb
101
+ - test/test_static_model_generator.rb
102
+ - test/test_static_model_scope.rb