hickey 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +1 -0
- data/LICENSE.TXT +203 -0
- data/LICENSE.txt +203 -0
- data/README.rdoc +106 -0
- data/Rakefile +129 -0
- data/TODO +53 -0
- data/hickey.gemspec +27 -0
- data/lib/hickey.rb +24 -0
- data/lib/hickey/acceptor.rb +25 -0
- data/lib/hickey/domain_detector.rb +17 -0
- data/lib/hickey/domain_detector/actions.rb +38 -0
- data/lib/hickey/domain_detector/associations.rb +80 -0
- data/lib/hickey/domain_detector/base.rb +55 -0
- data/lib/hickey/domain_detector/configurable.rb +38 -0
- data/lib/hickey/domain_detector/scopes.rb +30 -0
- data/test/belongs_to_association_test.rb +17 -0
- data/test/database_config.rb +20 -0
- data/test/enable_callbacks_test.rb +144 -0
- data/test/find_or_create_actions_test.rb +62 -0
- data/test/has_and_belongs_to_many_association_test.rb +10 -0
- data/test/has_many_association_test.rb +71 -0
- data/test/has_one_association_test.rb +53 -0
- data/test/model_association_test.rb +21 -0
- data/test/models/address.rb +5 -0
- data/test/models/author.rb +10 -0
- data/test/models/country.rb +4 -0
- data/test/models/project.rb +9 -0
- data/test/models/projects_member.rb +5 -0
- data/test/models/property_definition.rb +21 -0
- data/test/models/simple.rb +26 -0
- data/test/models/tag.rb +24 -0
- data/test/models/topic.rb +19 -0
- data/test/models/user.rb +9 -0
- data/test/models/writer.rb +9 -0
- data/test/schema.rb +88 -0
- data/test/single_model_test.rb +130 -0
- data/test/test_helper.rb +75 -0
- data/test/with_scope_test.rb +66 -0
- metadata +98 -0
data/Rakefile
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# Rakefile for Hickey -*- ruby -*-
|
2
|
+
|
3
|
+
# Copyright 2007 by Li Xiao (iam@li-xiao.com)
|
4
|
+
# All rights reserved.
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
|
7
|
+
require 'hickey'
|
8
|
+
|
9
|
+
require 'rake/clean'
|
10
|
+
require 'rake/testtask'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'rubygems'
|
15
|
+
require 'rake/gempackagetask'
|
16
|
+
rescue Exception
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# The default task is run if rake is given no explicit arguments.
|
21
|
+
|
22
|
+
desc "Default Task"
|
23
|
+
task :default => :test_all
|
24
|
+
|
25
|
+
# Common Abbreviations ...
|
26
|
+
|
27
|
+
task :test_all => [:test_units]
|
28
|
+
task :test => :test_units
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test_units) do |t|
|
31
|
+
t.test_files = FileList['test/*test.rb']
|
32
|
+
t.warning = false
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a task to build the RDOC documentation tree.
|
37
|
+
|
38
|
+
rd = Rake::RDocTask.new("rdoc") { |rdoc|
|
39
|
+
rdoc.rdoc_dir = 'html'
|
40
|
+
rdoc.template = 'html'
|
41
|
+
rdoc.title = "Hickey"
|
42
|
+
rdoc.options << '--line-numbers' << '--inline-source' <<
|
43
|
+
'--main' << 'README.rdoc' <<
|
44
|
+
'--title' << 'Hickey'
|
45
|
+
rdoc.rdoc_files.include('README.rdoc', 'LICENSE.txt', 'TODO', 'CHANGES')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
|
47
|
+
}
|
48
|
+
|
49
|
+
if ! defined?(Gem)
|
50
|
+
puts "Package Target requires RubyGEMs"
|
51
|
+
else
|
52
|
+
gem_content = <<-GEM
|
53
|
+
Gem::Specification.new do |spec|
|
54
|
+
spec.name = 'hickey'
|
55
|
+
spec.version = "0.0.2"
|
56
|
+
spec.summary = "Hickey provides a simple way of preparing test data inside test for Rails project."
|
57
|
+
|
58
|
+
#### Dependencies and requirements.
|
59
|
+
spec.files = #{(Dir.glob("lib/**/*.rb") + ["CHANGES", "hickey.gemspec", "lib", "LICENSE.TXT", "Rakefile", "README.rdoc", "TODO"]).inspect}
|
60
|
+
|
61
|
+
spec.test_files = #{Dir.glob("test/**/*.rb").inspect}
|
62
|
+
|
63
|
+
#### Load-time details: library and application (you will need one or both).
|
64
|
+
|
65
|
+
spec.require_path = 'lib' # Use these for libraries.
|
66
|
+
|
67
|
+
#### Documentation and testing.
|
68
|
+
|
69
|
+
spec.has_rdoc = true
|
70
|
+
spec.extra_rdoc_files = #{rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a.inspect}
|
71
|
+
spec.rdoc_options = #{rd.options.inspect}
|
72
|
+
|
73
|
+
#### Author and project details.
|
74
|
+
|
75
|
+
spec.author = "Li Xiao"
|
76
|
+
spec.email = "iam@li-xiao.com"
|
77
|
+
spec.homepage = "http://github.com/xli/hickey/tree/master"
|
78
|
+
spec.rubyforge_project = "hickey"
|
79
|
+
end
|
80
|
+
GEM
|
81
|
+
File.open(File.dirname(__FILE__) + '/hickey.gemspec', 'w') do |f|
|
82
|
+
f.write(gem_content)
|
83
|
+
end
|
84
|
+
|
85
|
+
#build gem package same steps with github
|
86
|
+
File.open(File.dirname(__FILE__) + '/hickey.gemspec') do |f|
|
87
|
+
data = f.read
|
88
|
+
spec = nil
|
89
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
90
|
+
package_task = Rake::GemPackageTask.new(spec) do |pkg|
|
91
|
+
#pkg.need_zip = true
|
92
|
+
#pkg.need_tar = true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Misc tasks =========================================================
|
98
|
+
|
99
|
+
def count_lines(filename)
|
100
|
+
lines = 0
|
101
|
+
codelines = 0
|
102
|
+
open(filename) { |f|
|
103
|
+
f.each do |line|
|
104
|
+
lines += 1
|
105
|
+
next if line =~ /^\s*$/
|
106
|
+
next if line =~ /^\s*#/
|
107
|
+
codelines += 1
|
108
|
+
end
|
109
|
+
}
|
110
|
+
[lines, codelines]
|
111
|
+
end
|
112
|
+
|
113
|
+
def show_line(msg, lines, loc)
|
114
|
+
printf "%6s %6s %s\n", lines.to_s, loc.to_s, msg
|
115
|
+
end
|
116
|
+
|
117
|
+
desc "Count lines in the main MM file"
|
118
|
+
task :lines do
|
119
|
+
total_lines = 0
|
120
|
+
total_code = 0
|
121
|
+
show_line("File Name", "LINES", "LOC")
|
122
|
+
FileList['lib/**/*.rb', 'lib/**/*.rake'].each do |fn|
|
123
|
+
lines, codelines = count_lines(fn)
|
124
|
+
show_line(fn, lines, codelines)
|
125
|
+
total_lines += lines
|
126
|
+
total_code += codelines
|
127
|
+
end
|
128
|
+
show_line("TOTAL", total_lines, total_code)
|
129
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
= Hickey Project -- To Do List
|
2
|
+
|
3
|
+
Send suggestions for this list to iam@li-xiao.com
|
4
|
+
|
5
|
+
=== To Do
|
6
|
+
* auto create model associations by each_card_type:
|
7
|
+
property_definitions_card_types => [:each_card_type => {}]
|
8
|
+
* specify item type by key name inside array list example: property_definitions => [{:enum_prop_def => {:name => 'xx'}}]
|
9
|
+
* with scope
|
10
|
+
* scope with user defined foreign_key id inside of model
|
11
|
+
* Single table inheritance
|
12
|
+
* has one
|
13
|
+
* belongs_to
|
14
|
+
* has_and_belongs_to_many
|
15
|
+
* log of creating domain progress for problem detecting
|
16
|
+
* auto_generate_position_for_model_acts_as_list
|
17
|
+
* prepare multi-models domain
|
18
|
+
* :source, :source_type options in has_many :through
|
19
|
+
* enable validations
|
20
|
+
* composed_of
|
21
|
+
* prepare with model validation
|
22
|
+
* should be in transaction?
|
23
|
+
* fast prepare by sql? really need it?
|
24
|
+
* enable callbacks
|
25
|
+
* enable specific method of callbacks?
|
26
|
+
=== Done
|
27
|
+
* prepare model data should not trigger any callback
|
28
|
+
* prepare model with attributes
|
29
|
+
* prepare multi-models domain
|
30
|
+
* prepare has_many relationship models
|
31
|
+
* prepare belongs_to relationship models
|
32
|
+
* prepare belongs_and_has_many relationship models
|
33
|
+
* specify class by :class_name
|
34
|
+
* has_one
|
35
|
+
* has_and_belongs_to_many
|
36
|
+
* has_many
|
37
|
+
* belongs_to
|
38
|
+
* :through in has_one
|
39
|
+
* polymorphic
|
40
|
+
* foreign_key
|
41
|
+
* Single table inheritance
|
42
|
+
* has many
|
43
|
+
* enable callbacks
|
44
|
+
* enable all callbacks
|
45
|
+
* enable specific callbacks
|
46
|
+
* actions: find or create
|
47
|
+
* find
|
48
|
+
* create as default
|
49
|
+
* find_or_create
|
50
|
+
* with scope
|
51
|
+
* simple scope with default model foreign_key id inside of model
|
52
|
+
|
53
|
+
(moved DONE list to CHANGES file)
|
data/hickey.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'hickey'
|
3
|
+
spec.version = "0.0.2"
|
4
|
+
spec.summary = "Hickey provides a simple way of preparing test data inside test for Rails project."
|
5
|
+
|
6
|
+
#### Dependencies and requirements.
|
7
|
+
spec.files = ["lib/hickey/acceptor.rb", "lib/hickey/domain_detector/actions.rb", "lib/hickey/domain_detector/associations.rb", "lib/hickey/domain_detector/base.rb", "lib/hickey/domain_detector/configurable.rb", "lib/hickey/domain_detector/scopes.rb", "lib/hickey/domain_detector.rb", "lib/hickey.rb", "CHANGES", "hickey.gemspec", "lib", "LICENSE.TXT", "Rakefile", "README.rdoc", "TODO"]
|
8
|
+
|
9
|
+
spec.test_files = ["test/belongs_to_association_test.rb", "test/database_config.rb", "test/enable_callbacks_test.rb", "test/find_or_create_actions_test.rb", "test/has_and_belongs_to_many_association_test.rb", "test/has_many_association_test.rb", "test/has_one_association_test.rb", "test/model_association_test.rb", "test/models/address.rb", "test/models/author.rb", "test/models/country.rb", "test/models/project.rb", "test/models/projects_member.rb", "test/models/property_definition.rb", "test/models/simple.rb", "test/models/tag.rb", "test/models/topic.rb", "test/models/user.rb", "test/models/writer.rb", "test/schema.rb", "test/single_model_test.rb", "test/test_helper.rb", "test/with_scope_test.rb"]
|
10
|
+
|
11
|
+
#### Load-time details: library and application (you will need one or both).
|
12
|
+
|
13
|
+
spec.require_path = 'lib' # Use these for libraries.
|
14
|
+
|
15
|
+
#### Documentation and testing.
|
16
|
+
|
17
|
+
spec.has_rdoc = true
|
18
|
+
spec.extra_rdoc_files = ["README.rdoc", "LICENSE.txt", "TODO", "CHANGES"]
|
19
|
+
spec.rdoc_options = ["--line-numbers", "--inline-source", "--main", "README.rdoc", "--title", "Hickey"]
|
20
|
+
|
21
|
+
#### Author and project details.
|
22
|
+
|
23
|
+
spec.author = "Li Xiao"
|
24
|
+
spec.email = "iam@li-xiao.com"
|
25
|
+
spec.homepage = "http://github.com/xli/hickey/tree/master"
|
26
|
+
spec.rubyforge_project = "hickey"
|
27
|
+
end
|
data/lib/hickey.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'active_record'
|
4
|
+
rescue LoadError
|
5
|
+
require 'rubygems'
|
6
|
+
require 'active_record'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'hickey/acceptor'
|
10
|
+
require 'hickey/domain_detector'
|
11
|
+
|
12
|
+
module Hickey
|
13
|
+
VERSION = '0.0.2'
|
14
|
+
|
15
|
+
def dump(domain)
|
16
|
+
DomainDetector::Base.new.visit(domain)
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup(domain={})
|
20
|
+
DomainDetector::Base.configurations.merge! domain
|
21
|
+
end
|
22
|
+
|
23
|
+
module_function :dump, :setup
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hickey
|
2
|
+
module Acceptor
|
3
|
+
module Hash
|
4
|
+
def accept_for_hickey(klass, visitor)
|
5
|
+
visitor.visit_hash(klass, self)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
module Array
|
9
|
+
def accept_for_hickey(klass, visitor)
|
10
|
+
collect do |o|
|
11
|
+
o.accept_for_hickey(klass, visitor)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
module ReturnSelf
|
16
|
+
def accept_for_hickey(klass, visitor)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Array.send(:include, Hickey::Acceptor::Array)
|
24
|
+
Hash.send(:include, Hickey::Acceptor::Hash)
|
25
|
+
ActiveRecord::Base.send(:include, Hickey::Acceptor::ReturnSelf)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'hickey/domain_detector/base'
|
2
|
+
require 'hickey/domain_detector/associations'
|
3
|
+
require 'hickey/domain_detector/configurable'
|
4
|
+
require 'hickey/domain_detector/actions'
|
5
|
+
require 'hickey/domain_detector/scopes'
|
6
|
+
|
7
|
+
Hickey::DomainDetector::Base.class_eval do
|
8
|
+
include Hickey::DomainDetector::BelongsToAssociation
|
9
|
+
include Hickey::DomainDetector::HasOneAssociation
|
10
|
+
include Hickey::DomainDetector::HasOneThroughAssociation
|
11
|
+
include Hickey::DomainDetector::HasManyAssociation
|
12
|
+
include Hickey::DomainDetector::HasManyThroughAssociation
|
13
|
+
include Hickey::DomainDetector::HasAndBelongsToManyAssociation
|
14
|
+
include Hickey::DomainDetector::Configurable
|
15
|
+
include Hickey::DomainDetector::Scopes
|
16
|
+
include Hickey::DomainDetector::Actions
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Hickey
|
2
|
+
module DomainDetector
|
3
|
+
module Actions
|
4
|
+
def self.included(base)
|
5
|
+
base.send(:alias_method_chain, :visit_hash, :find_or_create_actions)
|
6
|
+
end
|
7
|
+
|
8
|
+
def visit_hash_with_find_or_create_actions(attribute, record)
|
9
|
+
case record.keys.first
|
10
|
+
when :find
|
11
|
+
record = record[:find]
|
12
|
+
attribute = find_action(attribute, record)
|
13
|
+
when :create
|
14
|
+
record = record[:create]
|
15
|
+
when :find_or_create
|
16
|
+
record = record[:find_or_create]
|
17
|
+
if find_attribute = find_action(attribute, record)
|
18
|
+
attribute = find_attribute
|
19
|
+
end
|
20
|
+
end
|
21
|
+
visit_hash_without_find_or_create_actions(attribute, record)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_action(attribute, record)
|
25
|
+
model_type = compute_type(attribute, record)
|
26
|
+
conditions = record.inject({}) do |c, entity|
|
27
|
+
key, value = entity
|
28
|
+
unless [Hash, Array].include?(value.class)
|
29
|
+
#added if to filter column name for fixing hickey on acitverecord 1.2.6
|
30
|
+
c[key] = value if model_type.column_names.include?(key.to_s)
|
31
|
+
end
|
32
|
+
c
|
33
|
+
end
|
34
|
+
compute_type(attribute, record).find(:first, :conditions => conditions)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Hickey
|
2
|
+
module DomainDetector
|
3
|
+
module BelongsToAssociation
|
4
|
+
def belongs_to(owner, reflection, attr_value)
|
5
|
+
association = attr_value.accept_for_hickey(reflection.klass, self)
|
6
|
+
owner.send("#{reflection.primary_key_name}=", association.id)
|
7
|
+
Proc.new{}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module HasOneAssociation
|
12
|
+
def has_one(owner, reflection, attr_value)
|
13
|
+
return has_one_through(owner, reflection, attr_value) if reflection.through_reflection
|
14
|
+
|
15
|
+
Proc.new do
|
16
|
+
attr_value[reflection.primary_key_name] = owner.id
|
17
|
+
if reflection.options[:as]
|
18
|
+
attr_value["#{reflection.options[:as]}_type"] = owner.class.base_class.name.to_s
|
19
|
+
end
|
20
|
+
attr_value.accept_for_hickey(reflection.klass, self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module HasOneThroughAssociation
|
26
|
+
def has_one_through(owner, reflection, attr_value)
|
27
|
+
through_reflection = reflection.through_reflection
|
28
|
+
Proc.new do
|
29
|
+
target = attr_value.accept_for_hickey(reflection.klass, self)
|
30
|
+
association = ActiveRecord::Associations::HasOneThroughAssociation.new(owner, reflection).send(:construct_join_attributes, target)
|
31
|
+
association.accept_for_hickey(through_reflection.klass, self)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module HasManyAssociation
|
37
|
+
def has_many(owner, reflection, attr_value)
|
38
|
+
return has_many_through(owner, reflection, attr_value) if reflection.through_reflection
|
39
|
+
|
40
|
+
Proc.new do
|
41
|
+
attr_value.each do |obj|
|
42
|
+
obj[reflection.primary_key_name] = owner.id
|
43
|
+
if reflection.options[:as]
|
44
|
+
obj["#{reflection.options[:as]}_type"] = owner.class.base_class.name.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
attr_value.accept_for_hickey(reflection.klass, self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module HasManyThroughAssociation
|
53
|
+
def has_many_through(owner, reflection, attr_value)
|
54
|
+
through_reflection = reflection.through_reflection
|
55
|
+
Proc.new do
|
56
|
+
owner_associations = owner.send(reflection.name)
|
57
|
+
target = attr_value.accept_for_hickey(reflection.klass, self)
|
58
|
+
target.each do |record|
|
59
|
+
association = owner_associations.send(:construct_join_attributes, record)
|
60
|
+
proxy = through_reflection.klass.send(:with_scope, :create => association) do
|
61
|
+
visit_hash(through_reflection.klass, {})
|
62
|
+
end
|
63
|
+
owner_associations.proxy_target << proxy
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module HasAndBelongsToManyAssociation
|
70
|
+
def has_and_belongs_to_many(owner, reflection, attr_value)
|
71
|
+
association = attr_value.accept_for_hickey(reflection.klass, self)
|
72
|
+
Proc.new do
|
73
|
+
association.each do |record|
|
74
|
+
owner.send(reflection.name).send(:insert_record, record)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Hickey
|
2
|
+
module DomainDetector
|
3
|
+
class Base
|
4
|
+
def visit(domain)
|
5
|
+
r = {}
|
6
|
+
domain.each do |key, value|
|
7
|
+
r[key] = value.accept_for_hickey(key, self)
|
8
|
+
end
|
9
|
+
r.size == 1 ? r.values.first : r
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_hash(attribute, record)
|
13
|
+
owner = new_instance(attribute, record)
|
14
|
+
after_created = []
|
15
|
+
|
16
|
+
record.each do |key, value|
|
17
|
+
if reflection = owner.class.reflections[key]
|
18
|
+
after_created << send(reflection.macro, owner, reflection, value)
|
19
|
+
else
|
20
|
+
owner.send :write_attribute, key, value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#bypass new alias method chain of save! method
|
25
|
+
owner.save_with_validation!
|
26
|
+
|
27
|
+
after_created.each(&:call)
|
28
|
+
owner
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def new_instance(class_or_instance, record)
|
33
|
+
return class_or_instance unless ['Class', 'String', 'Symbol'].include?(class_or_instance.class.name)
|
34
|
+
compute_type(class_or_instance, record).new
|
35
|
+
end
|
36
|
+
|
37
|
+
def compute_type(class_name, record)
|
38
|
+
klass = class_name.is_a?(Class) ? class_name : class_name.to_s.classify.constantize
|
39
|
+
if (subclass_name = record[klass.inheritance_column.to_sym]).blank?
|
40
|
+
klass
|
41
|
+
else
|
42
|
+
begin
|
43
|
+
subclass_name.to_s.classify.constantize
|
44
|
+
rescue NameError
|
45
|
+
raise ActiveRecord::SubclassNotFound,
|
46
|
+
"The single-table inheritance mechanism failed to locate the subclass: '#{subclass_name}'. " +
|
47
|
+
"This error is raised because the column '#{klass.inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
48
|
+
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
49
|
+
"or overwrite #{klass.name}.inheritance_column to use another column for that information."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|