hickey 0.0.2

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