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/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